#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "rom/ets_sys.h"
// INÍCIO DA BIBLIOTECA DO LCD -------------------------------------------
// PINOS DO PCF8475
#define RS 0
#define RW 1
#define EN 2
#define BL 3
// MEDIDAS DOS DISPLAYS
#define DISPLAY_16X02 0
#define DISPLAY_20X04 1
// INSTRUÇÕES DO DISPLAY LCD
#define CLEAR_DISPLAY 0x01
#define RETURN_HOME_UNSHIFT 0x02
#define CURSOR_RIGHT_NO_SHIFT 0x04
#define CURSOR_RIGHT_SHIFT 0x05
#define CURSOR_RIGHT_NO_SHIFT_LEFT 0x06
#define CURSOR_RIGHT_SHIFT_LEFT 0x07
#define DISPLAY_OFF 0x08
#define DISPLAY_ON_CURSOR_OFF 0x0C
#define DISPLAY_ON_CURSOR_ON_STEADY 0x0E
#define DISPLAY_ON_CURSOR_ON_BLINK 0x0F
#define RETURN_HOME 0x80
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_CURSOR_RIGHT 0x14
#define SHIFT_DISPLAY_LEFT 0x18
#define SHIFT_DISPLAY_RIGHT 0x1C
#define SET_4BIT_MODE 0x28
typedef struct {
uint8_t address;
uint8_t num;
uint8_t backlight;
uint8_t size;
} lcd_i2c_handle_t;
void i2c_write_byte(lcd_i2c_handle_t * lcd, uint8_t data) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (lcd->address << 1) | I2C_MASTER_WRITE, 1);
i2c_master_write_byte(i2c_cmd, data, 1);
i2c_master_stop(i2c_cmd);
i2c_master_cmd_begin(lcd->num, i2c_cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
}
void lcd_i2c_write(lcd_i2c_handle_t * lcd, char rs_flag, char data_byte) {
uint8_t buffer_byte = ((1 << RS) * rs_flag) | ((1 << BL) * lcd->backlight);
buffer_byte |= (buffer_byte & 0x0F) | (0xF0 & data_byte);
buffer_byte |= (1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(10);
buffer_byte &= ~(1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(50);
buffer_byte = (buffer_byte & 0x0F) | (data_byte << 4);
buffer_byte |= (1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(10);
buffer_byte &= ~(1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(50);
if (data_byte == CLEAR_DISPLAY) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void lcd_i2c_init(lcd_i2c_handle_t * lcd) {
vTaskDelay(10 / portTICK_PERIOD_MS);
lcd_i2c_write(lcd, 0, RETURN_HOME_UNSHIFT);
lcd_i2c_write(lcd, 0, SET_4BIT_MODE);
lcd_i2c_write(lcd, 0, CLEAR_DISPLAY);
lcd_i2c_write(lcd, 0, DISPLAY_ON_CURSOR_OFF);
lcd_i2c_write(lcd, 0, CURSOR_RIGHT_NO_SHIFT_LEFT);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void lcd_i2c_cursor_set(lcd_i2c_handle_t * lcd, uint8_t column, uint8_t row) {
if (lcd->size == DISPLAY_16X02) {
if (row) lcd_i2c_write(lcd, 0, 0x80 + 0x40 + column);
else lcd_i2c_write(lcd, 0, 0x80 + column);
}
else if (lcd->size == DISPLAY_20X04) {
switch (row) {
case 0:
lcd_i2c_write(lcd, 0, 0x80 + column);
break;
case 1:
lcd_i2c_write(lcd, 0, 0x80 + 0x40 + column);
break;
case 2:
lcd_i2c_write(lcd, 0, 0x80 + 0x14 + column);
break;
case 3:
lcd_i2c_write(lcd, 0, 0x80 + 0x54 + column);
break;
default:
break;
}
}
}
void lcd_i2c_custom_char(lcd_i2c_handle_t * lcd, char char_address, const char * pixels) {
lcd_i2c_write(lcd, 0, 0x40 | (char_address << 3));
for (uint8_t i = 0; i < 8; i++) {
lcd_i2c_write(lcd, 1, pixels[i]);
}
lcd_i2c_write(lcd, 0, RETURN_HOME);
}
void lcd_i2c_print(lcd_i2c_handle_t * lcd, const char * format_string, ...) {
uint16_t i = 0;
char buffer_string[128];
va_list arguments;
va_start(arguments, format_string);
vsnprintf(buffer_string, sizeof(buffer_string), format_string, arguments);
va_end(arguments);
while (buffer_string[i] != '\0') {
lcd_i2c_write(lcd, 1, buffer_string[i]);
i++;
}
}
// FIM DA BIBLIOTECA DO LCD ----------------------------------------------
// INÍCIO DO CÓDIGO ESPECÍFICO DO PROJETO --------------------------------
// Endereços de caracteres especiais
#define SKULL1 1 // Boca aberta
#define SKULL2 2 // Boca fechada
#define DROP 3
#define A_COM_ACENTO 4 // á
#define SMILE 5
void i2c_init() {
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 22,
.sda_pullup_en = 1,
.scl_io_num = 21,
.scl_pullup_en = 1,
.master.clk_speed = 100000,
};
i2c_param_config(I2C_NUM_1, &i2c_config);
i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0);
}
void app_main() {
i2c_init();
lcd_i2c_handle_t display = {
.address = 0x27,
.num = I2C_NUM_1,
.backlight = 1,
.size = DISPLAY_20X04
};
// Declaração dos bytes dos caracteres especiais
char smile[8] = {0x00, 0x0A, 0x0A, 0x00, 0x11, 0x0E, 0x00, 0x00};
char skull1[8] = {0x0E, 0x15, 0x15, 0x1F, 0x0E, 0x11, 0x0E, 0x00};
char skull2[8] = {0x0E, 0x15, 0x15, 0x1F, 0x1F, 0x0E, 0x00, 0x00};
char drop[8] = {0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x1F, 0x0E};
char a_com_acento[8] = {0x02, 0x04, 0x0E, 0x01, 0x0F, 0x11, 0x0F, 0x00};
// Inicializa o display
lcd_i2c_init(&display);
// Envia os caracteres especiais para o display
lcd_i2c_custom_char(&display, SMILE, smile);
lcd_i2c_custom_char(&display, SKULL1, skull1);
lcd_i2c_custom_char(&display, SKULL2, skull2);
lcd_i2c_custom_char(&display, DROP, drop);
lcd_i2c_custom_char(&display, A_COM_ACENTO, a_com_acento);
int8_t count_drop = 0;
int8_t count_skull = 0;
int8_t count_hello = 0;
int8_t toggle_hello = 1;
while (1) {
if (!count_drop) lcd_i2c_cursor_set(&display, 0, 3);
else lcd_i2c_cursor_set(&display, 0, count_drop - 1);
lcd_i2c_print(&display, " ");
lcd_i2c_cursor_set(&display, 0, count_drop);
lcd_i2c_print(&display, "%c", DROP);
count_drop++;
if (count_drop > 3) count_drop = 0;
if (count_skull <= 1) lcd_i2c_cursor_set(&display, 19, 3);
else lcd_i2c_cursor_set(&display, 19, (count_skull / 2) - 1);
lcd_i2c_print(&display, " ");
lcd_i2c_cursor_set(&display, 19, count_skull / 2);
if (count_skull % 2) lcd_i2c_print(&display, "%c", SKULL1);
else lcd_i2c_print(&display, "%c", SKULL2);
count_skull++;
if (count_skull > 7) count_skull = 0;
lcd_i2c_cursor_set(&display, 4, count_hello / 4);
if (toggle_hello) lcd_i2c_print(&display, "Ol%c, Wokwi%c!", A_COM_ACENTO, SMILE);
else lcd_i2c_print(&display, " ");
count_hello++;
if (count_hello > 15) {
count_hello = 0;
toggle_hello = !toggle_hello;
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}
// FIM DO CÓDIGO ESPECÍFICO DO PROJETO -----------------------------------