前言
從51到STM32F4學習這么久了,總算找到點頭緒了,目前學習了GPIO,中斷,定時器,看門狗的基本使用,所以想試着看看能不能做個什么東西,就是想復習一下最近學習的知識。正好上學期單片機課程設計做過一個可以藍牙、按鍵校准、帶溫度顯示的時鍾,所以我想看能不能將程序移植到STM32上呢?說做就做,經過三天的時間,幾次的程序修改和調試,終於成功了!
由於STM32內部定時器的精度還是很高的(一小時慢1s),所以就沒有使用DS1302時鍾芯片(關鍵是手里沒有),顯示上和課程設計做的有些不一樣,沒有增加日期和星期顯示,就時間、鬧鍾和溫度的顯示。
1602液晶介紹
工業字符型液晶,能夠同時顯示16x02即32個字符。1602液晶也叫1602字符型液晶,它是一種專門用來顯示字母、數字、符號等的點陣型液晶模塊。它由若干個5X7或者5X11等點陣字符位組成,每個點陣字符位都可以顯示一個字符,每位之間有一個點距的間隔,每行之間也有間隔,起到了字符間距和行間距的作用,正因為如此所以它不能很好地顯示圖形(用自定義CGRAM,顯示效果也不好)。市面上字符液晶大多數是基於HD44780液晶芯片的,控制原理是完全相同的,因此基於HD44780寫的控制程序可以很方便地應用於市面上大部分的字符型液晶。1602LCD是指顯示的內容為16X2,即可以顯示兩行,每行16個字符液晶模塊(顯示字符和數字)。

可見用STM32的3.3v電平驅動顯示完全沒問題,看到這我就放心了,代碼經過多次的修改后,終於成功顯示了,所以還是要多看看數據手冊。
硬件電路連接

程序設計
1.控制線宏定義,通過位帶操作,以后就可以像51那樣RS=1來操作了,是不是很熟悉呢?這里還將數據線的8個端口定義為了一個LCD_DB
#define LCD_RS PAout(1)
#define LCD_RW PAout(4)
#define LCD_EN PAout(6)
#define LCD_DB GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15
2.LCD相連的GPIO配置,將數據線端口配置為OD(開漏)輸出模式,可以做雙向IO使用,在檢測LCD是否忙時,需要讀取D7位的狀態
void LCD_GPIO_Config(void)
{
GPIO_InitTypeDef IO_Init;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE,ENABLE);
/*控制線初始化 : */
IO_Init.GPIO_Mode=GPIO_Mode_OUT; //輸出
IO_Init.GPIO_OType=GPIO_OType_PP; //推挽模式
IO_Init.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_6;
IO_Init.GPIO_PuPd=GPIO_PuPd_UP; //
IO_Init.GPIO_Speed=GPIO_Speed_2MHz; //GPIO_Speed_2MHz
GPIO_Init(GPIOA,&IO_Init);
/*數據線初始化*/
IO_Init.GPIO_Mode=GPIO_Mode_OUT;
IO_Init.GPIO_OType=GPIO_OType_OD; //開漏輸出可雙向
IO_Init.GPIO_Pin=LCD_DB;
IO_Init.GPIO_PuPd=GPIO_PuPd_UP; //上拉
IO_Init.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOE,&IO_Init);
/*測試 : 高電平3.3v
LCD_RS=1;
LCD_RW=1;
LCD_EN=1;
GPIO_SetBits(GPIOE,LCD_DB); */
}
3.實現P1=0xff功能的函數,帶參數
void GPIO_OutData(u8 Dat)
{
u16 tmp;
tmp = 0;
tmp =Dat;
tmp <<= 8; //數據左移到高8位
GPIO_Write(GPIOE,tmp); //數據寫入到GPIOE高8位
}
4.檢測LCD是否忙
void LCD_CheckBusy(void)
{
u8 sta;
GPIO_OutData(0xff);
LCD_RS=0;
LCD_RW=1;
do{
LCD_EN=1;
delay_ms(5);
sta = GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_15);
LCD_EN =0;
}while(sta & 0x80);
}
5.向LCD寫一字節數據
/* LCD_RS = 1, LCD_RW = 0 */
void LCD_WriteData(u8 Dat)
{
LCD_CheckBusy(); //忙則等待
LCD_RS=1;
LCD_RW=0;
GPIO_OutData(Dat);
LCD_EN=1;
delay_ms(1);
LCD_EN = 0;
}
6.向LCD寫一字節命令
/*LCD_RS = 0, LCD_RW = 0*/
void LCD_WriteCmd(u8 Cmd)
{
LCD_CheckBusy(); //忙則等待
LCD_RS = 0;
LCD_RW = 0;
GPIO_OutData(Cmd);
LCD_EN = 1;
delay_ms(1);
LCD_EN = 0;
}
7.LCD初始化
void LCD_Init(void)
{
LCD_WriteCmd(0x38);
LCD_WriteCmd(0x0C);
LCD_WriteCmd(0x06); /*顯示光標移動設置*/ delay_ms(1);
LCD_WriteCmd(0x01); /*顯示清屏*/
}
8.LCD清屏
void LCD_ClearScrren(void)
{
LCD_WriteCmd(0x01);
}
9.根據xy坐標,寫入對應的數據位置
void LCD_SetCursor(u8 x, u8 y)
{
u8 addr;
if (y == 0)
addr = 0x00 + x;
else
addr = 0x40 + x;
LCD_WriteCmd(addr | 0x80);
}
10.根據xy坐標顯示一個字符
void LCD_DisChar(u8 x,u8 y,u8 ch)
{
LCD_SetCursor(x,y); //字符顯示位置設定
LCD_WriteData(ch);
}
11.顯示兩位的數字
void LCD_DisNumber(u8 x,u8 y,u8 Num)
{
LCD_SetCursor(x,y);
LCD_WriteData(0x30+Num/10);
LCD_SetCursor(x+1,y);
LCD_WriteData(0x30+Num%10);
}
12.顯示字符串
void LCD_DisString(u8 x,u8 y,u8 *str)
{
LCD_SetCursor(x, y);
while(*str != '\0')
{
LCD_WriteData(*str++);
}
}
主函數
int main(void)
{
delay_init(168);
LED_Init();
LCD_GPIO_Config();
LCD_Init();
LCD_ClearScrren();
while(1)
{
delay_ms(500);
LED1_ON;
LCD_DisString(0,0,"abcdefghijklmnop");
delay_ms(500);
LED1_OFF;
LCD_DisNumber(0,1,56);
LCD_DisChar(2,1,'a');
LCD_DisString(3,1," Hello World!");
}
}
實際顯示效果:
顯示非常完美,和51驅動沒有什么區別
總結:
參考資料:
歡迎查看我以前的單片機學習筆記: