STM32F1与STM32CubeIDE快速入门-I2C驱动LCD1602显示屏(基于PCF8574) I2C驱动LCD1602显示屏(基于PCF8574) LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。
PCF8574/74A 通过两线双向 I2C 总线(串行时钟 (SCL)、串行数据 (SDA))提供通用远程 I/O 扩展。
PCF8574/74A包括八个准双向端口、100 kHz I2C 总线接口、三个硬件地址输入和中断输出在 2.5 V 和 6 V 之间运行。准双向端口可以独立指定为输入以监控中断状态或键盘,或作为输出以激活 LED 等指示设备。 系统主机可以通过单个寄存器从输入端口读取或写入输出端口。
关于PCF8574 I2C驱动LCD1602相关文章,请参考:
本次实例将实现PCF8574驱动LCD1602,以节省宝贵的IO。
1、LCD1602-I2C配置 STM32CubeIDE创建工程、系统配置、调试配置,在这里不再做介绍,请参考:
PCF8574配置如下:
保存配置,并生成代码。
2、LCD1602-I2C驱动实现 2.1 基本定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 #ifndef STM32\_LCD\_I2C\_H #define STM32\_LCD\_I2C\_H /\* C++ detection \*/ #ifdef \_\_cplusplus extern "C" { #endif #include "stm32f1xx\_hal.h" #define DELAY\_MS(x) DWT\_Delay\_ms(x) // commands #define LCD\_CLEARDISPLAY 0x01 #define LCD\_RETURNHOME 0x02 #define LCD\_ENTRYMODESET 0x04 #define LCD\_DISPLAYCONTROL 0x08 #define LCD\_CURSORSHIFT 0x10 #define LCD\_FUNCTIONSET 0x20 #define LCD\_SETCGRAMADDR 0x40 #define LCD\_SETDDRAMADDR 0x80 // flags for display entry mode #define LCD\_ENTRYRIGHT 0x00 #define LCD\_ENTRYLEFT 0x02 #define LCD\_ENTRYSHIFTINCREMENT 0x01 #define LCD\_ENTRYSHIFTDECREMENT 0x00 // flags for display on/off control #define LCD\_DISPLAYON 0x04 #define LCD\_DISPLAYOFF 0x00 #define LCD\_CURSORON 0x02 #define LCD\_CURSOROFF 0x00 #define LCD\_BLINKON 0x01 #define LCD\_BLINKOFF 0x00 // flags for display/cursor shift #define LCD\_DISPLAYMOVE 0x08 #define LCD\_CURSORMOVE 0x00 #define LCD\_MOVERIGHT 0x04 #define LCD\_MOVELEFT 0x00 // flags for function set #define LCD\_8BITMODE 0x10 #define LCD\_4BITMODE 0x00 #define LCD\_2LINE 0x08 #define LCD\_1LINE 0x00 #define LCD\_5x10DOTS 0x04 #define LCD\_5x8DOTS 0x00 // flags for backlight control #define LCD\_BACKLIGHT 0x08 #define LCD\_NOBACKLIGHT 0x00 #define BIT\_EN 0b00000100 // Enable bit #define BIT\_RW 0b00000010 // Read/Write bit #define BIT\_RS 0b00000001 // Register select bit typedef struct { I2C_HandleTypeDef \*hi2c; uint8\_t Addr; uint8\_t displaycontrol; uint8\_t displaymode; uint8\_t backlight; uint8\_t numlines; uint8\_t cols; uint8\_t rows; } LCD_I2C_t; /\*\* \* @brief Display init \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par addr - address of the display. It could be get in I2C\_Scan \* @par cols - width of the display \* @par rows - height of the display \*/ void LCD\_Init(LCD_I2C_t \*lcd_i2c, I2C_HandleTypeDef \*, uint8\_t addr, uint8\_t cols, uint8\_t rows); /\*\* \* Set cursor to the left top corner. \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \*/ void LCD\_Home(LCD_I2C_t \*lcd_i2c); /\*\* \* Clear the display and set cursor to the left top corner. \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \*/ void LCD\_Clear(LCD_I2C_t \*lcd_i2c); /\*\* \* Turn the backlight on and off \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par on - 1 - turn on, 0 - turn off \*/ void LCD\_Backlight(LCD_I2C_t \*lcd_i2c, uint8\_t on); /\*\* \* Turn blinking cursor on and off \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par on - 1 - turn on, 0 - turn off \*/ void LCD\_Blink(LCD_I2C_t \*lcd_i2c, uint8\_t on); /\*\* \* Turn underline cursor on and off \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par on - 1 - turn on, 0 - turn off \*/ void LCD\_Cursor(LCD_I2C_t \*lcd_i2c, uint8\_t on); /\*\* \* Set cursor position \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par col - column \* @par row - row \*/ void LCD\_SetCursor(LCD_I2C_t \*lcd_i2c, uint8\_t col, uint8\_t row); /\*\* \* Display string \* @par \*lcd\_i2c - Pointer to @ref LCD\_I2C\_t working lcd\_i2c struct \* @par str - pointer to string \*/ void LCD\_SendString(LCD_I2C_t \*lcd_i2c, const char \*str); #ifdef \_\_cplusplus } #endif #endif
2.2 驱动实现 1)LCD1602命令、数据发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include "LCD\_i2c.h" //Prototypes uint8\_t LCD\_command(LCD_I2C_t \*lcd_i2c, uint8\_t command); uint8\_t LCD\_send(LCD_I2C_t \*lcd_i2c, uint8\_t data, uint8\_t flags); /\*\* \* 发送命令 \* @par \*lcd\_i2c - 指向 LCD\_I2C\_t的结构的指针 \* @par command - 命令 \* @retval 1 - 成功, 0 - 错误,设备无响应 \*/ uint8\_t LCD\_command(LCD_I2C_t \*lcd_i2c, uint8\_t command) { return LCD\_send(lcd_i2c, command, 0); } /\*\* \* 发送数据 \* @par cd\_i2c - 指向 LCD\_I2C\_t的结构的指针 \* @par data - 数据 \* @par flags - 1 - 表示发送数据; 0 - 表示发送命令 \* @return 1 - success, 0 - error divice not responding \*/ uint8\_t LCD\_send(LCD_I2C_t \*lcd_i2c, uint8\_t data, uint8\_t flags) { if (lcd_i2c->hi2c == NULL) return 0; HAL_StatusTypeDef res; // 检查设备是否准备好 res = HAL\_I2C\_IsDeviceReady(lcd_i2c->hi2c, lcd_i2c->Addr, 10, HAL_MAX_DELAY); // 失败 if(res != HAL_OK) return 0; // 将字节拆分为上下部分 uint8\_t up = data & 0xF0; //上部分 uint8\_t lo = (data << 4) & 0xF0; // 下部分 uint8\_t data_arr[4]; // 4-7 位包含信息,0-3 位包含配置 data_arr[0] = up|flags|lcd_i2c->backlight|BIT_EN; // 信号重复,此时在引脚 E 处为 0 // 再次发送,这次 IT 为零 data_arr[1] = up|flags|lcd_i2c->backlight; data_arr[2] = lo|flags|lcd_i2c->backlight|BIT_EN; data_arr[3] = lo|flags|lcd_i2c->backlight; // I2C主机发送数据 res = HAL\_I2C\_Master\_Transmit(lcd_i2c->hi2c, lcd_i2c->Addr, data_arr, sizeof(data_arr), HAL_MAX_DELAY); DELAY\_MS(5); if (res == HAL_OK) return 1; else return 0; }
2)LCD1602初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void LCD\_Init(LCD_I2C_t \*lcd_i2c, I2C_HandleTypeDef \*handle, uint8\_t addr, uint8\_t cols, uint8\_t rows) { lcd_i2c->hi2c = handle; lcd_i2c->Addr = (addr << 1); //correct address lcd_i2c->cols = cols; lcd_i2c->rows = rows; lcd_i2c->numlines = rows; lcd_i2c->backlight = LCD_NOBACKLIGHT; lcd_i2c->displaycontrol = LCD_CURSOROFF|LCD_BLINKOFF; DELAY\_MS(50); // 等待 >40ms LCD\_command(lcd_i2c, LCD_FUNCTIONSET|LCD_8BITMODE); // 8位接口 DELAY\_MS(5); LCD\_command(lcd_i2c, LCD_FUNCTIONSET|LCD_8BITMODE); // 8位接口 DELAY\_MS(120); LCD\_command(lcd_i2c, LCD_FUNCTIONSET|LCD_4BITMODE); // 4位接口 LCD\_command(lcd_i2c, LCD_FUNCTIONSET|LCD_4BITMODE|LCD_5x8DOTS|LCD_2LINE); LCD\_command(lcd_i2c, LCD_DISPLAYCONTROL|LCD_DISPLAYOFF|LCD_CURSOROFF|LCD_BLINKOFF); LCD\_command(lcd_i2c, LCD_CLEARDISPLAY); // 清屏 DELAY\_MS(2); LCD\_command(lcd_i2c, LCD_ENTRYMODESET|LCD_ENTRYLEFT|LCD_ENTRYSHIFTDECREMENT); LCD\_command(lcd_i2c, LCD_DISPLAYCONTROL|LCD_DISPLAYON|lcd_i2c->displaycontrol); }
2.3 功能实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 void LCD\_Home(LCD_I2C_t \*lcd_i2c) { LCD\_command(lcd_i2c, LCD_RETURNHOME); // 将光标定位在行首 DELAY\_MS(2); } void LCD\_Clear(LCD_I2C_t \*lcd_i2c) { // 发送清屏命令 LCD\_command(lcd_i2c, LCD_CLEARDISPLAY); DELAY\_MS(2); } void LCD\_Backlight(LCD_I2C_t \*lcd_i2c, uint8\_t backlight) { if (backlight) lcd_i2c->backlight = LCD_BACKLIGHT; else lcd_i2c->backlight = 0; LCD\_command(lcd_i2c, 0); } void LCD\_Blink(LCD_I2C_t \*lcd_i2c, uint8\_t on) { if (on) { lcd_i2c->displaycontrol |= LCD_BLINKON; } else { lcd_i2c->displaycontrol &= ~LCD_BLINKON; } LCD\_command(lcd_i2c, LCD_DISPLAYCONTROL|LCD_DISPLAYON|lcd_i2c->displaycontrol); } void LCD\_Cursor(LCD_I2C_t \*lcd_i2c, uint8\_t cur) { if (cur) { lcd_i2c->displaycontrol |= LCD_CURSORON; } else { lcd_i2c->displaycontrol &= ~LCD_CURSORON; } LCD\_command(lcd_i2c, LCD_DISPLAYCONTROL|LCD_DISPLAYON|lcd_i2c->displaycontrol); } void LCD\_SetCursor(LCD_I2C_t \*lcd_i2c, uint8\_t col, uint8\_t row) { if ((row+1)>lcd_i2c->rows) return; int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; if ( row > (4-1) ) { row = 4 - 1; // 从 w/0 开始计算行数 } LCD\_command(lcd_i2c, LCD_SETDDRAMADDR | (col + row_offsets[row])); } void LCD\_SendString(LCD_I2C_t \*lcd_i2c, const char \*str) { while(\*str && LCD\_send(lcd_i2c, (uint8\_t)(\*str), 1)) { str++; } }
2.4 基于DWT延时实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #ifndef DWT\_DELAY\_H\_ #define DWT\_DELAY\_H\_ #include "stm32f1xx\_hal.h" uint32\_t DWT\_Delay\_Init(void); // 此函数使用 DWT 提供以微秒为单位的延迟 __STATIC_INLINE void DWT\_Delay\_us(volatile uint32\_t au32_microseconds) { uint32\_t au32_initial_ticks = DWT->CYCCNT; uint32\_t au32_ticks = (HAL\_RCC\_GetHCLKFreq() / 1000000); au32_microseconds \*= au32_ticks; while ((DWT->CYCCNT - au32_initial_ticks) < au32_microseconds-au32_ticks); } // 此函数使用 DWT 提供以毫秒为单位的延迟 __STATIC_INLINE void DWT\_Delay\_ms(volatile uint32\_t au32_milliseconds) { uint32\_t au32_initial_ticks = DWT->CYCCNT; uint32\_t au32_ticks = (HAL\_RCC\_GetHCLKFreq() / 1000); au32_milliseconds \*= au32_ticks; while ((DWT->CYCCNT - au32_initial_ticks) < au32_milliseconds); } #endif /\* DWT\_DELAY\_H\_ \*/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 uint32\_t DWT\_Delay\_Init(void) { /\* Disable TRC \*/ CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; // ~0x01000000; /\* Enable TRC \*/ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 0x01000000; /\* Disable clock cycle counter \*/ DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001; /\* Enable clock cycle counter \*/ DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; //0x00000001; /\* Reset the clock cycle counter value \*/ DWT->CYCCNT = 0; /\* 3 NO OPERATION instructions \*/ __ASM volatile ("NOP"); __ASM volatile ("NOP"); __ASM volatile ("NOP"); /\* Check if clock cycle counter has started \*/ if(DWT->CYCCNT) { return 0; /\*clock cycle counter started\*/ } else { return 1; /\*clock cycle counter not started\*/ } }
3、主程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 /\* Includes ------------------------------------------------------------------\*/ #include "main.h" #include "i2c.h" #include "usart.h" #include "gpio.h" /\* Private includes ----------------------------------------------------------\*/ /\* USER CODE BEGIN Includes \*/ #include <LCD1602\_I2C/LCD\_i2c.h> #include <stdio.h> /\* USER CODE END Includes \*/ /\* Private user code ---------------------------------------------------------\*/ /\* USER CODE BEGIN 0 \*/ LCD_I2C_t lcd; /\* USER CODE END 0 \*/ int main(void) { /\* USER CODE BEGIN 1 \*/ /\* USER CODE END 1 \*/ /\* MCU Configuration--------------------------------------------------------\*/ /\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/ HAL\_Init(); /\* USER CODE BEGIN Init \*/ /\* USER CODE END Init \*/ /\* Configure the system clock \*/ SystemClock\_Config(); /\* USER CODE BEGIN SysInit \*/ /\* USER CODE END SysInit \*/ /\* Initialize all configured peripherals \*/ MX\_GPIO\_Init(); MX\_I2C1\_Init(); MX\_USART1\_UART\_Init(); /\* USER CODE BEGIN 2 \*/ // 延时初始化 DWT\_Delay\_Init(); // LCD1602初始化 LCD\_Init(&lcd, &hi2c1, 0x27, 16, 2); // 打开背光 LCD\_Backlight(&lcd, 1); //LCD\_Clear(&lcd); // 显示字符串 LCD\_SendString(&lcd, "Hello World!"); char msg[10] = { 0 }; uint16\_t counter = 0; /\* USER CODE END 2 \*/ /\* Infinite loop \*/ /\* USER CODE BEGIN WHILE \*/ while (1) { /\* USER CODE END WHILE \*/ /\* USER CODE BEGIN 3 \*/ LCD\_SetCursor(&lcd, 0, 1); sprintf(msg, "%d ", counter++); LCD\_SendString(&lcd, msg); // 显示字符串 DWT\_Delay\_ms(300); } /\* USER CODE END 3 \*/ }
4、运行结果
文章来源: https://iotsmart.blog.csdn.net/article/details/123607110
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!