LCD trên FPGA dùng verilog

Lu ROm

Administrator
Staff member
25 Tháng bảy 2014
481
119
43
32
One piece
vimach.net
Phần I : Cơ sở lý thuyết
Trong phần này mình chỉ nhắc đến những thứ quan trọng để hiểu rõ hơn thì các bạn đọc trong phần LCD của giáo trình thực hành kit DE2
a, các tín hiệu quan trọng.
LCD_DATA data 8 bit, LCD_RW read/wirte, LCD_EN tín hiệu enable, LCD_ON tín hiệu nguồn vào, LCD_BLON đèn màn hình, LCD_RS chọn đọc dữ liệu hay đọc Command ( Command dùng để chứa 1 số các thiết lập lcd cơ bản )
chú ý các tín hiệu LCD_ON và LCD_BLON luôn là 1
chúng ta quan tâm đến 2 tín hiệu LCD_RW và LCD_RS nếu chỉ hiển thị lên LCD thì tín hiệu LCD_RW = 0, khi cài đặt vị trí hiển thị kí tự thì ta reset LCD_RS xuống mức 0, ghi dữ liệu thì ta set lên 1.
6304647.png


Trong phần hiển thị thì tín hiệu LCD_R/W các bạn luôn đặt là 0, còn LCD_RS khi muốn cấu hình LCD thì là 0, khi hiển thị dữ liệu thì được set lên 1
b, bảng mã kí tự trong ROM, và thanh ghi AC
ví dụ trong LCD 1602 bạn muốn ghi kí tự A lên ô thứ 15 hàng 2 thì bạn phải set thanh ghi AC lên tương ứng với bảng vị trí các ô trong LCD như hình dưới


ô thứ 15 hàng 2 là 4E thanh ghi AC ( thanh ghi 7 bit ) là 1 0 0 1 1 1 0 còn kí tự A ta sẽ tìm mã ( 8 bit ) trong bảng kí tự trong ROM như hình dưới.


Bảng kí tự gồm chiều ngang là 4 bit cao chiều dọc là 4 bit thấp, bảng mã cung cấp các kí tự đơn giản đã được định nghĩa sẵn muốn hiển thị kí tự A thì dữ liệu vào sẽ là LHLL LLLH tức là 0100 0001 ok quá đơn giản.
Nói qua 1 chút nếu các bạn muốn có thêm những kí tự mới chúng ta cũng có thể định nghĩa 1 kí tự và lưu lại trong bộ tạo kí tự RAM ( đọc thêm ở tài liệu nhé )
c, Tập lệnh điều khiển LCD
LCD cung cấp cho chúng ta 1 số tập lệnh cơ bản để điều khiển LCD ví dụ : clear màn hình, tự động tăng con trỏ ( tự động tăng vị trí ), set chế độ giao tiếp là 4 bit hay 8 bit....
Muốn đọc 8 bit điều khiển LCD thì LCD_RS = 0 và LCD_R/W = 0, phần dưới mình sẽ đến những lệnh cơ bản nhất để tìm hiểu đầy đủ hãy đọc tài liệu
1, Clear Display
Mã lệnh: LCD_DATA= 0 0 0 0 0 0 0 1
lệnh này tắt hiển thị kí tự tất cả các ô sẽ có khoảng trắng, con trỏ về vị trí đầu tiên của LCD
2, Return home
Mã lệnh: LCD_DATA= 0 0 0 0 0 0 1 x
lệnh này trả thanh ghi AC về 0
3, Write DATA
lúc này tín hiệu LCD_RS = 1 và LCD_RW = 0, LCD_DATA = địa chỉ của kí tự cần ghi trong ROM
.............
Phần II: LCD Controller
Trong phần này chúng ta sẽ lập trình quá trình điều khiển đẩy dữ liệu từ input vào output LCD
Cài đặt chỉ hiển thị ( Write ) bằng lệnh
assign LCD_RW = 1'b0;
đoạn mã điều khiển quá trình Write Data
2737029_orig.png

