Hi all,
I have an interesting problem using Fleury's excellent LCD library: the LCD functions seem to take so long to finish that they cause the external interrupt readings to glitch if I am also writing to the LCD about every second. See code below -- works well as long as I keep lines around 171 commented out and don't see the every second reading. I realize I could read button input on regular tick, but I want to get experience with interrupts and it is just toy code.
My main question -- How would one calculate the timing necessary to run the LCD library? In the LCD code, there are lots of busy wait / delay functions, do folks add those up somehow?
I also wondered -- Does anyone have design ideas to handle problems with output like this? I am thinking about setting up two microcontrollers, one to run the LCD, one to communicate via serial. Might be fun and educational anyway, but I wonder if this approach would translate to a real application.
Looking forward to everyone's ideas!
/* lcd1.c Synopsis: Show stuff on lcd, increment counter with each push, reset with long hold Created: 2/10/2018 7:38:09 PM Author : wsprague HARDWARE DESCRIPTION PD2 (4) is button infput / extern interrupt PC5 (28) power on led PBx (*) lcd control pins Notes: 2018-03-11 wws: Still very flaky with hold down functionality. TODO: pull out lcd update into function. 2018-03-08 wws: Sort of working. Need to rework state machine because currently just pressing not doing anything on release, but have the state/ event structure for latter. 2018-02-10 wws: to do hold => reset correctly, need to set timer and interrupt, along with a state where button held down but timer elapsed. First doing button push increments only, then adding reset functionality. */ #define F_CPU 1000000UL // delay.h supplies reasonable default, but this fixes warning #include <stdint.h> #include <stdlib.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #include <avr/eeprom.h> #include <util/atomic.h> // include header file for lcd library #include "lcd.h" // define state names and numbers #define _0QUIES 0 #define _1HOLD 1 // define event names and numbers #define _0NULL 0 #define _1PUSH 1 #define _2REL 2 #define _3TICK 3 #define _4CLRTIMR 4 #define RADIX 15 // radix for lcd numeric output, want hex #define HOLD_TICKS 1000 // how many ticks to wait for hold before reset #define TICK_TICKS 3 // how many ticks before heartbeat // globals volatile uint8_t state = _0QUIES; // current state volatile uint8_t event = _0NULL; // latest event volatile uint32_t tick = 0; // ticks (~10ms) since reset, incremented each wake up. Overflows on 1.36 years volatile uint16_t pcount = 0; // count of button pushes, // volatile uint32_t ptick = 0; // tick at button push uint16_t EEMEM eeprom_pcount = 0 ; // eeprom memory allocation // progmem something with name of project // define short nop macro #define _NOP() do { __asm__ __volatile__ ("nop"); } while (0) // int main () { char buffer[32]; // to store lcd stuff // start state in 0, waiting for input state = _0QUIES; // initialize lcd lcd_init(LCD_DISP_ON); lcd_clrscr(); // lcd_gotoxy(0,0); // lcd_puts("Hello wife!"); // update lcd with pcount and progmem info on program // get pcount from EEPROM -- TODO busy wait until ready if nec, check error somehow // eeprom_is_ready() pcount = eeprom_read_word (& eeprom_pcount ); if (pcount == 0xFFFF) { pcount = 1; eeprom_update_word (& eeprom_pcount, pcount ); } lcd_gotoxy(0,1); itoa(pcount, buffer, RADIX); lcd_puts(buffer); // power on indicator light on PC5 (28), interrupt toggle PC4 (27) DDRC |= ( 1 << PC5) ; PORTC |= ( 1 << PC5); DDRC |= ( 1 << PC4) ; // config external interrupt (4) DDRD |= (0 << PD2 ); // input pin PORTD |= (1 << PD2 ); // set pull up EICRA |= (1 << ISC01); // 10 falling edge EIMSK |= (1 << INT0); // Allow external interrupt // config TC0 ticking at every something CTC. No pin output, just interrupt TCCR0A |= (1 << WGM01); // mode 2 CTC OCR0A = TICK_TICKS; // turn over when hit OCROA, maybe every sec??? just dorking TCCR0B |= (1 << CS02) | (1 << CS00); // turn on, 1024 prescale (think 64 prescale too high to do anything else) TIMSK0 |= (1 << OCIE0A); // fire off interrupt on match // config TC1 for simple timer for hold time, ~1 sec, but don't start until button push // defaults to mode 0 timer TIMSK1 |= (1 << OCIE1A); // enable timer interrupt OCR1A = HOLD_TICKS; // set match to delay 1 sec. XXX make constant or define TCNT1 = 0; // set start at 0 // set sleep mode for later set_sleep_mode(SLEEP_MODE_IDLE); sei(); while (1) { // sleep, waking on interrupts // sleep drill sleep_enable(); sleep_cpu(); sleep_disable(); // turn off interrupts cli(); // do state transitions and associated actions // ... push button if (event == _1PUSH) { event = _0NULL; // clear event flag state = _1HOLD; // set next state. XXX THIS IS CAUSING WEIRD DOUBLE PUSH THING // increment counter and write to eeprom ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { pcount = pcount+1; eeprom_update_word (& eeprom_pcount, pcount ); lcd_gotoxy(0,1); itoa(pcount, buffer, RADIX); lcd_puts(buffer); } // set interrupt to fire on rising edge (11) EICRA |= (1 << ISC01 ); EICRA |= (1 << ISC00 ); // start timer for hold and clear process // TCNT1 = 0; // TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 prescale // ... release button. } else if (event == _2REL) { event = _0NULL; // clear event flag state = _0QUIES; // set next state // set interrupt to fire on down edge (10) EICRA |= (1 << ISC01 ); EICRA &= ~(1 << ISC00 ); // stop timer for hold and clear, zero tcnt // TCNT1 = 0; // zero counter // TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // zero => stop } else if (event == _3TICK) { event = _0NULL; // clear event flag // leave state alone // tick updated in ISR // XXX Following block screws up regular increment if uncommented // ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // lcd_gotoxy(0,0); // itoa(tick/1000, buffer, RADIX); // should use bit ops for speed // lcd_puts(buffer); // } } else if (event == _4CLRTIMR) { event = _0NULL; // clear event flag // state = _0QUIES; // leave state alone // ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // // zero counter and write to eeprom // pcount = 0; // eeprom_update_word (& eeprom_pcount, pcount ); // lcd_gotoxy(0,1); // itoa(pcount, buffer, RADIX); // lcd_puts(buffer); // } // stop timer for hold and clear, zero tcnt // TCNT1 = 0; // zero counter // TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // zero => stop } else { } // turn on interrupts sei(); } } ISR(INT0_vect){ PINC |= (1 << PC4); // toggles led // set event based on current state if (state == _0NULL) { event = _1PUSH; } else if (state == _1HOLD) { event = _2REL; } } // ISR for TC0 CTC at 10ms ISR(TIMER0_COMPA_vect){ event = _3TICK; tick++; } // ISR for TC1 hold timer ISR(TIMER1_COMPA_vect){ event = _4CLRTIMR; }