Hello again
It's known that ISRs should be as short as possible but sometimes it's difficult to make them so. I'm not very experienced with embedded C-programming but have been using C language for about 10 years and now it's quite difficult for me to change my mind a bit.
I'm developing desktop clock with mini meteostation. I use CC25-12 7-segment display to show my info. I use dynamic indication - my ATmega8 switches common cathodes of 7-seg display on and off fast enough with the help of ISR while anodes are set to draw needed symbol. Things works just fine but I have some doubts about code implementation.
Actually I have two timers in my system. F_CPU is 8 MHz and Timer0 counts for 255 with prescaler 64 so I'm able to get my TIMER0_OVF interrupt approx every 2ms which is used for dynamic indication. It works pretty good and I don't see any flickering. My second Timer1 count for tics with specified length (e. g. 1 tic equals to 0.1 second and so on). These tics are used in software to switch between display modes - temperature [T], pressure [P], humidity [H] and time (every 5 seconds so 50 tics), display alarm situation if measured params are outside specs (status LED blinks every 0.5 secs so 5 tics), blink two middle clock LEDs (every second so 10 tics) and make measurements of [T], [P] and [H] (every 0.1 secs so 1 tics). Measurements are done with the help of Bosch's BMP280/BME280 via I2C protocol and handled inside eternal loop in main() function. Dynamic indication as well as switching between modes and printing necessary value on 7-seg display is done inside TIMER0_OVF ISR and because of this it become somewhat long (and with calls to functions).
And this is the point which confuses me. I feel that I make something wrong. Source code of main.c is attached below (I could upload other modules if there is a need). As for now I use more simple BMP280 so humidity is not shown (only dashes). Also I haven't solved the problem of attaching DS1307 RTC module to my 3.3 VCC setup so instead of real time I print minutes and seconds elapsed from powering on.
Because of I little-experienced embedded programmer I would like to get some advices with ISR/main task separation to improve my skills. Any help is greatly appreciated.
Source of main.c:
#include <avr/io.h> #include <avr/interrupt.h> #include <stdint.h> #include <util/atomic.h> #include <util/delay.h> #include <util/twi.h> #include "bmp280.h" #include "led_display.h" #include "twi.h" #define SYS_MEAS_DELAY 1 // in tics #define SYS_MODE_DELAY 5 // in seconds #define SYS_MIN_FLD 0 #define SYS_MAX_FLD 3 #define SYS_MIN_MODE 0 #define SYS_MAX_MODE 3 #define SYS_MODE_T 0 #define SYS_MODE_P 1 #define SYS_MODE_H 2 #define SYS_MODE_TIME 3 #define SYS_MODE_START 255 volatile const uint8_t mode_dots[4] = {(1 << LED_DOT_D1), (1 << LED_DOT_D2), (1 << LED_DOT_D3), 0}; // modes indicator volatile const unsigned char fields[4] = {(1 << LED_FLD_1), (1 << LED_FLD_2), (1 << LED_FLD_3), (1 << LED_FLD_4)}; // accessible fields volatile unsigned char symbols[4]; // corresponding symbols volatile uint8_t curr_mode = SYS_MODE_START; // current mode volatile uint8_t curr_fld; // current field volatile uint32_t tics; // TODO not need such wide integer volatile uint8_t ratio = 10; // tics to second ratio int32_t T_print; uint32_t P_print; volatile unsigned char T_alarm, P_alarm; void display_dashes(void) { LED_DDOT_PORT &= ~(1 << LED_DDOT_PIN); LED_DOT_PORT &= ~(1 << LED_DOT_D4_5); symbols[0] = LED_symb_dash; symbols[1] = LED_symb_dash; symbols[2] = LED_symb_dash; symbols[3] = LED_symb_dash; } void display_print_int(uint32_t i, uint8_t radix) { LED_DDOT_PORT &= ~(1 << LED_DDOT_PIN); LED_DOT_PORT &= ~(1 << LED_DOT_D4_5); if ((radix > 16) || (radix == 0)) display_dashes(); symbols[0] = LED_symb_digits[i / radix / radix / radix]; symbols[1] = LED_symb_digits[(i / radix / radix) % radix]; symbols[2] = LED_symb_digits[(i / radix) % radix]; symbols[3] = LED_symb_digits[i % radix]; } void display_print_T(void) { LED_DDOT_PORT |= (1 << LED_DDOT_PIN); LED_DOT_PORT &= ~(1 << LED_DOT_D4_5); int32_t T_tmp = T_print; if (T_print >= 0) symbols[0] = LED_symb_empty; else { T_tmp = -T_tmp; symbols[0] = LED_symb_dash; } uint32_t Ti, Tf; Ti = T_tmp / 100; Tf = (T_tmp % 100) / 10; if (Ti < 10) { symbols[1] = LED_symb_empty; symbols[2] = LED_symb_digits[Ti]; } else { symbols[1] = LED_symb_digits[Ti / 10]; symbols[2] = LED_symb_digits[Ti % 10]; } symbols[3] = LED_symb_digits[Tf]; } void display_print_P(void) { LED_DOT_PORT &= ~(1 << LED_DOT_D4_5); uint32_t Pi, Pf; Pi = (P_print * 760) / 101325; Pf = (((P_print * 760) % 101325) * 10) / 101325; if (Pi > 999) { LED_DDOT_PORT &= ~(1 << LED_DDOT_PIN); symbols[0] = LED_symb_digits[Pi / 1000]; symbols[1] = LED_symb_digits[(Pi / 100) % 10]; symbols[2] = LED_symb_digits[(Pi / 10) % 10]; symbols[3] = LED_symb_digits[Pi % 10]; } else { LED_DDOT_PORT |= (1 << LED_DDOT_PIN); symbols[0] = LED_symb_digits[Pi / 100]; symbols[1] = LED_symb_digits[(Pi / 10) % 10]; symbols[2] = LED_symb_digits[Pi % 10]; symbols[3] = LED_symb_digits[Pf]; } } void display_print_time(void) { // TODO print time from DS1307 LED_DDOT_PORT &= ~(1 << LED_DDOT_PIN); symbols[0] = LED_symb_digits[(((tics / ratio) / 60) / 10) % 10]; symbols[1] = LED_symb_digits[((tics / ratio) / 60) % 10]; symbols[2] = LED_symb_digits[(((tics / ratio) % 60) / 10) % 10]; symbols[3] = LED_symb_digits[((tics / ratio) % 60) % 10]; } ISR(TIMER0_OVF_vect) { LED_SEG_PORT &= ~LED_SEG_ALL; // shut down segments LED_SEG_PORT |= symbols[curr_fld]; LED_FLD_PORT |= LED_FLD_ALL; // pull all fields to high logic (cut-off current) LED_FLD_PORT &= ~fields[curr_fld]; if (curr_fld == SYS_MAX_FLD) curr_fld = SYS_MIN_FLD; else curr_fld++; LED_DOT_PORT &= ~LED_DOT_MODES; // shutdown all mode dots if (curr_mode == SYS_MODE_START) { display_dashes(); return; } if (((curr_mode == SYS_MODE_T) && (T_alarm)) || ((curr_mode == SYS_MODE_P) && (P_alarm))) { if (((tics * 2) / ratio) % 2) LED_DOT_PORT &= ~mode_dots[curr_mode]; else LED_DOT_PORT |= mode_dots[curr_mode]; } else LED_DOT_PORT |= mode_dots[curr_mode]; switch (curr_mode) { case SYS_MODE_T: display_print_T(); break; case SYS_MODE_P: display_print_P(); break; case SYS_MODE_TIME: if ((tics / ratio) % 2) LED_DOT_PORT &= ~(1 << LED_DOT_D4_5); else LED_DOT_PORT |= (1 << LED_DOT_D4_5); display_print_time(); break; default: display_dashes(); break; } } ISR(TIMER1_COMPA_vect) { tics++; } void setup_io(void) { LED_SEG_DDR |= LED_SEG_ALL; LED_FLD_DDR |= LED_FLD_ALL; LED_DDOT_DDR |= (1 << LED_DDOT_PIN); LED_DOT_DDR |= LED_DOT_ALL; } void setup_timers(void) { TCNT0 = 0; // start from zero TCCR0 = (1 << CS01) | (1 << CS00); // prescaler is 64 // OCR1A = 31249; // count for 1 second // ratio = 1; // OCR1A = 15624; // count for 0.5 second // ratio = 2; OCR1A = 3124; // count for 0.1 second (1 second = 10 tics) ratio = 10; TCCR1B = (1 << CS12) | (1 << WGM12); // prescaler is 256 and use CTC mode TIMSK = (1 << TOIE0) | (1 << OCIE1A); // enable overflow interrupt for Timer0 and CTC match for Timer1 } void get_tp(void) { int32_t T; uint32_t P; unsigned char aT, aP; BMP280_compensate(&T, &P, &aT, &aP); ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { T_print = T; T_alarm = aT; } ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { P_print = P; P_alarm = aP; } } int main(void) { setup_io(); LED_test(); setup_timers(); TWI_setup(); /* enable interrupts */ sei(); uint32_t lastsec_mode = 0; // TODO the same as tics' TODO uint32_t lasttic_meas = 0; while (1) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { if ((((tics / ratio) % SYS_MODE_DELAY) == 0) && (curr_mode != SYS_MODE_START)) { if (lastsec_mode != (tics / ratio)) { lastsec_mode = tics / ratio; if (curr_mode == SYS_MAX_MODE) curr_mode = SYS_MIN_MODE; else curr_mode++; } } } if ((tics % SYS_MEAS_DELAY) == 0) { if (lasttic_meas != tics) { lasttic_meas = tics; unsigned char id = 0; if (!BMP280_read_id(&id)) { display_dashes(); continue; } if (id != BMP280_ID) { display_dashes(); continue; } if (!BMP280_read_calibration()) { display_dashes(); continue; } if (!BMP280_set_acquisition(0x01, 0x01, 0x01)) { display_dashes(); continue; } unsigned char status; do { if (!BMP280_read_status(&status)) { LED_DOT_PORT |= (1 << LED_DOT_D1); LED_DOT_PORT &= ~(1 << LED_DOT_D2); display_dashes(); status = 1; break; } } while (status); if (status == 0) { if (!BMP280_read_TP()) { display_dashes(); continue; } get_tp(); if (curr_mode == SYS_MODE_START) curr_mode = SYS_MIN_MODE; } } } } cli(); }