差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
uart串口模块 [2017/06/15 10:43]
anran
uart串口模块 [2022/07/20 10:25] (当前版本)
zhijun [小结]
行 1: 行 1:
-======基于STEP FPGA的矩阵按键驱动======+======基于STEP FPGA的UART串口通信模块驱动======
  
-本节将和大家一起使用FPGA驱动底板上的UART接口通信。+本节将和大家一起使用[[FPGA]]驱动底板上的[[UART]]接口通信。
  
  
 ====硬件说明==== ====硬件说明====
 +
 ------- -------
-在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式使用行线和列线分别连接到按键开关的两端这样我们就可以通过4根行线4根列线(共8个I/​O口)连16个按键,而且按键数量越多优势越明显 +通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作[[UART]]是一种通数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接
- +
-FPGA驱动矩阵按键模块,首先我们来了解矩阵按键的硬件连接:+
 \\ \\
-{{ :​矩阵按键.jpg?​800 |}}+异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。两个相邻位间的时间间隔与UART通信的波特率有关,波特率用来表征[[UART]]通信中数据传输的速率,即每秒钟传送的二进制位数。例如数据传送速率为120字符/​秒,而每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位),则其传送的波特率为10×120=1200字符/​秒=1200波特。
 \\ \\
-上图为4x4矩阵按键的硬件电路图,可以看到4根行线(ROW1、ROW2、ROW3、ROW4)和4根列线(COL1、COL2、COL3、COL4),同列线通过上拉电阻连接到VCC电压(3.3V),对于矩阵按键来讲: +{{ :uart.jpg?800 |}}
-  - 4根行线是输入的,是由FPGA控制拉高或拉低, +
-  - 4根列线数输出的,是由4根行线的输入及按键的状态决定,输出给FPGA +
-当某一时刻,FPGA控制4根行线分别为ROW1=0、ROW2=1、ROW3=1、ROW4=1时, +
-  *   ​对于K1、K2、K3、K4按键:按下时对应4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,不按时对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, +
-  *   ​对于K5~~~K16之间的按键:无论按下与否,对应4根列线输出COL1=1、COL2=1、COL3=1、COL4=1, +
-通过上面的描述:在这一时刻只有K1、K2、K3、K4按键被按下,才会导致4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,否则COL1=1、COL2=1、COL3=1、COL4=1,反之当FPGA检测到列线(COL1、COL2、COL3、COL4)中有低电平信号时,对应的K1、K2、K3、K4按键应该是被按下了。 +
- +
-按照扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测,我们在程序中以这4个时刻对应状态机的4个状态。 +
-至于循环的周期,根据我们基础教程里可知,按键抖动的不稳定时间在10ms以内,所以对同一个按键采样的周期大于10ms,这同样取20ms时间。20ms时间对应4个状态,每5分钟进行一次状态转换。+
 \\ \\
-{{ :​矩阵按键程序框图.jpg?800 |}}+  * 起始位:先发出一个逻辑”0”信号,表示传输字符的开始。 
 +  * 数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输 
 +  * 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验) 
 +  * 停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 
 +  * 空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
 \\ \\
-{{ :矩阵按键扫描法原理.jpg |}}+我们这里使用的时序为去掉校验位的时序 
 +{{ :uart4.png?​600 ​|}}
 \\ \\
 +本设计共有四个模块,一个top模块,一个baud模块,一个接收模块和一个发送模块,大家可以根据自己的需求进行调整。
 +
 ====Verilog代码==== ====Verilog代码====
 +
 ------ ------
 <code verilog> <code verilog>
- 
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
 // >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ // >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
-// Module: ​Array_KeyBoard+// Module: ​Uart_bus
 //  // 
 // Author: Step // Author: Step
 //  // 
-// Description: ​Array_KeyBoard+// Description: ​The module for uart communication
 //  // 
 // Web: www.stepfapga.com // Web: www.stepfapga.com
-//+// 
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
 // Code Revision History : // Code Revision History :
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
 // Version: |Mod. Date:   ​|Changes Made: // Version: |Mod. Date:   ​|Changes Made:
-// V1.0     |2015/11/11   |Initial ver+// V1.0     |2016/04/20   |Initial ver
 // -------------------------------------------------------------------- // --------------------------------------------------------------------
-module ​Array_KeyBoard ​#+module ​Uart_Bus ​#
 ( (
- parameter NUM_FOR_200HZ ​60000 //定义计器cnt计数范围,例化时可更改+parameter BPS_PARA ​1250 //当使用12MHz时钟时波特率参选择1250对应9600波特率
 ) )
 ( (
- input clk_in,​ //​系统时钟 +input clk_in,​ //​系统时钟 
- input rst_n_in,​ //​系统复位,低有效 +input rst_n_in,​ //​系统复位,低有效 
- input [3:0] col, //矩阵按键列 +input rs232_rx, //FPGA中UART收端,分配给UART模块中的发送端TXD 
- output reg [3:​0] row,​ //矩阵按键行 +output rs232_tx //FPGA中UART发送端,分配给UART模块中的收端RXD 
- output reg [15:0] key_out //消抖后的信号+);  
 +  
 +/////////////////////////////////​UART接收功能模块例化////////////////////////////////////​ 
 +wire bps_en_rx,​bps_clk_rx;​ 
 +wire [7:0] rx_data; 
 + 
 +//​UART接收波特率时钟控制模块 例化 
 +Baud # 
 +
 +.BPS_PARA (BPS_PARA ) 
 +
 +Baud_rx 
 +(  
 +.clk_in (clk_in ), //系统时钟 
 +.rst_n_in (rst_n_in ),​ //​系统复位,低有效 
 +.bps_en (bps_en_rx ),​ //​接收时钟使能 
 +.bps_clk (bps_clk_rx ) //​接收时钟输出
 ); );
-/* 
-因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态 
-在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样 
-周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间 
-对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理 
-*/  
- localparam STATE0 = 2'b00; 
- localparam STATE1 = 2'b01; 
- localparam STATE2 = 2'b10; 
- localparam STATE3 = 2'b11; 
  
- //器计分频实现5ms周期信号clk_200hz +//​UART接收数据模块 例化 
- reg [15:0] cnt; +Uart_Rx Uart_Rx_uut 
- reg clk_200hz;​ +
- always@(posedge clk_in or negedge rst_n_in) begin +.clk_in (clk_in ),​ //​系统时钟 
- if(!rst_n_in) ​begin //复位时计数器cnt清零,clk_200hz信号起始电平低电平 +.rst_n_in (rst_n_in ), //系统复位,低有效 
- cnt <= 16'd0+.bps_en (bps_en_rx ),​ //​接收时钟使能 
- clk_200hz ​<= 1'​b0;​ +.bps_clk (bps_clk_rx ),​ //​接收时钟输入 
- end else begin +.rs232_rx (rs232_rx ),​ //​UART接收输入 
- if(cnt ​>= ((NUM_FOR_200HZ>>1) - 1)) begin //数字逻辑中右移1相当于除2 +.rx_data (rx_data ) //​接收到的据 
- cnt ​<= 16'd0+); 
- clk_200hz <= ~clk_200hz;​ //​clk_200hz信号取反 +  
- end ​else begin +  
- cnt ​<= cnt + 1'b1; +/////////////////////////////////​UART发送功能模块例化////////////////////////////////////​ 
- clk_200hz <= clk_200hz;​ +wire bps_en_tx,​bps_clk_tx;​ 
- end + 
- end+//​UART发送波特率时钟控制模块 例化 
 +Baud # 
 +
 +.BPS_PARA (BPS_PARA ) 
 +
 +Baud_tx 
 +
 +.clk_in (clk_in ),​ //​系统时钟 
 +.rst_n_in (rst_n_in ),​ //​系统复位,低有效 
 +.bps_en (bps_en_tx ),​ //​发送时钟使能 
 +.bps_clk (bps_clk_tx ) //​发送时钟输出 
 +); 
 + 
 +//​UART发送据模块 例化 
 +Uart_Tx Uart_Tx_uut 
 +
 +.clk_in (clk_in ),​ //​系统时钟 
 +.rst_n_in (rst_n_in ),​ //​系统复位,低有效 
 +.bps_en (bps_en_tx ),​ //​发送时钟使能 
 +.bps_clk (bps_clk_tx ),​ //​发送时钟输入 
 +.rx_bps_en (bps_en_rx ),​ //​因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送 
 +.tx_data (rx_data ),​ //​需要发出的数据 
 +.rs232_tx (rs232_tx ) //​UART发送输出 
 +); 
 + 
 +endmodule 
 +</​code>​ 
 +\\ 
 +<code verilog>​ 
 +// -------------------------------------------------------------------- 
 +// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ 
 +// -------------------------------------------------------------------- 
 +// Module: Baud 
 +//  
 +// Author: Step 
 +//  
 +// Description:​ Beat for uart transfer and receive baud rate 
 +//  
 +// Web: www.stepfapga.com 
 +//  
 +// -------------------------------------------------------------------- 
 +// Code Revision History : 
 +// -------------------------------------------------------------------- 
 +// Version: |Mod. Date:   ​|Changes Made: 
 +// V1.0     ​|2016/​04/​20 ​  ​|Initial ver 
 +// -------------------------------------------------------------------- 
 +module Baud # 
 +
 +parameter BPS_PARA = 1250 //​当使用12MHz时钟时波特率参数选择1250对应9600的波特率 
 +
 +
 +input clk_in,​ //​系统时钟 
 +input rst_n_in,​ //​系统复位,低有效 
 +input bps_en,​ //​接收或发送时钟使能 
 +output reg bps_clk //​接收或发送时钟输出 
 +);  
 + 
 +reg [12:0] cnt; 
 +//​计数器计数满足波特率时钟要求 
 +always @ (posedge clk_in or negedge rst_n_in) begin 
 + if(!rst_n_in) ​ 
 + cnt <= 1'​b0;​ 
 + else if((cnt >= BPS_PARA-1)||(!bps_en)) ​//钟信号不使能(bps_en为低电平)时,计数器清零并停止计数 
 + cnt <= 1'​b0;​ //​当时钟信号使能时,计数器对系统时钟计数,周期BPS_PARA个系统时钟周期 
 + else  
 + cnt <= cnt + 1'b1; 
 +end 
 +  
 +//​产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收 
 +always @ (posedge clk_in or negedge rst_n_in) 
 + begin 
 + if(!rst_n_in) ​ 
 + bps_clk ​<= 1'​b0;​ 
 + else if(cnt ​== (BPS_PARA>>​1)) ​ //BPS_PARA右移于除2,因计数器终值BPS_PARA为数据更替时间点,所以计数器中值时为数据最稳定时间点 
 + bps_clk ​<= 1'b1;  
 + else  
 + bps_clk ​<= 1'b0;
  end  end
  
