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 30void 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 = SWuint8_t config; // State of the config settingsuint8_t value[10];uint16_t decpts;uint8_t decpts_refresh; // Decimal point refreshint8_t outer; // State of the outer rotary encoderint8_t inner; // State of the inner rotary encoderuint8_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 timeuchar calibrationValue;calibrationValue = eeprom_read_byte(EEPROM_USBVCALVAL);if(calibrationValue != 0xff){OSCCAL = calibrationValue;}/*DDR : 1 = Output, 0 = InputPORT: 1 = Pullup for Input, otherwise set outputPIN : Read input pinPB0 - - LEDPB1 - - USB D- Low SpeedPB2 - - USB D+PB3 - - SCL i2c bbPB4 - - SDA i2c bbPB5 - - 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 >= 0x02xxif (display[i].version > 0x0200)display[i].config = getDisplayConfigData(display[i].address);elsedisplay[i].config = 0x00;}for(;;){wdt_reset();usbPoll();// Latch requests from the the USB hostif (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 dataif (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 datai2cbb_Init();i2cbb_Start();i2cbb_Write( i2c_addr << 1 );i2cbb_Write( I2C_GET_CONFIG_DATA );i2cbb_Stop();// Receive the button datai2cbb_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 thisstatic 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 modelif (display[i].version >= 0x200 &&rbi(display[i].config, 0)) {// Request for the inner rotary datai2cbb_Init();i2cbb_Start();i2cbb_Write( display[i].address << 1 );i2cbb_Write( I2C_GET_INNER_ROTARY );i2cbb_Stop();// Receive inner rotary datai2cbb_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 datai2cbb_Init();i2cbb_Start();i2cbb_Write( display[i].address << 1 );i2cbb_Write( I2C_GET_OUTER_ROTARY );i2cbb_Stop();// Receive outer rotary datai2cbb_Start();i2cbb_Write( (display[i].address << 1) + 1 );display[i].outer += (int8_t)i2cbb_Read(1);i2cbb_Stop();// Reset the rotary on display boardi2cbb_Init();i2cbb_Start();i2cbb_Write( display[i].address << 1 );i2cbb_Write( I2C_RESET_ROTARY );i2cbb_Stop();// Request the button datai2cbb_Init();i2cbb_Start();i2cbb_Write( display[i].address << 1 );i2cbb_Write( I2C_GET_BUTTON_DATA );i2cbb_Stop();// Receive the button datai2cbb_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 tasksstatic void updateDisplay(uint8_t dis) {// Send the display buffer to display boarduint8_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 updatedcase 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 pointscase 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, 160rif (tmr0_ovf>=64) {systime++;tmr0_ovf = 0;}}