Subversion Repositories group.NITPanels

Rev

Rev 10 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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 "config.h"
#include "avrutil.h"
#include "usbdrv.h"
#include "i2cbb.h"

#ifndef NULL
#define NULL    ((void *)0)
#endif


#define MAX_DISPLAYS                    4               // Address space for up to for displays
#define INPUT_REFRESH 50
#define FULL_REFRESH  10

#define I2C_BASE_ADDR                   0x26    // All display addresses start from here

#define I2C_GET_VERSION                 0x01
#define I2C_SET_DEBUG                   0x03
#define I2C_SET_DIGITS                  0x05
#define I2C_SET_DECIMAL_PTS             0x08
#define I2C_RESET_ROTARY                0x09
#define I2C_GET_OUTER_ROTARY    0x0a
#define I2C_GET_INNER_ROTARY    0x0b
#define I2C_GET_BUTTON_DATA     0x0c
#define I2C_GET_CONFIG_DATA     0x0d

#define USB_GET_VERSION                 01
#define USB_GET_DISPLAY_COUNT   10
#define USB_GET_DISPLAY_META    11

#define USB_SET_LATCH                   20
#define USB_SET_DISPLAY_DIGIT   21
#define USB_SET_POINTS                  23
#define USB_GET_INPUT                   30


void usbEventResetReady(void);
static void calibrateOscillator(void);
static void updateDecimals(uint8_t dis);
static void updateDisplay(uint8_t dis);
static void updateInput();
static uint16_t getDisplayVersion(uint8_t i2c_addr);
static uint8_t getDisplayConfigData(uint8_t i2c_addr);

struct display_type {
        uint8_t address;
        uint16_t version;       // HB = HW, LB = SW
        uint8_t config;         // State of the config settings

        uint8_t value[10];
        uint16_t decpts;
        uint8_t decpts_refresh; // Decimal point refresh

        int8_t outer;           // State of the outer rotary encoder
        int8_t inner;           // State of the inner rotary encoder
        uint8_t buttons;        // State of the buttons

} display[MAX_DISPLAYS];



static uint8_t usbReplyBuf[8];
static uint8_t latchDisplay = 255;

static uint8_t displays_attached = 0;


volatile uint8_t tmr0_ovf = 0;


int main(void) {
        // calibration value from last time
        uchar   calibrationValue;
        calibrationValue = eeprom_read_byte(EEPROM_USBVCALVAL);
        if(calibrationValue != 0xff){
                OSCCAL = calibrationValue;
        }

        /*
                DDR : 1 = Output, 0 = Input
                PORT: 1 = Pullup for Input, otherwise set output
                PIN : Read input pin

                PB0     -               - LED
                PB1     -               - USB D- Low Speed
                PB2     -               - USB D+
                PB3     -               - SCL i2c bb
                PB4     -               - SDA i2c bb
                PB5     -               - Reset
        */
        DDRB          = 0B00000001;
        PORTB         = 0B00000001;


    usbDeviceDisconnect();
    _delay_ms(500);
    usbDeviceConnect();

    systime = 0;
    uint32_t refresh = 0;
    sysclockInit();

    wdt_enable(WDTO_1S);
    usbInit();
    sei();

    uint8_t i;
    for (i=0; i<MAX_DISPLAYS; i++) {
        if (i2cbb_Sniff(I2C_BASE_ADDR + i))
                continue;

        displays_attached++;

        display[i].address = I2C_BASE_ADDR + i;
                display[i].decpts = 0x0000;
                display[i].decpts_refresh = 0;

                uint8_t j;
                for (j=0; j<10; j++)
                        display[i].value[j] = 0x01;
                updateDisplay(i);

                display[i].version = getDisplayVersion(display[i].address);

                // Only get the config if version >= 0x02xx
                if (display[i].version > 0x0200)
                        display[i].config = getDisplayConfigData(display[i].address);
                else
                        display[i].config = 0x00;

    }

    for(;;){
         wdt_reset();
         usbPoll();

        // Latch requests from the the USB host

        if (latchDisplay != 255) {
                updateDisplay(latchDisplay);
                latchDisplay = 255;
        }

        for (i=0; i<displays_attached; i++) {
                if (display[i].decpts_refresh) {
                        updateDecimals(i);
                                display[i].decpts_refresh = 0;
                }

        }

        // Refresh time for getting user input data
                if (systime > refresh) {
                        refresh = systime + INPUT_REFRESH;
                        updateInput();
                }
    }
    return 0;
}

