#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#include "unistd.h"
/* Chương trình thiết lập cấu hình hoạt động của I2C-PCF8574
dựa trên bảng Pin-mapping và tài liệu espressif.
I2C hỗ trợ 3 tần số:
100kHz (standard mode),
400kHz (fast mode)
3.4MHz (high-speed mode) */
#define I2C_MASTER_FREQ_HZ 100000
#define I2C_MASTER_SDA_IO GPIO_NUM_21
#define I2C_MASTER_SCL_IO GPIO_NUM_22
#define I2C_MASTER_NUM I2C_NUM_1
void i2c_master_init(void)
{
printf("I2C config ...\n");
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_21,
.scl_io_num = GPIO_NUM_22,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
};
// Wokwi không hỗ trợ .A.B trong khai báo cấu trúc, Đây là giải pháp thay thế.
conf.master.clk_speed = 100000;
/* I2C hoàn thành sau khi thực hiện i2c_param_config() và i2c_driver_install.
Tham khảo espressif */
i2c_param_config(I2C_MASTER_NUM, &conf);
i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);
printf("I2C Done.\n");
}
/* -------------------------------------------------------
Đoạn code truyền một COMMAND 8-Bit qua giao diện 4-Bit.
------------------------------------------------------- */
#define SLAVE_ADDRESS_LCD 0x27 // A2 A1 A0 = 1 1 1
esp_err_t err; // tạo biến ghi nhận lỗi để kiểm tra
void lcd_send_cmd (char cmd)
{
printf("LCD cmd: %02hhX | ",cmd&0xff);
esp_err_t err;
char data_u, data_l;
uint8_t data_t[4];
data_u = (cmd&0xf0); // Lấy 4 Bit cao D7-D4
data_l = ((cmd<<4)&0xf0); // Lấy 4 Bit thấp D3-D0 và dịch chuyển lên D7-D4
/* Chèn các bit điều khiển vô Sub-Byte, tạo Byte mới.
Tạo xung cạnh xuống E để LCD đọc D7-D4. Do đó,
sử dụng data_t[0] data_t[1] chỉ để truyền Sub-Byte data _t
Tương tự cho Sub-Byte data_l
RS = 0 để báo cho slave đây là COMMAND */
data_t[0] = data_u|0x0C; // OR 0000-1100 | => E=1, RS=0
data_t[1] = data_u|0x08; // OR 0000-1000 | => E=0, RS=0
data_t[2] = data_l|0x0C; // OR 0000-1100 | => E=1, RS=0
data_t[3] = data_l|0x08; // OR 0000-1000 | => E=0, RS=0
/* i2c_master_write_to_device() dùng để gửi dữ liệu từ Master sang Slave.
Thông số theo quy định trong tài liệu espressif */
err = i2c_master_write_to_device (I2C_MASTER_NUM, SLAVE_ADDRESS_LCD, data_t, 4, 1000);
printf(" | Err No. = %d | ", err);
printf("Done.\n");
}
/* ----------------------------------------------------
Đoạn code truyền một DATA 8-Bit qua giao diện 4-Bit.
---------------------------------------------------- */
void lcd_send_data(char data){
printf("LCD dat: %c | ",data);
char data_u, data_l;
uint8_t data_t[4];
data_u = (data&0xf0);
data_l = ((data<<4)&0xf0);
/* RS = 0 để báo cho slave đây là DATA */
data_t[0] = data_u|0x0D; // OR 0000-1101 | => en=1, rs=1
data_t[1] = data_u|0x09; // OR 0000-1001 | => en=0, rs=1
data_t[2] = data_l|0x0D; // OR 0000-1101 | => en=1, rs=1
data_t[3] = data_l|0x09; // OR 0000-1001 | => en=0, rs=1
err = i2c_master_write_to_device(I2C_MASTER_NUM, SLAVE_ADDRESS_LCD, data_t, 4, 1000);
printf("Err No. = %d | ", err);
printf("Done.\n");
}
/* Chương trình khởi tạo LCD:
- Cài đặt chế độ hoạt động 4-Bit Interface
- Cài đặt LCD: số dòng, cột; hiển thị con trỏ;
dịch chuyển con trỏ */
void lcd_init (void)
{
printf("LCD config...\n");
// 4 bit initialisation
usleep(50000); // wait for >40ms
lcd_send_cmd (0x33); // [NEED] select 8-bit interface
usleep(4500); // wait for >4.1ms
lcd_send_cmd (0x33); // [NEED] select 8-bit interface again
usleep(200); // wait for >100us
lcd_send_cmd (0x33);
lcd_send_cmd (0x32); // [NEED] select 4-bit interface
lcd_send_cmd (0x28); // [NEED] set 2 lines and 5x7(8?) dots per character
lcd_send_cmd (0x0C); // [NEED] display on, cursor off, corsor blinking off - 0x0C or 0x0F
lcd_send_cmd (0x06); // [No-NEED] move cursor right when writing, no scroll
lcd_send_cmd (0x80); // [No-NEED] set cursor to home position (row 1, column 1)
printf("LCD Done.\n");
}
/* Chương trình thiết lập tọa độ corsor tại DDRAM */
void lcd_put_cur(int row, int col)
{
switch (row)
{
/* col luôn bé hơn 16 (16 cột LCD), phép |=
sẽ đảm bảo vị trí đúng bảng địa chỉ DDRAM */
case 0:
col |= 0x80; // Hàng 1 của bộ nhớ
break;
case 1:
col |= 0xC0; // Hàng 2 của bộ nhớ
break;
}
lcd_send_cmd (col);
}
/* Chương trình nhận địa chỉ (pointer) của dữ liệu cần in
và xuất ra LCD */
void lcd_send_string (char *str)
{
while (*str) lcd_send_data (*str++);
}
/* Có thể clear màn hình thông qua Instrution 0x01 (theo Datasheet
HD44780 trang 24 lệnh clear display) */
void lcd_clear (void)
{
lcd_send_cmd (0x01);
usleep(5000);
}
/* Wokwi yêu cầu extern “C” (thực thi C trên nền Cpp)
Nếu dùng Eclipse IDE, cần xóa extern “C” */
extern "C" void app_main(void)
{
i2c_master_init(); // Thiết lập hoạt động I2C đúng với phần cứng
lcd_init(); // Thiết lập hoạt động LCD
lcd_clear(); // Clear màn hình
lcd_put_cur(0, 1); // Đặt con trỏ ở
lcd_send_string("Hello World!");
lcd_put_cur(1, 3);
lcd_send_string("from ESP32");
}