Subversion Repositories group.electronics

Rev

Rev 77 | 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];

volatile struct {
        uint8_t button;
        uint8_t timer;
} buttons;


static uchar    idleRate;           /* in 4 ms units */
volatile uint8_t tmr0_ovf = 0;
volatile uint32_t systime = 0;

/* ------------------------------------------------------------------------- */

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, 0x08,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)

    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION
};


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;
}


static void calibrateOscillator(void) {
    uchar step = 128;
    uchar trialValue = 0, optimumValue;
    int x, optimumDev;
    int 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;
}

void usbEventResetReady(void) {
    cli();
    calibrateOscillator();
    sei();
    eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
}


void usbSendHidReport(uchar * data, uchar len) {
        usbSetInterrupt(data, len);
}


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

        TIMSK = (1<<TOIE0);                    // Enable timer overflow
        TCNT0 = 0x00;                           // Set Timer0 initial value to 0
        TCCR0B = (1<< CS01) ;                   // /1 prescaler

  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()){ 

                // Check if our buttons need pressing
                reportBuffer.buttons = 0;
                if (buttons.timer != 0) {
                        if (buttons.button == 1)
                                reportBuffer.b1 = 1;
                        else
                                reportBuffer.b2 = 1;
                }

                usbSendHidReport(&reportBuffer, sizeof(reportBuffer));
        }
    }
    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;

                        buttons.button = 1;
                        buttons.timer = 5;

        } 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;

                        buttons.button = 2;
                        buttons.timer = 5;
        }

        // Clear the mask so we know we've delth with it
        pcInt[pcint].mask = 0;
}

ISR(PCINT0_vect) {
        pcInterrupt(0);
}

ISR(TIMER0_OVF_vect) {
        tmr0_ovf++;
        if (tmr0_ovf>=50) {
                systime++;
                tmr0_ovf = 0;

                if (buttons.timer != 0) 
                        buttons.timer--;
        }
}