#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "rom/ets_sys.h"
#define LCD_20X04 1 //Se define el identificador la LCD de 20x4.
#define I2C_ACK_CHECK_EN 1
#define I2C_ADDRESS_LCD 0x27 //Pin para
#define I2C_SCL_LCD 21 //Pin para usar SCL
#define I2C_SDA_LCD 22 //Pin para usar SDA
/* Comandos de la LCD */
#define CLEAR_DISPLAY 0x01 //Se limpia la pantalla de la lcd
#define RETURN_HOME_UNSHIFT 0x02 //Devuelve el cursor a la posicion inicial
#define CURSOR_RIGHT_SHIFT 0x05 //Se mueve el cursor hacia la izquierda
#define CURSOR_RIGHT_NO_SHIFT_LEFT 0x06 //Se mueve el cursor hacia la derecha sin desplazar la pantalla
#define CURSOR_RIGHT_SHIFT_LEFT 0x07 //Se mueve el cursor hacia la derecha y desplaza la pantalla hacia la izquierda
#define DISPLAY_OFF 0x08 //Apaga la pantalla
#define DISPLAY_ON_CURSOR_OFF 0x0C //Enciende la pantalla con el cursor apagado
#define DISPLAY_ON_CURSOR_ON_STEADY 0x0E //Enciende la pantalla con el cursor en posicion fija
#define DISPLAY_ON_CURSOR_ON_BLINK 0x0F //Enciende la pantalla con el cursor parpadeando.
#define SHIFT_CURSOR_LEFT 0x10 //Desplaza el cursor una posicion a la izquierda en la pantalla
#define SHIFT_CURSOR_RIGHT 0x14 //Desplaza el cursor una posicion a la derecha
#define SHIFT_DISPLAY_LEFT 0x18 //Desplaza todo el contenido de la pantalla una posicion a la izquierda
#define SHIFT_DISPLAY_RIGHT 0x1C //Desplaza todo el contenido de la pantalla una posicion a la derecha
#define SET_4BIT_MODE 0x28 //Se configura la LCD para operar en modo de 4 bits
#define RETURN_HOME 0x80 //Se configura la pantalla en la posicion original si se ha desplazado
/* PCF8574 */
#define PCF8574_RS 0 //Posicion del bit para el pin de seleccion de registro (RS)
#define PCF8574_RW 1 //Posicion del bit para el pin de lectura/escritura (R/W)
#define PCF8574_EN 2 //Posicion del bit para el pin Enable (EN)
#define PCF8574_BL 3 //Posicion del bit para el pin de control de retroiluminación (BL)
#define LCD_RS_CMD 0 //Se recibe un comando cuando el pin RS esta bajo
#define LCD_RS_DATA 1 //Se reciben datos cuando el pin RS esta alto
typedef struct {
uint8_t i2c_address;
uint8_t i2c_port;
uint8_t screen_size;
uint8_t screen_backlight;
} lcd_i2c_device_t;
void i2c_init(void); //Se inicializa el bus de comunicacion
void lcd_init(lcd_i2c_device_t * lcd); //Se inicializa el display LCD
void lcd_i2c_write_byte(lcd_i2c_device_t * lcd, uint8_t data); //Funcion para que el maestro I2C pueda enviar comandos a la pantalla LCD
void lcd_i2c_write_command(lcd_i2c_device_t * lcd, uint8_t register_select, uint8_t cmd); //Envia el comando a la LCD
void lcd_set_cursor(lcd_i2c_device_t * lcd, uint8_t column, uint8_t row); //Posición del cursor en la pantalla LCD en la columna y fila
void lcd_i2c_write_custom_char(lcd_i2c_device_t * lcd, uint8_t char_address, const uint8_t * pixels); //Esta funcion permite definir y mostrar caracteres personalizados en la pantalla LCD
void i2c_init(void)
{
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_SDA_LCD,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_SCL_LCD,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
esp_err_t error = i2c_param_config(I2C_NUM_1, &i2c_config);
if (error != ESP_OK) {
while(1);
}
i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0);
}
void lcd_init(lcd_i2c_device_t * lcd) //Inicializacion de la LCD
{
lcd_i2c_write_command(lcd, LCD_RS_CMD, RETURN_HOME_UNSHIFT); //Esto regresa el cursor a la posicion de inicio sin borrar la pantalla
lcd_i2c_write_command(lcd, LCD_RS_CMD, SET_4BIT_MODE); //Esto configura la LCD en modo de 4 bits
lcd_i2c_write_command(lcd, LCD_RS_CMD, CLEAR_DISPLAY); //Esto es para limpiar la LCD
lcd_i2c_write_command(lcd, LCD_RS_CMD, DISPLAY_ON_CURSOR_OFF); //Se enciende la pantalla y apaga el cursor
lcd_i2c_write_command(lcd, LCD_RS_CMD, CURSOR_RIGHT_NO_SHIFT_LEFT); //Se mueve el cursor una posicion a la derecha sin desplazar el texto actual en la pantalla
vTaskDelay(20 / portTICK_PERIOD_MS);
}
void lcd_i2c_write_byte(lcd_i2c_device_t * lcd, uint8_t data) //Funcion para escribir en la LCD
{
i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
i2c_master_start(cmd_handle);
i2c_master_write_byte(cmd_handle, (lcd->i2c_address << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
i2c_master_write_byte(cmd_handle, data, 1);
i2c_master_stop(cmd_handle);
i2c_master_cmd_begin(lcd->i2c_port, cmd_handle, 100 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd_handle);
}
void lcd_i2c_write_command(lcd_i2c_device_t * lcd, uint8_t register_select, uint8_t cmd) //Se crea la funcion para poder enviar los comandos
{
uint8_t config = (register_select)? (1 << PCF8574_RS) : 0; // Configura el bit RS (Registro Select)
config |= (lcd->screen_backlight)? (1 << PCF8574_BL) : 0; // Configura el bit de la luz de fondo si está habilitada
config |= (config & 0x0F) | (0xF0 & cmd); // Combina los bits más altos del comando con la configuracion actual
config |= (1 << PCF8574_EN); // Activa el bit EN (Enable)
lcd_i2c_write_byte(lcd, config); // Envía la configuración al LCD
ets_delay_us(10);
config &= ~(1 << PCF8574_EN); // Desactiva el bit EN
lcd_i2c_write_byte(lcd, config); // Envía la configuracion modificada al LCD
ets_delay_us(50);
config = (config & 0x0F) | (cmd << 4); // Combina los bits bajos del comando con la configuracion actual
config |= (1 << PCF8574_EN); // Activa el bit EN
lcd_i2c_write_byte(lcd, config); // Envía la configuracion al LCD
ets_delay_us(10);
config &= ~(1 << PCF8574_EN); // Desactiva el bit EN
lcd_i2c_write_byte(lcd, config); // Envía la configuracion modificada al LCD
ets_delay_us(50); // Pausa final para procesamiento
if (cmd == CLEAR_DISPLAY)
{
vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
void lcd_set_cursor(lcd_i2c_device_t * lcd, uint8_t column, uint8_t row) //Funcion para poder inicializar las columnas
{
switch (row) {
case 0:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x80 + column); //Se configura para mostrar en la LCD la primera columna
break;
case 1:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0xC0 + column); //Se configura para mostrar en la LCD la segunda columna
break;
case 2:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x94 + column); //Se configura para mostrar en la LCD la tercera columna
break;
case 3:
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0xD4 + column); //Se configura para mostrar en la LCD la cuarta columna
break;
default:
break;
}
}
void lcd_i2c_write_custom_char(lcd_i2c_device_t * lcd, uint8_t address, const uint8_t * pixels) //Ya explicada al inicializar la funcion
{
lcd_i2c_write_command(lcd, LCD_RS_CMD, 0x40 | (address << 3));
for (uint8_t i = 0; i < 8; i++) //Funcion para poder estar el comando y debe ser de tamaño de menos de 8 bits
{
lcd_i2c_write_command(lcd, LCD_RS_DATA, pixels[i]); //Se muestran los caracteres
}
lcd_i2c_write_command(lcd, LCD_RS_CMD, RETURN_HOME);
}
void lcd_i2c_print_msg(lcd_i2c_device_t * lcd, char * msg) //Funcion para poder enviar la cadena dada en el main
{
uint8_t i = 0;
while (msg[i] != '\0')
{
lcd_i2c_write_command(lcd, LCD_RS_DATA, msg[i++]);
}
}
#define SMILE 5
#define DROP 2
void app_main()
{
i2c_init();
lcd_i2c_device_t my_lcd = {
.i2c_port = I2C_NUM_1,
.i2c_address = I2C_ADDRESS_LCD,
.screen_size = LCD_20X04,
.screen_backlight = 1,
};
vTaskDelay(20 / portTICK_PERIOD_MS);
lcd_init(&my_lcd);
//Dar forma a los caracteres
uint8_t smile[8] = {0x00, 0x0A, 0x0A, 0x00, 0x11, 0x0E, 0x00, 0x00};
uint8_t drop[8] = {0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x1F, 0x0E};
lcd_i2c_write_custom_char(&my_lcd, SMILE, smile);
lcd_i2c_write_custom_char(&my_lcd, DROP, drop);
bool print_msg = true;
uint8_t conteo_msg = 0;
char message[] = {'H', 'o', 'l', 'a', ' ', 'M', 'u', 'n', 'd', 'o', DROP, SMILE, 0};
char spaces_char[] = " ";
while (1) {
lcd_set_cursor(&my_lcd, 2, conteo_msg); //Se manda a llamar la escructura de my_lcd y de donde se quiere que se empiece a mostrar el mensaje
if (print_msg)
{
lcd_i2c_print_msg(&my_lcd, message); //Si se cumple la condicion, se imprime el caracterer
}
else
{
lcd_i2c_print_msg(&my_lcd, spaces_char);//Si no se cumple, se imprimen espacios
}
conteo_msg++;
if (conteo_msg > 3)
{
conteo_msg = 0;
print_msg = !print_msg;
}
vTaskDelay(250 / portTICK_PERIOD_MS);
}
}