static uint16_t getDisplayVersion(uint8_t i2c_addr) {
        uint8_t hw = 0x00;
        uint8_t sw = 0x00;

        i2cbb_Init();
        i2cbb_Start();
        i2cbb_Write( i2c_addr << 1 );
        i2cbb_Write( I2C_GET_VERSION );
        i2cbb_Stop();


        i2cbb_Start();
        i2cbb_Write( (i2c_addr << 1) + 1 );
        hw += (int8_t)i2cbb_Read(1);
        sw += (int8_t)i2cbb_Read(1);
        i2cbb_Stop();

        return ((uint16_t)hw << 8) | ((uint16_t)sw);
}

static uint8_t getDisplayConfigData(uint8_t i2c_addr) {
        uint8_t config = 0;
        // Request the button data
        i2cbb_Init();
        i2cbb_Start();
        i2cbb_Write( i2c_addr << 1 );
        i2cbb_Write( I2C_GET_CONFIG_DATA );
        i2cbb_Stop();

        // Receive the button data
        i2cbb_Start();
        i2cbb_Write( (i2c_addr << 1) + 1 );
        config = i2cbb_Read(1);
        i2cbb_Stop();

        return config;
}

// Get the user input data from each display board
// @TODO: Changes to display board are needed to reduce
//                      the amount of i2c calls neede to retrieve all
//                      the button data. One i2c command should be able
//                      to do all of this
static void updateInput() {
        uint8_t i;
        for (i = 0; i < displays_attached; i++) {

                // Only check the inner rotary if HW version > 2 and
                //  the config tells us this is a PRO model
                if (display[i].version >= 0x200 &&
                                rbi(display[i].config, 0)) {
                        // Request for the inner rotary data
                        i2cbb_Init();
                        i2cbb_Start();
                        i2cbb_Write( display[i].address << 1 );
                        i2cbb_Write( I2C_GET_INNER_ROTARY );
                        i2cbb_Stop();

                        // Receive inner rotary data
                        i2cbb_Start();
                        i2cbb_Write( (display[i].address << 1) + 1 );
                        display[i].inner += (int8_t)i2cbb_Read(1);
                        i2cbb_Stop();
                } else {
                        display[i].inner = 0x00;
                }

                // Request for the outer rotary data
                i2cbb_Init();
                i2cbb_Start();
                i2cbb_Write( display[i].address << 1 );
                i2cbb_Write( I2C_GET_OUTER_ROTARY );
                i2cbb_Stop();

                // Receive outer rotary data
                i2cbb_Start();
                i2cbb_Write( (display[i].address << 1) + 1 );
                display[i].outer += (int8_t)i2cbb_Read(1);
                i2cbb_Stop();

                // Reset the rotary on display board
                i2cbb_Init();
                i2cbb_Start();
                i2cbb_Write( display[i].address << 1 );
                i2cbb_Write( I2C_RESET_ROTARY );
                i2cbb_Stop();

                // Request the button data
                i2cbb_Init();
                i2cbb_Start();
                i2cbb_Write( display[i].address << 1 );
                i2cbb_Write( I2C_GET_BUTTON_DATA );
                i2cbb_Stop();

                // Receive the button data
                i2cbb_Start();
                i2cbb_Write( (display[i].address << 1) + 1 );
                display[i].buttons = i2cbb_Read(1);
                i2cbb_Stop();
        }

}

static void updateDecimals(uint8_t dis) {
        i2cbb_Init();
        i2cbb_Start();
        i2cbb_Write( display[dis].address << 1 );
        i2cbb_Write( I2C_SET_DECIMAL_PTS );
        i2cbb_Write((uint8_t)(display[dis].decpts>>8));
        i2cbb_Write((uint8_t)display[dis].decpts);
        i2cbb_Stop();
}

