由于上次諾基亞5110的液晶屏接觸不良,所以沒辦法用在我要做的自平衡小車上面.我主要是想用液晶屏顯示PID參數,用兩個按鍵設置PID,方便平衡小車的調試.
我手中只剩下LCD1602這款液晶屏了,雖然一直很嫌棄它(驅動IO多,顯示字符少)但也沒辦法.
本文使用四線驅動LCD1602的方法,主要是八線驅動太占用IO口.關于四線驅動,網上有很多例程可以參考,我也是參考網上的程序寫的.但在編寫LCD1602驅動程序時,還是經歷了很多困難,時序問題就不提了.網上的很多程序都是沒有判斷忙標志位的,直接通過延時來實現.這樣,延時時間多少合適成為一個問題,我改了好多次延時,但發現延時時間都比較長,寫入一次數據的時間很長.這樣的液晶屏用著很鬧心,刷新一次時間那么,我的程序還有很多對時間要求比較苛刻的任務要執行呢,這樣的速度可不行.
關于四線驅動如何去判斷忙標志位,我也不懂,只好去網上搜,很少有這方面的例程,但最后還是找到了一個,照著寫,總算解決了.

上圖是單片機與LCD1602的接線圖,DB0~DB7為8條并行數據線.VL是調節液晶屏的對比度的,外接一個電位器到地,如果顯示不清晰,可以通過此電位器調節值最佳對比度.BLK,BLA接液晶背光.
除了8個數據線,和顯示控制有關的還有RS,RW,E三個端口,上圖是他們對應的功能.

通過向LCD1602寫入命令和數據來控制顯示.讀狀態是為了判斷LCD1602此時是否處于忙的狀態,如果忙,就只能等待不忙的時候再進行寫操作.通過讀狀態時序,讀到一個八位數據,最高位是忙標志位,此位為1說明此時忙,為了表示空閑.
大概沒什么要講的了,LCD1602的一些設置指令比較重要,手上也沒找到詳細的配置說明.

下圖是LCD1602驅動芯片的顯存,向對應的顯存地址寫入數據,就可以控制顯示字符.由于LCD1602自帶了一個ASCII碼字庫,所以一些常用的字符都可以直接顯示,不需要自己去用軟件生成字庫了.

四線驅動時,寫入的是0x28,八線驅動是寫入的是0x38,如上圖.
四線驅動的寫命令寫數據時序可以參考下面的例程.
本例程,使用STM8L的PB口驅動LCD1602,正好用完PB口的八個IO.高四位是數據接口,寫入一字節數據時,先寫高四位,再寫第四位.

