FPGA

[FPGA] AVR+FPGA+LCD Shift register 설계

썽우디 2021. 11. 12. 19:29

Editor : Vivado 2020.2

FPGA Board : Cmod A7-35t (xc7a35tcpg236-1)

AVR Board : ATmega128A

LCD : HD44780U (Dot Matrix Liquid Crystal Display Controller)


[ 요구사항 ]

1. LCD 에 "Hello FPGA!" 출력

2. LCD 는 SPI통신 -> Interfacing to a 4bit MPU

3. FPGA -> shift register module 설계 

   참고) 74HC595 datasheet


1. 구조 다이어그램

 

 

2. Shift register

[ Timing Sequence ]

< Sequence 설명 >

1) SS -> Low 일때만 data 저장

2) MOSI -> data 1bit 단위로 shift_clk 발생시에 SHIFT REGISTER에 data 저장 ( data : 11001010 )

3) SS -> High 일때 SHIFT REGISTER 에 저장된 값을 STORAGE REGISTER에 저장

 

 

[ Logic Diagram ]

 

[ Vivado CODE ]

`timescale 1ns / 1ps

module shift_register(
    input i_mosi,
    input i_shift_clk,
    input i_ss,
    output [7:0] o_data
    );
    
    reg [7:0] r_data = 0;
    reg [7:0] t_data = 0;
    
    assign o_data = t_data;
    
    always @(posedge i_shift_clk ) begin
        if(i_shift_clk) begin
            r_data <= {r_data[6:0], i_mosi};
        end
    end
    
    always @(posedge i_ss) begin
        if(i_ss) begin
            t_data <= r_data;
        end
    end
    
endmodule

 

3. LCD

[ Timing Sequence ]

< Sequence 설명 >

1) RS -> HIGH 이면 LCD 액정에 글자(data)를 출력, LOW 이면 register에 data 저장.  즉, HIGH 설정

2) RW -> LCD 액정에 출력하기 위해 Write 동작.  즉, LOW 설정

3) E -> data 동기화 신호

4) DB7 -> data line

[ AVR CODE ]

#include "lcd_4bit.h"

uint8_t lcdData = 0;

void slaveSelect(uint8_t state)
{
	if (state == LOW)
	{
		LCD_SS_PORT &= ~(1<<LCD_SS);
	}
	else
	{
		LCD_SS_PORT |= (1<<LCD_SS);
	}
}

void shiftClock()
{
	LCD_SHIFT_CLK_PORT |= (1<<LCD_SHIFT_CLK);
	LCD_SHIFT_CLK_PORT &= ~(1<<LCD_SHIFT_CLK);
}

void LCD_Data(uint8_t data)
{
	//LCD_DATA_PORT = data; // 8bit 데이터 전송
	
	// 8bit serial 전송, msb부터 전송
	// 0b11001010
	//i_ss핀을 low 만든다.
	slaveSelect(LOW);
	for (int i=0; i<8; i++)
	{
		if (data & (0x80>>i)) LCD_MOSI_PORT |= (1<<LCD_MOSI);
		else LCD_MOSI_PORT &= ~(1<<LCD_MOSI);
		
		shiftClock();
	}
	//i_ss핀을 high 만든다.
	slaveSelect(HIGH);
}

void LCD_Data4bit(uint8_t data)
{
	lcdData = (lcdData & 0x0f) | (data & 0xf0); // 상위 4bit 출력
	LCD_EnablePin();
	lcdData = (lcdData & 0x0f) | ((data & 0x0f) << 4); // 하위 4bit 출력
	LCD_EnablePin();
}

void LCD_Data4bitInit(uint8_t data)
{
	lcdData = (lcdData & 0x0f) | (data & 0xf0); // 상위 4bit 출력
	LCD_EnablePin();
}


void LCD_WriteMode()
{
	lcdData &= ~(1<<LCD_RW);
}


void LCD_EnablePin()
{
	lcdData &= ~(1<<LCD_E);
	LCD_Data(lcdData);
	lcdData |= (1<<LCD_E);
	LCD_Data(lcdData);
	lcdData &= ~(1<<LCD_E);
	LCD_Data(lcdData);
	_delay_us(1600);
}

void LCD_WriteCommand(uint8_t commadData)
{
	lcdData &= ~(1<<LCD_RS);
	LCD_WriteMode();
	LCD_Data4bit(commadData);
}

void LCD_WriteCommandInit(uint8_t commadData)
{
	lcdData &= ~(1<<LCD_RS);
	LCD_WriteMode();
	LCD_Data4bitInit(commadData);
}

void LCD_WriteData(uint8_t charData)
{
	lcdData |= (1<<LCD_RS);
	LCD_WriteMode();
	LCD_Data4bit(charData);
}

 

[ LCD Initializing ]

 

[ AVR CODE ]

void LCD_Init()
{
	LCD_DATA_DDR = 0xff;
	LCD_RS_DDR |= (1<<LCD_RS);
	LCD_RW_DDR |= (1<<LCD_RW);
	LCD_E_DDR |= (1<<LCD_E);
	LCD_SHIFT_CLK_DDR |= (1<<LCD_SHIFT_CLK);
	LCD_MOSI_DDR |= (1<<LCD_MOSI);
	LCD_SS_DDR |= (1<<LCD_SS);
	
	_delay_ms(20);
	LCD_WriteCommand(0x03);
	_delay_ms(5);
	LCD_WriteCommand(0x03);
	_delay_ms(1);
	LCD_WriteCommand(0x03);
	LCD_WriteCommand(0x02);
	LCD_WriteCommand(COMMAND_4_BIT_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
}

 

4. 동작영상 및 사진