回答

收藏

[评测分享] 【PGL22G开发板】+串口回环

#板卡评测 #板卡评测 1511 人阅读 | 0 人回复 | 2023-05-07

本帖最后由 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常亮


六、总结
本篇为过渡性文章,通过阅读串口回环章节进行一点拓展,本质上为了实现串口通讯,学会编程,熟悉模块设计以及开发方法及经验。





分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条