big refactoring and features added
This commit is contained in:
199
App/Devices/lcd1602_display.c
Normal file
199
App/Devices/lcd1602_display.c
Normal file
@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @file lcd1602_display.c
|
||||
* @brief Minimal SPLC780D/HD44780-compatible LCD1602 driver in 4-bit mode.
|
||||
*
|
||||
* Architectural note:
|
||||
* The chosen wiring keeps the whole standalone UI on GPIOG. This driver uses
|
||||
* write-only transfers with fixed delays, which keeps the implementation
|
||||
* compact and avoids a bidirectional R/W pin or extra state.
|
||||
*/
|
||||
|
||||
#include "lcd1602_display.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#define LCD1602_COLUMNS 16u
|
||||
#define LCD1602_LINE_1_ADDRESS 0x00u
|
||||
#define LCD1602_LINE_2_ADDRESS 0x40u
|
||||
#define LCD1602_COMMAND_DELAY_US 50u
|
||||
#define LCD1602_CLEAR_DELAY_US 2000u
|
||||
#define LCD1602_ENABLE_PULSE_US 1u
|
||||
#define LCD1602_POWER_ON_DELAY_US 50000u
|
||||
|
||||
#define LCD1602_DATA_MASK (UI_LCD_D4_Pin | UI_LCD_D5_Pin | UI_LCD_D6_Pin | UI_LCD_D7_Pin)
|
||||
|
||||
static uint8_t g_cycle_counter_available = 0u;
|
||||
|
||||
static void lcd1602_enable_cycle_counter(void);
|
||||
static void lcd1602_delay_us(uint32_t delay_us);
|
||||
static void lcd1602_write_nibble(uint8_t nibble);
|
||||
static void lcd1602_pulse_enable(void);
|
||||
static void lcd1602_write_byte(bool is_data, uint8_t value);
|
||||
static void lcd1602_write_command(uint8_t command);
|
||||
static void lcd1602_write_data(uint8_t value);
|
||||
static void lcd1602_set_cursor(uint8_t address);
|
||||
static void lcd1602_write_padded_line(const char *text);
|
||||
|
||||
void lcd1602_display_init(void)
|
||||
{
|
||||
lcd1602_enable_cycle_counter();
|
||||
|
||||
HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port, UI_LCD_RS_Pin, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_RESET);
|
||||
UI_LCD_D4_GPIO_Port->BSRR = ((uint32_t)LCD1602_DATA_MASK) << 16u;
|
||||
|
||||
lcd1602_delay_us(LCD1602_POWER_ON_DELAY_US);
|
||||
|
||||
lcd1602_write_nibble(0x03u);
|
||||
lcd1602_delay_us(5000u);
|
||||
lcd1602_write_nibble(0x03u);
|
||||
lcd1602_delay_us(150u);
|
||||
lcd1602_write_nibble(0x03u);
|
||||
lcd1602_delay_us(150u);
|
||||
lcd1602_write_nibble(0x02u);
|
||||
lcd1602_delay_us(LCD1602_COMMAND_DELAY_US);
|
||||
|
||||
lcd1602_write_command(0x28u);
|
||||
lcd1602_write_command(0x08u);
|
||||
lcd1602_write_command(0x01u);
|
||||
lcd1602_write_command(0x06u);
|
||||
lcd1602_write_command(0x0Cu);
|
||||
}
|
||||
|
||||
void lcd1602_display_set_lines(const char *line1, const char *line2)
|
||||
{
|
||||
lcd1602_set_cursor(LCD1602_LINE_1_ADDRESS);
|
||||
lcd1602_write_padded_line(line1);
|
||||
lcd1602_set_cursor(LCD1602_LINE_2_ADDRESS);
|
||||
lcd1602_write_padded_line(line2);
|
||||
}
|
||||
|
||||
static void lcd1602_enable_cycle_counter(void)
|
||||
{
|
||||
uint32_t start_cycles;
|
||||
uint32_t spin;
|
||||
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0u;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
start_cycles = DWT->CYCCNT;
|
||||
for (spin = 0u; spin < 64u; ++spin)
|
||||
{
|
||||
__NOP();
|
||||
}
|
||||
|
||||
/*
|
||||
* DWT-based microsecond delays are convenient, but they are not
|
||||
* guaranteed to be available in every production boot/debug state.
|
||||
* Falling back to HAL_Delay() keeps the LCD optional rather than
|
||||
* letting the standalone UI block the whole firmware startup.
|
||||
*/
|
||||
g_cycle_counter_available = (DWT->CYCCNT != start_cycles) ? 1u : 0u;
|
||||
}
|
||||
|
||||
static void lcd1602_delay_us(uint32_t delay_us)
|
||||
{
|
||||
if (delay_us == 0u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_cycle_counter_available != 0u)
|
||||
{
|
||||
uint32_t start_cycles = DWT->CYCCNT;
|
||||
uint32_t target_cycles = delay_us * (SystemCoreClock / 1000000u);
|
||||
|
||||
while ((DWT->CYCCNT - start_cycles) < target_cycles)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (delay_us > 1000u)
|
||||
{
|
||||
HAL_Delay(1u);
|
||||
delay_us -= 1000u;
|
||||
}
|
||||
|
||||
HAL_Delay(1u);
|
||||
}
|
||||
|
||||
static void lcd1602_write_nibble(uint8_t nibble)
|
||||
{
|
||||
uint32_t bsrr = ((uint32_t)LCD1602_DATA_MASK) << 16u;
|
||||
|
||||
if ((nibble & 0x01u) != 0u)
|
||||
{
|
||||
bsrr |= UI_LCD_D4_Pin;
|
||||
}
|
||||
if ((nibble & 0x02u) != 0u)
|
||||
{
|
||||
bsrr |= UI_LCD_D5_Pin;
|
||||
}
|
||||
if ((nibble & 0x04u) != 0u)
|
||||
{
|
||||
bsrr |= UI_LCD_D6_Pin;
|
||||
}
|
||||
if ((nibble & 0x08u) != 0u)
|
||||
{
|
||||
bsrr |= UI_LCD_D7_Pin;
|
||||
}
|
||||
|
||||
UI_LCD_D4_GPIO_Port->BSRR = bsrr;
|
||||
lcd1602_pulse_enable();
|
||||
}
|
||||
|
||||
static void lcd1602_pulse_enable(void)
|
||||
{
|
||||
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_SET);
|
||||
lcd1602_delay_us(LCD1602_ENABLE_PULSE_US);
|
||||
HAL_GPIO_WritePin(UI_LCD_E_GPIO_Port, UI_LCD_E_Pin, GPIO_PIN_RESET);
|
||||
lcd1602_delay_us(LCD1602_COMMAND_DELAY_US);
|
||||
}
|
||||
|
||||
static void lcd1602_write_byte(bool is_data, uint8_t value)
|
||||
{
|
||||
HAL_GPIO_WritePin(UI_LCD_RS_GPIO_Port, UI_LCD_RS_Pin, is_data ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
||||
lcd1602_write_nibble((uint8_t)(value >> 4u));
|
||||
lcd1602_write_nibble((uint8_t)(value & 0x0Fu));
|
||||
}
|
||||
|
||||
static void lcd1602_write_command(uint8_t command)
|
||||
{
|
||||
lcd1602_write_byte(false, command);
|
||||
|
||||
if ((command == 0x01u) || (command == 0x02u))
|
||||
{
|
||||
lcd1602_delay_us(LCD1602_CLEAR_DELAY_US);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd1602_write_data(uint8_t value)
|
||||
{
|
||||
lcd1602_write_byte(true, value);
|
||||
}
|
||||
|
||||
static void lcd1602_set_cursor(uint8_t address)
|
||||
{
|
||||
lcd1602_write_command((uint8_t)(0x80u | address));
|
||||
}
|
||||
|
||||
static void lcd1602_write_padded_line(const char *text)
|
||||
{
|
||||
uint8_t index;
|
||||
uint8_t value;
|
||||
|
||||
for (index = 0u; index < LCD1602_COLUMNS; ++index)
|
||||
{
|
||||
value = ' ';
|
||||
if ((text != NULL) && (text[index] != '\0'))
|
||||
{
|
||||
value = (uint8_t)text[index];
|
||||
}
|
||||
lcd1602_write_data(value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user