- reg [1:0] c_state+endmodule 
- //机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效 +</​code>​ 
- always@(posedge ​clk_200hz ​or negedge rst_n_in) begin +\\ 
- if(!rst_n_in) begin +<code verilog>​ 
- c_state ​<= STATE0; +// -------------------------------------------------------------------- 
- row <= 4'b1110+// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ 
- end else begin +// -------------------------------------------------------------------- 
- case(c_state) +// Module: Uart_Rx 
- STATE0: begin c_state ​<= STATE1; row <= 4'b1101end //​状态c_state跳转及对应状态下矩阵按键的row输出 +//  
- STATE1: begin c_state ​<= STATE2; row <= 4'b1011; end +// Author: Step 
- STATE2: begin c_state ​<= STATE3row <= 4'​b0111;​ end +//  
- STATE3: begin c_state ​<= STATE0row <= 4'​b1110;​ end +// Description:​ The receive module of uart interface 
- default:​begin c_state ​<= STATE0row <= 4'​b1110;​ end +//  
- endcase +// Web: www.stepfapga.com 
- end+//  
 +// -------------------------------------------------------------------- 
 +// Code Revision History : 
 +// -------------------------------------------------------------------- 
 +// Version: |Mod. Date:   ​|Changes Made: 
 +// V1.0     ​|2016/​04/​20 ​  ​|Initial ver 
 +// -------------------------------------------------------------------- 
 +module Uart_Rx 
 +
 +input clk_in,​ //​系统时钟 
 +input rst_n_in,​ //​系统复位,低有效 
 + 
 +output reg bps_en,​ //​接收时钟使能 
 +input bps_clk,​ //​接收时钟输入 
 + 
 +input rs232_rx,​ //​UART接收输入 
 +output reg [7:0] rx_data //​接收到的数据 
 +);  
 + 
 +reg rs232_rx0,​rs232_rx1,​rs232_rx2;  
 +//多级延时锁存去除亚稳态 
 +always @ (posedge ​clk_in ​or negedge rst_n_in) begin 
 + if(!rst_n_in) begin 
 + rs232_rx0 ​<= 1'b0
 + rs232_rx1 ​<= 1'b0
 + rs232_rx2 ​<= 1'b0; 
 + end else begin 
 + rs232_rx0 ​<= rs232_rx
 + rs232_rx1 ​<= rs232_rx0
 + rs232_rx2 ​<= rs232_rx1;
  end  end
