STM32驱动LCD1602
- 硬件连接关系
- STM32CUBEIDE设置
- 代码
- 项目设置
- 最后运行
硬件连接关系
LCD1602 | STM32 |
---|---|
VCC | VCC |
GND | GND |
VO | VCC-滑动变阻 |
RS | PB1 |
RW | PB2(BOOT1) |
E | PB0 |
D0 ~ D7 | PB8 ~ PB15 |
A | PA8 |
K | PA11 |
这是普中科技的C51开发板,送了一个stm32f103c6的小核心板,C51开发板上有个LCD1602的接口就直接使用了。
STM32CUBEIDE设置
- System Core -> RCC -> HSE -> Crystal/Ceramic Resonator
- System Core -> RCC -> LSE -> Crystal/Ceramic Resonator
- System Core -> SYS -> Debug -> Serial Wire
- Connectivity -> USART1 -> Mode -> Asynchronous(Baud Rate: 115200Bits/S, Word Length: 8Bits)
- PA8:
GPIO_Output, GPIO output level: High, GPIO Mode: Output Push Pull
- PA11:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull
- PB0:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_EN
- PB1:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_RS
- PB2:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_RW
- PB8:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB9:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB10:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB11:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB12:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB13:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB14:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- PB15:
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
- 设置时钟并保存
前面4行是设置晶振、JWD口、和UART调试信息。PA8,PA11是背光的控制口,设置推挽输出。PB0,PB1,PB2分别是LCD使能、LCD RS、LCD RW口,设置推挽输出。P8 ~ P15是数据输出和输入,因为输出同时要检测LCD是否忙,所以要读状态,需要设置为开漏输出。这里用的是8线输出方式。
代码
- Core->Inc文件夹下建立新的头文件"lcd1602.h"
#ifndef INC_LCD1602_H_
#define INC_LCD1602_H_
#include "main.h"
// 片选、读/写、数据/命令
#define LCD_CMD HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_RESET)
#define LCD_DATA HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_SET)
#define LCD_EN_HIGH HAL_GPIO_WritePin(LCD_EN_GPIO_Port, LCD_EN_Pin, GPIO_PIN_SET)
#define LCD_EN_LOWER HAL_GPIO_WritePin(LCD_EN_GPIO_Port, LCD_EN_Pin, GPIO_PIN_RESET)
#define LCD_WRITE HAL_GPIO_WritePin(LCD_RW_GPIO_Port, LCD_RW_Pin, GPIO_PIN_RESET)
#define LCD_READ HAL_GPIO_WritePin(LCD_RW_GPIO_Port, LCD_RW_Pin, GPIO_PIN_SET)
void lcd_init(void);
void lcd_wait_ready(void);
void lcd_write_cmd(unsigned char cmd);
void lcd_write_data(unsigned char data);
void lcd_set_cursor(unsigned char x, unsigned char y);
void lcd_area_clear(unsigned char x, unsigned char y, unsigned char len);
void lcd_full_clear(void);
void lcd_show_string(unsigned char x, unsigned char y, unsigned char *data);
void write_data(unsigned char data);
#endif /* INC_LCD1602_H_ */
- Core/Src下面建立"lcd1602.c"
#include "main.h"
#include "stdio.h"
#include "lcd1602.h"
void lcd_init(void){
lcd_write_cmd(0x38); // 16 * 2 显示 , 5 * 7 点阵, 8位接口
lcd_wait_ready();
lcd_write_cmd(0X0c); // 显示器开,光标关闭
lcd_wait_ready();
lcd_write_cmd(0x06); // 文字不动,地址自动加
lcd_wait_ready();
lcd_write_cmd(0x01); // 清屏
lcd_wait_ready();
printf("Init OK \r\n");
}
void lcd_wait_ready(void){
unsigned char status;
write_data(0xff);
LCD_CMD; // CMD
LCD_READ; // Read
do{
LCD_EN_HIGH; // 下降沿
status = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15);
LCD_EN_LOWER;
}while(status == 1);
}
void lcd_write_cmd(unsigned char cmd){
lcd_wait_ready();
LCD_CMD;
LCD_WRITE;
write_data(cmd);
LCD_EN_HIGH;
LCD_EN_LOWER;
lcd_wait_ready();
}
void lcd_write_data(unsigned char data){
lcd_wait_ready();
LCD_DATA;
LCD_WRITE;
write_data(data);
LCD_EN_HIGH;
LCD_EN_LOWER;
lcd_wait_ready();
}
void lcd_set_cursor(unsigned char x, unsigned char y){
unsigned char addr = 0;
// Row : y , Col : x
lcd_wait_ready();
// 从输入的屏幕计算显示RAM
if(y == 0){
addr = 0x00 + x;
}
else{
addr = 0x40 + x;
}
lcd_write_cmd(addr | 0x80);
}
void lcd_area_clear(unsigned char x, unsigned char y, unsigned char len){
lcd_set_cursor(x, y);
while(len--){
lcd_wait_ready();
lcd_write_data(' ');
}
}
void lcd_full_clear(void){
lcd_write_cmd(0x01);
}
void lcd_show_string(unsigned char x, unsigned char y, unsigned char *data){
lcd_set_cursor(x, y);
while(*data != '\0'){
lcd_wait_ready();
lcd_write_data(*data++);
}
}
void write_data(unsigned char data){
// 这里直接控制的ODR寄存器,更方便。第一行清除原来的PB高8位,第二行写新数据
GPIOB->ODR &= 0x00FF;
GPIOB->ODR |= (data << 8);
}
- main.c中加入代码
#include "stdio.h"
#include "lcd1602.h"
// 只是为了printf显示到串口信息中
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *stream)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
//
// 以下代码加入main函数中
unsigned char *str = (unsigned char *)"Hello everyone";
unsigned char *welcome = (unsigned char *)"Welcome to my world";
// 开背光
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
lcd_init();
HAL_Delay(10);
//尝试读LCD状态。status1要显示非0才是闲状态。如果一直读到1,可能是BOOT1接地了
write_data(0xff);
LCD_CMD;
LCD_READ;
LCD_EN_HIGH;
printf("Get status1: %d \r\n", HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15));
printf("Get status2: 0x%04X \r\n", (uint16_t)GPIOB->ODR);
printf("Get status2: 0x%04X \r\n", (uint16_t)GPIOB->IDR);
LCD_EN_LOWER;
lcd_show_string(0, 0, str);
lcd_show_string(0, 1, welcome);
HAL_Delay(5);
while (1)
{
HAL_Delay(1000);
lcd_write_cmd(0x18); //屏幕右移
//
项目设置
Project -> Properties -> C/C++ Build -> Setting -> MCU Setting -> Runtime Libray :Standard C。这个是用于printf显示用的。后面的两个也可以选上,用于显示小数
Project -> Properties -> C/C++ Build -> Setting -> MCU Post Build outputs -> Convert to intel Hex file 选中可以编译生成.HEX文件
最后的运行
- 因为我使用的是普中的板子,普中给的资料里面有一个UART烧写工具"普中ISP",这个工具烧写还是比较好用的,但烧写要BOOT0,BOOT1都接GND。而且只能烧写.HEX文件,所以我才会生成HEX文件
- 烧写完成后一定要记得拔下BOOT1的短接帽,因为工程中使用到PB2接口,默认这个PB2和BOOT1是同一个接口,我程序写好后怎么都不能读取到LCD状态
lcd_wait_ready()
死循环。花了一天时间才找到原因。 - CSDN上很多程序都没有
lcd_wait_ready()
,直接使用自己写的延迟代替。自己写来玩是可以用,但毕竟不是完美方法,要想lcd_wait_ready()
正常必须要记得BOOT1不能接地,PB8 ~ PB15要设置为开漏输出。 - 如果有问题可以使用Printf()显示变量信息,也可以使用Jlink调试。printf()要想能换行只用在字符串结束时加上’\r\n’就可以了。只加’\n’是不能换行的。