Загрузка данных
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <string.h>
#define MSG_SIZE 16
#define QUEUE_MAX_MSGS 10
#define LCD_THREAD_STACK 1024
#define LCD_THREAD_PRIORITY 13
/* Get LCD PINS from DeviceTree */
#define MY_LCD_NODE DT_PATH(zephyr_user)
static const struct gpio_dt_spec lcd_rs = GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_rs_gpios);
static const struct gpio_dt_spec lcd_rw = GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_rw_gpios);
static const struct gpio_dt_spec lcd_e = GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_e_gpios);
static const struct gpio_dt_spec lcd_bl = GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_bl_gpios);
/* massive data for set D4-D7 */
static const struct gpio_dt_spec lcd_data[4] = {
GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_d4_gpios),
GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_d5_gpios),
GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_d6_gpios),
GPIO_DT_SPEC_GET(MY_LCD_NODE, lcd_d7_gpios)
};
#define LCD_RS_PIN 1
#define LCD_CMD_PIN 0
static uint8_t backlight_state = 1;
struct lcd_msg_t {
char text[MSG_SIZE];
uint8_t row;
uint8_t col;
uint8_t mode;
uint8_t text_size;
};
K_MSGQ_DEFINE(lcd_msgq, sizeof(struct lcd_msg_t), QUEUE_MAX_MSGS, 4);
/* Sending 4 bits of data to physical pins */
void lcd_send_nibble(uint8_t nibble, uint8_t rs_flag)
{
// 1. We set the control signals RS and RW (RW is always at 0 - recording)
gpio_pin_set_dt(&lcd_rs, rs_flag);
gpio_pin_set_dt(&lcd_rw, 0);
gpio_pin_set_dt(&lcd_bl, backlight_state);
// 2. We decompose the tetrad (4 bits) into independent data lines
for (int i = 0; i < 4; i++) {
gpio_pin_set_dt(&lcd_data[i], (nibble >> i) & 0x01);
}
k_busy_wait(1); // Address setup time
// 3. Enable (E = 1 -> E = 0)
gpio_pin_set_dt(&lcd_e, 1);
k_busy_wait(1); // Minimum pulse width E (high level)
gpio_pin_set_dt(&lcd_e, 0);
k_busy_wait(5); // Data retention time after E decay
}
/* Sending a full byte (high nibble, then low nibble) */
void lcd_send_byte(uint8_t value, uint8_t rs_flag)
{
lcd_send_nibble(value >> 4, rs_flag);
lcd_send_nibble(value & 0x0F, rs_flag);
}
void lcd_command(uint8_t cmd) { lcd_send_byte(cmd, LCD_CMD_PIN); }
void lcd_send_data(uint8_t data) { lcd_send_byte(data, LCD_RS_PIN); }
/* Initializing the display in 4-bit mode using a new scheme */
int lcd_init(void)
{
// We check the readiness of all GPIO controllers
if (!gpio_is_ready_dt(&lcd_rs) || !gpio_is_ready_dt(&lcd_rw) ||
!gpio_is_ready_dt(&lcd_e) || !gpio_is_ready_dt(&lcd_bl)) {
printf("Error: Control GPIO devices are not ready\n");
return -ENODEV;
}
// Configuring control pins for output
gpio_pin_configure_dt(&lcd_rs, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&lcd_rw, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&lcd_e, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&lcd_bl, GPIO_OUTPUT_ACTIVE); // Turn on backlight at startup
// Configuring the data pins for the output
for (int i = 0; i < 4; i++) {
if (!gpio_is_ready_dt(&lcd_data[i])) {
printf("Error: Data GPIO pin D%d is not ready\n", i+4);
return -ENODEV;
}
gpio_pin_configure_dt(&lcd_data[i], GPIO_OUTPUT_INACTIVE);
}
// HD44780 standard timeout after power up
k_msleep(50);
// 4-bit mode initialization procedure (according to the HD44780 datasheet)
lcd_send_nibble(0x03, LCD_CMD_PIN);
k_msleep(5);
lcd_send_nibble(0x03, LCD_CMD_PIN);
k_busy_wait(150);
lcd_send_nibble(0x03, LCD_CMD_PIN);
k_busy_wait(150);
lcd_send_nibble(0x02, LCD_CMD_PIN); // Switch to 4 bits
k_msleep(2);
// Setting up display settings
lcd_command(0x28); // 4-bit, 2-line mode, 5x8 font
lcd_command(0x0C); // Display on, cursor off
lcd_command(0x06); // Auto-increment cursor (shift right)
lcd_command(0x01); // Clear the display
k_msleep(2);
return 1;
}
/* Set cursor position */
void lcd_set_cursor(uint8_t col, uint8_t row)
{
uint8_t row_offsets[2] = {0x00, 0x40};
lcd_command(0x80 | (col + row_offsets[row]));
}
/* Print line */
void lcd_print(const char *str, uint8_t len)
{
while (len && *str) {
lcd_send_data(*str++);
len--;
}
}
//mode = 0 - clear 16 symbols, add data
//mode = 1 - only clear 16 symbols
//mode = 2 - append mode data without clear
void lcd_render_thread(void *p1, void *p2, void *p3)
{
struct lcd_msg_t msg;
uint8_t i = 0;
while (1) {
// Wait message queue
if (k_msgq_get(&lcd_msgq, &msg, K_FOREVER) == 0)
{
printf("mode=%d col=%d row=%d text=%s\n",msg.mode,msg.col,msg.row,msg.text);
if(msg.mode != 2) //not append mode
{
lcd_set_cursor(msg.col, msg.row);
for(i=0; i < 16 - msg.col; i++)
lcd_send_data(' ');
}
if(msg.mode != 1)
{
lcd_set_cursor(msg.col, msg.row);
lcd_print(msg.text, msg.text_size);
}
k_msleep(1);
}
}
}
K_THREAD_DEFINE(lcd_thread_id, LCD_THREAD_STACK, lcd_render_thread, NULL, NULL, NULL, LCD_THREAD_PRIORITY, 0, 0);
int safe_lcd_print(uint8_t col, uint8_t row, const char *text, uint8_t mode)
{
struct lcd_msg_t msg;
size_t len = MSG_SIZE;
#ifdef USE_GUARD
check_guard_corruption();
#endif
len = strlen(text);
if(text == NULL || len <= 0)
return 0;
if(col >= 16)
col = 15;
if(row > 1)
row = 1;
msg.col = col;
msg.row = row;
msg.mode = mode;
if(len >= MSG_SIZE)
len = MSG_SIZE - 1;
msg.text_size = len;
strncpy(msg.text, text, len);
msg.text[len] = '\0';
// Set to queue, if queue is full - don't wait (K_NO_WAIT)
return k_msgq_put(&lcd_msgq, &msg, K_NO_WAIT);
}