1 /*****************************************************************************
\r
2 ** FILE IDENTIFICATION
\r
4 ** Name: serial_lcd_obj.cpp
\r
5 ** Purpose: Serial backback firmware with C++ objects
\r
6 ** Programmer: Kevin Rosenberg <kevin@rosenberg.net> (AVR Freaks member kmr)
\r
7 ** Date Started: Dec 2007
\r
9 ** Copyright (c) 2007-2008 by Kevin Rosenberg. All rights reserved.
\r
11 ** Compilers supported:
\r
12 ** - WinAVR-20071221
\r
16 ** See accompaning LICENSE file
\r
19 ** See accompaning CHANGES file for modifications log from original
\r
20 ******************************************************************************/
\r
23 #include "serial_lcd.h"
\r
25 //// Classes with only static methods
\r
29 INLINE_FUNC_DECLARE(static void init(void)) {
\r
31 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
32 WDTCSR = (1<<WDE)|(1<<WDP3)|(1<<WDP0); // 1024K cycles, 8.0sec
\r
34 INLINE_FUNC_DECLARE(static void off(void)) {
\r
37 MCUSR &= ~(1<<WDRF); // clear any watchdog interrupt flags
\r
38 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
39 WDTCSR &= ~(1<<WDE); // watchdog timer off
\r
43 INLINE_FUNC_DECLARE(static void on(void)) {
\r
46 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
47 WDTCSR &= ~(1<<WDCE);
\r
51 INLINE_FUNC_DECLARE(static void reset(void)) {
\r
59 INLINE_FUNC_DECLARE(static void initIdleMode(void)) {
\r
60 MCUCR &= ~((1<<SM1)|(1<<SM0)); // use idle sleep mode
\r
62 INLINE_FUNC_DECLARE(static void enterSleep()) {
\r
71 INLINE_FUNC_DECLARE(static void millisec(double ms)) {
\r
74 INLINE_FUNC_DECLARE(static void microsec(double ms)) {
\r
80 //// Classes with non-static functions
\r
84 static const unsigned char m_LED_BRIGHTNESS_LEVELS = 8;
\r
85 static const prog_uint8_t m_ledPwmPatterns[];
\r
87 volatile unsigned char m_ledPwmCount;
\r
88 volatile unsigned char m_ledPwmCycling;
\r
89 unsigned char m_ledPwmPattern;
\r
90 unsigned char m_BIT_led_on;
\r
91 volatile unsigned char * m_LedPort;
\r
92 volatile unsigned char * m_LedDir;
\r
93 unsigned char m_LedPin;
\r
96 INLINE_FUNC_DECLARE(void init(volatile unsigned char* port,
\r
97 volatile unsigned char* dir,
\r
98 unsigned char pin)) {
\r
103 // setup PWM to run at 1.25ms per interrupt.
\r
104 // This allows 8 levels of brightness at minimum of 100Hz flicker rate.
\r
105 TCCR0A = (1<<WGM01); // CTC mode
\r
106 TCCR0B = 0; // timer off
\r
107 OCR0A = 72; // 1.25ms with CLK/256 prescaler @ 14.7456MHz
\r
108 TIMSK = (1<<OCIE0A); // Turn on timer0 COMPA intr (all other timer intr off)
\r
109 *m_LedPort &= ~(1<<m_LedPin); // Ensure LED is off during initialization
\r
110 *m_LedDir |= (1<<m_LedPin); // Ensure LED is output port
\r
111 m_BIT_led_on = 0; // note that LED is off
\r
112 m_ledPwmPattern = 0xFF; // maximum brightness
\r
115 INLINE_FUNC_DECLARE(void stop(void)) {
\r
118 INLINE_FUNC_DECLARE(void start(void)) {
\r
119 TCCR0B = (1<<CS02);
\r
122 INLINE_FUNC_DECLARE(void switchOff(void)) {
\r
128 INLINE_FUNC_DECLARE(void switchOn(void)) {
\r
130 if (m_ledPwmPattern == 0xFF) { // maximum brightness, no need for PWM
\r
138 INLINE_FUNC_DECLARE(void setBrightness(unsigned char brightness)) {
\r
139 if (brightness == 0) { // turn backlight off for 0 brightness
\r
140 if (m_BIT_led_on) {
\r
142 m_ledPwmPattern = 0;
\r
143 m_ledPwmCycling = 0;
\r
149 unsigned char ledPwmPos = (brightness * (m_LED_BRIGHTNESS_LEVELS-1) + 127) >> 8;
\r
150 // Below is probably not required, but ensures we don't exceed array
\r
151 if (ledPwmPos > m_LED_BRIGHTNESS_LEVELS - 1)
\r
152 ledPwmPos = m_LED_BRIGHTNESS_LEVELS - 1;
\r
153 m_ledPwmPattern = pwmPattern(ledPwmPos);
\r
155 m_ledPwmCycling = m_ledPwmPattern;
\r
156 if (ledPwmPos >= m_LED_BRIGHTNESS_LEVELS-1) { // maximum brightness
\r
157 // don't need PWM to continuously on
\r
158 if (m_BIT_led_on) {
\r
163 if (m_BIT_led_on) {
\r
169 INLINE_FUNC_DECLARE(void cyclePwm(void)) {
\r
170 if (m_ledPwmCycling & 0x01) { // Set current LED state based on cycling variable
\r
176 // Update cycling variable for next interrupt
\r
177 if (m_ledPwmCount >= m_LED_BRIGHTNESS_LEVELS-1) {
\r
179 m_ledPwmCycling = m_ledPwmPattern;
\r
182 m_ledPwmCycling >>= 1;
\r
186 INLINE_FUNC_DECLARE(void lampOn(void)) {
\r
187 *m_LedPort |= (1<<m_LedPin);
\r
190 INLINE_FUNC_DECLARE(void lampOff(void)) {
\r
191 *m_LedPort &= ~(1<<m_LedPin);;
\r
194 INLINE_FUNC_DECLARE(unsigned char pwmPattern(unsigned char pos)) {
\r
195 return PGM_READ_BYTE (&m_ledPwmPatterns[pos]);
\r
199 // PWM Patterns (8-brightness levels)
\r
200 const prog_uint8_t LedPwm::m_ledPwmPatterns[] = {
\r
201 0x10, // 0b00010000 0
\r
202 0x11, // 0b00010001 1
\r
203 0x4A, // 0b01001010 2
\r
204 0x55, // 0b01010101 3
\r
205 0x5D, // 0b01011101 4
\r
206 0xEE, // 0b11101110 5
\r
207 0x7F, // 0b11110111 6
\r
208 0xFF, // 0b11111111 7
\r
214 // Turn on power to the display, no cursor
\r
215 static const unsigned char m_PWR_ON = 0x0C;
\r
216 // Clear display command
\r
217 static const unsigned char m_CLR_DSP = 0x01;
\r
218 // Set 8 data bits, 4 display lines
\r
219 static const unsigned char m_LINE_8x4 = 0x38;
\r
221 static const unsigned char m_DATA_8 = 0x30;
\r
223 volatile unsigned char * m_ctrlPort;
\r
224 volatile unsigned char * m_ctrlDir;
\r
225 volatile unsigned char * m_dataPort;
\r
226 volatile unsigned char * m_dataDir;
\r
227 volatile unsigned char * m_dataPinReg;
\r
228 unsigned char m_rsPin;
\r
229 unsigned char m_rwPin;
\r
230 unsigned char m_ePin;
\r
232 unsigned char busyWait(void);
\r
235 INLINE_FUNC_DECLARE(void init(volatile unsigned char* ctrlPort,
\r
236 volatile unsigned char* ctrlDir,
\r
237 volatile unsigned char* dataPort,
\r
238 volatile unsigned char* dataDir,
\r
239 volatile unsigned char* dataPinReg,
\r
240 unsigned char rsPin,
\r
241 unsigned char rwPin,
\r
242 unsigned char ePin)) {
\r
243 m_ctrlPort = ctrlPort;
\r
244 m_ctrlDir = ctrlDir;
\r
245 m_dataPort = dataPort;
\r
246 m_dataDir = dataDir;
\r
247 m_dataPinReg = dataPinReg;
\r
252 *m_ctrlPort &= ~((1<<m_rsPin)|(1<<m_rwPin)|(1<<m_ePin)); // set low
\r
253 *m_ctrlDir |= ((1<<m_rsPin)|(1<<m_rwPin)|(1<<m_ePin)); // set as output
\r
255 Delay::millisec(15);
\r
257 *m_ctrlPort |= (1<<LCD_RS); // Set LCD_RS HIGH
\r
259 // Initialize the AVR controller I/O
\r
260 *m_dataDir = 0xFF; // Set LCD_DATA_OUT as all outputs
\r
261 *m_dataPort = 0x00; // Set LCD_DATA_OUT to logic low
\r
263 // Initialize the LCD controller
\r
264 *m_ctrlPort &= ~(1<<LCD_RS);
\r
266 putChar (m_DATA_8);
\r
267 Delay::millisec(4.1);
\r
269 putChar (m_DATA_8);
\r
270 Delay::microsec(100);
\r
272 putChar (m_DATA_8);
\r
273 *m_ctrlPort |= (1<<LCD_RS);
\r
275 // The display will be out into 8 bit data mode here
\r
276 putCmd (m_LINE_8x4); // Set 8 bit data, 4 display lines
\r
277 putCmd (m_PWR_ON); // Power up the display
\r
278 putCmd (m_CLR_DSP); // Power up the display
\r
281 void putChar(unsigned char ch);
\r
282 void putCmd(unsigned char ch);
\r
286 void Lcd::putCmd (unsigned char Cmd) {
\r
287 *m_ctrlPort &= ~(1<<m_rsPin);
\r
289 *m_dataPort = Cmd;
\r
291 *m_ctrlPort |= (1<<m_ePin);
\r
293 *m_ctrlPort &= ~(1<<m_ePin);
\r
295 *m_ctrlPort |= (1<<m_rsPin);
\r
298 void Lcd::putChar (unsigned char c) {
\r
299 *m_ctrlPort &= ~(1<<m_rsPin);
\r
301 *m_ctrlPort |= (1<<m_rsPin);
\r
304 *m_ctrlPort |= (1<<m_ePin);
\r
306 *m_ctrlPort &= ~(1<<m_ePin);
\r
309 unsigned char Lcd::busyWait (void) {
\r
310 unsigned char LCDStatus;
\r
311 *m_dataDir = 0x00; // Set LCD data port to inputs
\r
312 *m_ctrlPort |= (1<<m_rwPin);
\r
316 *m_ctrlPort |= (1<<m_ePin);
\r
318 LCDStatus = *m_dataPinReg;
\r
319 *m_ctrlPort &= ~(1<<m_ePin);
\r
320 } while (LCDStatus & (1<<LCD_BUSY));
\r
321 *m_ctrlPort &= ~(1<<m_rwPin);
\r
322 *m_dataDir = 0xFF; // Set LCD data port to default output state
\r
323 return (LCDStatus);
\r
330 // Circular buffer for UART RX
\r
331 static const unsigned char UART_RX_BUFFER_SIZE = 48;
\r
332 volatile unsigned char m_UartRxBuf[UART_RX_BUFFER_SIZE];
\r
334 volatile unsigned char m_UartRxHead;
\r
335 volatile unsigned char m_UartRxTail;
\r
338 static const unsigned char UART_PARITY_NONE = 0;
\r
339 static const unsigned char UART_PARITY_EVEN = 1;
\r
340 static const unsigned char UART_PARITY_ODD = 2;
\r
342 INLINE_FUNC_DECLARE(void init(void)) {
\r
343 // Default initialization is 8-bit, parity none
\r
346 UCSRC = (1<<UCSZ1) | (1<<UCSZ0); // 8 bit data
\r
348 // Init circular buffer pointers
\r
353 CTS_PORT |= (1<<CTS_PIN); // bring signal high
\r
354 CTS_DIR |= (1<<CTS_PIN); // CTS is active low
\r
358 INLINE_FUNC_DECLARE(void setBaudDivisor(unsigned int baud_divisor)) {
\r
359 UBRRL = baud_divisor & 0xFF;
\r
360 UBRRH = baud_divisor >> 8;
\r
363 INLINE_FUNC_DECLARE(void rxEnable(void)) {
\r
364 UCSRB |= (1<<RXEN);
\r
367 INLINE_FUNC_DECLARE(void rxInterruptEnable(void)) {
\r
368 UCSRB |= (1<<RXCIE);
\r
371 INLINE_FUNC_DECLARE(void txEnable(void)) {
\r
372 UCSRB |= (1<<TXEN);
\r
375 INLINE_FUNC_DECLARE(void txInterruptEnable(void)) {
\r
376 UCSRB |= (1<<TXCIE);
\r
379 INLINE_FUNC_DECLARE(void setParity(unsigned char mode)) {
\r
380 unsigned char reg = UCSRC & ~((1<<UPM0)|(1<<UPM1));
\r
381 if (mode == UART_PARITY_EVEN)
\r
383 else if (mode == UART_PARITY_ODD)
\r
384 reg |= (1<<UPM0)|(1<<UPM1);
\r
388 INLINE_FUNC_DECLARE(unsigned char storeChar(unsigned char rx)) {
\r
389 // Calculate next buffer position.
\r
390 unsigned char tmphead = m_UartRxHead;
\r
391 if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
396 // store in buffer if there is room
\r
397 if (tmphead != m_UartRxTail) {
\r
398 m_UartRxHead = tmphead; // Store new index.
\r
399 m_UartRxBuf[tmphead] = rx;
\r
401 // no room to store character -- data loss
\r
403 CTS_PORT &= ~(1<<CTS_PIN);
\r
409 // check if buffer is now full, if so switch CTS to inactive
\r
410 if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
414 // CTS active if still room in buffer
\r
415 if (tmphead != m_UartRxTail) {
\r
416 CTS_PORT |= (1<<CTS_PIN);
\r
418 // no room left in buffer, so make CTS inactive
\r
419 CTS_PORT &= ~(1<<CTS_PIN);
\r
425 INLINE_FUNC_DECLARE(unsigned char charAvail(void)) {
\r
426 unsigned char tail = m_UartRxTail; // explicitly set order of volatile access
\r
427 return (tail != m_UartRxHead) ? 1 : 0;
\r
430 unsigned char waitRxChar (void);
\r
433 unsigned char Uart::waitRxChar (void) {
\r
434 // waits for next RX character, then return it
\r
435 unsigned char tail;
\r
437 tail = m_UartRxTail; // explicitly set order of volatile variable access
\r
439 } while (m_UartRxHead == tail); // while buffer is empty
\r
442 // turn off interrupts so that if UART char ready to be stored, then storage
\r
443 // will occur after CTS pin is set active. This allows UART ISR to set
\r
444 // CTS inactive if byte fills buffer after we remove current byte
\r
448 // increment tail position
\r
449 if (tail == UART_RX_BUFFER_SIZE-1)
\r
455 CTS_PORT |= (1<<CTS_PIN); // Ensure CTS is active since just removed a byte
\r
456 sei(); // Allow UART ISR to read character and change potentially change CTS
\r
459 return m_UartRxBuf[m_UartRxTail];
\r
462 // Round to nearest integer
\r
463 #define UART_UBRR(baud) ((F_CPU+(8*(unsigned long) (baud)))/ (16*((unsigned long) (baud))) - 1)
\r
464 const prog_uint8_t BaudLookupTable[] =
\r
465 {UART_UBRR(115200), UART_UBRR(38400), UART_UBRR(19200), UART_UBRR(9600)};
\r
468 INLINE_FUNC_DECLARE(static unsigned char GetBaudDivisor());
\r
469 static inline unsigned char GetBaudDivisor() {
\r
470 // Set LCD_control BAUD_J2:BAUD_J1 to inputs
\r
471 BAUD_DIR &= ~((1<<BAUD_J2) | (1<<BAUD_J1));
\r
472 // Set BAUD_J2:BAUD_J1 PULL-UPS active
\r
473 BAUD_PORT |= (1<<BAUD_J2) | (1<<BAUD_J1);
\r
475 // Get BAUD rate jumper settings
\r
476 unsigned char BaudSelectJumpersValue = BAUD_PIN_REG;
\r
477 // Mask off unwanted bits
\r
478 BaudSelectJumpersValue &= (1<<BAUD_J2) | (1<<BAUD_J1);
\r
479 // BAUD_J2 = PD.3, BAUD_J1 = PD.2
\r
480 // This is two bits too far to the left and the array index will
\r
481 // increment by 4, instead of 1.
\r
482 // Shift BAUD_J2 & BAUD_J1 right by two bit positions for proper array indexing
\r
483 BaudSelectJumpersValue = (BaudSelectJumpersValue >> BAUD_J1);
\r
485 return PGM_READ_BYTE (&BaudLookupTable[BaudSelectJumpersValue]);
\r
493 // This class has only static methods
\r
494 class SerialCommandProcessor {
\r
496 // ASCII control code to set brightness level
\r
497 // Next byte is brightness ranging from 0 (no backlight) to 255 (maximum)
\r
498 static const unsigned char m_LED_SET_BRIGHTNESS = 0xFB;
\r
499 // ASCII control code turns on LED
\r
500 static const unsigned char m_LED_SW_ON = 0xFC;
\r
501 // ASCII control code turns off LED
\r
502 static const unsigned char m_LED_SW_OFF = 0xFD;
\r
503 // Character generator RAM command
\r
504 static const unsigned char m_REG_MODE = 0xFE;
\r
507 INLINE_FUNC_DECLARE(static void processChar(unsigned char ch)) {
\r
508 // avoid use of switch statement as ImageCraft and GCC produce signifcantly
\r
509 // more code for a switch statement than a sequence of if/else if
\r
510 if (ch == m_LED_SW_OFF) {
\r
512 } else if (ch == m_LED_SW_ON) {
\r
514 } else if (ch == m_LED_SET_BRIGHTNESS) {
\r
515 // read next byte which will be brightness
\r
516 gLed.setBrightness(gUart.waitRxChar());
\r
517 } else if (ch == m_REG_MODE) {
\r
518 gLcd.putCmd (gUart.waitRxChar()); // Send LCD command character
\r
520 gLcd.putChar (ch); // Send LCD data character
\r
527 MCUSR = 0; // clear all reset flags
\r
530 SleepMode::initIdleMode();
\r
532 gUart.init(); // Initialize the AVR USART
\r
533 gUart.setBaudDivisor(GetBaudDivisor());
\r
535 gUart.rxInterruptEnable();
\r
536 gLed.init(&LED_PORT, &LED_DIR, LED_PIN);
\r
537 gLcd.init(&LCD_CONTROL_PORT, &LCD_CONTROL_DIR, &LCD_DATA_PORT,
\r
538 &LCD_DATA_DIR, &LCD_DATA_PIN_REG, LCD_RS, LCD_RW, LCD_E);
\r
542 if (gUart.charAvail()) {
\r
543 SerialCommandProcessor::processChar (gUart.waitRxChar());
\r
544 } else { // No characters waiting in RX buffer
\r
546 SleepMode::enterSleep();
\r
554 ISR(USART_RX_vect) {
\r
555 gUart.storeChar(UDR);
\r
558 ISR(TIMER0_COMPA_vect) {
\r
559 sei(); // Okay to allow USART interrupts while controlling LED PWM
\r