LCD12864 液晶顯示-漢字及自定義顯示(串口)


在網上找了許久,發現FPGA用串口驅動LCD12864程序很少,基本上沒有。剛開始竊喜,中間郁悶,最后還是高興,為什么這樣說呢!頭一回在沒有參考程序的情況下,完全是照時序圖寫(自信),中間調試過程遇到一點小插曲(郁悶),后來搞定(高興),也算是對這段時間學習FPGA的一個能力檢測吧。廢話少說,趕緊步入正題。

首先來看一下串口模式的幾個重要管腳:

1、lcd_cs(PIN4),使能信號,高有效(有的資料上寫着低有效,高低我都試過,確認是低有效),定義output。

2、lcd_sid(PIN5),數據傳輸線,相當於I2C的SBDA數據傳輸線,可定義雙向,這里僅只有寫,所以定義output。

3、lcd_scl(PIN6),數據傳輸的時鍾,相當於I2C的SBCL時鍾線,定義output,這里注意,很多資料上沒說這頻率多少合適,而有的資料上寫的是高脈沖和低脈沖不能小於800ns或其他數據,一開始本以為這個時鍾會比並口時鍾會快一些,參考“verilog hdl的那些事兒”中,頻率設置100KHZ,他的是ST7566P,我的是ST7920,同一個系列,頻率應該會差不多吧,以上條件完全讓我把頻率設置在100KHZ,那么這個實驗注定要失敗,我就是栽倒在這,中間調試時,把頻率往小降,往大增(就是沒想到要那么慢的時鍾),結果就是不行,要么出現亂碼,要么不顯示,后來干脆就把這頻率設置成與並口一樣的時鍾,周期6MS(當然這個頻率不是最佳值,到底多少只有自己去試試了),喔、喔,終於正確的顯示了,高興阿。

4、PSB(PIN15),串口模式,可以直接接地,程序中是設置為低,定義output。

管腳都清楚了,那么接下來看時序圖,是很重要的一步:在發送數據前,CS要為高,發送數據是先連續發送5個1,用來啟動一個周期,此時傳輸計數被重置,並且串行傳輸被同步。緊接的兩個位指定傳輸方向(RW,確定讀還是寫)和傳輸性質(RS,確定是命令寄存器還是數據寄存器),最后的第八位是一個“0”,一個啟動字節發送完畢,那么緊接着發送數據或是指令。一個數據或指令要分為兩個字節來處理,先發高4bit,緊接着連續發送4個0,在發送低4bit,又緊接着發送4個0,也就是說完整發送完一個數據或命令,lcd_scl得打24個節拍。由於本實驗也是只寫不讀,所以得避開LCD的忙狀態,所以每次發送完一個數據或是命令后,在打一個節拍(25個),本程序中一個節拍的時間(6MS)足夠避開LCD的忙狀態。這里也要注意,當把所有數據處理完之后,lcd_scl得關閉,也就是拉低,否則,屏會一直刷新,覆蓋掉原有的內容,最終全屏成亂碼

 OK,時序搞定,那么可以利用狀態機完成,代碼實現

