本帖最后由 feifeiz 于 2023-5-7 09:54 编辑
一、前言
继上一篇完成蜂鸣器进行音乐播放后,对开发板的资源以及开发环境有了一定了解,本次继续对开发板功能进行探索。串口是开发过程中基本都会使用的一个外设,可以通过串口进行通信,调试等,如可以使用开发板的串口与其他板子或者模块进行通信,因此,本次在开发板实现串口与上位机通信功能,同时实现串口控制板载的LED。
二、串口简介串行接口(SerialInterface)是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种,按接口可以划分为RS232,RS485等,在工业控制领域中应用非常广泛。 三、硬件连接
打开PGL22G开发板原理图,找到USB转串口部分,如下所示,测试中需确保P1是连接的,默认为连接状态。
继续查找CH340与FPGA连接的引脚,如下所示,UART1_TX接到了T10引脚,UART1_RX接到芯片的T10引脚
同时查看LED0的硬件连接,如下所示,接到芯片的F3引脚
接下来就得到总的引脚使用情况
信号名
| 方向
| 管脚
| 端口说明
| Sys_clk
| input
| B5
| 系统时钟
| Sys_rst_n
| input
| G5
| 系统复位
| Uart_rxd
| input
| R10
| 串口接收
| Uart_txd
| input
| T10
| 串口发送
| led
| output
| F3
| LED输出
|
四、程序设计
总体设计,通过上位机给开发板下发数据,开发板对接收数据进行判断,当接收到的数据是约定的数据时,LED常亮,默认为熄灭状态,模块设计,顶层模块,串口接收模块,总体实现框图。
在顶层模块设计中,添加LED控制输出定义。
moduleuart_loopback_top( input sys_clk, //外部50M时钟
input sys_rst_n, //外部复位信号,低有效
input uart_rxd, //UART接收端口
output uart_txd, //UART发送端口
output led
);
//parameter define
parameter CLK_FREQ = 50000000; //定义系统时钟频率
parameter UART_BPS = 115200; //定义串口波特率
//wire define
wire uart_recv_done; //UART接收完成
wire [7:0] uart_recv_data; //UART接收数据
wire uart_send_en; //UART发送使能
wire [7:0] uart_send_data; //UART发送数据
wire uart_tx_busy; //UART发送忙状态标志
//*****************************************************
//** main code
//*****************************************************
//串口接收模块
uart_recv #(
.CLK_FREQ (CLK_FREQ), //设置系统时钟频率
.UART_BPS (UART_BPS)) //设置串口接收波特率
u_uart_recv(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_done (uart_recv_done),
.uart_data (uart_recv_data),
.led (led)
);
//串口发送模块
uart_send #(
.CLK_FREQ (CLK_FREQ), //设置系统时钟频率
.UART_BPS (UART_BPS)) //设置串口发送波特率
u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_en (uart_send_en),
.uart_din (uart_send_data),
.uart_tx_busy (uart_tx_busy),
.uart_txd (uart_txd)
);
//串口环回模块
uart_loop u_uart_loop(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.recv_done (uart_recv_done), //接收一帧数据完成标志信号
.recv_data (uart_recv_data), //接收的数据
.tx_busy (uart_tx_busy), //发送忙状态标志
.send_en (uart_send_en), //发送使能信号
.send_data (uart_send_data) //待发送数据
);
Endmodule
接收模块中,对接收到的数据进行判断,当接收到上位机下发的数据为1后LED常亮。
moduleuart_recv( input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
input uart_rxd, //UART接收端口
output reg uart_done, //接收一帧数据完成标志
output reg rx_flag, //接收过程标志信号
output reg [3:0] rx_cnt, //接收数据计数器
output reg [7:0] rxdata,
output reg [7:0] uart_data, //接收的数据
output reg led
);
//parameter define
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT =CLK_FREQ/UART_BPS; //为得到指定波特率, //需要对系统时钟计数BPS_CNT次
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [15:0] clk_cnt; //系统时钟计数器
//wire define
wire start_flag;
//*****************************************************
//** main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1& (~uart_rxd_d0);
//对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk ornegedge sys_rst_n) begin if (!sys_rst_n) begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//当脉冲信号start_flag到达时,进入接收过程 always @(posedge sys_clk ornegedge sys_rst_n) begin if (!sys_rst_n)
rx_flag <= 1'b0;
else begin
if(start_flag) //检测到起始位
rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高
//计数到停止位中间时,停止接收过程
else if((rx_cnt == 4'd9)&& (clk_cnt == BPS_CNT/2)) rx_flag <= 1'b0; //接收过程结束,标志位rx_flag拉低
else
rx_flag <= rx_flag;
end
end
//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk ornegedge sys_rst_n) begin if (!sys_rst_n)
clk_cnt <= 16'd0;
else if ( rx_flag ) begin //处于接收过程
if (clk_cnt < BPS_CNT - 1)
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零
end
else
clk_cnt <= 16'd0; //接收过程结束,计数器清零
end
//进入接收过程后,启动接收数据计数器
always @(posedge sys_clk ornegedge sys_rst_n) begin if (!sys_rst_n)
rx_cnt <= 4'd0;
else if ( rx_flag ) begin //处于接收过程
if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期
rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1
else
rx_cnt <= rx_cnt;
end
else
rx_cnt <= 4'd0; //接收过程结束,计数器清零
end
//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk ornegedge sys_rst_n) begin if ( !sys_rst_n)
rxdata <= 8'd0;
else if(rx_flag) //系统处于接收过程
if (clk_cnt == BPS_CNT/2) begin//判断系统时钟计数器计数到数据位中间 case ( rx_cnt )
4'd1 : rxdata[0] <=uart_rxd_d1; //寄存数据位最低位 4'd2 : rxdata[1] <=uart_rxd_d1; 4'd3 : rxdata[2] <=uart_rxd_d1; 4'd4 : rxdata[3] <=uart_rxd_d1; 4'd5 : rxdata[4] <=uart_rxd_d1; 4'd6 : rxdata[5] <=uart_rxd_d1; 4'd7 : rxdata[6] <=uart_rxd_d1; 4'd8 : rxdata[7] <=uart_rxd_d1; //寄存数据位最高位 default:;
endcase
end
else
rxdata <= rxdata;
else
rxdata <= 8'd0;
end
always @(posedge sys_clk ornegedge sys_rst_n) begin if ( !sys_rst_n)
led <= 1'b0;
else if(rxdata == 1)
led <= 1'b1;
end
//数据接收完毕后给出标志信号并寄存输出接收到的数据
always @(posedge sys_clk ornegedge sys_rst_n) begin if (!sys_rst_n) begin
uart_data <= 8'd0;
uart_done <= 1'b0;
end
else if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时 uart_data <= rxdata; //寄存输出接收到的数据
uart_done <= 1'b1; //并将接收完成标志位拉高
end
else begin
uart_data <= 8'd0;
uart_done <= 1'b0;
end
end
endmodule
五、实验现象
编译工程,生成.sbit文件
连接开发板
下载
打开XCOM,在发送区输入1,点击发送
开发板状态,LED0常亮
六、总结
本篇为过渡性文章,通过阅读串口回环章节进行一点拓展,本质上为了实现串口通讯,学会编程,熟悉模块设计以及开发方法及经验。
|