Subversion Repositories group.NITPanels

Rev

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

Rev Author Line No. Line
2 pfowler 1
#include <avr/io.h>
2
#include <avr/wdt.h>
3
#include <avr/eeprom.h>
4
#include <avr/interrupt.h>
5
#include <avr/pgmspace.h>
6
#include <util/delay.h>
7
 
8
#include "config.h"
9
#include "avrutil.h"
10
#include "usbdrv.h"
11
#include "i2cbb.h"
12
 
13
#ifndef NULL
14
#define NULL    ((void *)0)
15
#endif
16
 
26 pfowler 17
 
18
#define MAX_DISPLAYS 			4		// Address space for up to for displays
2 pfowler 19
#define INPUT_REFRESH 50
9 pfowler 20
#define FULL_REFRESH  10
2 pfowler 21
 
26 pfowler 22
#define I2C_BASE_ADDR			0x26	// All display addresses start from here
23
 
2 pfowler 24
#define I2C_GET_VERSION			0x01
25
#define I2C_SET_DEBUG			0x03
26
#define I2C_SET_DIGITS			0x05
27
#define I2C_SET_DECIMAL_PTS		0x08
28
#define I2C_RESET_ROTARY		0x09
26 pfowler 29
#define I2C_GET_OUTER_ROTARY 	0x0a
30
#define I2C_GET_INNER_ROTARY 	0x0b
2 pfowler 31
#define I2C_GET_BUTTON_DATA 	0x0c
26 pfowler 32
#define I2C_GET_CONFIG_DATA 	0x0d
2 pfowler 33
 
26 pfowler 34
#define USB_GET_VERSION			01
35
#define USB_GET_DISPLAY_COUNT	10
36
#define USB_GET_DISPLAY_META	11
37
 
2 pfowler 38
#define USB_SET_LATCH			20
26 pfowler 39
#define USB_SET_DISPLAY_DIGIT	21
9 pfowler 40
#define USB_SET_POINTS			23
2 pfowler 41
#define USB_GET_INPUT			30
42
 
43
 
44
void usbEventResetReady(void);
45
static void calibrateOscillator(void);
10 pfowler 46
static void updateDecimals(uint8_t dis);
2 pfowler 47
static void updateDisplay(uint8_t dis);
48
static void updateInput();
26 pfowler 49
static uint16_t getDisplayVersion(uint8_t i2c_addr);
50
static uint8_t getDisplayConfigData(uint8_t i2c_addr);
2 pfowler 51
 
52
struct display_type {
53
	uint8_t address;
26 pfowler 54
	uint16_t version;	// HB = HW, LB = SW
55
	uint8_t config;		// State of the config settings
56
 
2 pfowler 57
	uint8_t value[10];
58
	uint16_t decpts;
26 pfowler 59
	uint8_t decpts_refresh;	// Decimal point refresh
9 pfowler 60
 
26 pfowler 61
	int8_t outer;		// State of the outer rotary encoder
62
	int8_t inner;		// State of the inner rotary encoder
2 pfowler 63
	uint8_t buttons;	// State of the buttons
64
 
26 pfowler 65
} display[MAX_DISPLAYS];
2 pfowler 66
 
67
 
26 pfowler 68
 
2 pfowler 69
static uint8_t usbReplyBuf[8];
70
static uint8_t latchDisplay = 255;
71
 
26 pfowler 72
static uint8_t displays_attached = 0;
2 pfowler 73
 
26 pfowler 74
 
2 pfowler 75
volatile uint8_t tmr0_ovf = 0;
76
 
77
 
78
int main(void) {
79
	// calibration value from last time
80
	uchar   calibrationValue;
26 pfowler 81
	calibrationValue = eeprom_read_byte(EEPROM_USBVCALVAL);
2 pfowler 82
	if(calibrationValue != 0xff){
83
		OSCCAL = calibrationValue;
84
	}
85
 
86
	/*
87
		DDR : 1 = Output, 0 = Input
88
		PORT: 1 = Pullup for Input, otherwise set output
89
		PIN : Read input pin
90
 
26 pfowler 91
		PB0	-		- LED
2 pfowler 92
		PB1	- 		- USB D- Low Speed
93
		PB2	-		- USB D+
94
		PB3	- 		- SCL i2c bb
95
		PB4	-		- SDA i2c bb
26 pfowler 96
		PB5	-		- Reset
2 pfowler 97
	*/
98
	DDRB          = 0B00000001;
99
	PORTB         = 0B00000001;
100
 
101
 
102
    usbDeviceDisconnect();
103
    _delay_ms(500);
104
    usbDeviceConnect();
105
 
106
    systime = 0;
107
    uint32_t refresh = 0;
108
    sysclockInit();
109
 
110
    wdt_enable(WDTO_1S);
111
    usbInit();
112
    sei();
113
 
114
    uint8_t i;
26 pfowler 115
    for (i=0; i<MAX_DISPLAYS; i++) {
116
    	if (i2cbb_Sniff(I2C_BASE_ADDR + i))
117
    		continue;
2 pfowler 118
 
26 pfowler 119
    	displays_attached++;
2 pfowler 120
 
26 pfowler 121
    	display[i].address = I2C_BASE_ADDR + i;
122
		display[i].decpts = 0x0000;
123
		display[i].decpts_refresh = 0;
124
 
125
		uint8_t j;
126
		for (j=0; j<10; j++)
127
			display[i].value[j] = 0x01;
128
		updateDisplay(i);
129
 
130
		display[i].version = getDisplayVersion(display[i].address);
131
 
132
		// Only get the config if version >= 0x02xx
133
		if (display[i].version > 0x0200)
134
			display[i].config = getDisplayConfigData(display[i].address);
135
		else
136
			display[i].config = 0x00;
137
 
2 pfowler 138
    }
139
 
140
    for(;;){
141
    	 wdt_reset();
142
    	 usbPoll();
143
 
144
    	// Latch requests from the the USB host
26 pfowler 145
 
2 pfowler 146
    	if (latchDisplay != 255) {
147
    		updateDisplay(latchDisplay);
148
    		latchDisplay = 255;
149
    	}
150
 
26 pfowler 151
    	for (i=0; i<displays_attached; i++) {
152
    		if (display[i].decpts_refresh) {
10 pfowler 153
    			updateDecimals(i);
26 pfowler 154
				display[i].decpts_refresh = 0;
9 pfowler 155
    		}
156
 
157
    	}
158
 
2 pfowler 159
    	// Refresh time for getting user input data
160
		if (systime > refresh) {
161
			refresh = systime + INPUT_REFRESH;
162
			updateInput();
163
		}
164
    }
165
    return 0;
166
}
167
 
26 pfowler 168
static uint16_t getDisplayVersion(uint8_t i2c_addr) {
2 pfowler 169
	uint8_t hw = 0x00;
170
	uint8_t sw = 0x00;
171
 
172
	i2cbb_Init();
173
	i2cbb_Start();
26 pfowler 174
	i2cbb_Write( i2c_addr << 1 );
2 pfowler 175
	i2cbb_Write( I2C_GET_VERSION );
176
	i2cbb_Stop();
177
 
26 pfowler 178
 
2 pfowler 179
	i2cbb_Start();
26 pfowler 180
	i2cbb_Write( (i2c_addr << 1) + 1 );
2 pfowler 181
	hw += (int8_t)i2cbb_Read(1);
182
	sw += (int8_t)i2cbb_Read(1);
183
	i2cbb_Stop();
184
 
26 pfowler 185
	return ((uint16_t)hw << 8) | ((uint16_t)sw);
2 pfowler 186
}
187
 
26 pfowler 188
static uint8_t getDisplayConfigData(uint8_t i2c_addr) {
189
	uint8_t config = 0;
190
	// Request the button data
191
	i2cbb_Init();
192
	i2cbb_Start();
193
	i2cbb_Write( i2c_addr << 1 );
194
	i2cbb_Write( I2C_GET_CONFIG_DATA );
195
	i2cbb_Stop();
196
 
197
	// Receive the button data
198
	i2cbb_Start();
199
	i2cbb_Write( (i2c_addr << 1) + 1 );
200
	config = i2cbb_Read(1);
201
	i2cbb_Stop();
202
 
203
	return config;
204
}
205
 
2 pfowler 206
// Get the user input data from each display board
26 pfowler 207
// @TODO: Changes to display board are needed to reduce
208
//			the amount of i2c calls neede to retrieve all
209
//			the button data. One i2c command should be able
210
// 			to do all of this
2 pfowler 211
static void updateInput() {
212
	uint8_t i;
26 pfowler 213
	for (i = 0; i < displays_attached; i++) {
214
 
215
		// Only check the inner rotary if HW version > 2 and
216
		//  the config tells us this is a PRO model
217
		if (display[i].version >= 0x200 &&
218
				rbi(display[i].config, 0)) {
219
			// Request for the inner rotary data
220
			i2cbb_Init();
221
			i2cbb_Start();
222
			i2cbb_Write( display[i].address << 1 );
223
			i2cbb_Write( I2C_GET_INNER_ROTARY );
224
			i2cbb_Stop();
225
 
226
			// Receive inner rotary data
227
			i2cbb_Start();
228
			i2cbb_Write( (display[i].address << 1) + 1 );
229
			display[i].inner += (int8_t)i2cbb_Read(1);
230
			i2cbb_Stop();
231
		} else {
232
			display[i].inner = 0x00;
233
		}
234
 
235
		// Request for the outer rotary data
2 pfowler 236
		i2cbb_Init();
237
		i2cbb_Start();
238
		i2cbb_Write( display[i].address << 1 );
26 pfowler 239
		i2cbb_Write( I2C_GET_OUTER_ROTARY );
2 pfowler 240
		i2cbb_Stop();
241
 
26 pfowler 242
		// Receive outer rotary data
2 pfowler 243
		i2cbb_Start();
244
		i2cbb_Write( (display[i].address << 1) + 1 );
26 pfowler 245
		display[i].outer += (int8_t)i2cbb_Read(1);
2 pfowler 246
		i2cbb_Stop();
247
 
248
		// Reset the rotary on display board
249
		i2cbb_Init();
250
		i2cbb_Start();
251
		i2cbb_Write( display[i].address << 1 );
252
		i2cbb_Write( I2C_RESET_ROTARY );
253
		i2cbb_Stop();
254
 
255
		// Request the button data
256
		i2cbb_Init();
257
		i2cbb_Start();
258
		i2cbb_Write( display[i].address << 1 );
259
		i2cbb_Write( I2C_GET_BUTTON_DATA );
260
		i2cbb_Stop();
261
 
262
		// Receive the button data
263
		i2cbb_Start();
264
		i2cbb_Write( (display[i].address << 1) + 1 );
265
		display[i].buttons = i2cbb_Read(1);
266
		i2cbb_Stop();
267
	}
268
 
269
}
270
 
10 pfowler 271
static void updateDecimals(uint8_t dis) {
272
	i2cbb_Init();
273
	i2cbb_Start();
274
	i2cbb_Write( display[dis].address << 1 );
275
	i2cbb_Write( I2C_SET_DECIMAL_PTS );
276
	i2cbb_Write((uint8_t)(display[dis].decpts>>8));
277
	i2cbb_Write((uint8_t)display[dis].decpts);
278
	i2cbb_Stop();
279
}
280
 
2 pfowler 281
// We can select which display to update as this can
282
//  get slow if updates are being done all the time,
283
//  which might affect the user input data tasks
284
static void updateDisplay(uint8_t dis) {
285
    // Send the display buffer to display board
286
    uint8_t update = 0;
287
    uint8_t n;
288
    for (n=0; n<10; n++) {
289
    	if (rbi(display[dis].value[n], 7)) {
290
    		update = 1;
291
    		break;
292
    	}
293
    }
9 pfowler 294
 
2 pfowler 295
    if (!update) return;
296
 
297
    i2cbb_Init();
298
    i2cbb_Start();
299
    i2cbb_Write( display[dis].address << 1);
300
    i2cbb_Write( I2C_SET_DIGITS );
301
 
302
    for (n=0; n<10; n++) {
303
    	if (rbi(display[dis].value[n], 7)) {
304
    		cbi(display[dis].value[n], 7);
305
    		uint8_t send = (n << 4) | display[dis].value[n];
306
    		i2cbb_Write( send );
307
    	}
308
    }
309
    i2cbb_Stop();
310
}
311
 
312
// The USB functions to transmit/receive data from USB host.
313
usbMsgLen_t usbFunctionSetup(uchar data[8])
314
{
315
    usbRequest_t    *rq = (void *)data;
316
 
317
	switch (rq->bRequest ) {
318
		// Request for a display boards digits to be updated
319
		case USB_SET_LATCH: {
26 pfowler 320
			latchDisplay = rq->wValue.bytes[0];
2 pfowler 321
			break;
322
		}
323
 
9 pfowler 324
		case USB_SET_POINTS: {
10 pfowler 325
			if (display[0].decpts != rq->wValue.word) {
26 pfowler 326
				display[0].decpts_refresh = 1;
10 pfowler 327
				display[0].decpts = rq->wValue.word;
328
			}
9 pfowler 329
 
10 pfowler 330
			if (display[1].decpts != rq->wIndex.word) {
26 pfowler 331
				display[1].decpts_refresh = 1;
10 pfowler 332
				display[1].decpts = rq->wIndex.word;
333
			}
9 pfowler 334
		}
335
 
2 pfowler 336
		// Sets the display boards digit buffer. Only on display
337
		//  board is updated per request. Also does decimal points
26 pfowler 338
		case USB_SET_DISPLAY_DIGIT: {
2 pfowler 339
			uint8_t dis = rq->wValue.bytes[1];
340
			uint8_t dig = rq->wValue.bytes[0];
341
			uint8_t val = rq->wIndex.bytes[0];
342
 
343
			if ((display[dis].value[dig] & 0x0f) != val) {
344
				display[dis].value[dig] = val;
345
				sbi(display[dis].value[dig], 7);
346
			}
347
			break;
348
		}
349
 
350
		// Return the user input data all at once. Its populated from
351
		//  buffered data from the updateInput() function.
352
		case USB_GET_INPUT: {
26 pfowler 353
			uint8_t disnum = rq->wValue.bytes[0];
354
 
355
			usbReplyBuf[0] = display[disnum].buttons;
356
			usbReplyBuf[1] = display[disnum].outer;
357
			usbReplyBuf[2] = display[disnum].inner;
358
 
2 pfowler 359
			usbMsgPtr = usbReplyBuf;
360
			return sizeof(usbReplyBuf);
361
			break;
362
		}
363
 
364
		// Return the version numbers for the controller board
365
		//  and for all attached display boards.
366
		case USB_GET_VERSION: {
367
			usbReplyBuf[0] = HW_VERSION;
368
			usbReplyBuf[1] = SW_VERSION;
26 pfowler 369
			//uint8_t i;
370
			//for (i=0; i<displays_attached; i++) {
371
			//	usbReplyBuf[2+(i*2)] = (uint8_t)(display[i].version >> 8);
372
			//	usbReplyBuf[2+(i*2)+1] = (uint8_t)(display[i].version && 0xff);
373
			//}
2 pfowler 374
			usbMsgPtr = usbReplyBuf;
375
			return sizeof(usbReplyBuf);
376
			break;
377
		}
378
 
26 pfowler 379
		case USB_GET_DISPLAY_COUNT: {
380
			usbReplyBuf[0] = displays_attached;
381
			usbMsgPtr = usbReplyBuf;
382
			return sizeof(usbReplyBuf);
383
			break;
384
		}
385
 
386
		case USB_GET_DISPLAY_META: {
387
			uint8_t disnum = rq->wValue.bytes[0];
388
			usbReplyBuf[0] = (uint8_t)(display[disnum].version >> 8);
389
			usbReplyBuf[1] = (uint8_t)(display[disnum].version  & 0xff);
390
			usbReplyBuf[2] = display[disnum].address;
391
			usbReplyBuf[3] = display[disnum].config;
392
			usbMsgPtr = usbReplyBuf;
393
			return sizeof(usbReplyBuf);
394
			break;
395
		}
396
 
2 pfowler 397
	}
398
	return 0;
399
}
400
 
401
static void calibrateOscillator(void) {
402
    uchar step = 128;
403
    uchar trialValue = 0, optimumValue;
404
    int x, optimumDev;
405
    int targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
406
 
407
    /* do a binary search: */
408
    do {
409
        OSCCAL = trialValue + step;
410
        x = usbMeasureFrameLength();    /* proportional to current real frequency */
411
        if(x < targetValue)             /* frequency still too low */
412
            trialValue += step;
413
        step >>= 1;
414
    } while(step > 0);
415
    /* We have a precision of +/- 1 for optimum OSCCAL here */
416
    /* now do a neighborhood search for optimum value */
417
    optimumValue = trialValue;
418
    optimumDev = x; /* this is certainly far away from optimum */
419
    for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
420
        x = usbMeasureFrameLength() - targetValue;
421
        if(x < 0)
422
            x = -x;
423
        if(x < optimumDev){
424
            optimumDev = x;
425
            optimumValue = OSCCAL;
426
        }
427
    }
428
    OSCCAL = optimumValue;
429
}
430
 
431
 
432
void usbEventResetReady(void) {
433
    cli();
434
    calibrateOscillator();
435
    sei();
436
    eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
437
}
438
 
439
ISR(TIM0_OVF_vect) {
440
 
441
	tmr0_ovf++;
442
 
443
	// Clk/1 TCCR0B = (1<< CS00);
444
	//20.0Mhz, 1ms = 78ovf
445
	//16.5Mhz, 1ms = 64ovf
446
	//16.0Mhz, 1ms = 62ovf
447
	//12.0Mhz, 1ms = 46ovf
448
	// 8.0Mhz, 1ms = 31ovf
449
	// 8.0Mhz, .5ms = 15ovf, 160r
450
 
451
	if (tmr0_ovf>=64) {
452
			systime++;
453
			tmr0_ovf = 0;
454
	}
455
 
456
}