mình sẽ giải thích đoạn code trên như sau. tín hiệu bắt đầu hiển thị mStart được điều khiển bởi tín hiệu input iStart và preStart
giả sử khi chư có dữ liệu được vào thì preStart và iStart đều bằng 0, khi bắt đầu có tín hiệu thì iStart được set lên 1 và preStart <=0 iStart ( có nghĩa là sau khi chu trình sau thì preStart mới được lên 1) lúc này preStart,iStart = 01 thì mStart sẽ được set lên 1 bắt đầu vào quá trình điều khiển.
thực chất vòng lặp case là 1 máy trạng thái ( đã giới thiệu ở phần FGPA cơ bản )
  1. Đầu tiên ST ( state ) là 0 và được gán là 1 không quan tâm đến tín hiệu đầu vào thực chất là đây chỉ là 1 trạng thái đợi để tín hiệu phải trễ đi 1 khoảng thời gian của 1 xung clock
  2. Tiếp theo ST là 1 tín hiệu LCD_EN được set lên 1 ST được gán là 2
  3. Dùng 1 biến đếm từ 0 - 16 thực chất là đợi khoảng 16 x 1/ tần số của xung clock đầu vào dữ liệu lúc này đang được đẩy vào để hiển thị, chắc các bạn thắc mắc tại sao k thấy tín hiệu LCD_DATA trong máy trạng thái này vì ở bên trên tín hiệu LCD_DATA (output) đã được gán với tín hiệu iDATA (input) và chỉ được hiển thị khi LCD_EN = 1 sau đó ST gán là 3 ( quá hay phải k :D )
  4. Trong khi ST = 3 tín hiệu LCD_EN= 0 , mStart = 0, oDone = 1 (tín hiệu báo hiệu quá trình hiển thị 1 kí tự kết thúc LCD lại đang nhàn rỗi đợi kí tự tiếp theo) Cont = 0, ST = 0, thực chất ở đây các tín hiệu được reset về trạng thái ban đầu.OK
Câu hỏi đặt ra là sao không dùng 1 tín hiệu iStat để điều khiển mStart mà lại cần 2 làm gì cho mệt ra, thực chất là như sau ví dụ bạn đang hiển thị kí tự A trong khi kí tự B lại được đẩy vào thì lúc này preStart làm nhiệm vụ báo cho quá trình hiển thị sau biết là tao đang bận đợi tí ( vì lúc này preStart= 1 và preStart,iStart = 2'b11 nên mStart = 0) không cho phép dữ liệu của kí tự B được vào.OK
Code LCD Controll dowload tại đây

Phần III: Hiển thị chuỗi kí tự.
Giả sử chúng ta cần hiển thị từ HELLO WORLD lên dòng 1 LCD, từ chúng ta muốn hiển thị bao gồm 11 kí tự, 1 hàng gồm 16 ô mình sẽ dùng 2 ô đầu tiên và 3 ô cuối cùng của dòng 1 là ký tự trống và 16 ô hàng 2 cũng là kí tự trống.
Muốn hiển thị lên LCD 11 kí tự trên ta phải có 1 bộ chia tần ( chính là module div_frequency trong module chính ) cái này mình nói rồi trong phần FPGA cơ bản.
Tiếp theo 11 kí tự sẽ được điều khiển để trở thành input của module lcd_controller, để làm được điều này ta dùng 1 biến LUT_DATA 9 bit ( gồm 1 bit cao là bit LCD_RS và 8 bit còn lại là 8 bit dữ liệu )
Biến LUT_INDEX chạy từ 0 đến 38:
  1. Từ 0 - 4 chính là cấu hình cho LCD hoạt động ở chế độ 8 bit và clear màn hình ban đầu ( LCD_INTIAL+0 -> LCD_INTIAL+4) tín hiệu LCD_RS = 0
  2. Từ 16 chỉ số tiếp theo là dữ liệu hiển thị lên dòng 1 LCD ( LCD_LINE1+0 -> LCD_LINE1+15) tín hiệu LCD_RS = 1
  3. Chỉ số 17 để cấu hình cho LCD xuống dòng (LCD_CH_LINE) tín hiệu LCD_RS = 0
  4. 16 chỉ số cuối cùng để hiển thị dữ liệu lên dòng 2 LCD (LCD_LINE2+0 -> LCD_LINE2+15) tín hiệu LCD_RS = 1

Cũng giống như LCD_controller cũng cần máy trạng thái để điều khiển quá trình từ input ra output, thì LCD_test ( chính là chuong trình hiển thị HELLO WORLD) cũng cần máy trạng thái để điều khiển quá trình lựa chọn các giá trị LCD_DATA và LCD_RS.
Các bạn tự đọc để hiểu rèn luyện thêm nhé: CODE

Nguồn: fpgavietnam.weebly.com​
 
`timescale 1ns / 1ps

module LCD(
clk,
chars,
lcd_rs, lcd_rw, lcd_e, lcd_4, lcd_5, lcd_6, lcd_7);

// inputs and outputs
input clk;
input [256:0] chars;// 257 bit
output lcd_rs, lcd_rw, lcd_e, lcd_4, lcd_5, lcd_6, lcd_7;

wire [256:0] chars;
reg lcd_rs, lcd_rw, lcd_e, lcd_4, lcd_5, lcd_6, lcd_7;

// internal variables //bien trong
reg [5:0] lcd_code;// 6 bit
reg [1:0] write = 2'b10; // write code has 10 for rs=1//thanh ghi ma lenh, rw=0 ghi dlieu en lcd

// delays
reg [1:0] before_delay = 3; // time before on// thoi gian chuan bi
reg [3:0] on_delay = 13; // time on/// ?
reg [23:0] off_delay = 750_001; // time off/// ?

// states and counters
reg [6:0] Cs = 0;
reg [19:0] count = 0;
reg [1:0] delay_state = 0;

// character data
reg [256:0] chars_hold = " ";
wire [3:0] chars_data [63:0]; // array of characters

// redirects characters data to an array// chuyen ki tu vao mang
generate
genvar i;
for (i = 64; i > 0; i = i-1)
begin : for_name
assign chars_data[64-i] = chars_hold[i*4-1:i*4-4];
end
endgenerate

e mới học verilog đang lập trình khối lcd cho fpga, e có tìm hiểu code nhưng đây là đoạn đầu e chưa hiểu về ngôn ngữ làm , mong mn giúp đỡ

đoạn này
// delays
reg [1:0] before_delay = 3; // time before on// thoi gian chuan bi
reg [3:0] on_delay = 13; // time on/// ?
reg [23:0] off_delay = 750_001; // time off/// ?
cho e hỏi là mấy cái thông số thời gian trên họ khai báo theo kiểu gì ạ

ý nghĩa của đoạn này nữa a.
// character data
reg [256:0] chars_hold = " ";
wire [3:0] chars_data [63:0]; // array of characters

e có tìm hiểu phần help của generate nhưng ở đây cấu trúc lệnh thì đúng như help nhưng trong help ko nói ý nghĩa dùng làm gì
generate
genvar i;
for (i = 64; i > 0; i = i-1)
begin : for_name
assign chars_data[64-i] = chars_hold[i*4-1:i*4-4];
end
endgenerate
mong mn giúp đỡ, e cảm ơn,
 
`timescale 1ns / 1ps


e mới học verilog đang lập trình khối lcd cho fpga, e có tìm hiểu code nhưng đây là đoạn đầu e chưa hiểu về ngôn ngữ làm , mong mn giúp đỡ

đoạn này
// delays
reg [1:0] before_delay = 3; // time before on// thoi gian chuan bi
reg [3:0] on_delay = 13; // time on/// ?
reg [23:0] off_delay = 750_001; // time off/// ?
cho e hỏi là mấy cái thông số thời gian trên họ khai báo theo kiểu gì ạ

ý nghĩa của đoạn này nữa a.
// character data
reg [256:0] chars_hold = " ";
wire [3:0] chars_data [63:0]; // array of characters

e có tìm hiểu phần help của generate nhưng ở đây cấu trúc lệnh thì đúng như help nhưng trong help ko nói ý nghĩa dùng làm gì
generate
genvar i;
for (i = 64; i > 0; i = i-1)
begin : for_name
assign chars_data[64-i] = chars_hold[i*4-1:i*4-4];
end
endgenerate
mong mn giúp đỡ, e cảm ơn,
Theo mình thì :
đoạn này
// delays
reg [1:0] before_delay = 3; // time before on// thoi gian chuan bi
reg [3:0] on_delay = 13; // time on/// ?
reg [23:0] off_delay = 750_001; // time off/// ?
khao báo biến với giá trị có sẵn phụ thuộc vào `timescale 1ns / 1ps bạn set bao nhiêu..
đoạn tiếp theo:
/ character data
reg [256:0] chars_hold = " ";
wire [3:0] chars_data [63:0]; // array of characters
khai báo 2 biến chars_hold với giá trị rỗng ( có thể tác giả định dùng chứa kí tự ) và biến chars_ data là một mảng có độ rộng 4bit và có độ dày là 64bit. Cái này mình không nhớ rõ. mình lấy ví dụ bạn dễ hiểu hơn nó giống thiết kế bộ Ram 4 bit và có 64 thanh ghi vậy đó
còn ý cuối cùng mình chưa dùng generate lần nào nên mình cũng không rõ lắm