(转载)FPGA基础知识篇----深入学习串口模块
本篇博客转载来源于http://www.cnblogs.com/kingst/,仅供学习。
3.4 实验十:串口模块
单片机?串口?这些已经是众所周知的组合了吧。但是有一点你是否明白过串口传输的细小部分呢?我们先抛开硬件接口不谈(基本上没有什么好谈),在传统的串口实验。我们只是在串口的表面上,对单片机的寄存器进行配置和查询,来实现串口的操作~
实际上你知不知道,串口在传输的期间,到底发生了什么事情?使用Verilog HDL对串口建模,你会很底层的窥探到它。
串口传输数据都是一帧数据(11位)
位
|
位作用
|
0
|
起始位
|
1~7
|
数据位
|
9
|
校验位
|
10
|
停止位
|
在串口的总线上“高电平”是默认的状态,当一帧数据的开始传输必须先拉低电平,这就是第0位的作用。第0位过后就是8个数据位,这八个数据位才是一帧数据中最有意义的东西。最后的校验位或者停止位,基本上是没有重要意义,作用如同命名般一样。
串口传输还有另一个重要参数就是“波特率”。很多朋友都误解“波特率”是串口传输的传输速度,这样的理解基本上是无误。但是在微观上“波特率”就是“一个位的周期”,换句话说就是“一个位所逗留的时间”。
常用的波特率有9600 bps 和 115200 bps ( bit per second )。“9600 bps” 表示每秒可以传输9600位。但是经过公式计算“一个位的周期”就会暴露出来。
一个位的周期 = 1 / bps
= 1/ 9600
= 0.000104166666666667
从上述的公式,我们明白一个事实 9600 bps ,一位数据占用 0.000104166666666667s 时间。如果是一帧11位的数据,就需要
0.000104166666666667 x 11 = 0.00114583333333334
那么一秒钟内可以传输
1 / 0.00114583333333334 = 872.727272727268
872.727272727268 个帧数据。
当然这只是在数字上计算出来而已,但是实际上还有许多看不见的延迟因数。
实验十之一:串口接收模块
关系到串口接收,基本上就是“采样”的操作。当你明白“bps”的意义后,对于串口的接收时非常的简单。
实验十之一是要建立如上图的 rx_module.v 。Rx_module.v 是一个组合模块,主要是包含 detect_module.v , bps_module.v 和 rx_control_module.v,2个功能模块,和1个组合模块。
detect_module.v 的输入是连结无理引脚rx,它主要检测一帧数据的第0位,也就是起始位,然后产生一个高脉冲经 H2L_Sig 给 rx_control_module.v ,以表示一帧数据接收工作已经开始。
rx_bps_module.v 是产生波特率定时的功能模块。换一句话说,它是配置波特率的模块。
当rx_control_module.v 拉高 Count_Sig, bps_module.v 经 BPS_CLK 对 rx_control_module.v 产生定时。
rx_control_module.v 是核心控制模块。对串口的配置主要是1帧11位的数据,重视八位数据位,无视起始位,校验位和结束位。当RX_En_Sig 拉高,这个模块就开始工作,他将采集来自 RX_Pin_In 的数据,当完成一帧数据接收的时候,就会产生一个高脉冲给 RX_Done_Sig。
说道“采集”,它到底是如何实现采集的呢?
首先,你猜猜看,在什么时候数据是最稳定?如上图所示,数据采集都是在“每位数据的中间”进行着。在上图中 RX_Pin_In 输入一帧数据,当 detect_module.v 检测到低电平(起始位),rx_control_module.v 和 rx_bps_module.v 就产生定时(与RX_Pin_In的波特率是一致),然而rx_bps_module.v 产生的定时是在每个位时间的中间。
在第0位数据,采取忽略的态度,然后接下来的8位数据位都被采集,最后校验位和停止位,却是采取了忽略的操作。有一点你必须好好注意,串口传输数据“从最低位开始,到最高位结束”。
上述的内容,主要是 rx_module.v 的内容。对于全双工的串口来说 rx_module.v 必须与 tx_module.v 保持独立关系,所以 RX_En_Sig 和 RX_Done_Sig 都是被必须的。
实验十之一源码:
detect_module.v
嗯!detect_module.v 这个功能模块,估计也非常眼熟了吧,而该功能模块是为了检查电平由高变低。当检测到电平又高变低,在第31行就会输出高脉冲。
rx_bps_module.v
9600 bps 传输速度使一位数据的周期是 0.000104166666666667s 。以20Mhz时钟频率要得到上述的定时需要:
N = 0.000104166666666667 / ( 1 / 20Mhz )
= 2083
如果从零开始算起 2083 - 1 亦即 2082 个计数(20行)。然而,采集数据要求“在周期的中间”,那么结果是 2082 / 2 ,结果等于 1041(29行)。基本上 rx_bps_module.v 只有在 Count_Sig 拉高的时候(22行),模块才会开始计数。
rx_control_module.v
在37~61行,是 rx_control_module.v 的核心控制功能。在37行,表示了如果 RX_En_Sig 如果没有拉高,这个模块是不会工作。(在26行定义了 isCount 标志寄存器,为了使告知 rx_bps_module.v 输出 bps 定时)当 rx_control_module.v 模块被使能,该模块就会处于就绪状态,一旦 detect_module.v 检查到又高变低的电平变化(41行),会使步骤i进入第0位采集,然而 isCount 标志寄存器同时也会被设置为逻辑1, rx_bps_module.v 便会开始产生波特率的定时。
我们知道 rx_bps_module.v 产生的定时是在“每位数据的中间”。在43~44行,第一次的定时采集时第0位数据(起始位),保持忽略态度。在46~47行,定时采集的是八位数据位,每一位数据位会依低位到最高位储存入 rData 寄存器。
49~53行,是最后两位的定时采集(校验位,停留位),同时采取忽略的态度。当进入55~59行,这表示一帧数据的采集工作已经结束。最后会产生一个完成信号的高脉冲,同时间 isCount会被设置为逻辑0,亦即停止rx_bps_module.v 的操作。
至于65~67行, isCount标志寄存器是 Count_Sig 输出的驱动器,rData 寄存器是 RX_Data 输出的驱动器,最后 isDone 标志寄存器是 RX_Done_Sig 输出的寄存器。
rx_module.v
实验十之一说明:
当rx_module.v 组合模块,组织一切以后只留出,输入RX_Pin_In 和RX_En_Sig, 输出RX_Data 和RX_Done_Sig,只要知道 rx_module.v 基本的操作, 那么这个模块就很容易的控制。
实验十之一最难理解的地方就是“定时采集”那部分了,在前面之所以我说大部分的同学对“波特率”有所误解,关系也就是关于这一部分。如果从另一个角度去思考的话,如果一个位所占的时间越短,那么在1秒以内,可以传输的位就越多,传输速率当然就会越高。
在试验中, rx_bps_module.v 的是“配置波特率”,然而 rx_control_module.v 的工作则对 “数据位位宽”“奇偶校验”“停止位位宽”作出配置。但是在普遍的应用上“1位起始位,8位数据位,1位校验位,和1位停止位”已经成为流行。当然 波特率的速度是 9600 bps 还是 115200 bps 这是看距离而定。
说说一点而外的话吧,波特率越低,虽然传输速度慢,但是支持的距离就越长。波特率越高,虽然在短时间内数据的传输量高,但是支持的距离却越短。可以把,距离和波特率看成正反比吧
完成后的扩展图:
实验十之一结论:
“定时采集”的部分,比较难搞明白,但是深思熟虑过后,你会发现这样的写法很有逻辑。
实验十之一演示:
在这一章,如上图 rx_module_demo ,建立了一个控制模块,对rx_module.v 的调用。
一开始 control_module.v 会拉高 RX_En_Sig 使能 rx_module.v 。当有一阵数据经 RX_Pin_In 传入, rx_module.v 就会接收然后将输出输出致 RX_Data , 再产生一个高脉冲给 RX_Done_Sig。当 control_module.v 接收到 RX_Done_Sig 的高脉冲,就会将 RX_Data 的“前四位”输出致 4 位LED资源。
实验十之一演示源码:
control_module.v
在22~27行就是这个控制模块的全部了。 一开始的时候(27行)就将 isEn设置为逻辑1, 这个标志寄存器在32行驱动着 RX_En_Sig 。换句话说,此时的 rx_module.v 已经进入就绪状态,那么同样的 control_module.v 等待着 RX_Done_Sig 的通知(25行)。一旦一帧数据接收完毕, RX_Done_Sig 就会产生高脉冲 , 然后 rData就会被赋予 RX_Data 的值,同时间 isEn被设置为逻辑0(26行)。在下一瞬间, control_module.v 再一次设置 isEn 为逻辑1,做好接收下一组数据的准备。
rx_module_demo.v
这是实验十之一演示的组合模块,没有什么特别的。连线关系基本上和“图形”一样。
在 45行,对输出进行“载减”致前四位。
实验十之一演示说明:
这个演示主要是在“串口调试助手”以 “十六进制”发送,如:输出 0F ,那么四位LED就会亮了起来,输出 0A ,第四位和第二位LED亮了起来。
完成后扩展图:
实验十之一演示结论:
在设计组合模块的时候,细心的要求,要求非常高。此外这个演示,不过是演示 rx_module.v 是如何调用而已。
实验十之二:串口发送模块
经实验十之一之后,基本上掌握“波特率”的概念了吧?在这一章实验“波特率”依然是保持很重要的角色。
上图是 tx_module.v 组合模块。与rx_module.v比较tx_module.v 少了 detect_module.v,因为在串口发送一帧数据的时候,根本不需要到它。
tx_bps_module.v 同样是作为“定时”的功能。当 TX_En_Sig 拉低电平的时候,它是处于随眠的状态。一旦TX_En_Sig 拉高电平,那么 tx_bps_module.v 就开始计数。然后定时产生一个高脉冲经 BPS_CLK 给 tx_control_module.v。
tx_control_module.v 控制模块是最为中心的一部分,当 TX_En_Sig 拉高电平,同时间 tx_bps_module.v 也会开始计数。tx_control_module.v 将 TX_Data 的值,按 tx_bps_module.v 产生的定时,有节奏的往 TX_Pin_Out 发送。当一帧数据发送完毕后,就产生一个 TX_Done_Sig 的高脉冲。
tx_module.v 和 rx_module.v比较不同的是: rx_module.v 是“定时采集”,然而tx_module.v是“定时发送”。假设我配置的波特率为9600 bps,那么每隔 0.000104166666666667 这个模块就会产生一个高脉冲给 tx_control_module.v,该控制模块会按这个节拍将数据一位一位的数据发送出去。
一帧数据有11位,那么 tx_bps_module.v 需要产生12次定时。
实验十之一源码:
tx_bps_module.v
tx_bps_module.v 是配置为 9600 bps 的波特率(20行),基本上和 rx_bps_module.v没有分别。但是有一点值得注意的是,它现在的工作是“定时发送”。然而有一点可能会使读者混乱 ...
我们知道 BPS_CLK 的产生是在“一位数据的中间”,如果这个模式在“定时采集”的作用上很容易理解,但是换做“定时发送”的模式又有什么不同呢?
左图有3个“定时发送”的产生。每个“定时发送”是在计数 12'd1041 发生。读者尝试数数看,两个“定时发送”的之间到底相差了多少个计数?没错,是12'd2082个计数。这下明白怎么一回事了吧!上一个定时的产生与下一个定时产生的之间才是重点,也就是说“一位数据的周期"定义在两个定时的之间。
tx_control_module.v
比起 rx_control_module.v , tx_control_module.v 相对比较简单。当 TX_En_Sig 拉高(32行)该控制模块就会开始工作了(同时间 tx_bps_module.v 也会开始计数)。每当 tx_bps_module.v 产生一个定时的高脉冲 BPS_CLK, tx_control_module.v 都会发送一位数据。
第0位数据是起始位,所以 rTX寄存器 被赋值位逻辑0(36行)。接下来的八位都是数据位,tx_control_module.v 从 TX_Data 按最低位到最高位将数据赋值给 rTX寄存器(39行)。然而最后两位数据都则是校验位和停止位,如果没有什么特别需求,就随便填上逻辑1就行了(41~45行)。最后产生一个 TX_Done_Sig 的高脉冲(47~51行)。
在58行 TX_Done_Sig的输出是被 isDone 这个标志寄存器驱动着,然而 TX_Pin_Out 的输出是由 rTX这个寄存器驱动。
tx_module.v
在组合模块 tx_module.v 中的连线,基本上和“图形”是一模一样。但是有一点比较特别的是在 24行,Count_Sig 输入是由 TX_En_Sig 直接连线。
实验十之二说明:
发送模块 tx_module.v 基本上与 rx_module.v 比较起来建模跟简单,只要好好的掌握“定时采集”和“定时发送”的区别即可。
tx_bps_module.v 依然是扮演着配置“波特率”的角色。tx_control_module.v 的功能是对一字节数据,包装后以一帧的格式发送出去。至于要不要加入“校验位”,设置不同“数据位位宽”,或者设置不同的“停留位位宽”,视目的而定。普通的应用下有1个起始位,8个数据位,1个校验位和1个停止位。如果没有什么特别的要求,校验位可以随便填。
完成后的扩展图:
实验十之二结论:
tx_module.v 可以视为是一个独立的模块,为了更好的调用它,设计的时候留下 TX_En_Sig 和 TX_Done_Sig 对往后的调用非常有用。
实验十之二演示:
上图是实验十之二演示的组合模块。在 tx_module_demo.v 中的 control_module.v 主要是每秒往 tx_module.v 发送 0x31 的数据。
一开始 control_module.v 往 TX_Data 输出数据,然后拉高TX_Done_Sig 使 tx_module.v 开始工作。当 tx_module.v 发往一帧数据以后,就会对 TX_Done_Sig 产生一个高脉冲,以示发送完毕。
实验十之二演示源码:
control_module.v
第17行是1秒的定义常量,在23~29是1秒的定时器。control_module.v 主要是每秒发送 0x31的数据,换句话说就是每秒设置一次 isEn标志寄存器。当isEn被设置后,tx_module.v 就会开始工作,发完一帧数据位 TX_Done_Sig 会产生高脉冲。这使得(42行),control_module.v 会重新赋值 rData寄存器 然后复位 isEn标志寄存器。直到下一秒的到来,isEn标志寄存器会再一次被设置。
tx_module_demo.v
该组合模块比较简单,没有什么特别的。
实验十之二演示说明:
在实验十之二演示中,“串口调试助手”会一直显示 1 ... 如果以“十六进制”显示的话,结果会是 "31",每一次发送的时间间隔是 1秒。
完成的扩展图:
实验十之二演示结论:
这个演示是演示该 tx_module.v 如何调用。
实验十说明:
在前面我也说过了,串口是全双工执行的。在建模的时候,理应考虑“发送”和“接收”是独立的模块,或者说“发送模块”和“接收模块”是并行操作着。
无论是“发送模块”还是“接收模块”,xx_bps_module.v 控制着波特率的配置,然而 xx_control_module.v 控制着一帧数据的配置。
此外,为了要很好的调用该模块,Done_Sig 都扮演着重要的角色。还记的传统的串口实验吗?在单片机上,我们都是使用“中断”或者“查询”的方式来得知各个模块的工作状态。但是实验十的建模涉及刚好是两中方法的中间,或者应该称为“触发”更为贴切。
在过去的实验八我曾说过。在微观上,每个模块都是独立执行着。但是有些模块比较特殊,平常在时钟的上升沿它们都是处于就绪状态。一旦有“触发”的信号,它就会马上工作。在这一章实验 Done_Sig 可以视为一种“完成反馈”的“触发信号”。
实验十结论:
实验十的“低级建模”比较简单,将一个串口模块,分为两个模块,亦即“发送模块”和“接收模块”。然而每个模块又分为“小模块”,每个“小模块”都有自己的功能。
如 xx_bps_module.v 的工作就是产生定时,xx_control_module.v 是针对一帧信息数据格式的配置。最后在组合模块上,留下“xx_En_Sig”和 “xx_Done_Sig”以方便调用。
实际上,实验十已经涉及许多有关“仿顺序操作”的基本知识了(以往的实验同样也涉及一点),因为“仿顺序操作”的“建模构造”会有标志性的 Start_Sig 和 Done_Sig。
关于更多信息,下一章就会开始讲解了。
总结:
嗯!当读者看到这里,你已经掌握最基本的“低级建模”的建模思路了。这一章笔记,其中最为重要的就是“图形”和“准则”。因为“低级建模”存在的缺点,“图形”和“准则”都可以很有效的去克服,其外还产生“意想不到的效果”。这个“意想不到的效果”到底是什么?笔者就不说什么了,因为那种感觉真的不是文字可以表达得到 ...... 笔者估计读者也渐渐感受了其中“不可思议”的感觉吧?
posted on 2011-01-06 15:04 yingfang18 阅读(3444) 评论(0) 编辑 收藏 举报