- /*硬件連接*/
- // PB0 --> RS
- // PB1 --> RW
- // PB2 --> E
- // PB3 --> A(背光正極)
- // PB4 --> D4
- // PB5 --> D5
- // PB6 --> D6
- // PB7 --> D7
- // PD6 --> 按鍵+
- // PD7 --> 按鍵-
- #define RS_H PB_ODR_ODR0=1
- #define RS_L PB_ODR_ODR0=0
- #define RW_H PB_ODR_ODR1=1
- #define RW_L PB_ODR_ODR1=0
- #define EN_H PB_ODR_ODR2=1
- #define EN_L PB_ODR_ODR2=0
- /****************************************************************************************
- *開發環境:IAR for stm8 v6.5.3
- *硬件平臺:STM8L-DISCOVERY
- *功能說明:LCD1602四線驅動控制,檢測忙標志位 第一行顯示一串字符,第二行顯示一個可以用按鍵改變大小的三位數字
- *作 者:茗風
- ****************************************************************************************/
- #include"iostm8l152c6.h"
- #include"stdbool.h"
- #include"stdint.h"
- const uint8_t Num_to_Char[10]="0123456789";
- /******************************************************************************************************
- * 名 稱:void delay_100us(uint8_t x_us)
- * 功 能:延時100us
- * 入口參數:無
- * 出口參數:無
- * 說 明:
- * 范 例:無
- ******************************************************************************************************/
- void delay_100us(uint16_t x_us)
- {
- uint8_t j;
- while(x_us--)
- {
- for(j=0;j<33;j++);//2*40個指令周期
- }
- //delay_10ms共消耗 x_ms*2*255+2*x_ms個指令周期
- //*2*40+2*x_ms=80us+
- //16M/8/2=1M 一個指令周期為1us
- }
- ///******************************************************************** **********************************
- //* 功 能 :LCD1602端口初始化函數
- //* 入口參數 :無
- //* 出口參數 :無
- //* 說 明 :對LCD1602用的IO端口進行初始化
- //* 范 例 :無
- //******************************************************************************************************/
- void LCD1602_GPIO_Init(void)
- {
-
- PB_DDR =0xFF;//PB設置為輸出
- PB_CR1 =0xFF;//PB設置為推挽輸出
- PB_CR2 =0xFF;//PB的IO輸出速率為10MHz
- PB_ODR =0x00;//PB輸出高電平
- PB_ODR |=0x08;
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602忙檢測,若不忙則退出,忙則等待
- //* 入口參數 :無
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void LCD1602_Busy_Check(void)
- {
- PB_CR1 &=0x7F;//浮空輸入
- PB_CR2 &=0x7F;//無中斷能力
- PB_DDR &=0x7F;
- PB_ODR |=0x80;
- RS_L;
- RW_H;
- EN_H;
- while(PB_IDR&0x80);
- PB_DDR |=0x80;//PB設置為輸出
- PB_CR1 |=0x80;//PB設置為推挽輸出
- PB_CR2 |=0x80;//PB的IO輸出速率為10MHz
- EN_L;
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602寫命令函數
- //* 入口參數 :d 寫入的一字節命令
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void LCD1602_Write_One_Byte_Cmd(uint8_t d)
- {
- LCD1602_Busy_Check();
-
- RW_L;//寫
- RS_L;//命令
-
- PB_ODR &=0x0F;//清高四位
- PB_ODR |=(d&0xF0);
- EN_H;//
- asm("nop");//200ns延時
- EN_L;
- asm("nop");//200ns延時
-
- d<<=4; //低四位移到到高四位
- PB_ODR &=0x0F;//清高四位
- PB_ODR |=(d&0xF0);
- EN_H;
- asm("nop");//200ns延時
- EN_L;
- asm("nop");//200ns延時
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602寫數據函數
- //* 入口參數 :d 寫入的一字節數據
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void LCD1602_Write_One_Byte_Data(char d)
- {
- LCD1602_Busy_Check();
- RS_H;//數據
- RW_L;//寫
-
- PB_ODR &=0x0F;//清高四位
- PB_ODR |=(d&0xF0);
- EN_H;
- asm("nop");
- EN_L;
- // asm("nop");
- // delay_100us(1500);
-
- d<<=4; //低四位移到到高四位
- PB_ODR &=0x0F;//清高四位
- PB_ODR |=(d&0xF0);
- EN_H;
- asm("nop");
- EN_L;
- asm("nop");
- // delay_100us(1500);
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602一串字符函數
- //* 入口參數 :x x軸坐標,取值范圍(0~15)
- //* y y軸坐標,取值范圍(0~1),一共兩行
- //* *s 一串字符
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- uint8_t LCD1602_Display_String(uint8_t x,uint8_t y,uint8_t *s)
- {
- if(y==1)
- {
- LCD1602_Write_One_Byte_Cmd(0xC0+x);//設置寫入字符開始坐標
- }
- else
- {
- LCD1602_Write_One_Byte_Cmd(0x80+x);//設置寫入字符開始坐標
- }
- while(*s != '\0')
- {
- LCD1602_Write_One_Byte_Data(*s++);
- }
- return 0;
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602顯示兩位數字
- //* 入口參數 :x x軸坐標,取值范圍(0~15)
- //* y y軸坐標,取值范圍(0~1),一共兩行
- //* *s 一串字符
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月22日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void LCD1602_Display_Number(uint8_t x,uint8_t y,uint8_t number)
- {
- if(y==1)
- {
- LCD1602_Write_One_Byte_Cmd(0xC0+x);//設置寫入字符開始坐標
- }
- else
- {
- LCD1602_Write_One_Byte_Cmd(0x80+x);//設置寫入字符開始坐標
- }
- if(number<10)
- {
- LCD1602_Write_One_Byte_Data(' ');
- LCD1602_Write_One_Byte_Data(' ');
- LCD1602_Write_One_Byte_Data(Num_to_Char[number%10]);
- }
- else if(number<100)
- {
- LCD1602_Write_One_Byte_Data(' ');
- LCD1602_Write_One_Byte_Data(Num_to_Char[number/10]);
- LCD1602_Write_One_Byte_Data(Num_to_Char[number%10]);
- }
- else
- {
- LCD1602_Write_One_Byte_Data(Num_to_Char[number/100]);
- LCD1602_Write_One_Byte_Data(Num_to_Char[number%100/10]);
- LCD1602_Write_One_Byte_Data(Num_to_Char[number%10]);
- }
-
- }
- ///******************************************************************************************************
- //* 功 能 :LCD1602初始化配置
- //* 入口參數 :無
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void LCD1602_init(void)
- {
- LCD1602_GPIO_Init();
- LCD1602_Write_One_Byte_Cmd(0x28);//四線驅動
- LCD1602_Write_One_Byte_Cmd(0x01);//清屏
- LCD1602_Write_One_Byte_Cmd(0x06);//這個是上面指令碼是 000001NS的設
- LCD1602_Write_One_Byte_Cmd(0x0C);//顯示開及光標設置
- }
- ///******************************************************************************************************
- //* 功 能 :按鍵GPIO初始化配置
- //* 入口參數 :無
- //* 出口參數 :無
- //* 說 明 :
- //* 編寫日期 :2016年10月21日
- //* 作 者 :茗風
- //******************************************************************************************************/
- void GPIO_Config(void)
- {
- //PE7 LED控制引腳
- PE_DDR_DDR7 =1;//輸出
- PE_CR1_C17 =1;//推挽輸出
- PE_CR2_C27 =1;//輸出速率10M
-
- //配置閥門到位檢測引腳PD6,PD7
- PD_DDR_DDR6 =0;
- PD_CR1_C16 =1;//帶上拉電阻輸入
- PD_CR2_C26 =1;//使能外部中斷
-
- PD_DDR_DDR7 =0;
- PD_CR1_C17 =1;//帶上拉電阻輸入
- PD_CR2_C27 =1;//使能外部中斷
-
- EXTI_CR3_PDIS =2;//PD口上升沿中斷
- /*00: Falling edge and low level
- 01: Rising edge only
- 10: Falling edge only
- 11: Rising and falling edge*/
- EXTI_CONF_PDHIS =1;//PD[7:4] are used for EXTID interrupt generation
-
- //設置中斷
- //CPU_CCR |=MASK_CPU_CCR_I1+MASK_CPU_CCR_I0;//啟用硬件優先級,禁用軟件優先級
- }
- uint8_t num=30;
- void main(void)
- {
- GPIO_Config();
- LCD1602_init();
- LCD1602_Display_String(0,0,"nannan wo ai ni!");
- LCD1602_Display_String(0,1,"NUM:");
- asm("rim"); //enable interrupts
- while(1)
- {
- LCD1602_Display_Number(4,1,num);
- PE_ODR ^=0x80;
- asm("wfi");
- //delay_100us(10000);
- }
- }
- #pragma vector=EXTID_vector
- __interrupt void EXTID_ISR(void)
- {
- delay_100us(100);//10ms延時,暫時這么做
- if(PD_IDR_IDR6==0)//加
- {
- if(num>256)
- num=255;
- else
- num++;
- }
- else if(PD_IDR_IDR7==0)//減
- {
- if(num==0)
- num=0;
- else
- num--;
- }
- EXTI_SR2_PDF =1;//They are cleared by writing a ‘1’ by software
- }
復制代碼
|