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的接口就直接使用了。
GPIO_Output, GPIO output level: High, GPIO Mode: Output Push Pull
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_EN
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_RS
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Push Pull, User Label: LCD_RW
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
GPIO_Output, GPIO output level: Low, GPIO Mode: Output Open Drain
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线输出方式。
#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_ */
#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);
}
#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文件
lcd_wait_ready()
死循环。花了一天时间才找到原因。lcd_wait_ready()
,直接使用自己写的延迟代替。自己写来玩是可以用,但毕竟不是完美方法,要想lcd_wait_ready()
正常必须要记得BOOT1不能接地,PB8 ~ PB15要设置为开漏输出。