From 99175efc8c73e89b52bd4de2ce8236fb3c9406bc Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 16 Sep 2021 22:02:00 +0200 Subject: [PATCH 1/5] Add basic TWI support To support basic TWI support some defines are created to keep track of port, pins and address. Fundamental procedures such as: - `twi_error()` - `twi_start()` - `twi_transmit()` - `twi_receive()` - `twi_stop()` are created. Those procedures are used in `twi_read_register()` and `twi_write_register()` which read and write data from and to registers respectively. Lastly the TWI bit rate is set to 200kHz. --- firmware/src/main.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/firmware/src/main.c b/firmware/src/main.c index 44d33da..3ef0c76 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,12 @@ #define ENC_A (PINB & (1 << PB6)) #define ENC_B (PINB & (1 << PB7)) +#define TWI_PORT PORTC +#define TWI_SDA PC4 +#define TWI_SCL PC5 + +#define SI5351_ADDRESS (0x60 << 1) + #define SYM_ENTRY(SYM) { SYM, sizeof(SYM) / 2 } static uint8_t EEMEM eeprom_contrast = 8; @@ -312,6 +319,83 @@ static void lcd_splash(void) { } } +static void twi_error(void) { + lcd_fill(0xFF); + + for (;;); +} + +static void twi_start(void) { + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Start transmission + while (!(TWCR & (1 << TWINT))); // Wait until start is transmitted + + if (TW_STATUS != TW_START) // Check status + twi_error(); +} + +static uint8_t twi_transmit(const uint8_t data) { + TWDR = data; + TWCR = (1 << TWINT) | (1 << TWEN); // Transmit data + while (!(TWCR & (1 << TWINT))); // Wait until data is transmitted + + return TW_STATUS; +} + +static uint8_t twi_receive(void) { + TWCR = (1 << TWINT) | (1 << TWEN); // Receive data + while (!(TWCR & (1 << TWINT))); // Wait until data is received + + return TW_STATUS; +} + +static void twi_stop(void) { + TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); +} + +static uint8_t twi_read_register(const uint8_t address, + const uint8_t reg) { + twi_start(); + + if (twi_transmit(address) != TW_MT_SLA_ACK) + twi_error(); + + if (twi_transmit(reg) != TW_MT_DATA_ACK) + twi_error(); + + twi_stop(); + + _delay_us(2); + + twi_start(); + + if (twi_transmit(address + 1) != TW_MR_SLA_ACK) + twi_error(); + + if (twi_receive() != TW_MR_DATA_NACK) + twi_error(); + + twi_stop(); + + return TWDR; +} + +static void twi_write_register(const uint8_t address, + const uint8_t reg, + const uint8_t data) { + twi_start(); + + if (twi_transmit(address) != TW_MT_SLA_ACK) + twi_error(); + + if (twi_transmit(reg) != TW_MT_DATA_ACK) + twi_error(); + + if (twi_transmit(data) != TW_MT_DATA_ACK) + twi_error(); + + twi_stop(); +} + static void lcd_home(void) { lcd_fill(0x00); @@ -682,6 +766,12 @@ int main(void) { // Enable interrupts sei(); + // Set TWI bit rate to 200kHz + TWBR = 12; + + (void) &twi_read_register; + (void) &twi_write_register; + // Run... for (;;); } From 664aa4bad5c7f03a3fe5e696117c86bb4420ee35 Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 23 Sep 2021 22:45:46 +0200 Subject: [PATCH 2/5] Add underscore and extend symbol lookup table To be able to print the underscore symbol `_` the symbol is added and the symbol lookup table is extended. --- firmware/src/main.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/firmware/src/main.c b/firmware/src/main.c index 3ef0c76..037a8f0 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -95,6 +95,9 @@ static const __flash uint8_t sym_t[] = { 0x0C, 0x0C, 0xFC, 0xFC, 0x0C, 0x0C, static const __flash uint8_t sym_u[] = { 0xFC, 0xFC, 0x00, 0x00, 0xFC, 0xFC, 0x3F, 0x3F, 0x30, 0x30, 0x3F, 0x3F }; +static const __flash uint8_t sym_underscore[] = { 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x20, 0x20, 0x20, 0x20 }; + static const __flash uint8_t sym_invalid[] = { 0x80, 0xE0, 0x98, 0xCC, 0x4C, 0x18, 0xE0, 0x80, 0x01, 0x07, 0x1F, 0x24, 0x25, 0x1F, 0x07, 0x01}; @@ -140,7 +143,18 @@ static const struct symbol symbol_table[] = { SYM_ENTRY(sym_0), SYM_ENTRY(sym_r), SYM_ENTRY(sym_s), SYM_ENTRY(sym_t), - SYM_ENTRY(sym_u) }; + SYM_ENTRY(sym_u), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_invalid), + SYM_ENTRY(sym_underscore), +}; enum input { cw, ccw, click, hold }; From 2579ae36869acdc7bfa06deb97d2293ecb31f910 Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 23 Sep 2021 22:47:17 +0200 Subject: [PATCH 3/5] Print actual status and data on TWI error The contents of `TWSR` (TWI Status Register) and `TWDR` (TWI Data Register) are printed when an error occured. --- firmware/src/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/firmware/src/main.c b/firmware/src/main.c index 037a8f0..fd8062a 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -336,6 +336,20 @@ static void lcd_splash(void) { static void twi_error(void) { lcd_fill(0xFF); + for (uint8_t i = 0; i < 2; i++) { + lcd_move_cursor(0, i); + lcd_write_string_page("TW_STATUS:\0", i, true); + lcd_write_kerning(2, true); + lcd_write_integer_page(TW_STATUS, 3, i, true); + } + + for (uint8_t i = 0; i < 2; i++) { + lcd_move_cursor(0, i + 2); + lcd_write_string_page("TW_DATA:\0", i, true); + lcd_write_kerning(16, true); + lcd_write_integer_page(TWDR, 3, i, true); + } + for (;;); } From 3aa458c4bc0d05ccd049dfa6ff3e61f105c6731e Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 23 Sep 2021 22:52:54 +0200 Subject: [PATCH 4/5] Furbish code and comments Add `TODO` comment to optimize waiting times, fix capitalization in comments, add blank line and break up long lines. To improve code semantics `lcd_home()` and `lcd_setup()` were renamed to `draw_home()` and `draw_setup()` respectively. Improve ordering of function calls in `main()`. --- firmware/src/main.c | 57 ++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/firmware/src/main.c b/firmware/src/main.c index fd8062a..29e7447 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -187,6 +187,7 @@ static void lcd_write(const uint8_t data) { } static void lcd_init(void) { + // TODO: Optimize waiting times SPI_DDR |= (1 << LCD_CD) | (1 << LCD_RST); _delay_ms(1); SPI_PORT |= (1 << LCD_RST); @@ -221,12 +222,12 @@ static void lcd_update_backlight(void) { case 3: case 4: case 5: - TCCR0B = 0x0C; // prescaler = 256; + TCCR0B = 0x0C; // Prescaler = 256; DDRD |= (1 << PD5); OCR0B = value_backlight - 1; break; default: - TCCR0B = 0x0B; // prescaler = 64; + TCCR0B = 0x0B; // Prescaler = 64; DDRD |= (1 << PD5); OCR0B = value_backlight - 4; break; @@ -301,6 +302,7 @@ static void lcd_write_integer_page(const uint8_t integer, uint16_t comperator = 1; for (; comperator <= integer; comperator *= 10, input_digits++); + for (int8_t i = digits - input_digits; i > 0; i--) { lcd_write_kerning(2, invert); lcd_write_digit_page(0, page, invert); @@ -424,7 +426,7 @@ static void twi_write_register(const uint8_t address, twi_stop(); } -static void lcd_home(void) { +static void draw_home(void) { lcd_fill(0x00); bool ch1_selected = false; @@ -466,7 +468,7 @@ static void lcd_home(void) { } } -static void lcd_setup(void) { +static void draw_setup(void) { lcd_fill(0x00); bool contrast_selected = false; @@ -505,7 +507,8 @@ static void lcd_setup(void) { lcd_write_string_page("CONTRAST:\0", i, contrast_selected); lcd_write_kerning(2, contrast_selected); lcd_write_kerning(16, false); - lcd_write_integer_page(value_contrast, 2, i, change_contrast_selected); + lcd_write_integer_page(value_contrast, 2, i, + change_contrast_selected); lcd_write_kerning(2, change_contrast_selected); } @@ -514,7 +517,8 @@ static void lcd_setup(void) { lcd_write_string_page("BACKLIGHT:\0", i, backlight_selected); lcd_write_kerning(2, backlight_selected); lcd_write_kerning(5, false); - lcd_write_integer_page(value_backlight, 3, i, change_backlight_selected); + lcd_write_integer_page(value_backlight, 3, i, + change_backlight_selected); lcd_write_kerning(2, change_backlight_selected); } @@ -528,7 +532,7 @@ static void lcd_setup(void) { static void change_state(const enum state new_state) { switch (new_state) { case home: - lcd_home(); + draw_home(); current_state = home; break; case ch1: @@ -539,7 +543,7 @@ static void change_state(const enum state new_state) { break; case setup: setup_state = contrast; - lcd_setup(); + draw_setup(); current_state = setup; break; } @@ -551,13 +555,13 @@ static void update_home(const enum input event) { home_state++; if (home_state > setup) home_state = ch1; - lcd_home(); + draw_home(); break; case ccw: home_state--; if (home_state > setup) home_state = setup; - lcd_home(); + draw_home(); break; case click: change_state(home_state); @@ -591,7 +595,7 @@ static void update_setup(const enum input event) { } break; } - lcd_setup(); + draw_setup(); break; case ccw: switch (setup_state) { @@ -615,17 +619,17 @@ static void update_setup(const enum input event) { } break; } - lcd_setup(); + draw_setup(); break; case click: switch (setup_state) { case contrast: setup_state = change_contrast; - lcd_setup(); + draw_setup(); break; case backlight: setup_state = change_backlight; - lcd_setup(); + draw_setup(); break; case back: change_state(home); @@ -633,12 +637,12 @@ static void update_setup(const enum input event) { case change_contrast: eeprom_update_byte(&eeprom_contrast, value_contrast); setup_state = contrast; - lcd_setup(); + draw_setup(); break; case change_backlight: eeprom_update_byte(&eeprom_backlight, value_backlight); setup_state = backlight; - lcd_setup(); + draw_setup(); break; } break; @@ -653,13 +657,13 @@ static void update_setup(const enum input event) { setup_state = contrast; value_contrast = eeprom_read_byte(&eeprom_contrast); lcd_update_contrast(); - lcd_setup(); + draw_setup(); break; case change_backlight: setup_state = backlight; value_backlight = eeprom_read_byte(&eeprom_backlight); lcd_update_backlight(); - lcd_setup(); + draw_setup(); break; } break; @@ -726,7 +730,7 @@ ISR(PCINT0_vect) { sei(); } -// encoder button interrupt +// Encoder button interrupt ISR(PCINT1_vect) { cli(); @@ -762,13 +766,12 @@ int main(void) { value_contrast = eeprom_read_byte(&eeprom_contrast); value_backlight = eeprom_read_byte(&eeprom_backlight); - // Init backlight: FastPWM: 1.25kHz - // TODO: Try to get the backlit even more dim + // Init backlight TCCR0A |= (1 << WGM01) | (1 << WGM00) | (1 << COM0B1); OCR0A = 255; lcd_update_backlight(); - // SPI setup + // SPI and LCD init spi_init(); lcd_init(); @@ -786,13 +789,12 @@ int main(void) { OCR1A = 65535; TIMSK1 |= (1 << OCIE1A); // Enable match compare A - // Show splash screen and load the menu + // Show splash screen lcd_splash(); _delay_ms(2000); - change_state(current_state); - // Enable interrupts - sei(); + // Load the menu + change_state(current_state); // Set TWI bit rate to 200kHz TWBR = 12; @@ -800,6 +802,9 @@ int main(void) { (void) &twi_read_register; (void) &twi_write_register; + // Enable interrupts + sei(); + // Run... for (;;); } From c0f8cac065634bf5ff77950bfd32578e56f9f1e9 Mon Sep 17 00:00:00 2001 From: finga Date: Thu, 23 Sep 2021 22:57:03 +0200 Subject: [PATCH 5/5] Proper debouncing and dechattering Move debouncing and dechattering to the beginning of the interrupt service routines to handle them properly. --- firmware/src/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/src/main.c b/firmware/src/main.c index 29e7447..27add27 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -691,6 +691,9 @@ static void update_state(const enum input event) { ISR(PCINT0_vect) { cli(); + // Dechatter + _delay_ms(3); + switch (enc) { case 0: if (ENC_A && !ENC_B) @@ -724,9 +727,6 @@ ISR(PCINT0_vect) { break; } - // TODO: proper dechattering - _delay_ms(3); - sei(); } @@ -734,6 +734,9 @@ ISR(PCINT0_vect) { ISR(PCINT1_vect) { cli(); + // Debounce + _delay_ms(2); + if (PINC & (1 << PC0)) { // Release if (TCCR1B & (1 << CS10)) // If release before hold update_state(click); // Switch to selected state @@ -745,9 +748,6 @@ ISR(PCINT1_vect) { TCCR1B |= (1 << CS11) | (1 << CS10); // Enable Timer/Counter1 } - // TODO: Proper debouncing - _delay_ms(3); - sei(); }