1 /*****************************************************************************
\r
2 ** FILE IDENTIFICATION
\r
4 ** Name: serial_lcd.cpp
\r
5 ** Purpose: Serial Backpack firmware with C++ methods (no member variables)
\r
6 ** Programmer: Kevin Rosenberg <kevin@rosenberg.net> (AVR Freaks member kmr)
\r
7 ** Date Started: Dec 2007 (based on Jan 2, 2007 release by C.W. Livinston)
\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 // register variables
\r
26 REGISTER_VAR(unsigned char ledPwmCount, "r4", 15);
\r
27 REGISTER_VAR(unsigned char sUartRxHead, "r5", 14);
\r
28 REGISTER_VAR(unsigned char sUartRxTail, "r6", 13);
\r
29 REGISTER_VAR(unsigned char ledPwmCycling, "r7", 12);
\r
31 #define ledPwmPattern GPIOR0
\r
32 #define ledStatus GPIOR1
\r
33 #define BIT_led_on REGISTER_BIT(GPIOR1,0)
\r
35 //// Classes with only static methods
\r
39 INLINE_FUNC_DECLARE(static void init(void)) {
\r
41 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
42 WDTCSR = (1<<WDE)|(1<<WDP3)|(1<<WDP0); // 1024K cycles, 8.0sec
\r
44 INLINE_FUNC_DECLARE(static void off(void)) {
\r
47 MCUSR &= ~(1<<WDRF); // clear any watchdog interrupt flags
\r
48 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
49 WDTCSR &= ~(1<<WDE); // watchdog timer off
\r
53 INLINE_FUNC_DECLARE(static void on(void)) {
\r
56 WDTCSR |= (1<<WDCE)|(1<<WDE); // start timed sequence (keep old prescaler)
\r
57 WDTCSR &= ~(1<<WDCE);
\r
61 INLINE_FUNC_DECLARE(static void reset(void)) {
\r
68 INLINE_FUNC_DECLARE(static void initIdleMode(void)) {
\r
69 MCUCR &= ~((1<<SM1)|(1<<SM0)); // use idle sleep mode
\r
71 INLINE_FUNC_DECLARE(static void enterSleep()) {
\r
80 INLINE_FUNC_DECLARE(static void millisec(double ms)) {
\r
83 INLINE_FUNC_DECLARE(static void microsec(double ms)) {
\r
89 //// Classes with non-static methods
\r
91 #define USE_LED_PWM_IO_MEMBERS 0
\r
95 static const prog_uint8_t ledPwmPatterns[];
\r
96 static const unsigned char LED_BRIGHTNESS_LEVELS = 8;
\r
98 #if USE_LED_PWM_IO_MEMBERS
\r
99 // LED backlight control pin
\r
100 static volatile unsigned char * const m_LedDirPtr;
\r
101 static volatile unsigned char * const m_LedPortPtr;
\r
102 static const unsigned char m_LedPin = LED_PIN;
\r
106 INLINE_FUNC_DECLARE(void init(void)) {
\r
107 // setup PWM to run at 1.25ms per interrupt.
\r
108 // This allows 8 levels of brightness at minimum of 100Hz flicker rate.
\r
109 TCCR0A = (1<<WGM01); // CTC mode
\r
110 TCCR0B = 0; // timer off
\r
111 OCR0A = 72; // 1.25ms with CLK/256 prescaler @ 14.7456MHz
\r
112 TIMSK = (1<<OCIE0A); // Turn on timer0 COMPA intr (all other timer intr off)
\r
113 #if USE_LED_PWM_IO_MEMBERS
\r
114 *m_LedPortPtr &= ~(1<<m_LedPin); // Ensure LED is off during initialization
\r
115 *m_LedDirPtr |= (1<<m_LedPin); // Ensure LED is output port
\r
117 LED_PORT &= ~(1<<LED_PIN); // Ensure LED is off during initialization
\r
118 LED_DIR |= (1<<LED_PIN); // Ensure LED is output port
\r
120 BIT_led_on = 0; // note that LED is off
\r
121 ledPwmPattern = 0xFF; // maximum brightness
\r
124 INLINE_FUNC_DECLARE(void stop(void)) {
\r
127 INLINE_FUNC_DECLARE(void start(void)) {
\r
128 TCCR0B = (1<<CS02);
\r
131 INLINE_FUNC_DECLARE(void switchOff(void)) {
\r
137 INLINE_FUNC_DECLARE(void switchOn(void)) {
\r
139 if (ledPwmPattern == 0xFF) { // maximum brightness, no need for PWM
\r
147 INLINE_FUNC_DECLARE(void setBrightness(unsigned char brightness)) {
\r
148 if (brightness == 0) { // turn backlight off for 0 brightness
\r
158 unsigned char ledPwmPos = (brightness * (LED_BRIGHTNESS_LEVELS-1) + 127) >> 8;
\r
159 // Below is probably not required, but ensures we don't exceed array
\r
160 if (ledPwmPos > LED_BRIGHTNESS_LEVELS - 1)
\r
161 ledPwmPos = LED_BRIGHTNESS_LEVELS - 1;
\r
162 ledPwmPattern = pwmPattern(ledPwmPos);
\r
164 ledPwmCycling = ledPwmPattern;
\r
165 if (ledPwmPos >= LED_BRIGHTNESS_LEVELS-1) { // maximum brightness
\r
166 // don't need PWM to continuously on
\r
178 INLINE_FUNC_DECLARE(void cyclePwm(void)) {
\r
179 if (ledPwmCycling & 0x01) { // Set current LED state based on cycling variable
\r
185 // Update cycling variable for next interrupt
\r
186 if (ledPwmCount >= LED_BRIGHTNESS_LEVELS-1) {
\r
188 ledPwmCycling = ledPwmPattern;
\r
191 ledPwmCycling >>= 1;
\r
195 INLINE_FUNC_DECLARE(void lampOn(void)) {
\r
196 #if USE_LED_PWM_IO_MEMBERS
\r
197 *m_LedPortPtr |= (1<<m_LedPin);
\r
199 LED_PORT |= (1<<LED_PIN);
\r
203 INLINE_FUNC_DECLARE(void lampOff(void)) {
\r
204 #if USE_LED_PWM_IO_MEMBERS
\r
205 *m_LedPortPtr &= ~(1<<m_LedPin);;
\r
207 LED_PORT &= ~(1<<LED_PIN);;
\r
211 INLINE_FUNC_DECLARE(unsigned char pwmPattern(unsigned char pos)) {
\r
212 return PGM_READ_BYTE (&ledPwmPatterns[pos]);
\r
216 #if USE_LED_PWM_IO_MEMBERS
\r
217 volatile unsigned char * const LedPwm::m_LedDirPtr = &LED_DIR;
\r
218 volatile unsigned char * const LedPwm::m_LedPortPtr = &LED_PORT;
\r
221 // PWM Patterns (8-brightness levels)
\r
222 const prog_uint8_t LedPwm::ledPwmPatterns[] = {
\r
223 0x10, // 0b00010000 0
\r
224 0x11, // 0b00010001 1
\r
225 0x4A, // 0b01001010 2
\r
226 0x55, // 0b01010101 3
\r
227 0x5D, // 0b01011101 4
\r
228 0xEE, // 0b11101110 5
\r
229 0x7F, // 0b11110111 6
\r
230 0xFF, // 0b11111111 7
\r
236 // Turn on power to the display, no cursor
\r
237 static const unsigned char m_PWR_ON = 0x0C;
\r
238 // Clear display command
\r
239 static const unsigned char m_CLR_DSP = 0x01;
\r
240 // Set 8 data bits, 4 display lines
\r
241 static const unsigned char m_LINE_8x4 = 0x38;
\r
243 static const unsigned char m_DATA_8 = 0x30;
\r
246 INLINE_FUNC_DECLARE(void init(void)) {
\r
247 // Set BAUD_J2:BAUD_J1 PULL-UPS active
\r
248 LCD_CONTROL_PORT = (1<<BAUD_J2) | (1<<BAUD_J1);
\r
249 // Set LCD_control BAUD_J2:BAUD_J1 to inputs
\r
250 LCD_CONTROL_DIR = 0xF2;
\r
252 Delay::millisec(15);
\r
254 LCD_CONTROL_PORT |= (1<<LCD_RS); // Set LCD_RS HIGH
\r
256 // Initialize the AVR controller I/O
\r
257 LCD_DATA_DIR = 0xFF; // Set LCD_DATA_PORT as all outputs
\r
258 LCD_DATA_PORT = 0x00; // Set LCD_DATA_PORT to logic low
\r
260 // Initialize the LCD controller
\r
261 LCD_CONTROL_PORT &= ~(1<<LCD_RS);
\r
263 putChar (m_DATA_8);
\r
264 Delay::millisec(4.1);
\r
266 putChar (m_DATA_8);
\r
267 Delay::microsec(100);
\r
269 putChar (m_DATA_8);
\r
270 LCD_CONTROL_PORT |= (1<<LCD_RS);
\r
272 // The display will be out into 8 bit data mode here
\r
273 putCmd (m_LINE_8x4); // Set 8 bit data, 4 display lines
\r
274 putCmd (m_PWR_ON); // Power up the display
\r
275 putCmd (m_CLR_DSP); // Power up the display
\r
278 void putChar(unsigned char ch);
\r
279 void putCmd(unsigned char ch);
\r
282 unsigned char busyWait(void);
\r
286 // round to nearest integer
\r
290 // Circular buffer for UART RX
\r
291 //NO_INIT_DECLARE(volatile unsigned char sUartRxBuf[UART_RX_BUFFER_SIZE]);
\r
292 static const prog_uint8_t BaudLookupTable[];
\r
294 static const unsigned char UART_RX_BUFFER_SIZE = 48;
\r
295 volatile unsigned char m_UartRxBuf[UART_RX_BUFFER_SIZE];
\r
297 INLINE_FUNC_DECLARE(static unsigned char baud()) {
\r
298 unsigned char BaudSelectJumpersValue;
\r
300 // Get BAUD rate jumper settings
\r
301 BaudSelectJumpersValue = BAUD_PIN_REG;
\r
302 // Mask off unwanted bits
\r
303 BaudSelectJumpersValue &= (1<<BAUD_J2) | (1<<BAUD_J1);
\r
304 // BAUD_J2 = PD.3, BAUD_J1 = PD.2
\r
305 // This is two bits too far to the left and the array index will
\r
306 // increment by 4, instead of 1.
\r
307 // Shift BAUD_J2 & BAUD_J1 right by two bit positions for proper array indexing
\r
308 BaudSelectJumpersValue = (BaudSelectJumpersValue >> BAUD_J1);
\r
310 return PGM_READ_BYTE (&BaudLookupTable[BaudSelectJumpersValue]);
\r
314 INLINE_FUNC_DECLARE(void init(void)) {
\r
315 // Initialize UART0
\r
316 UCSRB = 0; // Disable while setting baud rate
\r
318 UCSRC = (1<<UCSZ1) | (1<<UCSZ0); // 8 bit data
\r
320 UBRRH = 0; // Set baud rate hi
\r
321 UCSRB = (1<<RXEN)|(1<<RXCIE); // RXEN = Enable
\r
323 // Init circular buffer pointers
\r
328 CTS_PORT |= (1<<CTS_PIN); // bring signal high
\r
329 CTS_DIR |= (1<<CTS_PIN); // CTS is active low
\r
333 INLINE_FUNC_DECLARE(unsigned char storeChar(unsigned char rx)) {
\r
334 // Calculate next buffer position.
\r
335 unsigned char tmphead = sUartRxHead;
\r
336 if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
341 // store in buffer if there is room
\r
342 if (tmphead != sUartRxTail) {
\r
343 sUartRxHead = tmphead; // Store new index.
\r
344 m_UartRxBuf[tmphead] = rx;
\r
346 // no room to store character -- data loss
\r
348 CTS_PORT &= ~(1<<CTS_PIN);
\r
354 // check if buffer is now full, if so switch CTS to inactive
\r
355 if (tmphead == UART_RX_BUFFER_SIZE-1)
\r
359 // CTS active if still room in buffer
\r
360 if (tmphead != sUartRxTail) {
\r
361 CTS_PORT |= (1<<CTS_PIN);
\r
363 // no room left in buffer, so make CTS inactive
\r
364 CTS_PORT &= ~(1<<CTS_PIN);
\r
370 INLINE_FUNC_DECLARE(unsigned char charAvail(void)) {
\r
371 unsigned char tail = sUartRxTail; // explicitly set order of volatile access
\r
372 return (tail != sUartRxHead) ? 1 : 0;
\r
375 unsigned char waitRxChar (void);
\r
378 #define UART_UBRR(baud) ((F_CPU+(8*(unsigned long) (baud)))/ (16*((unsigned long) (baud))) - 1)
\r
379 const prog_uint8_t Uart::BaudLookupTable[] =
\r
380 {UART_UBRR(115200), UART_UBRR(38400), UART_UBRR(19200), UART_UBRR(9600)};
\r
382 unsigned char Uart::waitRxChar (void) {
\r
383 // waits for next RX character, then return it
\r
384 unsigned char tail;
\r
386 tail = sUartRxTail; // explicitly set order of volatile variable access
\r
388 } while (sUartRxHead == tail); // while buffer is empty
\r
391 // turn off interrupts so that if UART char ready to be stored, then storage
\r
392 // will occur after CTS pin is set active. This allows UART ISR to set
\r
393 // CTS inactive if byte fills buffer after we remove current byte
\r
397 // increment tail position
\r
398 if (tail == UART_RX_BUFFER_SIZE-1)
\r
404 CTS_PORT |= (1<<CTS_PIN); // Ensure CTS is active since just removed a byte
\r
405 sei(); // Allow UART ISR to read character and change potentially change CTS
\r
408 return m_UartRxBuf[sUartRxTail];
\r
416 class SerialCommandProcessor {
\r
418 // ASCII control code to set brightness level
\r
419 // Next byte is brightness ranging from 0 (no backlight) to 255 (max backlight)
\r
420 static const unsigned char m_LED_SET_BRIGHTNESS = 0xFB;
\r
421 // ASCII control code turns on LED
\r
422 static const unsigned char m_LED_SW_ON = 0xFC;
\r
423 // ASCII control code turns off LED
\r
424 static const unsigned char m_LED_SW_OFF = 0xFD;
\r
425 // Character generator RAM command
\r
426 static const unsigned char m_REG_MODE = 0xFE;
\r
429 INLINE_FUNC_DECLARE(static void processChar(unsigned char ch)) {
\r
430 // avoid use of switch statement as ImageCraft and GCC produce signifcantly
\r
431 // more code for a switch statement than a sequence of if/else if
\r
432 if (ch == m_LED_SW_OFF) {
\r
434 } else if (ch == m_LED_SW_ON) {
\r
436 } else if (ch == m_LED_SET_BRIGHTNESS) {
\r
437 // read next byte which will be brightness
\r
438 gLed.setBrightness(gUart.waitRxChar());
\r
439 } else if (ch == m_REG_MODE) {
\r
440 gLcd.putCmd (gUart.waitRxChar()); // Send LCD command character
\r
442 gLcd.putChar (ch); // Send LCD data character
\r
449 MCUSR = 0; // clear all reset flags
\r
452 SleepMode::initIdleMode();
\r
456 gUart.init(); // Initialize the AVR USART
\r
460 if (gUart.charAvail()) {
\r
461 SerialCommandProcessor::processChar (gUart.waitRxChar());
\r
463 // No characters waiting in RX buffer
\r
466 SleepMode::enterSleep();
\r
473 void Lcd::putCmd (unsigned char Cmd) {
\r
474 LCD_CONTROL_PORT &= ~(1<<LCD_RS);
\r
476 LCD_DATA_PORT = Cmd;
\r
478 LCD_CONTROL_PORT |= (1<<LCD_E);
\r
480 LCD_CONTROL_PORT &= ~(1<<LCD_E);
\r
482 LCD_CONTROL_PORT |= (1<<LCD_RS);
\r
485 void Lcd::putChar (unsigned char c) {
\r
486 LCD_CONTROL_PORT &= ~(1<<LCD_RS);
\r
488 LCD_CONTROL_PORT |= (1<<LCD_RS);
\r
491 LCD_CONTROL_PORT |= (1<<LCD_E);
\r
493 LCD_CONTROL_PORT &= ~(1<<LCD_E);
\r
496 unsigned char Lcd::busyWait (void) {
\r
497 unsigned char LCDStatus;
\r
498 LCD_DATA_DIR = 0x00; // Set LCD data port to inputs
\r
499 LCD_CONTROL_PORT |= (1<<LCD_RW);
\r
503 LCD_CONTROL_PORT |= (1<<LCD_E);
\r
505 LCDStatus = LCD_DATA_PIN_REG;
\r
506 LCD_CONTROL_PORT &= ~(1<<LCD_E);
\r
507 } while (LCDStatus & (1<<LCD_BUSY));
\r
508 LCD_CONTROL_PORT &= ~(1<<LCD_RW);
\r
509 LCD_DATA_DIR = 0xFF; // Set LCD data port to default output state
\r
510 return (LCDStatus);
\r
516 gUart.storeChar(UDR);
\r
519 ISR(TIMER0_COMPA_vect)
\r
521 sei(); // Okay to allow USART interrupts while controlling LED PWM
\r