/* * Display.cpp * * Created on: Aug 12, 2023 * Author: Carst */ #include #include #include "STM32G071KBT6.hpp" #include "Display.hpp" /// address for character generator ram #define CMD_CGRAM_ADDR 0x40u #define pulse_delay() for(uint32_t i = 0UL; i < 1UL; i++); #define PIN_RS LL_GPIO_PIN_4 #define PIN_RW LL_GPIO_PIN_5 #define PIN_E LL_GPIO_PIN_6 #define PIN_BL LL_GPIO_PIN_7 namespace ElektronischeLast { void Display::init(void) { LL_GPIO_InitTypeDef init = { .Pin = LL_GPIO_PIN_0 | LL_GPIO_PIN_1 | LL_GPIO_PIN_2 | LL_GPIO_PIN_3 | PIN_RS | PIN_RW | PIN_E | PIN_BL, .Mode = LL_GPIO_MODE_OUTPUT, .Speed = LL_GPIO_SPEED_FREQ_LOW, .OutputType = LL_GPIO_OUTPUT_PUSHPULL, .Pull = LL_GPIO_PULL_NO, }; LL_GPIO_Init(GPIOB, &init); this->set_backlight(true); this->current_state = StateDelay; this->next_state = StateInit01; this->start_timer(15UL); // 15ms Startup Delay } void Display::run(void) { char *data; switch (this->current_state) { case StateIdle: if(this->active_line == ActiveLineNone) { if(this->new_data1) { this->new_data1 = false; this->active_line = ActiveLine1; this->next_state = StateWriteData; set_cursor(Line1, 0U); } else if(this->new_data2) { this->new_data2 = false; this->active_line = ActiveLine2; this->next_state = StateWriteData; set_cursor(Line2, 0U); } this->pointer = 0U; } break; case StateDelay: if(this->timer_elapsed()) { this->current_state = this->next_state; this->run(); } break; case StateWaitWhileBusy: break; case StateInit01: write_command_8bit_4pin(0x30u); this->start_timer(5UL); // 5ms Delay this->next_state = StateInit02; break; case StateInit02: write_command_8bit_4pin(0x30u); this->start_timer(1UL); // 1ms Delay this->next_state = StateInit03; break; case StateInit03: write_command_8bit_4pin(0x30u); this->start_timer(1UL); // 1ms Delay this->next_state = StateInit04; break; case StateInit04: // 4-bit mode write_command_8bit_4pin(0x20u); this->start_timer(1UL); this->next_state = StateInit05; break; case StateInit05: // 4-bit mode, 2 lines write_command_4bit(0x28u); this->start_timer(1UL); this->next_state = StateInit06; break; case StateInit06: // Display off write_command_4bit(0x08u); this->start_timer(1UL); this->next_state = StateInit07; break; case StateInit07: // Clear Display write_command_4bit(0x01u); this->start_timer(1UL); this->next_state = StateInit08; break; case StateInit08: // increment DDRAM write_command_4bit(0x06u); this->start_timer(1UL); this->next_state = StateInit09; break; case StateInit09: // return cursor home write_command_4bit(0x02u); this->start_timer(1UL); this->next_state = StateInit10; break; case StateInit10: // Display on, Cursor on, Blink on write_command_4bit(0x0Fu); this->start_timer(1UL); this->next_state = StateIdle; break; case StateWriteData: if(this->active_line == ActiveLine1) { data = this->line1; } else if(this->active_line == ActiveLine2) { data = this->line2; } write_data_4bit((std::uint8_t)data[this->pointer++]); if(data[this->pointer] == '\0') { this->next_state = StateIdle; this->active_line = ActiveLineNone; } else { this->next_state = StateWriteData; } break; default: break; } } void Display::set_backlight(bool on) { if(on) { LL_GPIO_SetOutputPin(GPIOB, PIN_BL); } else { LL_GPIO_ResetOutputPin(GPIOB, PIN_BL); } } void Display::set_cursor(Line_t line, std::uint32_t position) { lcd_cmd(0x80u | line | (position & 0x7F)); } /// \brief switch display on/off and set cursor mode /// \param[in] display \ref eDisplay_t /// \param[in] cursor \ref eCursor_t /// \param[in] blink \ref eCursorBlink_t void Display::lcd_set_display(eDisplay_t display, eCursor_t cursor, eCursorBlink_t blink) { lcd_cmd(0x08u | display | cursor | blink); } /// \brief set user defined character in display ram /// \details e.g. https://maxpromer.github.io/LCD-Character-Creator/ to generate character data /// \param[in] character \ref eUserDefinedCharakter_t /// \param[in] character_data pixel-data of character (8-Byte) void Display::lcd_set_user_chars(Display::UserDefinedCharakter_t character, const uint8_t character_data[]) { lcd_cmd((uint8_t)character | CMD_CGRAM_ADDR); for(uint8_t idx = 0u; idx < 8u; idx++) { while(!ready_for_data()) { run(); } lcd_data(character_data[idx]); } } void Display::print(Display::Line_t line, const char* const string) { if(line == Display::Line1) { strncpy(this->line1, string, sizeof(this->line1)); this->new_data1 = true; } else if(line == Display::Line2) { strncpy(this->line2, string, sizeof(this->line2)); this->new_data2 = true; } } bool Display::ready_for_data(void) { return (this->current_state == StateIdle); } bool Display::timer_elapsed(void) { return (this->timer < systick); } void Display::start_timer(std::uint32_t timeout) { this->timer = systick + 1U + timeout; // +1 damit auch mindestens 1ms vorbei geht. this->current_state = StateDelay; } void Display::write_command_8bit_4pin(std::uint32_t data) { // RS=low, RW=low LL_GPIO_ResetOutputPin(GPIOB, PIN_RS | PIN_RW | PIN_E); std::uint32_t tmp = data >> 4U; tmp |= ((~(data >> 4U) & 0xFU) << GPIO_BSRR_BR0_Pos); LL_GPIO_SetOutputPin(GPIOB, tmp); __NOP(); LL_GPIO_SetOutputPin(GPIOB, PIN_E); pulse_delay(); LL_GPIO_ResetOutputPin(GPIOB, PIN_E); } void Display::write_command_4bit(std::uint32_t command) { // RS=low, RW=low LL_GPIO_ResetOutputPin(GPIOB, PIN_RS | PIN_RW | PIN_E); std::uint32_t tmp = command >> 4U; tmp |= ((~(command >> 4U) & 0xFU) << GPIO_BSRR_BR0_Pos); LL_GPIO_SetOutputPin(GPIOB, tmp); __NOP(); LL_GPIO_SetOutputPin(GPIOB, PIN_E); pulse_delay(); LL_GPIO_ResetOutputPin(GPIOB, PIN_E); tmp = command & 0x0FU; tmp |= ((~command & 0xFU) << GPIO_BSRR_BR0_Pos); LL_GPIO_SetOutputPin(GPIOB, tmp); __NOP(); LL_GPIO_SetOutputPin(GPIOB, PIN_E); pulse_delay(); LL_GPIO_ResetOutputPin(GPIOB, PIN_E); } /// \brief write command to lcd and wait until busy is gone /// \param[in] cmd command to transfer void Display::lcd_cmd(uint8_t cmd) { write_command_4bit(cmd); this->start_timer(1UL); } /// \brief write data to display /// \param[in] data data which to write to display void Display::lcd_data(uint8_t data) { write_data_4bit(data); this->start_timer(1UL); } /// \brief write data to lcd in 4 bit mode /// \details write data given by \p data to lcd. /// First write upper nibble and in second step lower nibble /// \param[in] data data to transfer void Display::write_data_4bit(uint8_t data) { // RS=high, RW=low LL_GPIO_SetOutputPin(GPIOB, PIN_RS | PIN_RW << GPIO_BSRR_BR0_Pos | PIN_E << GPIO_BSRR_BR0_Pos); std::uint32_t tmp = data >> 4U; tmp |= ((~(data >> 4U) & 0xFU) << GPIO_BSRR_BR0_Pos); LL_GPIO_SetOutputPin(GPIOB, tmp); __NOP(); LL_GPIO_SetOutputPin(GPIOB, PIN_E); pulse_delay(); LL_GPIO_ResetOutputPin(GPIOB, PIN_E); tmp = data & 0x0FU; tmp |= ((~data & 0xFU) << GPIO_BSRR_BR0_Pos); LL_GPIO_SetOutputPin(GPIOB, tmp); __NOP(); LL_GPIO_SetOutputPin(GPIOB, PIN_E); pulse_delay(); LL_GPIO_ResetOutputPin(GPIOB, PIN_E); this->start_timer(1UL); } }