-  +end 
- //因为每个状态中单行有效,通过对列电平状态采样得到对应4个按键的状态,依次循环 + 
- always@(negedge clk_200hz ​or negedge rst_n_in) begin +//​检测UART接收输入信号的下降沿 
- if(!rst_n_in) ​begin +wire neg_rs232_rx = rs232_rx2 & rs232_rx1 & (~rs232_rx0) & (~rs232_rx);​  
- key_out ​<= 16'hffff+  
- end else begin +reg [3:​0] num;​  
- case(c_state+//接收时钟使能信号控制 
- STATE0:​key_out[3:0] <= col; //采集状态的数据赋值给对应的寄存器位 +always @ (posedge clk_in ​or negedge rst_n_in) begin 
- STATE1:​key_out[7:​4<= col+ if(!rst_n_in) 
- STATE2:​key_out[11:​8<= col+ bps_en ​<= 1'b0
- STATE3:​key_out[15:12] <= col+ else if(neg_rs232_rx && (!bps_en)) //​当空闲状态(bps_en为低电平)时检测到UART接收信号下降沿,进入工作状态(bps_en为高电平),控制时钟模块产生接收时钟 
- default:​key_out ​<= 16'hffff+ bps_en <= 1'​b1;​  
- endcase+ else if(num==4'​d9)       //​当完成一次UART接收操作后,退出工作状态,恢复空闲状态 
 + bps_en <= 1'​b0;​  
 +end 
 + 
 +reg [7:0] rx_data_r; 
 +//当处于工作状态中时,按照接收时钟节拍获取数据 
 +always @ (posedge clk_in or negedge rst_n_in) begin 
 + if(!rst_n_in) begin 
 + num <= 4'd0; 
 + rx_data ​<= 8'd0
 + rx_data_r <= 8'd0; 
 + end else if(bps_en) begin  
 + if(bps_clk) begin  
 + num ​<= num+1'​b1
 + if(num<​=4'​d8) 
 + rx_data_r[num-1]<=rs232_rx; //​先接受低位再接收高位,8位有效数据 
 + end else if(num == 4'd9) begin //​完成一次UART接收操作后,将获取的数据输出 
 + num ​<= 4'd0;  
 + rx_data <= rx_data_r;
  end  end
  end  end
 +end
  
 endmodule endmodule
  
 </​code>​ </​code>​
 +\\
 +<code verilog>
 +// --------------------------------------------------------------------
 +// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​
 +// --------------------------------------------------------------------
 +// Module: Uart_Tx
 +// 
 +// Author: Step
 +// 
 +// Description:​ The transfer module of uart interface
 +// 
 +// Web: www.stepfapga.com
 +// 
 +// --------------------------------------------------------------------
 +// Code Revision History :
 +// --------------------------------------------------------------------
 +// Version: |Mod. Date:   ​|Changes Made:
 +// V1.0     ​|2016/​04/​20 ​  ​|Initial ver
 +// --------------------------------------------------------------------
 +module Uart_Tx
 +(
 +input clk_in,​ //​系统时钟
 +input rst_n_in,​ //​系统复位,低有效
 +output reg bps_en,​ //​发送时钟使能
 +input bps_clk,​ //​发送时钟输入
 +input rx_bps_en,​ //​因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送
 +input [7:​0] tx_data,​ //​需要发出的数据
 +output reg rs232_tx //​UART发送输出
 +);
  
-\\  ​ +reg rx_bps_en_r;​ 
-\\  ​+//​延时锁存接收时钟使能信号 
 +always @ (posedge clk_in or negedge rst_n_in) begin 
 + if(!rst_n_in) rx_bps_en_r <= 1'​b0;​ 
 + else rx_bps_en_r <= rx_bps_en;​ 
 +end 
 +  
 +//​检测接收时钟使能信号的下降沿,因为下降沿代表接收数据的完成,以此作为发送信号的激励 
 +wire neg_rx_bps_en = rx_bps_en_r & (~rx_bps_en);​ 
 +  
 +reg [3:​0] num;​ 
 +reg [9:​0] tx_data_r;​  
 +//​根据接收数据的完成,驱动发送数据操作 
 +always @ (posedge clk_in or negedge rst_n_in) begin 
 + if(!rst_n_in) begin 
 + bps_en <= 1'​b0;​ 
 + tx_data_r <= 8'​d0;​ 
 + end else if(neg_rx_bps_en)begin  
 + bps_en <= 1'​b1;​ //​当检测到接收时钟使能信号的下降沿,表明接收完成,需要发送数据,使能发送时钟使能信号 
 + tx_data_r <= {1'​b1,​tx_data,​1'​b0};​  
 + end else if(num==4'​d10) begin  
 + bps_en <= 1'​b0;​ //​一次UART发送需要10个时钟信号,然后结束 
 + end 
 +end
  
 +//​当处于工作状态中时,按照发送时钟的节拍发送数据
 +always @ (posedge clk_in or negedge rst_n_in) begin
 + if(!rst_n_in) begin
 + num <= 1'b0;
 + rs232_tx <= 1'b1;
 + end else if(bps_en) begin
 + if(bps_clk) begin
 + num <= num + 1'b1;
 + rs232_tx <= tx_data_r[num];​
 + end else if(num>​=4'​d10) ​
 + num <= 4'd0;
 + end
 +end
 +
 +endmodule
 +
 +</​code>​
  
 ====小结==== ====小结====
 +
 ------ ------
-本节主要为大家讲解了矩阵按键工作原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。+本节主要为大家讲解了[[UART]]通信的原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成[[FPGA]]配置文件加载测试。
 \\ \\
-如果你对Diamond软件的使用不了解,请参考这里:[[lattice_diamond的使用|Diamond的使用]]。+如果你对Diamond软件的使用不了解,请参考这里:[[lattice_fpga|Diamond的使用]]。
  
 ====相关资料==== ====相关资料====
 +
 ------ ------
 \\ \\
-使用[[STEP-MXO2第二代]]的矩阵按键程序: ​ 后续会有下载连接 ​ 待更新+使用[[STEP-MXO2第二代]]的UART通信程序: ​ 后续会有下载连接 ​ 待更新
 \\ \\
-使用[[STEP-MAX10]]的矩阵按键程序: ​ 后续会有下载连接 ​ 待更新+使用[[STEP-MAX10]]的UART通信程序: ​ 后续会有下载连接 ​ 待更新
 \\ \\