Skip to content

truong92cdv/axi_lcd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AXI_LCD

Thiết kế IP core điều khiển LCD 4 bit theo chuẩn AXI

Đề bài: Assignment AXI4 - LCD 4 bit - Thầy Huỳnh Nhật Hải - FPT Jetking

Báo cáo nộp: Assignment Report

0. Demo

Demo

Chuẩn bị Lab (ZuBoard 1CG - LCD 16x2). Đảm bảo nối dây theo file constraint. Tôi có dùng thêm 1 biến trở để chỉnh độ sáng màn hình.

1. Thiết kế module lcd_4bit_controller

lcd_4bit_controller.v

module lcd_4bit_controller(
    input wire clk,
    input wire resetn,
    input wire enable,          // enable module
    input wire start,           // start transaction (từ AXI)
    input wire rs,              // RS = 0: command, 1: data (từ AXI)
    input wire [7:0] data_in,   // dữ liệu 8-bit (từ AXI)
    output reg busy,            // busy flag
    output reg lcd_rs,
    output reg lcd_rw,
    output reg lcd_en,
    output reg [3:0] lcd_d  
);
  • resetn: chuyển FSM sang trạng thái INIT để thực thi chuỗi lệnh khởi tạo LCD tự động.
  • enable: kích hoạt module.
  • start: chuyển FSM sang trạng thái SEND để gửi cmd (hoặc data).
  • rs: xác định byte lưu trong data_in là cmd hay data.
  • data_in: lưu byte code của cmd (hoặc data).
  • busy: cờ báo bận, feedback cho CPU biết.
  • lcd_rs, lcd_rw, lcd_en, lcd_d: các chân nối ra hardware.
// FSM states
localparam [1:0]    IDLE    = 2'b00,
                    INIT    = 2'b01,
                    SEND    = 2'b10;

FSM gồm 3 trạng thái:

  • Trạng thái nghỉ IDLE.
  • Nếu có tín hiệu resetn sẽ chuyển sang trạng thái INIT thực hiện chuỗi khởi tạo LCD (resetn được nối với tín hiệu resetn từ cpu, nên khi khởi chạy phần cứng, LCD sẽ tự động được init).
  • Nếu tín hiệu enablestart đều được bật, chuyển sang trạng thái SEND để gửi cmd hoặc data.

1 số lưu ý:

  • Các thông số k,j,n được set để phù hợp với clk 100 MHz từ Zuboard 1CG.
  • Module dùng 1 biến đếm count duy nhất. Khi khởi tạo sẽ dùng bộ đếm tần số thấp hơn (count[j+7:j+2] # 190 Hz), còn khi gửi lệnh hoặc data thì dùng bộ đếm tần số cao hơn (count[k+7:k+2] # 6 KHz).
  • lcd_en được lấy = ^count[j+1:j+0] để tạo 1 xung vuông ngay giữa bộ đếm count[j+7:j+2], mục đích tạo xung xuống của lcd_en để kích hoạt lệnh gửi.

2. Testbench module lcd_4bit_controller.v

Để test module lcd_4bit_controller.v, cần chỉnh thông số k = 1 để tb chạy nhanh hơn. Và thêm file top.vtop_tb.v. Chạy tb ra được waveform. Để ý thấy xung Enable được tạo ngay giữa mỗi chu kỳ lệnh như ta mong muốn. Source testbench tại đây

waveform 1 waveform 2

3. Thiết kế AXI IP

  • Mở Vivado, tạo project, chọn đúng board Zuboard 1CG.
  • Tools -> Create and Package New IP -> Create a new AXI4 peripheral. Đặt tên IP: myip_axi_lcd. Để các thông số IP mặc định:

create_ip

  • Vivado sẽ tạo 1 AXI4 Lite Slave với 4 thanh ghi nội có độ rộng 32 bit (slv_reg0 đến slv_reg3).
  • Edit IP -> Finish.
  • Vivado sẽ mở 1 project mới, có sẵn 2 file myip_axi_lcd.vmyip_axi_lcd_slave_lite_v1_0_S00_AXI.v.
  • Chọn Add Sources -> add file lcd_4bit_controller.v vào.
  • Mở file myip_axi_lcd.v, tìm đoạn // Users to add ports here, thêm vào khai báo port cho LCD:
// Users to add ports here
output wire lcd_rs,
output wire lcd_rw,
output wire lcd_en,
output wire [3:0] lcd_d,
// User ports ends
  • Tiếp tục tìm đoạn // Add user logic here, thêm vào đoạn khai báo submodule lcd_4bit_controller:
// Add user logic here
wire start_w;
wire rs_w;
wire [7:0] data_in_w;
wire enable_w;
wire busy_w;

lcd_4bit_controller u_lcd (
    .clk      (s00_axi_aclk),
    .resetn   (s00_axi_aresetn),
    .enable   (enable_w),
    .start    (start_w),
    .rs       (rs_w),
    .data_in  (data_in_w),
    .busy     (busy_w),
    .lcd_rs   (lcd_rs),
    .lcd_rw   (lcd_rw),
    .lcd_en   (lcd_en),
    .lcd_d    (lcd_d)
);
// User logic ends
  • Tìm đoạn // Instantiation of Axi Bus Interface S00_AXI. Thêm vào các khai báo port cho submodule:
// Instantiation of Axi Bus Interface S00_AXI
  ...
		.S_AXI_RVALID(s00_axi_rvalid),
		.S_AXI_RREADY(s00_axi_rready),

		.lcd_start		(start_w),
		.lcd_rs			(rs_w),
		.lcd_data_out	(data_in_w),
		.lcd_enable     (enable_w),
		.lcd_busy		(busy_w)
);
endmodule
  • Mở file thứ 2 myip_axi_lcd_slave_lite_v1_0_S00_AXI.v. Tìm đoạn // Users to add ports here, thêm các khai báo port:
// Users to add ports here
output wire lcd_start,
output wire lcd_rs,
output wire [7:0] lcd_data_out,
output wire lcd_enable,   // chip select cho lcd_controller
input  wire lcd_busy,
// User ports ends
  • Xuống cuối file, tìm đoạn // Add user logic here. Bỏ dòng gán assign S_AXI_RDATA = ... ngay phía trên đi, ta sẽ thay bằng đoạn code sau:
// Add user logic here

		// Busy flag mapped into CONTROL readback bit7
		// Final CONTROL register readback = slv_reg0 but with bit7 replaced by lcd_busy
		
		wire [31:0] slv_reg0_with_busy;
		assign slv_reg0_with_busy = { slv_reg0[31:8], lcd_busy, slv_reg0[6:0] };

		// Override AXI read data for CONTROL register (address 0x00)
		assign S_AXI_RDATA =
			(axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 2'h0) ? slv_reg0_with_busy :
			(axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 2'h1) ? slv_reg1 :
			(axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 2'h2) ? slv_reg2 :
			(axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] == 2'h3) ? slv_reg3 : 32'd0;
 
		//--------------------------------------------------------------
		// USER LOGIC
		//--------------------------------------------------------------

		// output registers
		reg       lcd_rs_reg;
		reg [7:0] lcd_data_reg;
		reg       lcd_start_reg;

		assign lcd_rs       = lcd_rs_reg;
		assign lcd_data_out = lcd_data_reg;
		assign lcd_start    = lcd_start_reg;

		// chip select từ CONTROL bit0
		assign lcd_enable   = slv_reg0[0];

		// -----------------------------------------------------
		// thực hiện gửi LCD sau 1 chu kỳ ghi
		// -----------------------------------------------------
		reg last_wvalid;
		reg [1:0] wr_addr_latched;

		// detect write finish (template Vivado thường lệch AW/W)
		wire write_pulse = (S_AXI_WVALID && !last_wvalid);

		always @(posedge S_AXI_ACLK) begin
			last_wvalid <= S_AXI_WVALID;

			if (!S_AXI_ARESETN)
				wr_addr_latched <= 0;
			else if (write_pulse) begin
				wr_addr_latched <= 
					(S_AXI_AWVALID) ?
						S_AXI_AWADDR[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] :
						axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB];
			end
		end

		//------------------------------------------------------------
		// MAIN LOGIC - GỬI LỆNH/DATA SAU KHI REG ĐÃ ỔN ĐỊNH
		//------------------------------------------------------------
		always @(posedge S_AXI_ACLK) begin
			if (!S_AXI_ARESETN) begin
				lcd_rs_reg    <= 0;
				lcd_data_reg  <= 0;
				lcd_start_reg <= 0;
			end else begin
				lcd_start_reg <= 0;   // mặc định

				if (write_pulse && !lcd_busy) begin
					case (wr_addr_latched)

						//------------------------------------------------
						// WRITE CONTROL
						//------------------------------------------------
						2'h0: begin
							lcd_rs_reg <= 0;

							if (slv_reg0[2])        // CLEAR
								lcd_data_reg <= 8'h01;
							else if (slv_reg0[3])   // HOME
								lcd_data_reg <= 8'h80;
							else if (slv_reg0[1])   // LINE2
								lcd_data_reg <= 8'hC0;
							else                    // LINE1
								lcd_data_reg <= 8'h80;

							lcd_start_reg <= 1;
						end

						//------------------------------------------------
						// WRITE DATA
						//------------------------------------------------
						2'h1: begin
							lcd_rs_reg    <= 1;
							lcd_data_reg  <= slv_reg1[7:0];
							lcd_start_reg <= 1;
						end

					endcase
				end
			end
		end

// User logic ends
  • Save code. Mở component.xml từ cửa sổ Sources.
  • Tại cửa sổ Packing Steps thực hiện lần lượt: File Groups -> Merge changes from File Group Wizard. Customization Parameters -> Merge changes from Customization Parameters Wizard. Review and Package -> IP has been modified Review and Package -> Re-Package IP.
  • Sau khi Pakaging IP, Vivado sẽ đóng project edit IP và trở về project chính.
  • Tham khảo source code 2 file submodule tại đây: myip_axi_lcd.v, myip_axi_lcd_slave_lite_v1_0_S00_AXI.v.

4. Thiết kế Block Design cho project chính

  • Create Block Design -> Add IP Zynq UltraScale+ MPSoC -> Run Block Automation -> Apply Board Preset.
  • Double cick vào IP Zynq UltraScale+ MPSoC -> PS-PL Configuration -> PS-PL Interfaces -> Master Interface -> disable AXI HPM1 FPD.
  • Add IP myip_axi_lcd -> Run Connection Automation.
  • Click vào các output port của IP -> Make External -> đổi tên các ports cho phù hợp với file constraint.
  • Click vào nút Regenerate Layout -> Validate Design. Đảm bảo Vivado không báo lỗi (Validation successful).

Block Design

  • Tại cửa sổ Sources -> click phải vào design name -> Create HDL Wrapper -> Let Vivado manage wrapper and auto-update.
  • Add file constraint vào project.
  • Generate Bitstream.
  • File -> Export -> Export Hardware ... -> Include Bitstream -> chọn tên platform -> Finish.

5. Viết code trên Vitis

  • Lauch Vitis.
  • Set Workspace.
  • Create Platform Component, chọn file .xsa vừa export bên Vivado để tạo platform. Sau đó Build platform.
  • Create Embedded Application form Example -> chọn Application mẫu Hello World -> Create ...
  • Mở file helloworld.c. Thay bằng code mới helloworld.c.
  • Chú ý, tại dòng này:
#define LCD_BASE_ADDR XPAR_MYIP_AXI_LCD_0_BASEADDR

cần thay đổi XPAR_MYIP_AXI_LCD_0_BASEADDR theo project cụ thể của bạn (phụ thuộc vào tên IP bạn đã đặt, có thể mở file xparameters.h để kiểm tra tên chính xác của hằng số Base Addr):

/* Definitions for peripheral MYIP_AXI_LCD_0 */
#define XPAR_MYIP_AXI_LCD_0_BASEADDR 0xa0000000
#define XPAR_MYIP_AXI_LCD_0_HIGHADDR 0xa000ffff
  • Build & Run Application.

About

AXI custom IP - control LCD 4 bit display

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors