feat: initial

This commit is contained in:
2025-08-17 09:19:24 +03:00
parent 9c6ca62eca
commit d95823281b
5 changed files with 359 additions and 1 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

120
README.md
View File

@@ -1,2 +1,120 @@
# zh_avr_160x_i2c
# FreeRTOS based AVR library for liquid crystal display module 1602(4)A via I2C connection (PCF8574)
## Features
1. Support of 16 LCD 160X on one bus.
## Connection
| 1602(4)A | PCF8574 |
| -------- | ------- |
| RS | P0 |
| E | P2 |
| D4 | P4 |
| D5 | P5 |
| D6 | P6 |
| D7 | P7 |
## Dependencies
1. [zh_avr_free_rtos](http://git.zh.com.ru/avr_libraries/zh_avr_free_rtos)
2. [zh_avr_vector](http://git.zh.com.ru/avr_libraries/zh_avr_vector)
3. [zh_avr_common](http://git.zh.com.ru/avr_libraries/zh_avr_common)
4. [zh_avr_i2c](http://git.zh.com.ru/avr_libraries/zh_avr_i2c)
5. [zh_avr_pcf8574](http://git.zh.com.ru/avr_libraries/zh_avr_pcf8574)
## Using
In an existing project, run the following command to install the components:
```text
cd ../your_project/lib
git clone http://git.zh.com.ru/avr_libraries/zh_avr_free_rtos
git clone http://git.zh.com.ru/avr_libraries/zh_avr_vector
git clone http://git.zh.com.ru/avr_libraries/zh_avr_i2c
git clone http://git.zh.com.ru/avr_libraries/zh_avr_common
git clone http://git.zh.com.ru/avr_libraries/zh_avr_pcf8574
git clone http://git.zh.com.ru/avr_libraries/zh_avr_160x_i2c
```
In the application, add the component:
```c
#include "zh_avr_160x_i2c.h"
```
## Examples
One LCD on bus:
```c
#include "avr/io.h"
#include "stdio.h"
#include "zh_avr_160x_i2c.h"
#define BAUD_RATE 9600
#define BAUD_PRESCALE (F_CPU / 16 / BAUD_RATE - 1)
int usart(char byte, FILE *stream)
{
while ((UCSR0A & (1 << UDRE0)) == 0)
{
}
UDR0 = byte;
return 0;
}
FILE uart = FDEV_SETUP_STREAM(usart, NULL, _FDEV_SETUP_WRITE);
zh_avr_pcf8574_handle_t lcd_160x_handle = {0};
void lcd_160x_example_task(void *pvParameters)
{
zh_avr_i2c_master_init(false);
zh_avr_pcf8574_init_config_t pcf8574_init_config = ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT();
pcf8574_init_config.i2c_address = 0x27;
zh_avr_pcf8574_init(&pcf8574_init_config, &lcd_160x_handle);
zh_avr_160x_init(&lcd_160x_handle, ZH_LCD_16X2); // For LCD 16X2.
// zh_avr_160x_init(&lcd_160x_handle, ZH_LCD_16X4); // For LCD 16X4.
for (;;)
{
zh_avr_160x_set_cursor(&lcd_160x_handle, 0, 0);
zh_avr_160x_print_char(&lcd_160x_handle, "LCD 160X");
zh_avr_160x_set_cursor(&lcd_160x_handle, 1, 0);
zh_avr_160x_print_char(&lcd_160x_handle, "Hello World!");
vTaskDelay(5000 / portTICK_PERIOD_MS);
zh_avr_160x_set_cursor(&lcd_160x_handle, 0, 0); // For LCD 16X2.
// zh_avr_160x_set_cursor(&lcd_160x_handle, 2, 0); // For LCD 16X4.
zh_avr_160x_print_char(&lcd_160x_handle, "Progress: ");
for (uint8_t i = 0; i <= 100; ++i)
{
zh_avr_160x_set_cursor(&lcd_160x_handle, 0, 10); // For LCD 16X2.
// zh_avr_160x_set_cursor(&lcd_160x_handle, 2, 10); // For LCD 16X4.
zh_avr_160x_print_int(&lcd_160x_handle, i);
zh_avr_160x_print_char(&lcd_160x_handle, "%");
zh_avr_160x_print_progress_bar(&lcd_160x_handle, 1, i); // For LCD 16X2.
// zh_avr_160x_print_progress_bar(&lcd_160x_handle, 3, i); // For LCD 16X4.
vTaskDelay(100 / portTICK_PERIOD_MS);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
zh_avr_160x_lcd_clear(&lcd_160x_handle);
vTaskDelay(5000 / portTICK_PERIOD_MS);
printf("Task Remaining Stack Size %d.\n", uxTaskGetStackHighWaterMark(NULL));
}
vTaskDelete(NULL);
}
int main(void)
{
UBRR0H = (BAUD_PRESCALE >> 8);
UBRR0L = BAUD_PRESCALE;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
stdout = &uart;
xTaskCreate(lcd_160x_example_task, "lcd 160x example task", 124, NULL, tskIDLE_PRIORITY, NULL);
vTaskStartScheduler();
return 0;
}
void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event) // Do not delete! Required for zh_avr_pcf8574 library work.
{
}
```

99
include/zh_avr_160x_i2c.h Normal file
View File

@@ -0,0 +1,99 @@
#pragma once
#include "zh_avr_pcf8574.h"
#include "util/delay.h"
#include "stdio.h"
#define ZH_LCD_16X2 1
#define ZH_LCD_16X4 0
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Initializes the LCD 160X.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] size LCD size (ZH_LCD_16X2 or ZH_LCD_16X4).
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_init(zh_avr_pcf8574_handle_t *handle, bool size);
/**
* @brief Clears the LCD screen.
*
* @param[in] handle Pointer to unique PCF8574 handle.
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_lcd_clear(zh_avr_pcf8574_handle_t *handle);
/**
* @brief Sets the cursor to a specific position on the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] row The row number.
* @param[in] col The column number (015).
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_set_cursor(zh_avr_pcf8574_handle_t *handle, uint8_t row, uint8_t col);
/**
* @brief Prints a string to the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] str Pointer to the string to be displayed.
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_print_char(zh_avr_pcf8574_handle_t *handle, const char *str);
/**
* @brief Prints an integer to the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] num The integer to be displayed.
*
* @return AVR_OK if success or an error code otherwise..
*/
avr_err_t zh_avr_160x_print_int(zh_avr_pcf8574_handle_t *handle, int num);
/**
* @brief Prints a floating-point number to the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] num The floating-point number to be displayed.
* @param[in] precision The number of decimal places to display.
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_print_float(zh_avr_pcf8574_handle_t *handle, float num, uint8_t precision);
/**
* @brief Displays a progress bar on a specific row of the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] row The row number.
* @param[in] progress The progress percentage (0100).
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_print_progress_bar(zh_avr_pcf8574_handle_t *handle, uint8_t row, uint8_t progress);
/**
* @brief Clears a specific row on the LCD.
*
* @param[in] handle Pointer to unique PCF8574 handle.
* @param[in] row The row number.
*
* @return AVR_OK if success or an error code otherwise.
*/
avr_err_t zh_avr_160x_clear_row(zh_avr_pcf8574_handle_t *handle, uint8_t row);
#ifdef __cplusplus
}
#endif

1
version.txt Normal file
View File

@@ -0,0 +1 @@
1.0.0

139
zh_avr_160x_i2c.c Normal file
View File

@@ -0,0 +1,139 @@
#include "zh_avr_160x_i2c.h"
#define LCD_160X_PULSE \
zh_avr_pcf8574_write_gpio(handle, 2, true); \
_delay_us(300); \
zh_avr_pcf8574_write_gpio(handle, 2, false); \
_delay_us(400);
static void _zh_avr_160x_lcd_init(zh_avr_pcf8574_handle_t *handle);
static void _zh_avr_160x_send_command(zh_avr_pcf8574_handle_t *handle, uint8_t command);
static void _zh_avr_160x_send_data(zh_avr_pcf8574_handle_t *handle, uint8_t data);
avr_err_t zh_avr_160x_init(zh_avr_pcf8574_handle_t *handle, bool size)
{
ZH_ERROR_CHECK(handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
handle->system = pvPortCalloc(1, sizeof(uint8_t));
ZH_ERROR_CHECK(handle->system != NULL, AVR_ERR_INVALID_ARG);
*(uint8_t *)handle->system = size;
_zh_avr_160x_lcd_init(handle);
return AVR_OK;
}
avr_err_t zh_avr_160x_lcd_clear(zh_avr_pcf8574_handle_t *handle)
{
ZH_ERROR_CHECK(handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
_zh_avr_160x_send_command(handle, 0x01);
return AVR_OK;
}
avr_err_t zh_avr_160x_set_cursor(zh_avr_pcf8574_handle_t *handle, uint8_t row, uint8_t col)
{
ZH_ERROR_CHECK(row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4) && col < 16 && handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
_zh_avr_160x_send_command(handle, 0x80 | ((row == 0) ? col : (row == 1) ? (0x40 + col)
: (row == 2) ? (0x10 + col)
: (0x50 + col)));
return AVR_OK;
}
avr_err_t zh_avr_160x_print_char(zh_avr_pcf8574_handle_t *handle, const char *str)
{
ZH_ERROR_CHECK(str != NULL && handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
while (*str != 0)
{
_zh_avr_160x_send_data(handle, (uint8_t)*str++);
}
return AVR_OK;
}
avr_err_t zh_avr_160x_print_int(zh_avr_pcf8574_handle_t *handle, int num)
{
ZH_ERROR_CHECK(handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
char buffer[12];
sprintf(buffer, "%d", num);
zh_avr_160x_print_char(handle, buffer);
return AVR_OK;
}
avr_err_t zh_avr_160x_print_float(zh_avr_pcf8574_handle_t *handle, float num, uint8_t precision)
{
ZH_ERROR_CHECK(handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
char buffer[16];
sprintf(buffer, "%.*f", precision, num);
zh_avr_160x_print_char(handle, buffer);
return AVR_OK;
}
avr_err_t zh_avr_160x_print_progress_bar(zh_avr_pcf8574_handle_t *handle, uint8_t row, uint8_t progress)
{
ZH_ERROR_CHECK(row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4) && progress <= 100 && handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
uint8_t blocks = (progress * 16) / 100;
zh_avr_160x_set_cursor(handle, row, 0);
for (uint8_t i = 0; i < 16; ++i)
{
if (i < blocks)
{
zh_avr_160x_print_char(handle, "\xFF");
}
else
{
zh_avr_160x_print_char(handle, " ");
}
}
return AVR_OK;
}
avr_err_t zh_avr_160x_clear_row(zh_avr_pcf8574_handle_t *handle, uint8_t row)
{
ZH_ERROR_CHECK(row < ((*(uint8_t *)handle->system == ZH_LCD_16X2) ? 2 : 4) && handle != NULL, AVR_ERR_INVALID_ARG);
ZH_ERROR_CHECK(handle->is_initialized == true, AVR_ERR_INVALID_STATE);
zh_avr_160x_set_cursor(handle, row, 0);
for (uint8_t i = 0; i < 16; ++i)
{
zh_avr_160x_print_char(handle, " ");
}
zh_avr_160x_set_cursor(handle, row, 0);
return AVR_OK;
}
static void _zh_avr_160x_lcd_init(zh_avr_pcf8574_handle_t *handle)
{
vTaskDelay(20 / portTICK_PERIOD_MS);
zh_avr_pcf8574_write(handle, 0x30);
LCD_160X_PULSE;
zh_avr_pcf8574_write(handle, 0x30);
LCD_160X_PULSE;
zh_avr_pcf8574_write(handle, 0x30);
LCD_160X_PULSE;
zh_avr_pcf8574_write(handle, 0x20);
LCD_160X_PULSE;
_zh_avr_160x_send_command(handle, 0x28);
_zh_avr_160x_send_command(handle, 0x28);
_zh_avr_160x_send_command(handle, 0x28);
_zh_avr_160x_send_command(handle, 0x0C);
_zh_avr_160x_send_command(handle, 0x01);
_zh_avr_160x_send_command(handle, 0x06);
}
static void _zh_avr_160x_send_command(zh_avr_pcf8574_handle_t *handle, uint8_t command)
{
zh_avr_pcf8574_write(handle, (command & 0xF0) | 0x08);
LCD_160X_PULSE;
zh_avr_pcf8574_write(handle, (command << 4) | 0x08);
LCD_160X_PULSE;
}
static void _zh_avr_160x_send_data(zh_avr_pcf8574_handle_t *handle, uint8_t data)
{
zh_avr_pcf8574_write(handle, (data & 0xF0) | 0x09);
LCD_160X_PULSE;
zh_avr_pcf8574_write(handle, (data << 4) | 0x09);
LCD_160X_PULSE;
}