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:
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