Embedded-Software-Projects

Log | Files | Refs | README | LICENSE

commit 54edcadbdb33757f59feb056026c99ef4e6b4c6d
parent 26d4100490351b025f5f46c347277a554898ba67
Author: William Lindholm <william_lindholm@outlook.com>
Date:   Wed, 23 Oct 2024 12:43:50 +0100

Added precalculation of voltage thresholds during program startup

Diffstat:
MProject1/.vs/Project1/v14/.atsuo | 0
MProject1/Project1/main.c | 203+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
2 files changed, 120 insertions(+), 83 deletions(-)

diff --git a/Project1/.vs/Project1/v14/.atsuo b/Project1/.vs/Project1/v14/.atsuo Binary files differ. diff --git a/Project1/Project1/main.c b/Project1/Project1/main.c @@ -3,6 +3,7 @@ * * Created: 2024-10-09 11:21:00 * Authors: William Lindholm, Thomas Berry + * Advanced project */ #include <avr/io.h> @@ -14,25 +15,28 @@ /************************************************************************/ /* Precalculated Time & Voltage values */ /************************************************************************/ -#define THREE_VOLT 153 // 3*255 /5 = 153 -#define EIGHT_S 2441 // 20Mhz => 0.05 uS, 0.05 * 1024 = 51.2 uS, 0.125 S = 125 000 uS, 125 000 / 51.2 ~2441 counts -#define HALF_S 9766 // 20Mhz => 0.05 uS, 0.05 * 1024 = 51.2 uS, 0.5 S = 500 000 uS, 500 000 / 51.2 ~9766 counts +#define THREE_VOLT 614 // 3*1023 /5 = ~613 +#define EIGHT_S 2441 // 20Mhz => 0.05 uS, 0.05 * 1024 = 51.2 uS, 0.125 S = 125 000 uS, 125 000 / 51.2 ~2441 counts +#define HALF_S 9766 // 20Mhz => 0.05 uS, 0.05 * 1024 = 51.2 uS, 0.5 S = 500 000 uS, 500 000 / 51.2 ~9766 counts -#define SPLIT_LED 4 // When in split mode, split on led 4 +#define SPLIT_LED 4 // When in split mode, split on led 4 /************************************************************************/ -/* Global program modes */ +/* Global program modes */ /************************************************************************/ enum MODES { - VOLTAGE, // Show voltage reading (full display; 10 LED:s) - CYLON, // Show Cylon animation (full display; 10 LED:s) - SPLIT // Show Cylon animation (half display, 5 LED:s) & voltage reading (half display, 5 LED:s) + VOLTAGE, // Show voltage reading (full display; 10 LED:s) + CYLON, // Show Cylon animation (full display; 10 LED:s) + SPLIT // Show Cylon animation (half display, 5 LED:s) & voltage reading (half display, 5 LED:s) }; enum MODES DISPLAY_MODE = CYLON; +bool ADC_READING_READY = false; +uint16_t ADC_VALUE; + /************************************************************************/ /* LED PORT definitions */ @@ -47,8 +51,12 @@ struct LED_BITS LED_Array[10] = { {&PORTC, PIN5_bm}, {&PORTC, PIN4_bm}, {&PORTA, PIN0_bm}, {&PORTF, PIN5_bm}, {&PORTC, PIN6_bm}, {&PORTB, PIN2_bm}, {&PORTF, PIN4_bm}, {&PORTA, PIN1_bm}, {&PORTA, PIN2_bm}, {&PORTA, PIN3_bm} }; +// Precalculated values for non-split display are stored in precalculated_thresholds[0][0-9] +// Precalculated values for non-split display are stored in precalculated_thresholds[1][0-9] +uint16_t precalculated_thresholds[2][sizeof(LED_Array) / sizeof(LED_Array[0]) - 1]; + /************************************************************************/ -/* Function declarations */ +/* Function declarations */ /************************************************************************/ void clock_init (void); void set_leds_output(void); @@ -60,6 +68,7 @@ void configure_button_input(void); void display_voltage(void); void configure_TCB0(void); void configure_RTC(void); +void precalculate_voltage_thresholds(void); /************************************************************************/ @@ -68,21 +77,46 @@ void configure_RTC(void); int main(void) { clock_init(); + precalculate_voltage_thresholds(); set_leds_output(); - set_clr_leds(0); // ensure all LED:s are turned off by default + set_clr_leds(0); // ensure all LED:s are turned off by default configure_TCA0(); configure_ADC0(); configure_button_input(); configure_RTC(); - sei(); // Enable global interrupts + sei(); // Enable global interrupts - for (;;){} + while (true) + { + // show voltage reading + if (DISPLAY_MODE != CYLON && ADC_READING_READY) + { + display_voltage(); + ADC_READING_READY = false; // Reset flag, voltage reading used + } + } } /************************************************************************/ -/* Set the CPU frequency to 20MHz, default 6MHz */ +/* Precalculate the voltage values for the thermometer display */ +/************************************************************************/ +void precalculate_voltage_thresholds() { + // Calculate for full display + + uint8_t total_n_leds = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; + uint8_t split_n_leds = total_n_leds - (SPLIT_LED + 1); + + for (uint16_t i = 0; i < total_n_leds; i++) { + precalculated_thresholds[0][i] = (((i+1) * 1023) / (total_n_leds + 1)); // full display values are stored in arr[0] + precalculated_thresholds[1][i] = (((i+1) * 1023) / (split_n_leds + 1)); // split display values are stored in arr[1] + } +} + + +/************************************************************************/ +/* Set the CPU frequency to 20MHz, default 6MHz */ /************************************************************************/ void clock_init (void) { @@ -92,69 +126,70 @@ void clock_init (void) /************************************************************************/ -/* Set all led pins to output mode */ +/* Set all led pins to output mode */ /************************************************************************/ void set_leds_output() { - PORTC.DIR = PIN6_bm | PIN5_bm | PIN4_bm; // Set PORTC pin 4, 5 and 6 as output - PORTA.DIR = PIN3_bm | PIN2_bm | PIN1_bm | PIN0_bm; // Set PORTA pin 0, 1, 2 and 3 as output - PORTB.DIR = PIN2_bm; // Set PORTB pin 2 as output - PORTF.DIR = PIN5_bm | PIN4_bm; // Set PORTF pin 4 and 5 as output + PORTC.DIR = PIN6_bm | PIN5_bm | PIN4_bm; // Set PORTC pin 4, 5 and 6 as output + PORTA.DIR = PIN3_bm | PIN2_bm | PIN1_bm | PIN0_bm; // Set PORTA pin 0, 1, 2 and 3 as output + PORTB.DIR = PIN2_bm; // Set PORTB pin 2 as output + PORTF.DIR = PIN5_bm | PIN4_bm; // Set PORTF pin 4 and 5 as output } /************************************************************************/ -/* Enable and configure TCA0 */ +/* Enable and configure TCA0 */ /************************************************************************/ void configure_TCA0(void) { - TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; // Counter overflow interrupt option - TCA0.SINGLE.CTRLB = 0x00; // Normal Mode selected - TOP value in PER register - TCA0.SINGLE.EVCTRL = 0x00; // TCA0 can count events from the EVENT module - disable this option + TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; // Counter overflow interrupt option + TCA0.SINGLE.CTRLB = 0x00; // Normal Mode selected + TCA0.SINGLE.EVCTRL = 0x00; // TCA0 can count events from the EVENT module - disable this option - TCA0.SINGLE.PER = 9766; // The TCA0 counter will overflow when it reaches the PER value (count 9766) - TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1024_gc | TCA_SINGLE_ENABLE_bm; // Prescale set to /1024 and enable TCA0 (start count) + TCA0.SINGLE.PER = 9766; // The TCA0 counter will overflow when it reaches the PER value (count 9766 initially) + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1024_gc | TCA_SINGLE_ENABLE_bm; // Prescale set to /1024 and enable TCA0 (start count) } /************************************************************************/ -/* Enable and configure ADC0 */ +/* Enable and configure ADC0 */ /************************************************************************/ void configure_ADC0() { - ADC0.CTRLA = ADC_RESSEL_8BIT_gc | ADC_FREERUN_bm; // Set 8 bit resolution and free run mode - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_REFSEL0_bm | ADC_PRESC_DIV128_gc; // Enable SAMPCAP, Set reference voltage to 5V and set ADC prescaler to div 128 - ADC0.MUXPOS = ADC_MUXPOS_AIN3_gc; // Set input to Analog in 3 - ADC0.INTCTRL = ADC_RESRDY_bm; // Enable interrupt on result ready - ADC0.CTRLA |= ADC_ENABLE_bm; // Enable ADC - ADC0.COMMAND = ADC_STCONV_bm; // Start first measurement + ADC0.CTRLA = ADC_RESSEL_10BIT_gc | ADC_FREERUN_bm; // Set 8 bit resolution and free run mode + ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_REFSEL_VDDREF_gc | ADC_PRESC_DIV64_gc; // Enable SAMPCAP, Set reference voltage to VDD, Set ADC prescaler to div 128 + ADC0.MUXPOS = ADC_MUXPOS_AIN3_gc; // Set input to Analog in 3 + ADC0.INTCTRL = ADC_RESRDY_bm; // Enable interrupt on result ready + ADC0.CTRLD = ADC_INITDLY_DLY16_gc; // Initial delay of 16 cycles + ADC0.CTRLA |= ADC_ENABLE_bm; // Enable ADC + ADC0.COMMAND = ADC_STCONV_bm; // Start first measurement } /************************************************************************/ -/* Configure PORTE bit 1 button */ +/* Configure PORTE bit 1 button */ /************************************************************************/ void configure_button_input() { - PORTE.DIRCLR = (1<<1); // Set port E pin 1 to input mode - PORTE.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // Enable pull up resistor, set interrupt to both edges + PORTE.DIRCLR = (1<<1); // Set port E pin 1 to input mode + PORTE.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // Enable pull up resistor, set interrupt to both edges } /************************************************************************/ -/* Configure and enable Real Time Counter */ +/* Configure and enable Real Time Counter */ /************************************************************************/ void configure_RTC() { // 16 / (1/1024hz) = 16384 cycles for 16 second timer - RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // Select Internal 1.024 kHz oscillator - RTC.PITCTRLA = RTC_PERIOD_CYC16384_gc | (1<<0); // Interrupt every 16384 cycles, Periodic Interrupt Timer enabled - RTC.PITINTCTRL = (1<<0); // The periodic interrupt is enabled - RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (1<<0); // Select prescaler div1 and enable + RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // Select Internal 1.024 kHz oscillator + RTC.PITCTRLA = RTC_PERIOD_CYC16384_gc | (1<<0); // Interrupt every 16384 cycles, Periodic Interrupt Timer enabled + RTC.PITINTCTRL = (1<<0); // The periodic interrupt is enabled + RTC.CTRLA = RTC_PRESCALER_DIV1_gc | (1<<0); // Select prescaler div1 and enable } /************************************************************************/ -/* Set or clear all LED:s or a range of LED:s */ +/* Set or clear all LED:s or a range of LED:s */ /************************************************************************/ void set_clr_leds(bool set) { @@ -198,8 +233,8 @@ void display_voltage() if (DISPLAY_MODE == SPLIT) { - bot_led = SPLIT_LED + 1; // Start from SPLIT_LED - top_led = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; // Use LEDs from SPLIT_LED to the end + bot_led = SPLIT_LED + 1; // Start from SPLIT_LED + top_led = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; // Use LEDs from SPLIT_LED to the end } else { @@ -207,16 +242,19 @@ void display_voltage() top_led = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; } - uint8_t n_leds = top_led - bot_led + 1; // Number of LEDs used for voltage display - - set_clr_leds_range(0, bot_led, top_led); // Clear only the LEDs used for voltage display + uint8_t n_leds = top_led - bot_led + 1; // Number of LEDs used for voltage display - uint8_t scaled_value = (ADC0.RES * n_leds) / 255; // Scale ADC value to the number of LEDs - - for (uint8_t i = bot_led; i < bot_led + scaled_value; i++) - { - LED_Array[i].LED_PORT->OUTSET = LED_Array[i].bit_mapping; + set_clr_leds_range(0, bot_led, top_led); // Clear only the LEDs used for voltage display + + + uint8_t i; + for (i = 0; i < n_leds; i++) { + if (ADC_VALUE < precalculated_thresholds[DISPLAY_MODE == SPLIT ? 1 : 0][i]) { + break; + } } + + set_clr_leds_range(1, bot_led, bot_led + i); } @@ -228,8 +266,8 @@ void display_cylon() uint8_t top_led; uint8_t bot_led = 0; - static uint8_t i = 0; // Index of the current LED - static bool direction = 1; // direction of the cylon. 0 = left, 1 = right + static uint8_t i = 0; // Index of the current LED + static bool direction = 1; // direction of the cylon. 0 = left, 1 = right // Handle split display mode if (DISPLAY_MODE == SPLIT) @@ -238,13 +276,13 @@ void display_cylon() } else { - top_led = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; // length of a LED array + top_led = sizeof(LED_Array) / sizeof(LED_Array[0]) - 1; // length of a LED array } - // Switch off the current LED - LED_Array[i].LED_PORT->OUTCLR = LED_Array[i].bit_mapping; - + // Switch off all cylon LED:s + set_clr_leds_range(0, bot_led, top_led); + // If i ends up outside the range (during switch to split) reset position if (i < bot_led || i > top_led) { i = bot_led; @@ -269,23 +307,29 @@ void display_cylon() /************************************************************************/ ISR(PORTE_PORT_vect) { - if ((PORTE.IN & PIN1_bm) == 0) // Falling edge + PORTE.INTFLAGS = PIN1_bm; // Clear interrupt flag + + if (DISPLAY_MODE == SPLIT) // Ignore button interrupt when in split mode { - DISPLAY_MODE = VOLTAGE; + return; } - else // Rising edge + + if ((PORTE.IN & PIN1_bm) == 0) // Falling edge { + //set_clr_leds(0); // Clear + DISPLAY_MODE = VOLTAGE; + } + else // Rising edge + { DISPLAY_MODE = CYLON; - set_clr_leds(0); // Clear voltage reading from display - display_cylon(); // restart cylon (prevent delay) + set_clr_leds(0); // Clear voltage reading from display + display_cylon(); // restart cylon (prevent delay) } - - PORTE.INTFLAGS = PIN1_bm; // Clear interrupt flag } /************************************************************************/ -/* Timer Counter A ISR (trigger cylon animation) */ +/* Timer Counter A ISR (trigger cylon animation) */ /************************************************************************/ ISR(TCA0_OVF_vect) { @@ -294,52 +338,45 @@ ISR(TCA0_OVF_vect) display_cylon(); } - TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // Clear interrupt flag + TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // Clear interrupt flag } /************************************************************************/ -/* ADC0 result ready ISR (handle voltage reading measurements) */ +/* ADC0 result ready ISR (handle voltage reading measurements) */ /************************************************************************/ ISR(ADC0_RESRDY_vect) { // adjust speed of cylon based on voltage - if (ADC0.RESL <= THREE_VOLT) + if (ADC0.RES <= THREE_VOLT) { - // reset counter to prevent PER being set to lower value than CNT during PER change - if (TCA0.SINGLE.PER == HALF_S) - { - TCA0.SINGLE.CNT = 0; - } - TCA0.SINGLE.PER = EIGHT_S; + TCA0.SINGLE.PERBUF = EIGHT_S; } else { - TCA0.SINGLE.PER = HALF_S; + TCA0.SINGLE.PERBUF = HALF_S; } - // show voltage reading - if (DISPLAY_MODE != CYLON) - { - display_voltage(); - } + ADC_READING_READY = true; // Set flag informing that new voltage value is ready + ADC_VALUE = ADC0.RES; // Store the reading of the ADC result in a global variable - ADC0.INTFLAGS = ADC_RESRDY_bm; // Clear interrupt flag + ADC0.INTFLAGS = ADC_RESRDY_bm; // Clear interrupt flag } /************************************************************************/ -/* ISR for RTC, Change to split mode every 16 seconds */ +/* ISR for RTC, Change to split mode every 16 seconds */ /************************************************************************/ ISR(RTC_PIT_vect) { if (DISPLAY_MODE == SPLIT) { - set_clr_leds(0); // Clear voltage reading after changing back to cylon mode + set_clr_leds(0); // Clear voltage reading after changing back to cylon mode DISPLAY_MODE = CYLON; } else { + set_clr_leds(0); // Clear lingering LED:s DISPLAY_MODE = SPLIT; } - RTC.PITINTFLAGS = (1<<0); // Clear interrupt flag + RTC.PITINTFLAGS = (1<<0); // Clear interrupt flag } \ No newline at end of file