aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--justfile8
-rw-r--r--src/button.c51
-rw-r--r--src/button.h17
-rw-r--r--src/display.c34
-rw-r--r--src/display.h14
-rw-r--r--src/main.c2
-rw-r--r--src/mode.c74
-rw-r--r--src/mode.h22
-rw-r--r--src/rtc.c23
-rw-r--r--src/rtc.h4
-rw-r--r--test/button_test.c67
-rw-r--r--test/justfile13
-rw-r--r--test/stubbed/avr/io.h46
-rw-r--r--test/stubbed/testindex.c26
14 files changed, 309 insertions, 92 deletions
diff --git a/justfile b/justfile
index e08c5fb..67ac3f0 100644
--- a/justfile
+++ b/justfile
@@ -1,6 +1,8 @@
set export
set unstable
+mod test
+
default:
just --choose || true
@@ -8,7 +10,7 @@ AVR_LIBC := env("AVR_LIBC", "/usr/lib/avr")
F_CPU := "32768"
MCU := "attiny84a"
MCU_PART := "attiny84"
-SRCS := "src/main.c src/display.c src/rtc.c src/button.c"
+SRCS := "src/main.c src/display.c src/rtc.c src/button.c src/mode.c"
OUTDIR := "./out"
PROGRAMMER := "arduino"
UPLOAD_SPEED := "19200"
@@ -40,10 +42,6 @@ write-fuses: check-hexfile-exists
read-fuses:
{{AVRDUDE}} -U lfuse:r:-:h -U hfuse:r:-:h -U efuse:r:-:h
-test:
- gcc -g -std=gnu99 -DSTUBBED=1 -I./src -I./test/stubbed/ -Wall -Wno-unused-variable {{SRCS}} -o {{OUTDIR}}/test
- {{OUTDIR}}/test
-
clean:
rm -rf {{OUTDIR}}
diff --git a/src/button.c b/src/button.c
index dc52265..f7c2a07 100644
--- a/src/button.c
+++ b/src/button.c
@@ -2,42 +2,27 @@
#include <stdint.h>
#include "button.h"
-#include "rtc.h"
+#include "mode.h"
-enum ButtonState check_button_state();
-
-void button_tick() {
- enum ButtonState button_state = check_button_state();
- switch (button_state) {
- case BUTTON_PRESSED:
- if (seconds <= 10)
- seconds = 0;
- else
- seconds -= 10;
- break;
- case BUTTON_LONG_PRESSED:
- seconds += 10;
- break;
- default:
- break;
- }
-}
+void button_tick() { apply_button_state(check_button_state()); }
bool is_button_active = false;
uint16_t button_tick_count = 0;
+uint16_t button_inactive_for_ticks = 0;
enum ButtonState check_button_state() {
- if (is_button_active && button_tick_count >= LONG_PRESS_TICK_COUNT) {
- button_tick_count = 0;
- is_button_active = false;
- return BUTTON_LONG_PRESSED;
+ // Disable button for some time
+ if (button_inactive_for_ticks > 0) {
+ button_inactive_for_ticks--;
+ return ButtonIdle;
}
- bool in_debounce_period = is_button_active && button_tick_count > 0 &&
- button_tick_count < DEBOUNCE_TICK_COUNT;
+ bool was_active = is_button_active;
+ bool in_debounce_period =
+ button_tick_count > 0 && button_tick_count < DEBOUNCE_TICK_COUNT;
// Ignore readings from debounce period
- if (!in_debounce_period) {
+ if (!is_button_active || !in_debounce_period) {
DDRA &= ~(1 << PA0);
PORTA &= ~(1 << PA0);
is_button_active = (PINA & (1 << PA0)) == 0;
@@ -45,12 +30,22 @@ enum ButtonState check_button_state() {
if (is_button_active) {
button_tick_count++;
+ if (button_tick_count >= LONG_PRESS_TICK_COUNT) {
+ button_tick_count = 0;
+ is_button_active = false;
+ button_inactive_for_ticks = BUTTON_INACTIVE_TICK_COUNT;
+ return ButtonLongPressed;
+ }
+ return ButtonActive;
} else {
if (button_tick_count >= DEBOUNCE_TICK_COUNT) {
button_tick_count = 0;
- return BUTTON_PRESSED;
+ button_inactive_for_ticks = BUTTON_INACTIVE_TICK_COUNT;
+ return ButtonPressed;
}
+ if (!was_active)
+ button_tick_count = 0;
}
- return BUTTON_IDLE;
+ return ButtonIdle;
}
diff --git a/src/button.h b/src/button.h
index 8aa7442..5152336 100644
--- a/src/button.h
+++ b/src/button.h
@@ -1,19 +1,22 @@
#ifndef _LOADED_BUTTON
#define _LOADED_BUTTON
#include <stdbool.h>
-#include <stdint.h>
+
+#include "mode.h"
#define DEBOUNCE_TICK_COUNT 10
#define LONG_PRESS_TICK_COUNT 60
#define MAX_TICK_COUNT 80
+#define BUTTON_INACTIVE_TICK_COUNT 10
+
+bool extern is_button_active;
-enum ButtonState {
- BUTTON_IDLE = 0,
- BUTTON_PRESSED = 1,
- BUTTON_LONG_PRESSED = 2,
-};
+void button_tick();
+enum ButtonState check_button_state();
+
+// Exposed for test
uint16_t extern button_tick_count;
+uint16_t extern button_inactive_for_ticks;
-void button_tick();
#endif
diff --git a/src/display.c b/src/display.c
index 8380b81..3d83337 100644
--- a/src/display.c
+++ b/src/display.c
@@ -4,8 +4,17 @@
#include <util/delay.h>
#include "display.h"
+#include "mode.h"
#include "rtc.h"
+// "_gfedcba"
+// Reversed because a-g is mapped to pa0-6. (pa7 is decimal)
+// Complemented because display is common anode
+static const uint32_t digit_masks[] = {
+ 0b1000000, 0b1111001, 0b0100100, 0b0110000, 0b0011001,
+ 0b0010010, 0b0000010, 0b1111000, 0b0000000, 0b0010000,
+};
+
void display_setup() {
// Set prescaler CS01 + CS00 (F_CPU / 64) + CTC
// For 32768Hz -> 32768/64 = 512Hz
@@ -26,11 +35,27 @@ void display_setup() {
PORTB = (1 << PB2);
}
+void write_time(uint8_t digit_index, const uint8_t digits[4]);
+
+// Current digit position
volatile uint8_t current_digit = 0;
// Render current digit
void display_render(void) {
- write_time(current_digit);
+ switch (current_mode) {
+ case ModeDisplayTime: {
+ uint8_t digits[] = {minutes_digit1, minutes_digit2, seconds_digit1,
+ seconds_digit2};
+ write_time(current_digit, digits);
+ break;
+ }
+ case ModeSetTime: {
+ uint8_t digits[] = {new_hours / 10, new_hours % 10, new_minutes / 10,
+ new_minutes % 10};
+ write_time(current_digit, digits);
+ break;
+ }
+ }
// Cycle through the 4 digits
current_digit = (current_digit + 1) % 4;
@@ -53,14 +78,11 @@ static uint8_t special_segment_pins[] = {5, 10, 6, 7};
// Digit position -> special segment anti-pin (pair of pin)
static uint8_t special_segment_antipins[] = {10, 5, 7, 6};
-void write_time(uint8_t digit_index) {
+void write_time(uint8_t digit_index, const uint8_t digits[4]) {
// reset pins
uint16_t ddr = ~SPECIAL_SEGMENT_PINS;
uint16_t port = 0;
- uint8_t digits[] = {seconds_digit1, seconds_digit2, minutes_digit1,
- minutes_digit2};
-
uint8_t digit = digits[digit_index];
uint16_t segments = digit_masks[digit];
uint8_t pin = special_segment_pins[digit_index];
@@ -92,7 +114,7 @@ void write_time(uint8_t digit_index) {
}
DDRA = ddr;
- DDRB = ddr >> 8;
PORTA = port;
+ DDRB = ddr >> 8;
PORTB = port >> 8;
}
diff --git a/src/display.h b/src/display.h
index 44c0c0e..0353705 100644
--- a/src/display.h
+++ b/src/display.h
@@ -1,22 +1,8 @@
#ifndef _LOADED_DISPLAY
#define _LOADED_DISPLAY
-#include <stdint.h>
-
-// "_gfedcba"
-// Reversed because a-g is mapped to pa0-6. (pa7 is decimal)
-// Complemented because display is common anode
-static const uint32_t digit_masks[] = {
- 0b1000000, 0b1111001, 0b0100100, 0b0110000, 0b0011001,
- 0b0010010, 0b0000010, 0b1111000, 0b0000000, 0b0010000,
-};
-
-// Current digit position
-volatile extern uint8_t current_digit;
void display_setup(void);
-void write_time(uint8_t digit_index);
-
void display_render(void);
#endif
diff --git a/src/main.c b/src/main.c
index 98b63a2..2e20b7b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -28,7 +28,7 @@ ISR(TIM0_COMPA_vect, ISR_FLATTEN) {
ISR(TIM1_COMPA_vect, ISR_FLATTEN) { rtc_increment(); }
// Button interrupt
-ISR(PCINT0_vect, ISR_FLATTEN) { button_tick(); }
+// ISR(PCINT0_vect, ISR_FLATTEN) { button_tick(); }
int main(void) {
PRR = 0b0011; // Power reduction register. Shut down USI and ADC
diff --git a/src/mode.c b/src/mode.c
new file mode 100644
index 0000000..b6fad97
--- /dev/null
+++ b/src/mode.c
@@ -0,0 +1,74 @@
+#include <stdint.h>
+
+#include "mode.h"
+#include "rtc.h"
+
+enum Mode current_mode = ModeDisplayTime;
+volatile uint8_t new_minutes = 0;
+volatile uint8_t new_hours = 0;
+volatile uint8_t current_digit_index = 0;
+volatile uint16_t time_set_timeout_ticks = 0;
+
+void increment_digit() {
+ switch (current_digit_index) {
+ case 0:
+ new_hours = (new_hours + 10) % 24;
+ break;
+ case 1:
+ new_hours = (new_hours + 1) % 24;
+ break;
+ case 2:
+ new_minutes = (new_minutes + 10) % 60;
+ break;
+ case 3:
+ new_minutes = (new_minutes + 1) % 60;
+ break;
+ }
+}
+
+void apply_button_state(enum ButtonState button_state) {
+ switch (current_mode) {
+ case ModeDisplayTime:
+ switch (button_state) {
+ case ButtonLongPressed:
+ new_minutes = minutes;
+ new_hours = hours;
+ current_digit_index = 0;
+ current_mode = ModeSetTime;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ModeSetTime:
+ if (button_state == ButtonIdle) {
+ time_set_timeout_ticks =
+ time_set_timeout_ticks == 0 ? 0 : time_set_timeout_ticks - 1;
+ if (time_set_timeout_ticks == 0) {
+ current_mode = ModeDisplayTime;
+ return;
+ }
+ } else {
+ time_set_timeout_ticks = 10 * 1024;
+ }
+
+ switch (button_state) {
+ case ButtonPressed:
+ increment_digit();
+ break;
+ case ButtonLongPressed:
+ if (current_digit_index == 3) {
+ rtc_set_minutes(new_minutes);
+ rtc_set_hours(new_hours);
+ current_mode = ModeDisplayTime;
+ current_digit_index = 0;
+ } else {
+ current_digit_index++;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+}
diff --git a/src/mode.h b/src/mode.h
new file mode 100644
index 0000000..db65fc8
--- /dev/null
+++ b/src/mode.h
@@ -0,0 +1,22 @@
+#ifndef _LOADED_MODE
+#define _LOADED_MODE
+#include <stdint.h>
+enum Mode {
+ ModeDisplayTime,
+ ModeSetTime,
+};
+
+enum ButtonState {
+ ButtonIdle = 0,
+ ButtonActive = 1,
+ ButtonPressed = 2,
+ ButtonLongPressed = 3,
+};
+
+extern enum Mode current_mode;
+extern volatile uint8_t new_minutes;
+extern volatile uint8_t new_hours;
+
+void apply_button_state(enum ButtonState button_state);
+
+#endif
diff --git a/src/rtc.c b/src/rtc.c
index 53a5983..bbf79e1 100644
--- a/src/rtc.c
+++ b/src/rtc.c
@@ -38,13 +38,20 @@ void rtc_increment(void) {
seconds_digit2 = (seconds / 10) % 10;
if (s >= 60) {
uint8_t m = minutes + 1;
- minutes = m % 60;
- minutes_digit1 = minutes % 10;
- minutes_digit2 = (minutes / 10) % 10;
- if (m >= 60) {
- hours = (hours + 1) % 24;
- hours_digit1 = hours % 10;
- hours_digit2 = (hours / 10) % 10;
- }
+ rtc_set_minutes(m);
+ if (m >= 60)
+ rtc_set_hours(hours + 1);
}
}
+
+void rtc_set_minutes(uint8_t m) {
+ minutes = m % 60;
+ minutes_digit1 = minutes % 10;
+ minutes_digit2 = (minutes / 10) % 10;
+}
+
+void rtc_set_hours(uint8_t h) {
+ hours = h % 24;
+ hours_digit1 = hours % 10;
+ hours_digit2 = (hours / 10) % 10;
+}
diff --git a/src/rtc.h b/src/rtc.h
index c1c1730..991c188 100644
--- a/src/rtc.h
+++ b/src/rtc.h
@@ -17,4 +17,8 @@ void rtc_setup(void);
void rtc_increment(void);
+void rtc_set_minutes(uint8_t m);
+
+void rtc_set_hours(uint8_t h);
+
#endif
diff --git a/test/button_test.c b/test/button_test.c
new file mode 100644
index 0000000..3c48e43
--- /dev/null
+++ b/test/button_test.c
@@ -0,0 +1,67 @@
+#include "button.h"
+#include "mode.h"
+#include <assert.h>
+#include <avr/io.h>
+
+void test_button_press() {
+ assert(check_button_state() == ButtonIdle);
+
+ PINA &= ~(1 << PA0); // Button is pressed
+ assert(check_button_state() == ButtonActive);
+ for (int i = 0; i < DEBOUNCE_TICK_COUNT; i++)
+ assert(check_button_state() == ButtonActive);
+
+ PINA |= (1 << PA0); // Button is released
+
+ assert(check_button_state() == ButtonPressed); // Detects button press
+ assert(check_button_state() == ButtonIdle); // Resets to idle
+}
+
+void test_button_long_press() {
+ assert(check_button_state() == ButtonIdle);
+
+ // Button is pressed for a while
+ PINA &= ~(1 << PA0);
+ for (int i = 0; i < LONG_PRESS_TICK_COUNT - 1; i++)
+ assert(check_button_state() == ButtonActive);
+
+ assert(check_button_state() == ButtonLongPressed); // Detects button press
+
+ assert(check_button_state() == ButtonIdle); // Resets to idle
+}
+
+void test_button_inactive_after_press() {
+ assert(check_button_state() == ButtonIdle);
+
+ // Button is pressed
+ PINA &= ~(1 << PA0);
+ for (int i = 0; i < DEBOUNCE_TICK_COUNT; i++)
+ assert(check_button_state() == ButtonActive);
+ PINA |= (1 << PA0); // Button is released
+ assert(check_button_state() == ButtonPressed);
+ assert(check_button_state() == ButtonIdle); // Resets to idle
+
+ // Button is pressed again
+ PINA &= ~(1 << PA0);
+
+ // Button stays inactive for some time
+ for (int i = 0; i < BUTTON_INACTIVE_TICK_COUNT - 1; i++)
+ assert(check_button_state() == ButtonIdle);
+ // Button becomes active again after delay
+ assert(check_button_state() == ButtonActive);
+}
+
+void before_each() {
+ PINA = (1 << PA0); // Active on low
+ button_tick_count = 0;
+ button_inactive_for_ticks = 0;
+}
+#define TEST before_each();
+
+int main() {
+ TEST test_button_press();
+ TEST test_button_long_press();
+ TEST test_button_inactive_after_press();
+ return 0;
+}
+
diff --git a/test/justfile b/test/justfile
new file mode 100644
index 0000000..626e094
--- /dev/null
+++ b/test/justfile
@@ -0,0 +1,13 @@
+OUTDIR := "../out"
+TSRCS := "../src/display.c ../src/rtc.c ../src/button.c ../src/mode.c ./stubbed/testindex.c"
+
+TCFLAGS := f"-g -std=gnu99 -DSTUBBED=1 -I../src -I./stubbed/ -Wall -Wno-unused-variable {{TSRCS}}"
+
+main:
+ gcc {{TCFLAGS}} ../src/main.c -o {{OUTDIR}}/test_main
+ {{OUTDIR}}/test_main
+
+button:
+ gcc {{TCFLAGS}} ./button_test.c -o {{OUTDIR}}/test_button
+ {{OUTDIR}}/test_button
+
diff --git a/test/stubbed/avr/io.h b/test/stubbed/avr/io.h
index 40cc460..c056ef1 100644
--- a/test/stubbed/avr/io.h
+++ b/test/stubbed/avr/io.h
@@ -2,13 +2,13 @@
#define STUB_AVR_IO
#include <stdio.h>
-static int DDRA = 0;
-static int DDRB = 0;
+extern int DDRA;
+extern int DDRB;
-static int PORTA = 0;
-static int PORTB = 0;
-static int PINA = 0;
-static int PINB = 0;
+extern int PORTA;
+extern int PORTB;
+extern int PINA;
+extern int PINB;
#define PA0 0
#define PA1 1
@@ -24,23 +24,23 @@ static int PINB = 0;
#define PB3 3
#define PB4 4
-static int PRR = 0;
-static int TCCR0B = 0;
-static int TCCR0A = 0;
-static int TIMSK0 = 0;
-static int TIFR0 = 0;
-static int OCR0A = 0;
-static int OCR0B = 0;
-static int TCNT0 = 0;
-static int TCCR1B = 0;
-static int TCCR1A = 0;
-static int TIMSK1 = 0;
-static int TIFR1 = 0;
-static int OCR1A = 0;
-static int OCR1B = 0;
-static int TCNT1 = 0;
-static int TCNT1H = 0;
-static int TCNT1L = 0;
+extern int PRR;
+extern int TCCR0B;
+extern int TCCR0A;
+extern int TIMSK0;
+extern int TIFR0;
+extern int OCR0A;
+extern int OCR0B;
+extern int TCNT0;
+extern int TCCR1B;
+extern int TCCR1A;
+extern int TIMSK1;
+extern int TIFR1;
+extern int OCR1A;
+extern int OCR1B;
+extern int TCNT1;
+extern int TCNT1H;
+extern int TCNT1L;
#define CS00 1
#define CS01 1
diff --git a/test/stubbed/testindex.c b/test/stubbed/testindex.c
new file mode 100644
index 0000000..e3c8866
--- /dev/null
+++ b/test/stubbed/testindex.c
@@ -0,0 +1,26 @@
+
+int DDRA = 0;
+int DDRB = 0;
+
+int PORTA = 0;
+int PORTB = 0;
+int PINA = 0;
+int PINB = 0;
+
+int PRR = 0;
+int TCCR0B = 0;
+int TCCR0A = 0;
+int TIMSK0 = 0;
+int TIFR0 = 0;
+int OCR0A = 0;
+int OCR0B = 0;
+int TCNT0 = 0;
+int TCCR1B = 0;
+int TCCR1A = 0;
+int TIMSK1 = 0;
+int TIFR1 = 0;
+int OCR1A = 0;
+int OCR1B = 0;
+int TCNT1 = 0;
+int TCNT1H = 0;
+int TCNT1L = 0;