LCD12864_SPI.v

  1 module LCD12864_SPI(
  2                         //input
  3                         sys_clk,
  4                         rst_n,
  5                         
  6                         //output
  7                         lcd_cs,
  8                         lcd_sid,
  9                         lcd_scl,
 10                         lcd_psb
 11                     );
 12 input sys_clk;//50MHZ
 13 input rst_n;
 14 
 15 output lcd_cs;  //enable,H active
 16 output lcd_sid; //SPI data
 17 output lcd_scl; //SPI clk
 18 output lcd_psb;    ////H:parallel module   L:SPI module
 19 /***************************************************/
 20 assign lcd_psb = 1'b0;//串口模式
 21 /***************************************************/
 22 parameter   T3MS         = 18'd149_999;
 23 parameter   IDLE        = 4'd0,  //准備狀態
 24             SEND_1      = 4'd1,     //連續發送5個1
 25             SNED_RW     = 4'd2,  //是寫還是讀
 26             SEND_RS     = 4'd3,  //是指令還是數據
 27             SEND_0      = 4'd4,  //發送一個0,代表啟動字節結束
 28             SEND_DATA_H = 4'd5,  //發送數據的高4bit
 29             SEND_FOUR_0 = 4'd6,     //連續發送四個0
 30             SEND_DATA_L = 4'd7,     //發送數據的低4bit
 31             DELAY       = 4'd8,  //延時一個lcd_clk周期,避開LCD的忙狀態
 32             STOP        = 4'd9;  //結束
 33 /***************************************************/
 34 ////產生周期為6MS的lcd_clk給LCD
 35 reg [17:0] cnt;
 36 reg lcd_clk;
 37 always @(posedge sys_clk or negedge rst_n)
 38 if(!rst_n) begin
 39     cnt <= 18'd0;
 40     lcd_clk <= 1'b0;
 41 end
 42 else if(cnt == T3MS)begin
 43     cnt <= 18'd0;
 44     lcd_clk <= ~lcd_clk;
 45 end
 46 else
 47     cnt <= cnt + 1'b1;
 48 /***************************************************/
 49 assign lcd_scl = en ? lcd_clk : 1'b0; //發送完之后,必須得停止,否則屏一直刷新,最后成全屏亂碼
 50 /***************************************************/
 51 //在下降沿設置數據或命令
 52 reg lcd_sid;
 53 reg lcd_cs;
 54 reg [2:0] i;
 55 reg [2:0] j;
 56 reg [6:0] num;
 57 reg [3:0] state; 
 58 reg [7:0] dis_data;
 59 reg flag;  //用來標志一個dis_data數據發送完
 60 reg en;  //lcd_scl的使能信號
 61 always @(posedge lcd_clk or negedge rst_n)
 62 if(!rst_n) begin
 63     lcd_sid <= 1'bz;
 64     lcd_cs <= 1'b0;
 65     i <= 3'd0;
 66     j <= 3'd0;
 67     num <= 7'd0;
 68     state <= IDLE;
 69     flag <= 1'b0;
 70     en <= 1'b1;
 71 end
 72 else
 73     case(state)
 74     
 75     IDLE:
 76     begin
 77         lcd_cs <= 1'b1;
 78         state <= SEND_1;
 79     end
 80     
 81     SEND_1://發送5個1
 82     begin
 83         i <= i + 1'b1;
 84         lcd_sid <= 1'b1;
 85         if(i == 3'd4) begin
 86             i <= 3'd0;
 87             state <= SNED_RW;
 88         end
 89         else
 90             state <= SEND_1;
 91     end
 92         
 93     SNED_RW:
 94     begin 
 95         lcd_sid <= 1'b0;//寫
 96         state <= SEND_RS;
 97     end
 98     
 99     SEND_RS:
100     begin
101         state <= SEND_0;
102         if((num <= 7'd5)  || (num == 7'd18)
103         || (num == 7'd25) || (num == 7'd34)
104         || (num == 7'd41) || (num == 7'd42)
105         || (num == 7'd75))
106             lcd_sid <= 1'b0;//命令
107         else
108             lcd_sid <= 1'b1;//數據
109     end
110     
111     SEND_0:
112     begin
113         lcd_sid <= 1'b0;//一個啟動字節結束
114         state <= SEND_DATA_H;
115     end
116     
117     SEND_DATA_H: //進入發送高4bit
118     begin
119         j <= j + 1'b1;
120         lcd_sid <= dis_data[7-j];
121         if(j == 3'd3)
122             state <= SEND_FOUR_0;
123         else
124             state <= SEND_DATA_H;
125     end
126     
127     SEND_FOUR_0: //進入連續發送四個0
128     begin
129         i <= i + 1'b1;
130         lcd_sid <= 1'b0;
131         if(i == 3'd3) begin
132             i <= 3'd0;
133             if(flag) begin
134                 num <= num + 1'b1;
135                 flag <= 1'b0;
136                 state <= DELAY;
137             end
138             else
139                 state <= SEND_DATA_L;
140         end
141         else
142             state <= SEND_FOUR_0;
143     end
144     
145     SEND_DATA_L: //進入連續發送低4bit
146     begin
147         j <= j + 1'b1;
148         lcd_sid <= dis_data[7-j];
149         if(j == 3'd7) begin
150             j <= 3'd0;
151             flag <= 1'b1;  //標志一個字節發送完
152             state <= SEND_FOUR_0;
153         end
154         else
155             state <= SEND_DATA_L;
156     end
157 
158     DELAY:   //延時,避開LCD的忙狀態
159     if(num <= 7'd77) 
160         state <= SEND_1;
161     else 
162         state <= STOP;
163         
164     STOP: 
165     begin
166         lcd_cs <= 1'b0;
167         en <= 1'b0;
168         state <= STOP;
169     end 
170 
171     endcase
172 /***************************************************/
173 always @(num)
174     case(num)
175         7'd0   :    dis_data = 8'h30;//功能設定
176         7'd1   :    dis_data = 8'h30;//功能設定
177         7'd2   :    dis_data = 8'h0c;//顯示設定
178         7'd3   :    dis_data = 8'h01;//清屏
179         7'd4   :    dis_data = 8'h06;//進入設定點
180         7'd5   :    dis_data = 8'h81;//設置DDRAM地址
181         //歡迎訪問博客
182         7'd6   :    dis_data = 8'hbb; 
183         7'd7   :    dis_data = 8'hb6; 
184         7'd8   :    dis_data = 8'hd3; 
185         7'd9   :    dis_data = 8'had; 
186         7'd10  :    dis_data = 8'hb7; 
187         7'd11  :    dis_data = 8'hc3; 
188         7'd12  :    dis_data = 8'hce; 
189         7'd13  :    dis_data = 8'hca; 
190         7'd14  :    dis_data = 8'hb2; 
191         7'd15  :    dis_data = 8'ha9; 
192         7'd16  :    dis_data = 8'hbf; 
193         7'd17  :    dis_data = 8'hcd; 
194         //2line 顯示 文少清
195         7'd18  :    dis_data = 8'h92;//設置DDRAM地址
196         7'd19  :    dis_data = 8'hce; 
197         7'd20  :    dis_data = 8'hc4; 
198         7'd21  :    dis_data = 8'hc9; 
199         7'd22  :    dis_data = 8'hd9; 
200         7'd23  :    dis_data = 8'hC7; 
201         7'd24  :    dis_data = 8'he5; 
202         //3line 顯示 LCD12864   
203         7'd25  :    dis_data = 8'h8a;//設置DDRAM命令
204         7'd26  :    dis_data =  "L";  
205         7'd27  :    dis_data =  "C";  
206         7'd28  :    dis_data =  "D";  
207         7'd29  :    dis_data =  "1";  
208         7'd30  :    dis_data =  "2";  
209         7'd31  :    dis_data =  "8";  
210         7'd32  :    dis_data =  "6";  
211         7'd33  :    dis_data =  "4";  
212         //4Line顯示 謝謝! 
213         7'd34  :    dis_data = 8'h9d;//設置DDRAM地址
214         7'd35  :    dis_data = 8'hd0; 
215         7'd36  :    dis_data = 8'hbb; 
216         7'd37  :    dis_data = 8'hd0; 
217         7'd38  :    dis_data = 8'hbb; 
218         7'd39  :    dis_data = 8'ha3; 
219         7'd40  :    dis_data = 8'ha1; 
220         //4Line顯示喇叭
221         7'd41  :    dis_data = 8'h30;//功能設定
222         7'd42  :    dis_data = 8'h40;//設定CGRAM字符的位置
223         7'd43  :    dis_data = 8'h00; 
224         7'd44  :    dis_data = 8'h39; 
225         7'd45  :    dis_data = 8'h00; 
226         7'd46  :    dis_data = 8'h6a; 
227         7'd47  :    dis_data = 8'h00; 
228         7'd48  :    dis_data = 8'ha8; 
229         7'd49  :    dis_data = 8'h01; 
230         7'd50  :    dis_data = 8'h29; 
231         7'd51  :    dis_data = 8'h7e; 
232         7'd52  :    dis_data = 8'h2a; 
233         7'd53  :    dis_data = 8'hfc; 
234         7'd54  :    dis_data = 8'h28; 
235         7'd55  :    dis_data = 8'hfc; 
236         7'd56  :    dis_data = 8'h29;     
237         7'd57  :    dis_data = 8'hcc; 
238         7'd58  :    dis_data = 8'h2a; 
239         7'd59  :    dis_data = 8'hcc; 
240         7'd60  :    dis_data = 8'h28; 
241         7'd61  :    dis_data = 8'hfc; 
242         7'd62  :    dis_data = 8'h29; 
243         7'd63  :    dis_data = 8'hfc; 
244         7'd64  :    dis_data = 8'h2a; 
245         7'd65  :    dis_data = 8'h7e; 
246         7'd66  :    dis_data = 8'h28; 
247         7'd67  :    dis_data = 8'h01; 
248         7'd68  :    dis_data = 8'h29;         
249         7'd69  :    dis_data = 8'h00; 
250         7'd70  :    dis_data = 8'haa; 
251         7'd71  :    dis_data = 8'h00; 
252         7'd72  :    dis_data = 8'h68; 
253         7'd73  :    dis_data = 8'h00; 
254         7'd74  :    dis_data = 8'h38;         
255         7'd75  :    dis_data = 8'h99;//設置DDRAM地址
256         7'd76  :    dis_data = 8'h00;//設置自定義顯示字符編碼 高8bit 是數據不是命令
257         7'd77  :    dis_data = 8'h00;//設置自定義顯示字符編碼 低8bit 
258         default:    dis_data = 8'h00;
259     endcase 
260 
261 endmodule
View Code

顯示效果:這次把星星月亮換成一個小喇叭

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM