RTL, Synthesis, P&R

UART 통신 - (2) Arm보드로 FPGA LCD 제어

MiddleJo 2024. 9. 3. 22:13

- 이전 글

(1) RTL : https://chonh0531.tistory.com/5

 

UART 통신 - (1) RTL

목차1. 배경2. 과제 정의 및 개요3. 소스코드4. 시뮬레이션 결과  1. 배경지난 프로젝트에서 컴퓨터와 신호를 주고받기 위해 UART 통신을 사용하였는데요,UART 통신도 이해하고 Verilog로 구현해보고

chonh0531.tistory.com

 

목차

1. 과제 개요

2. 소스코드

3. 실습 결과

 

 

1. 과제 개요

이전에 구현해 본 UART_RX를 테스트해 보기로 합니다.

UART 신호 생성은 ARM Cortex-M4 Core가 있는 NUCLEO-F429ZI 보드를 이용하여

FPGA에서 LCD를 제어해 보도록 합니다.

 

1. 컴퓨터에서 UART 통신으로 Nucleo 보드에 명령을 전달
2. Nucleo 보드는 FPGA에 다시 UART 통신으로 명령을 전달
3. FPGA는 data 부분의 마지막 4비트를 모터 속도제어에 이용

 

 

 

2. 소스코드

- Top 모듈 Uart_lcd

`timescale 1ns / 1ps

module uart_lcd(
input clk,
input rst,
input rx,
output [7:0]data,
output lcd_e,
output lcd_rs
    );
 wire rx_clk;
     
 wire [7:0]rx_register;
 wire [4:0]clk_cnt;
 wire [3:0]rx_cnt;    
 wire [7:0]lcd_register;
 wire [6:0]lcd_cnt;
 
uart_clock #(.max(163)) C0 (.clk_in(clk),.rst(rst),.clk_out(rx_clk));    
    
uart_rx R0 (.clk(rx_clk),.rst(rst),.rx(rx),.rx_register(rx_register),.clk_cnt(clk_cnt),.rx_cnt(rx_cnt));

lcd L0 (.clk(clk),.rx_clk(rx_clk),.rst(rst),.rx_register(rx_register),.rx_cnt(rx_cnt),.clk_cnt(clk_cnt),.lcd_e(lcd_e),.lcd_rs(lcd_rs),.data(data),.lcd_cnt(lcd_cnt),.lcd_register(lcd_register));

ila_0 ila_0 (.clk(clk),.probe0(lcd_cnt),.probe1(rx),.probe2(data),.probe3(lcd_register),.probe4(rx_cnt));
    
endmodule

 

 

- UART_RX

module uart_rx(
input clk,
input rx,
input rst,
output reg [7:0]rx_register,
output reg rx_end,
output reg [4:0]clk_cnt,
output reg [3:0]rx_cnt
    );  

localparam RX_IDLE = 2'b00;
localparam RX_START = 2'b01;
localparam RX_STOP = 2'b10;

reg [1:0]current_state ;
reg [1:0]next_state;
    
reg [15:0]rx_check;
    
always @(posedge clk or posedge rst) begin
    if (rst)
        clk_cnt <= 0;   
    else if (clk_cnt == 16 && rx_cnt != 11)
        clk_cnt <= 1;
    else if (clk_cnt == 16 && rx_cnt == 11)
        clk_cnt <= 0;    
    else 
        case (next_state)
        RX_IDLE : clk_cnt <= 0;
        RX_START : clk_cnt <= clk_cnt+1;
        RX_STOP : clk_cnt <= clk_cnt+1;
        default : clk_cnt <= clk_cnt;
        endcase                
    end

always @(posedge clk or posedge rst) begin
    if (rst)
        rx_cnt <= 0;
    else if (rx_cnt == 11 && clk_cnt == 16)
        rx_cnt <= 0;
    else if (current_state == RX_IDLE && rx == 0)
        rx_cnt <= 1;
    else if (clk_cnt == 16)
        rx_cnt <= rx_cnt+1;
    else
        rx_cnt <= rx_cnt;
    end                            
    
always @(posedge clk or posedge rst) begin
    if (rst)
        current_state <= RX_IDLE;
    else
        current_state <= next_state;
    end   

always @(negedge clk or posedge rst) begin
    if (rst)
        next_state <= RX_IDLE;
    else
        case(current_state)
        RX_IDLE : if (rx == 0)
                    next_state <= RX_START;
               else       
                    next_state <= next_state;                    
        RX_START : if (rx_cnt == 11)
                    next_state <= RX_STOP;
                else 
                    next_state <= next_state;                              
        RX_STOP : if (rx_cnt == 0)
                    next_state <= RX_IDLE;      
               else
                    next_state <= next_state;
        default : next_state <= next_state;                           
        endcase
    end    
    
 reg [4:0]rx_score;
    
always @(posedge clk or posedge rst) begin
    if (rst)
        rx_check <= 16'b1111_1111_1111_1111;          
    else 
        rx_check <= {rx_check[15:0], rx};                   
    end

always @(posedge clk or posedge rst) begin
    if (rst)
        rx_score <= 0;
    else if (rx_cnt == 0)
        rx_score <= 0;    
    else if (clk_cnt ==16)
        rx_score <= 0;
    else if (rx_cnt == 11)
        rx_score <= 0;        
    else if (rx == 1)
        rx_score <= rx_score+1;       
    else 
        rx_score <= rx_score;
    end             

reg rx_sampling;

always @(posedge clk or posedge rst) begin
    if (rst)
        rx_sampling <= 1;
    else if (rx_cnt == 0)
        rx_sampling <= 1;    
    else if (clk_cnt == 16 && rx_score > 8 && rx_cnt != 11)        
        rx_sampling <= 1;
    else if (clk_cnt == 16 && rx_score < 8 && rx_cnt != 11)        
        rx_sampling <= 0;    
    else 
        rx_sampling <= rx_sampling;
    end

reg [9:0]rx_received;    
    
always @(posedge clk or posedge rst) begin
    if (rst)
        rx_received <= 10'b00_0000_0000;
    else if (clk_cnt == 1 && rx_cnt >1) 
        rx_received <= {rx_received[8:0], rx_sampling};
    else 
        rx_received <= rx_received;
    end    

always @(posedge clk or posedge rst) begin
    if (rst)
        rx_register <= 8'b0000_0000;
    else if (rx_cnt == 11 && clk_cnt == 2)
        rx_register <= rx_received[8:1];
    else 
        rx_register <= rx_register;
    end                                              

always @(posedge clk or posedge rst) begin
    if (rst)
        rx_end <= 1;
    else 
        case (next_state)
        RX_IDLE : rx_end <= 1;
        RX_START : rx_end <= 0;
        RX_STOP : rx_end <= 0;
        default : rx_end <= rx_end;
        endcase
    end    
       
endmodule

 

이전 포스팅에서 언급하였듯이,

코드를 최적화하지 않고 직관적으로 코딩한 후 그대로 두었습니다.

 

- UART_Clock

`timescale 1ns / 1ps

module uart_clock(
input clk_in,
input rst,
output reg clk_out
    );
    
reg [10:0]clk_cnt;

parameter max = 1;
    
always @(posedge clk_in or posedge rst) begin
    if (rst) begin
        clk_cnt <= 0;
        end
    else if (clk_cnt == max) begin        
        clk_cnt <= 0;
        end
    else 
        clk_cnt <= clk_cnt + 1;
    end

always @(posedge clk_in or posedge rst) begin
    if (rst) begin
        clk_out <= 0;
        end
    else if (clk_cnt == max) begin        
        clk_out <= !clk_out;
        end
    else 
        clk_out <= clk_out;
    end    
            
endmodule

 

 

- LCD

`timescale 1ns / 1ps

module lcd(
input clk,
input rx_clk,
input rst,
input [7:0]rx_register,
input [3:0]rx_cnt,
input [4:0]clk_cnt,
output reg lcd_e,
output reg lcd_rs,
output reg [7:0]data,
output reg [6:0]lcd_cnt,
output reg [7:0]lcd_register
    );
    
 reg [7:0] dspdata [0:37];
 integer i = 0;
 integer j = 0;
 
    always @(posedge rx_clk or posedge rst) begin
        if (rst)
            lcd_register <= 8'h80;
        else if (rx_cnt == 11 && clk_cnt == 16) begin
            lcd_register[0] <= rx_register[7];
            lcd_register[1] <= rx_register[6];
            lcd_register[2] <= rx_register[5];
            lcd_register[3] <= rx_register[4];
            lcd_register[4] <= rx_register[3];
            lcd_register[5] <= rx_register[2];
            lcd_register[6] <= rx_register[1];
            lcd_register[7] <= rx_register[0];
            end
        else
            lcd_register <= lcd_register;
        end            
    
    always @(posedge rx_clk or posedge rst) begin
        if (rst)
            lcd_cnt <= 4;
        else if (lcd_cnt == 20 && rx_cnt == 11 && clk_cnt == 16)
            lcd_cnt <= 22;
        else if (lcd_cnt == 37 && rx_cnt == 11 && clk_cnt == 16)
            lcd_cnt <= 5;       
        else if (rx_cnt== 11 && clk_cnt == 16)
            lcd_cnt <= lcd_cnt+1;             
        else
            lcd_cnt <= lcd_cnt;
        end                    
    
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            dspdata[0] <= 8'b00111000;    // function set 8bit, 2line, 5x7 dot
            dspdata[1] <= 8'b00001100;    // display on/off , display on, cursor off, cursor blink off 
            dspdata[2] <= 8'b00000110;    // entry mode set increment cursor position, no display shift
            dspdata[3] <= 8'b00000001;    // clear display
            dspdata[4] <= 8'h80;    // set cg ram address 1000 0000   1번라인 첫번째부터
            dspdata[5] <= 8'b00110000;    // 0
            dspdata[6] <= 8'b00110000;    // 0
            dspdata[7] <= 8'b00110000;    // 0
            dspdata[8] <= 8'b00110000;    // 0
            dspdata[9] <= 8'b00110000;    // 0
            dspdata[10] <= 8'b00110000;    // 0
            dspdata[11] <= 8'b00110000;    // 0
            dspdata[12] <= 8'b00110000;    // 0
            dspdata[13] <= 8'b00110000;    // 0
            dspdata[14] <= 8'b00110000;    // 0
            dspdata[15] <= 8'b00110000;    // 0
            dspdata[16] <= 8'b00110000;    // 0
            dspdata[17] <= 8'b00110000;    // 0
            dspdata[18] <= 8'b00110000;    // 0
            dspdata[19] <= 8'b00110000;    // 0
            dspdata[20] <= 8'b00110000;    // 0           
            dspdata[21] <= 8'hC0;   // set cg ram address 1100 0000   2번라인 첫번째부터
            dspdata[22] <= 8'b00110000;    // 0
            dspdata[23] <= 8'b00110000;    // 0
            dspdata[24] <= 8'b00110000;    // 0
            dspdata[25] <= 8'b00110000;    // 0
            dspdata[26] <= 8'b00110000;    // 0
            dspdata[27] <= 8'b00110000;    // 0
            dspdata[28] <= 8'b00110000;    // 0
            dspdata[29] <= 8'b00110000;    // 0
            dspdata[30] <= 8'b00110000;    // 0
            dspdata[31] <= 8'b00110000;    // 0
            dspdata[32] <= 8'b00110000;    // 0
            dspdata[33] <= 8'b00110000;    // 0
            dspdata[34] <= 8'b00110000;    // 0
            dspdata[35] <= 8'b00110000;    // 0
            dspdata[36] <= 8'b00110000;    // 0
            dspdata[37] <= 8'b00110000;    // 0
            end
        else if (rx_cnt == 0)
            dspdata[lcd_cnt] <= lcd_register;    
        else begin
            dspdata[0] <= dspdata[0];
            dspdata[1] <= dspdata[1];
            dspdata[2] <= dspdata[2];
            dspdata[3] <= dspdata[3];            
            dspdata[4] <= dspdata[4];
            dspdata[5] <= dspdata[5];
            dspdata[6] <= dspdata[6];
            dspdata[7] <= dspdata[7];
            dspdata[8] <= dspdata[8];
            dspdata[9] <= dspdata[9];
            dspdata[10] <= dspdata[10];
            dspdata[11] <= dspdata[11];
            dspdata[12] <= dspdata[12];
            dspdata[13] <= dspdata[13];
            dspdata[14] <= dspdata[14];
            dspdata[15] <= dspdata[15];
            dspdata[16] <= dspdata[16];
            dspdata[17] <= dspdata[17];
            dspdata[18] <= dspdata[18];
            dspdata[19] <= dspdata[19];
            dspdata[20] <= dspdata[20];            
            dspdata[21] <= dspdata[21];
            dspdata[22] <= dspdata[22];
            dspdata[23] <= dspdata[23];
            dspdata[24] <= dspdata[24];
            dspdata[25] <= dspdata[25];
            dspdata[26] <= dspdata[26];
            dspdata[27] <= dspdata[27];
            dspdata[28] <= dspdata[28];
            dspdata[29] <= dspdata[29];
            dspdata[30] <= dspdata[30];
            dspdata[31] <= dspdata[31];
            dspdata[32] <= dspdata[32];
            dspdata[33] <= dspdata[33];
            dspdata[34] <= dspdata[34];
            dspdata[35] <= dspdata[35];
            dspdata[36] <= dspdata[36];
            dspdata[37] <= dspdata[37];
            end
    end                                
    
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            i <= 0;            
            lcd_e <= 0;            
            data <= 8'b0;
            end
        else begin
            if(i <= 1000000) begin
                i <= i + 1;
                lcd_e <= 1;
                data <= dspdata[j];
                end 
            else if ((i > 1000000) && (i < 2000000)) begin
                i <= i + 1;
                lcd_e <= 0;
                end 
            else if (i == 2000000) begin              
                i <= 1'b0;
                end 
                                      
            else begin
                i <= i;
                lcd_e <= lcd_e;
                data <= data;
                end
        end
   end     
   
   always @(posedge clk or posedge rst) begin
       if (rst)
        j <= 0;    
    else if (j == 38)   
        j <= 4;
    else if (i == 2000000)
        j <= j+1;    
    else 
        j <= j;    
    end
    
    always @(posedge clk or posedge rst) begin
        if (rst)
            lcd_rs <= 0;            
        else if (j <= 4)
            lcd_rs <= 0;
        else if (j > 4 && j < 21)
            lcd_rs <= 1;
        else if (j == 21)
            lcd_rs <= 0;
        else if (j > 21 && j < 38)
            lcd_rs <= 1;
        else 
            lcd_rs <= lcd_rs;
        end                               
endmodule

 

 

- Nucleo Board 제어용 코드

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
  uint8_t rx_data;
  int led_mode = 0;

/* USER CODE END PV */


int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART3_UART_Init();

  /* Initialize interrupts */
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */

  HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  switch (led_mode)
	  {
	  	  case 1 : GPIOB -> ODR = 0x0001; break;
	  	  case 2 : GPIOB -> ODR = 0x0080; break;
	  	  case 3 : GPIOB -> ODR = 0x4000; break;
	  	  case 0 : GPIOB -> ODR = 0x0000; break;
	  }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart -> Instance == USART3)
  {
	  HAL_UART_Receive_IT(&huart3, (uint8_t *)&rx_data, 1);
	  HAL_UART_Transmit(&huart3, (uint8_t *)&rx_data, 1, 500);
	  switch (rx_data)
	  {
	  case '1': led_mode = 1; break;
	  case '2': led_mode = 2; break;
	  case '3': led_mode = 3; break;
	  default: led_mode = 0;
	  }

  }

}
/* USER CODE END 4 */

 

CUBE IDE를 다루는 법은 본 글의 목적에 맞지 않으므로

자세히 설명하지 않고, 필요한 부분만 적어두었습니다.

led 부분의 경우 uart 통신이 잘 이루어지는지 확인하기 위해 넣었습니다.

 

3. 실습결과

FPGA는 ZYNQ-7000의 것을 사용하였고,

Tool은 Vivado를 사용하였습니다.

Nucleo 보드는 CUBE IDE에서 제어하였습니다.

 

 

 

 

키보드를 통해 입력한 문자들이

Nucleo 보드를 거쳐 LCD에 잘 출력되는 것을 볼 수 있습니다.

 

이번에는 합성 및 P&R 단계를 생략하고 테스트를 먼저 진행하였습니다.

이후 시간이 된다면 해당 부분도 포스팅하도록 하겠습니다.