Rev 9 | Go to most recent revision | 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"#define HW_VERSION 0x01#define SW_VERSION 0x01#ifndef NULL#define NULL ((void *)0)#endif#define DISPLAYS_ATTACHED 2#define INPUT_REFRESH 50#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_ROTARY_DATA 0x0a#define I2C_GET_BUTTON_DATA 0x0c#define USB_GET_VERSION 01#define USB_SET_LATCH 20#define USB_SET_DISPLAY1 21#define USB_SET_DISPLAY2 22#define USB_GET_INPUT 30void usbEventResetReady(void);static void calibrateOscillator(void);static void updateDisplay(uint8_t dis);static void updateInput();static void getDisplayVersion(uint8_t dis);struct display_type {uint8_t address;uint8_t value[10];uint16_t decpts;uint16_t version; // HB = HW, LB = SWint8_t rotary; // State of the rotary encoderuint8_t buttons; // State of the buttons} display[DISPLAYS_ATTACHED];static uint8_t usbReplyBuf[8];static uint8_t latchDisplay = 255;volatile uint8_t tmr0_ovf = 0;int main(void) {// calibration value from last timeuchar calibrationValue;calibrationValue = eeprom_read_byte(0);if(calibrationValue != 0xff){OSCCAL = calibrationValue;}/*DDR : 1 = Output, 0 = InputPORT: 1 = Pullup for Input, otherwise set outputPIN : Read input pinPB0 -PB1 - - USB D- Low SpeedPB2 - - USB D+PB3 - - SCL i2c bbPB4 - - SDA i2c bbPB5 -*/DDRB = 0B00000001;PORTB = 0B00000001;usbDeviceDisconnect();_delay_ms(500);usbDeviceConnect();systime = 0;uint32_t refresh = 0;sysclockInit();wdt_enable(WDTO_1S);usbInit();sei();// Setup the display data, blank each displayuint8_t i;for (i=0; i<DISPLAYS_ATTACHED; i++) {display[i].address = 0x26 + i;display[i].decpts = 0x00;uint8_t j;for (j=0; j<10; j++)display[i].value[j] = 0x0a;updateDisplay(i);getDisplayVersion(i);}for(;;){wdt_reset();usbPoll();// Latch requests from the the USB hostif (latchDisplay != 255) {updateDisplay(latchDisplay);latchDisplay = 255;}// Refresh time for getting user input dataif (systime > refresh) {refresh = systime + INPUT_REFRESH;updateInput();}}return 0;}static void getDisplayVersion(uint8_t dis) {uint8_t hw = 0x00;uint8_t sw = 0x00;i2cbb_Init();i2cbb_Start();i2cbb_Write( display[dis].address << 1 );i2cbb_Write( I2C_GET_VERSION );i2cbb_Stop();i2cbb_Start();i2cbb_Write( (display[dis].address << 1) + 1 );hw += (int8_t)i2cbb_Read(1);sw += (int8_t)i2cbb_Read(1);i2cbb_Stop();display[dis].version = ((uint16_t)hw << 8) | ((uint16_t)sw);}// Get the user input data from each display boardstatic void updateInput() {uint8_t i;for (i = 0; i < DISPLAYS_ATTACHED; i++) {// Request for the rotary datai2cbb_Init();i2cbb_Start();i2cbb_Write( display[i].address << 1 );i2cbb_Write( I2C_GET_ROTARY_DATA );i2cbb_Stop();// Receive rotary datai2cbb_Start();i2cbb_Write( (display[i].address << 1) + 1 );display[i].rotary += (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();}}// The the display digit display buffer to the board// 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) {cbi(PORTB, PB0);// 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();// Send the decimal pointif (rbi(display[dis].decpts, 15)) {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();}sbi(PORTB, PB0);}// 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;}// Sets the display boards digit buffer. Only on display// board is updated per request. Also does decimal pointscase USB_SET_DISPLAY1: {uint8_t dis = rq->wValue.bytes[1];uint8_t dig = rq->wValue.bytes[0];uint8_t dp = rq->wIndex.bytes[1];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);}if (dp) {sbi(display[dis].decpts, 1 << dig);} else {cbi(display[dis].decpts, 1 << dig);}break;}// Return the user input data all at once. Its populated from// buffered data from the updateInput() function.case USB_GET_INPUT: {uint8_t i;for (i=0; i<DISPLAYS_ATTACHED; i++) {usbReplyBuf[(i*2)] = display[i].buttons;usbReplyBuf[(i*2+1)] = display[i].rotary;display[i].rotary = 0;}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;}}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;}}