// We can select which display to update as this can
//  get slow if updates are being done all the time,
//  which might affect the user input data tasks
static void updateDisplay(uint8_t dis) {
    // Send the display buffer to display board
    uint8_t update = 0;
    uint8_t n;
    for (n=0; n<10; n++) {
        if (rbi(display[dis].value[n], 7)) {
                update = 1;
                break;
        }
    }

    if (!update) return;

    i2cbb_Init();
    i2cbb_Start();
    i2cbb_Write( display[dis].address << 1);
    i2cbb_Write( I2C_SET_DIGITS );

    for (n=0; n<10; n++) {
        if (rbi(display[dis].value[n], 7)) {
                cbi(display[dis].value[n], 7);
                uint8_t send = (n << 4) | display[dis].value[n];
                i2cbb_Write( send );
        }
    }
    i2cbb_Stop();
}

// The USB functions to transmit/receive data from USB host.
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
    usbRequest_t    *rq = (void *)data;

        switch (rq->bRequest ) {
                // Request for a display boards digits to be updated
                case USB_SET_LATCH: {
                        latchDisplay = rq->wValue.bytes[0];
                        break;
                }

                case USB_SET_POINTS: {
                        if (display[0].decpts != rq->wValue.word) {
                                display[0].decpts_refresh = 1;
                                display[0].decpts = rq->wValue.word;
                        }

                        if (display[1].decpts != rq->wIndex.word) {
                                display[1].decpts_refresh = 1;
                                display[1].decpts = rq->wIndex.word;
                        }
                }

                // Sets the display boards digit buffer. Only on display
                //  board is updated per request. Also does decimal points
                case USB_SET_DISPLAY_DIGIT: {
                        uint8_t dis = rq->wValue.bytes[1];
                        uint8_t dig = rq->wValue.bytes[0];
                        uint8_t val = rq->wIndex.bytes[0];

                        if ((display[dis].value[dig] & 0x0f) != val) {
                                display[dis].value[dig] = val;
                                sbi(display[dis].value[dig], 7);
                        }
                        break;
                }

                // Return the user input data all at once. Its populated from
                //  buffered data from the updateInput() function.
                case USB_GET_INPUT: {
                        uint8_t disnum = rq->wValue.bytes[0];

                        usbReplyBuf[0] = display[disnum].buttons;
                        usbReplyBuf[1] = display[disnum].outer;
                        usbReplyBuf[2] = display[disnum].inner;

                        usbMsgPtr = usbReplyBuf;
                        return sizeof(usbReplyBuf);
                        break;
                }

                // Return the version numbers for the controller board
                //  and for all attached display boards.
                case USB_GET_VERSION: {
                        usbReplyBuf[0] = HW_VERSION;
                        usbReplyBuf[1] = SW_VERSION;
                        //uint8_t i;
                        //for (i=0; i<displays_attached; i++) {
                        //      usbReplyBuf[2+(i*2)] = (uint8_t)(display[i].version >> 8);
                        //      usbReplyBuf[2+(i*2)+1] = (uint8_t)(display[i].version && 0xff);
                        //}
                        usbMsgPtr = usbReplyBuf;
                        return sizeof(usbReplyBuf);
                        break;
                }

                case USB_GET_DISPLAY_COUNT: {
                        usbReplyBuf[0] = displays_attached;
                        usbMsgPtr = usbReplyBuf;
                        return sizeof(usbReplyBuf);
                        break;
                }

                case USB_GET_DISPLAY_META: {
                        uint8_t disnum = rq->wValue.bytes[0];
                        usbReplyBuf[0] = (uint8_t)(display[disnum].version >> 8);
                        usbReplyBuf[1] = (uint8_t)(display[disnum].version  & 0xff);
                        usbReplyBuf[2] = display[disnum].address;
                        usbReplyBuf[3] = display[disnum].config;
                        usbMsgPtr = usbReplyBuf;
                        return sizeof(usbReplyBuf);
                        break;
                }

        }
        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 */
}

ISR(TIM0_OVF_vect) {

        tmr0_ovf++;

        // Clk/1 TCCR0B = (1<< CS00);
        //20.0Mhz, 1ms = 78ovf
        //16.5Mhz, 1ms = 64ovf
        //16.0Mhz, 1ms = 62ovf
        //12.0Mhz, 1ms = 46ovf
        // 8.0Mhz, 1ms = 31ovf
        // 8.0Mhz, .5ms = 15ovf, 160r

        if (tmr0_ovf>=64) {
                        systime++;
                        tmr0_ovf = 0;
        }

}