From 3fb309951f0c2ca6f516f854f1ab942179f28b47 Mon Sep 17 00:00:00 2001 From: Rutherther <rutherther@ditigal.xyz> Date: Thu, 28 Nov 2024 15:44:05 +0100 Subject: [PATCH] feat(arm03): implement display --- arm03/include/display.h | 44 +++++++++++++ arm03/src/display.c | 139 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 arm03/include/display.h create mode 100644 arm03/src/display.c diff --git a/arm03/include/display.h b/arm03/include/display.h new file mode 100644 index 0000000..5aec4cc --- /dev/null +++ b/arm03/include/display.h @@ -0,0 +1,44 @@ +#include <stdint.h> +#include "pin.h" + +#ifndef DISPLAY_H +#define DISPLAY_H + +typedef struct { + // Display board configuration + uint16_t digits_count; + pin_t pin_data; + pin_t pin_sftclk; + pin_t pin_strobe; + + // Current application data + uint32_t bcd_digits; + uint8_t digit_dots; + + // Current application state + uint8_t digits_en; + + // Current internal state of the display + uint16_t current_cycle; + uint32_t current_shifter; + uint8_t current_digit; + uint16_t max_value; +} display_t; + +typedef enum { + DISPLAY_UPDATE_NEW_DIGIT, + DISPLAY_UPDATE_DISPLAY_DIGIT, + DISPLAY_UPDATE_SHIFT, +} display_update_state_t; + +void display_init(display_t* display, uint16_t digits, pin_t pin_data, pin_t pin_sftclk, pin_t pin_strobe); + +void display_convert_number(display_t* display, uint32_t number); +void display_digit_set(display_t* display, uint8_t digit, uint8_t number); + +void display_dots(display_t* display, uint8_t dots); +void display_enable_digit(display_t* display, uint8_t digit, uint8_t enable); + +display_update_state_t display_update(display_t* display); + +#endif // DISPLAY_H diff --git a/arm03/src/display.c b/arm03/src/display.c new file mode 100644 index 0000000..4c77e5e --- /dev/null +++ b/arm03/src/display.c @@ -0,0 +1,139 @@ +#include "display.h" +#include "registers.h" + +#define CYCLES 17 + +const char numbers_seven_segments[11] = { + 0x3F, /* 0 */ + 0x06, /* 1 */ + 0x5B, /* 2 */ + 0x4F, /* 3 */ + 0x66, /* 4 */ + 0x6D, /* 5 */ + 0x7D, /* 6 */ + 0x07, /* 7 */ + 0x7F, /* 8 */ + 0x6F, /* 9 */ + 0x40, /* - */ +}; + +uint16_t pow16(uint16_t num, uint8_t pow) { + for (uint16_t i = 0; i < pow; i++) { + num *= num; + } + return num; +} + + +void display_init(display_t *display, uint16_t digits, pin_t pin_data, + pin_t pin_sftclk, pin_t pin_strobe) { + display->digits_count = digits; + display->bcd_digits = 0; + + display->digits_en = ~0; + display->digit_dots = ~0; + + display->pin_data = pin_data; + display->pin_sftclk = pin_sftclk; + display->pin_strobe = pin_strobe; + + display->max_value = pow16(10, digits); + display->current_cycle = 0; + display->current_shifter = 0; + display->current_digit = 0; +} + +/** + * @brief Convert a digit from a regular number to seven segments + * @param[in] number The number to convert + * @param[in] digit The digit from least significant, starting with 0 + * @return Description + */ +uint16_t seven_segment_convert(uint16_t number, uint16_t digit, uint8_t dot); + +/** + * @brief Converts a number to serial strem to send via the SIPO register. + * @param[in] number The bcd number to convert to the values for shift register + * @param[in] digit The digit from the least significant, starting with 0 + * @return Description + */ +uint16_t convert_num_digit(uint16_t bcd_number, uint8_t digits_count, uint8_t digit, uint8_t dot); + +uint16_t seven_segment_convert(uint16_t bcd_number, uint16_t digit, uint8_t dot) { + return ~(numbers_seven_segments[(bcd_number >> (digit << 2)) & 0xF] | dot << 7); +} + +uint16_t convert_num_digit(uint16_t number, uint8_t digits_count, uint8_t digit, uint8_t dot) { + uint16_t seven_segments = seven_segment_convert(number, digits_count - 1 - digit, dot); + uint16_t digits = 1 << digit; + + return (seven_segments << 8) | digits; +} + +void display_dots(display_t* display, uint8_t dots) { + display->digit_dots = dots; +} + +void display_convert_number(display_t *display, uint32_t number) { + uint32_t bcd = 0; + for (uint8_t i = 0; i < display->digits_count; i++) { + bcd |= (number % 10) << (i << 2); + number /= 10; + } + display->bcd_digits = bcd; +} + +void display_enable_digit(display_t *display, uint8_t digit, uint8_t enable) { + uint16_t digits = 1 << digit; + if (enable != 0) { + display->digits_en |= digits; + } else { + display->digits_en &= ~digits; + } +} + +void display_digit_set(display_t *display, uint8_t digit, uint8_t number) { + reg_write_bits_pos(&display->bcd_digits, number, digit << 2, 0xF); +} + +display_update_state_t display_update(display_t *display) { + display_update_state_t state = DISPLAY_UPDATE_SHIFT; + + if (display->current_cycle == 0) { + // Set up. + display->current_digit = (display->current_digit + 1) % display->digits_count; + + if (display->digits_en & (1 << display->current_digit)) { + display->current_shifter = convert_num_digit( + display->bcd_digits, display->digits_count, display->current_digit, + (display->digit_dots >> display->current_digit) & 1); + } else { + // Still send the data even for disabled digits, + // to prevent display having different brightness + // depending on how many digits are currently on. + display->current_shifter = 0; + } + + state = DISPLAY_UPDATE_NEW_DIGIT; + } + + if (display->current_cycle < CYCLES - 1) { + pin_write(&display->pin_data, + reg_read_bits_pos(&display->current_shifter, + 15 - display->current_cycle, + 1)); + + pin_set(&display->pin_sftclk); + pin_reset(&display->pin_sftclk); + } else { + // Send strobe + pin_set(&display->pin_strobe); + pin_reset(&display->pin_strobe); + + state = DISPLAY_UPDATE_DISPLAY_DIGIT; + } + + display->current_cycle = (display->current_cycle + 1) % CYCLES; + + return state; +} -- 2.48.1