Rev 76 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/* Name: main.c
* Project: EasyLogger
* Author: Christian Starkjohann
* Creation Date: 2006-04-23
* Tabsize: 4
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
* License: Proprietary, free under certain conditions. See Documentation.
* This Revision: $Id$
*/
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"
#include "config.h"
#ifndef NULL
#define NULL ((void *)0)
#endif
/* ------------------------------------------------------------------------- */
struct {
union {
int16_t axis[3];
struct {
int16_t axis0:16;
int16_t axis1:16;
int16_t axis2:16;
};
uint8_t buttons;
struct {
int8_t b1:1;
int8_t b2:2;
int8_t reserved:6;
};
};
} reportBuffer;
volatile struct {
uint8_t current;
uint8_t last;
uint8_t mask;
} pcInt[1];
static uchar idleRate; /* in 4 ms units */
/* ------------------------------------------------------------------------- */
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x38, // USAGE (Y)
0x16, 0x00, 0x80, // Log Min -32768
0x26, 0xff, 0x7f, // Log max 32768
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x03, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x02, // USAGE_MAXIMUM (Button 2)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x06, // REPORT_COUNT (6)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
/* ------------------------------------------------------------------------- */
/* ------------------------ interface to USB driver ------------------------ */
/* ------------------------------------------------------------------------- */
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
//usbMsgPtr = reportBuffer;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){
if(rq->bRequest == USBRQ_HID_GET_REPORT){
return sizeof(reportBuffer);
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
}
return 0;
}
/* ------------------------------------------------------------------------- */
/* ------------------------ Oscillator Calibration ------------------------- */
/* ------------------------------------------------------------------------- */
/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
* derived from the 66 MHz peripheral clock by dividing. Our timing reference
* is the Start Of Frame signal (a single SE0 bit) available immediately after
* a USB RESET. We first do a binary search for the OSCCAL value and then
* optimize this value with a neighboorhod search.
* This algorithm may also be used to calibrate the RC oscillator directly to
* 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
* is wide outside the spec for the OSCCAL value and the required precision for
* the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
* experimental purposes only!
*/
static void calibrateOscillator(void)
{
uchar step = 128;
uchar trialValue = 0, optimumValue;
int x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
/* do a binary search: */
do{
OSCCAL = trialValue + step;
x = usbMeasureFrameLength(); /* proportional to current real frequency */
if(x < targetValue) /* frequency still too low */
trialValue += step;
step >>= 1;
}while(step > 0);
/* We have a precision of +/- 1 for optimum OSCCAL here */
/* now do a neighborhood search for optimum value */
optimumValue = trialValue;
optimumDev = x; /* this is certainly far away from optimum */
for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
x = usbMeasureFrameLength() - targetValue;
if(x < 0)
x = -x;
if(x < optimumDev){
optimumDev = x;
optimumValue = OSCCAL;
}
}
OSCCAL = optimumValue;
}
/*
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
the optimum value is far below 192. It may therefore exceed the allowed clock
frequency of the CPU in low voltage designs!
You may replace this search algorithm with any other algorithm you like if
you have additional constraints such as a maximum CPU clock.
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
both regions.
*/
void usbEventResetReady(void)
{
/* Disable interrupts during oscillator calibration since
* usbMeasureFrameLength() counts CPU cycles.
*/
cli();
calibrateOscillator();
sei();
eeprom_write_byte(0, OSCCAL); /* store the calibrated value in EEPROM */
}
void usbSendHidReport(uchar * data, uchar len) {
while(1)
{
usbPoll();
if (usbInterruptIsReady())
{
usbSetInterrupt(data, len);
break;
}
}
}
int main(void)
{
uchar i;
uchar calibrationValue;
calibrationValue = eeprom_read_byte(0); /* calibration value from last time */
if(calibrationValue != 0xff){
OSCCAL = calibrationValue;
}
odDebugInit();
usbDeviceDisconnect();
for(i=0;i<20;i++){ /* 300 ms disconnect */
_delay_ms(15);
}
usbDeviceConnect();
wdt_enable(WDTO_1S);
PCMSK |= (( 1 << PCINT3 ) | ( 1 << PCINT4 )); //enable encoder pins interrupt sources
GIMSK |= ( 1 << PCIE ); //enable pin change interupts
DDRB = 0B00000001;
PORTB = 0B00000000;
usbInit();
sei();
reportBuffer.axis0 = 0;
reportBuffer.axis1 = 0;
reportBuffer.axis2 = 0;
reportBuffer.buttons = 0;
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(usbInterruptIsReady()){
usbSendHidReport(&reportBuffer, sizeof(reportBuffer));
reportBuffer.buttons = 0;
}
}
return 0;
}
void pcInterrupt(uint8_t pcint) {
switch (pcint) {
case 0: pcInt[pcint].current = PINB; break;
}
pcInt[pcint].mask = pcInt[pcint].current ^ pcInt[pcint].last;
pcInt[pcint].last = pcInt[pcint].current;
if (pcInt[pcint].mask == 0)
return;
// Check which pin caused the interrupt. If they both
// equal 0, the pin that interrupted is the direction
if (rbi(pcInt[pcint].current, PCINT3) == 0
&& rbi(pcInt[pcint].current, PCINT4) == 0
&& rbi(pcInt[pcint].mask, PCINT4) ) {
if (reportBuffer.axis2 < 32500)
reportBuffer.axis2 += 200;
reportBuffer.b1 = 1;
} else if (rbi(pcInt[pcint].current, PCINT3) == 0
&& rbi(pcInt[pcint].current, PCINT4) == 0
&& rbi(pcInt[pcint].mask, PCINT3) ) {
if (reportBuffer.axis2 > -32500)
reportBuffer.axis2 -= 200;
reportBuffer.b2 = 1;
}
// Clear the mask so we know we've delth with it
pcInt[pcint].mask = 0;
}
ISR(PCINT0_vect) {
pcInterrupt(0);
}