Загрузка данных


#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);
}