【紫光HiYou开源入门轻量级PCIE开发板PG2L25G】实验例程3-基于紫光FPGA 的UART 串口通信

电子说

1.4w人已加入

描述

在FPGA技术于5G、AI及低空经济等新兴领域持续“破圈”、国产替代加速的宏大背景下,小眼睛科技联合紫光同创及电子发烧友共同发起了“拥抱开源——一起来做FPGA开发板”的项目。历时半年,集结了100多位来自各行各业的工程师智慧,我们成功打造了这款凝聚集体心血的紫光HiYou开源开发板(OPHW-25H)。
这款基于国产紫光同创Logos-2系列芯片的开发板,不仅是千元内入门级产品中唯一配备PCIe接口的轻量级利器,更实现了从硬件设计到底层代码的全方位开源,旨在为广大开发者提供一个高性价比、高扩展性的国产FPGA学习与验证平台,共同点燃技术创新的火花。

开发板

 1.实验简介

实验目的:

OPHW-25开发板集成了一路USB转串口模块,采用的USB-UART芯片CP2102,USB接口采用USBTypeC接口,可以用一根USBTypeC线连接到PC的USB口进行串口数据通信(详情请查看“OPHW-25开发板硬件使用手册”)。通过本实验实现FPGA与PC之间的串口收发实验。串口通信时波特率设置为115200bps,数据格式为1位起始位、8位数据位、无校验位、1位结束位。板子1s向串口助手发送一次十进制显示的“www.meyesemi.com”,通过串口助手向板子以十六进制形式发送数字(00~FF),LED以二进制显示亮起。

实验环境:

Window11

PDS2022.2

硬件环境:

OPHW-25开发板

2.实验原理

2.1.串口原理

从图 8.2-1我们可以看到标准串口接口是 9 根线,具体含义如下:

开发板

数据线:

TXD(pin 3):串口数据输出(Transmit Data)

RXD(pin 2):串口数据输入(Receive Data)

握手:

RTS(pin 7):发送数据请求(Request to Send)

CTS(pin 8):清除发送(Clear to Send)

DSR(pin 6):数据发送就绪(Data Send Ready)

DCD(pin 1):数据载波检测(Data Carrier Detect)

DTR(pin 4):数据终端就绪(Data Terminal Ready)

地线:

GND(pin 5):地线

其它:

RI(pin 9):铃声指示

通常我们用 RS232 串口仅用到了9根传输线中的三根:TXD,RXD,GND。但是对于数据传输,双方必须对数据传输采用使用相同的波特率,约定同样的传输模式(传输架构,握手条件等)。尽管这种方法对于大多数应用已经足够,但是对于接收方过载的情况这种使用受到限制。

RS232 的串口连接方式:

开发板

串口传输协议如下:

开发板

起始位:先发出一个逻辑”0”信号,表示传输字符的开始。

数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8 位)。LSB表示低位,MSB表示高位,有效数据的传输顺序为低位在前高位在后。

校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)。

停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。

空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

 

波特率:uart 中的波特率就可以认为是比特率,即每秒传输的位数(bit)。一般选波特率都会有9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit)。

引入波特率的概念后可得到串口的传输节奏如下:

开发板

细心的话可以发现数据的传输都是在数据稳定后的中心时刻,接收数据其实也是,都是在中心时刻采集数据,此时的数据是最稳定的。

2.2.串口发送字符

从前面串口协议中可以了解到串口每次传输可以以有5~8bit数据,在计算机中字符通常用ASCII码(7bit)表示,所以字符的发送可以用ASCII码发送。查询ASCII码表格可得到:“www.meyesemi.com”用到的字符对应ASCII码;

开发板

3.实验源码设计

从实验目的分析可将实验做如下划分:

开发板

从原理上分析波特率的计算是一个计数器,发射和接收可复用,我们在设计时为保持TX,或RX的完整性,故将波特周期计数器集成在各自模块内部;

上述分析仅仅搭建好OPHW-25的与PC通信的桥梁UART,传输的数据没有体现。故而需要增加发送数据模块,与接收数据模块;

开发板

图 8.4-2

3.1.串口发送模块设计

目标:接收到一个发送命令信号时,将data[7:0]->依次发出{start,data[0:7],stop}共10bit数据(无校验位,停止位1bit);

有两种方法可以将一个并行数据串行化;

方法一:通过bit计数与baud计数控制移位输出;

开发板开发板

这段代码用于实现 UART 模块中每一位数据的发送逻辑。模块通过一个时钟同步控制信号 txd 的高低电平,从而实现串行数据的输出。

首先看第2-30行的 always 块逻辑,这是在每个时钟上升沿触发的时序逻辑。模块首先判断复位信号 rstn,如果复位有效,txd 被置为高电平 1'b1,保持串口空闲状态。因为 UART总是空闲高电平,复位时也要保证输出正确。

当复位结束后,如果传输使能信号 trans_en 有效,模块进入数据传输状态。代码通过 case语句根据 trans_bit 的值选择发送哪一位。具体来说,trans_bit 从 0 到 9 分别对应 UART串口的起始位、8 位数据位和停止位:当 trans_bit=0 时,发送起始位 0,表示数据传输开始;

当 trans_bit=1 到 8 时,依次发送数据寄存器 tx_data_reg[0]到 tx_data_reg[7]的数据位,按照从低位到高位顺序传输;当 trans_bit=9 时,发送停止位 1,表示数据传输结束;默认情况下,也保持高电平 1,保证空闲状态输出正确。

如果传输使能 trans_en 为无效状态,模块将 txd 保持高电平,这样可以保证 UART 总是在空闲时输出逻辑 1。

方法二:通过bit计数与baud计数控制状态跳转,在状态机中输出;

开发板开发板开发板开发板开发板

模块代码使用状态机实现了 UART 串口的字节发送功能,通过波特率控制时钟分频和状态机管理数据传输流程。模块以 50MHz 时钟为基础,通过参数 BPS_NUM 配置波特率的时钟周期数,例如 4800 波特率对应 10417 个时钟周期,9600 波特率对应 5208 个时钟周期,115200 波特率对应 434 个时钟周期。

代码的48行到166行是 UART 串口发送控制逻辑,主要用于将输入的字节数据 tx_data按照设定的波特率发送到串口输出 uart_tx,并通过 tx_busy 输出模块忙状态指示。

首先代码的48-51行是 tx_pluse 同步逻辑,通过寄存器 tx_pluse_reg 在每个时钟上升沿采样 tx_pluse 信号,用于检测发送触发的上升沿。紧接着在代码的54-60行,通过判断 tx_pluse 上升沿设置发送使能 tx_en,当检测到触发信号时拉高 tx_en,表示模块开始发送;当状态机进入发送结束状态 SEND_END 时,将 tx_en 清零,模块返回空闲状态。

代码的63-69行是波特率周期计数器 clk_div_cnt,用于将系统时钟分频以满足指定波特率。在每个波特周期计数完成或发送触发信号到来时清零计数器,否则每个时钟上升沿累加 1,实现对波特周期的精确控制。

在代码的72-82行,tx_bit_cnt 用于记录当前发送的数据位编号。在发送使能为低时计数器清零;在 SEND_DATA 状态下,每完成一个波特周期累加 1,发送完 8 位数据后清零,为发送停止位做好准备。

状态机逻辑在代码的89-134行实现。状态机共包含五个状态:IDLE(空闲)、SEND_START(发送起始位)、SEND_DATA(发送数据位)、SEND_STOP(发送停止位)和 SEND_END(发送结束)。状态跳转逻辑通过组合逻辑 tx_state_n 计算下一状态:在空闲状态检测到发送触发信号时进入起始位状态,发送一个波特周期的低电平后进入数据发送状态,依次发送8 位数据后进入停止位状态,发送一个波特周期的高电平后进入发送结束状态,最终回到空闲状态。状态更新在每个时钟上升沿通过 tx_state <=tx_state_n 完成。

在代码的135-168行是状态机的输出逻辑。在发送使能有效时,根据当前状态控制 uart_tx输出电平:空闲和结束状态输出高电平,起始位输出低电平,数据发送状态按 tx_bit_cnt 输出对应的数据位,停止位输出高电平;当发送使能无效时,输出保持高电平,保证串口线在空闲时为逻辑 1。

3.2.串口接收模块设计

串口接收模块是发射模块的逆过程,设计思路区别不大,但是有如下几点需要注意:

1.接收开始信号,当rx下降沿到来后保持几个时钟周期的低电平,表明进入接收start;

2.接收数据提取位置,前面讲发射的时候都是在波特周期开始的位置变更数据,接收数据提取时需要在rx稳定时刻取数,去波特周期的中间位置取数;

3.最终输出数据锁存,在最后1bit存入寄存器后需要对接收数据锁存,并在之后需要给出数据使能信号,表示输出数据有效;

Module设计如下:

开发板开发板开发板开发板

串口接收模块代码的46行到158行是 UART 串口接收控制逻辑,主要用于从串口输入 uart_rx 接收字节数据,并输出接收数据 rx_data 与有效信号 rx_en,同时通过 rx_finish 指示接收过程是否完成。

46-50行是 uart_rx 信号同步逻辑,通过寄存器 uart_rx_1d 和 uart_rx_2d 在每个时钟上升沿采样 uart_rx 信号,消除异步信号干扰,并用于检测起始位。紧接着在52行,通过组合逻辑 start 检测字节起始信号,即串口线从高电平下降到低电平时标记一个字节接收开始。 rx_finish 信号在状态机进入 RECEIV_END 状态时拉高,表示接收过程完成。

而56-62行是波特率周期计数器 clk_div_cnt,用于将系统时钟分频以满足指定波特率。在空闲状态或计数达到波特周期时计数器清零,否则每个时钟上升沿累加 1。

在代码的67-77行,rx_bit_cnt用于记录当前接收的数据位编号。在空闲状态清零计数器;在RECEIV_DATA状态下,每完成一个波特周期累加1,接收完8位数据后清零,为接收停止位做好准备。

状态机逻辑在代码的83-129 行实现。状态机共包含五个状态:IDLE(空闲)、 RECEIV_START(接收起始位)、RECEIV_DATA(接收数据位)、RECEIV_STOP(接收停止位)和 RECEIV_END(接收结束)。状态跳转逻辑通过组合逻辑rx_state_n计算下一状态:空闲状态检测到start信号时进入起始位状态,接收一个波特周期的低电平后进入数据接收状态,依次接收8位数据后进入停止位状态,接收一个波特周期的高电平后进入结束状态,最终回到空闲状态;如果在结束状态检测到串口线被拉低,表示新字节开始接收,则跳转到起始位状态。状态更新在每个时钟上升沿通过rx_state <=rx_state_n完成。

132-158行是状态机的输出逻辑。在空闲和起始位状态,将接收数据缓冲寄存器和数据使能清零;在数据接收状态,通过在波特周期中间采样串口线的电平,将数据按低位先行的顺序填入缓冲寄存器;在停止位状态,将缓冲寄存器的值赋给输出寄存器rx_data并拉高rx_en表示数据有效;在结束状态清零缓冲寄存器,为下一次接收做好准备。

3.3.串口发送控制模块设计

目标:产生1S间隔的触发信号并输出第一个发送字节,busy的下降沿时输出下一个字节;

Module如下:

开发板开发板开发板

串口发送控制模块,用于按照固定时间间隔生成指定字符序列并通过串口发送。模块以系统时钟 clk 为基础,通过控制逻辑管理发送触发和数据输出。

代码的14行到98行是串口数据生成与发送控制逻辑,主要功能是每隔一定时间生成一个指定字符串(如 "www.meyesemi.comrn")并通过 write_data 和 write_en 输出给 UART发送模块。

首先,代码的14-19行是时间计数器time_cnt,每个时钟上升沿累加1,用于实现发送周期控制,本实验中是实现功能是每秒触发一次字符串发送。

在22-30行,通过 work_en 信号控制串口发送工作区间:当time_cnt达到设定值时开始发送,当发送的数据量达到write_max_num-1时结束发送。work_en_1d在32-35行用于寄存器同步,实现发送使能的边沿检测。

38-42行通过寄存器tx_busy_reg同步tx_busy信号,并生成tx_busy_f信号,用于检测UART发送模块的忙状态下降沿,作为发送触发条件之一。

代码的45-57行生成write_pluse发送脉冲信号:在工作使能状态下,如果刚开始发送或检测到tx_busy下降沿,则拉高write_pluse,触发 UART 发送模块发送一个字节。

在59-65行,data_num 用于记录当前发送的字节序号:当发送结束且tx_busy下降沿出现时清零;当write_pluse有效时累加,为依次输出字符做索引。write_en在67-70行直接由write_pluse 同步输出。

在73-98行,通过case语句将data_num对应到具体ASCII字符,实现字符映射输出:如索引0-2输出'w',索引4输出'.',索引5-10输出"meyese"等,最后两个索引输出回车换行0x0d 0x0a, default情况下输出read_data。

3.4.串口实验顶层模块设计

目标:板子1s向串口助手发送一次十进制显示的“www.meyesemi.com”,通过串口助手向板子以十六进制形式发送数字,LED以二进制显示亮起。

Uart_data_gen模块产生一个间隔1S钟的触发信号,同时输出第一个发送字节,等待uart_tx输出的busy下降沿到来,获知uart_tx进入空闲状态可发送下一个byte时,再次给出串口发送的触发脉冲,并输出下一个字节;

Uart_rx模块接收到数据后输出一个rx_en信号(接收数据使能信号)、一组接收数据信号;接收的数据信号是锁存的,可直接点亮LED灯;

具体的module实现如下:

开发板开发板

模块进行信号声明与内部连线数据生成模块实例化,实例化 uart_data_gen 模块,用于定时生成串口发送数据。UART 发送模块实例化(uart_tx),负责将 tx_data 按波特率发送到 uart_tx 输出端。UART 接收模块实例化(uart_rx)接收外部串口信号 uart_rx,并将接收到的数据输出到 rx_data,同时生成 rx_en 和 rx_finish 信号。37-38 行使用寄存器 receive_data缓存 LED 状态。72 行将 rx_data 直接输出到 LED,实现接收到的数据通过 LED 显示。

顶层模块主要是整合 UART 接收、数据生成和 UART 发送模块,实现了串口数据收发与 LED 显示功能。接收到的数据实时显示在 LED 上,同时模块可按照设定周期生成数据并通过 UART 发送,实现串口双向通信。

 4.实验现象

用SSCOM串口调试工具,波特率设置为115200bps,数据格式为1位起始位、8位数据位、无校验位、1位结束位,用Type-C连接开发板与电脑后有如下现象:

实验现象一:在串口工具中每隔1S中打印一次:“www.meyesemi.com”;

开发板

实验现象二:

在串口工具上以Hex格式发送55;我们可看到OPHW-25板卡上的LED0,LED2为熄灭, LED1,LED3为点亮状态;

开发板

发送F0;我们可看到OPHW-25板卡上的LED0~3为熄灭。

开发板
审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分