RS232串口通信控制器实验报告.docx
《RS232串口通信控制器实验报告.docx》由会员分享,可在线阅读,更多相关《RS232串口通信控制器实验报告.docx(38页珍藏版)》请在冰点文库上搜索。
RS232串口通信控制器实验报告
摘要
本实验报告为RS232串口通信控制器。
实验设计了基于VHDL描述的RS232串口通信控制器,通过串口调试工具实现PC和CPLD互发和接收数据。
完成的功能包括实现收发一帧10个bit、波特率为9600的串口通信控制器,CPLD接收数据后可译码显示在7段数码管上。
实验的重心放在了RS232串口通信控制器发送模块和接收模块的设计,采用了自顶向下的思路进行设计,系统的核心为有限状态机。
报告中给出了完整的设计思路和过程,并将系统分模块进行了详细的设计,给出了VHDL语言描述。
完成了状态机和核心模块以及系统整体的仿真验证。
最终下载到实验板上测试通过。
关键词:
RS232;VHDL;串口通信;状态机
第一部分任务要求
1.1课题要求
设计并实现一个可以和PC机通过RS232协议进行通信的串口通信控制器。
1.PC机通过串口调试工具来验证程序的功能;
2.实现一个收发一帧10个bit的串口控制器,10个bit是1位起始位,8位数据位,1位结束位;
3.串口的波特率选择9600bit,串口处于全双工工作状态;
4.按“发送数据”按键后,CPLD向PC机发送字符串(内容自定,串口调试工具设成按ASCII码接受方式);
5.PC机可随时向CPLD发送0~F的十六进制数据,CPLD接收后译码显示在7段数码管上。
1.2设计目标
根据课题要求,实验中将目标进行了细化,叙述如下:
1.设定数据帧格式为10bit,其中第一位为起始位,定位低电平,8位数据位,1位结束位,无数据传输时为高电平;
2.按键控制CPLD向PC发送字符串;
3.通过串口调试工具向CPLD发送数据,CPLD接收后译码显示在7段数码管上,要求可以显示20以内的整数;
第二部分系统设计
2.1系统结构设计
2.1.1系统结构框图
图2.1RS232串口通信控制器系统结构框图
串口通信控制器的输入是按键输入信息和PC发送的数据。
当发送数据按键按下时,CPLD向PC发送已定义好的字符串。
同时,当CPLD检测到PC发送数据帧的起始位时,开始接收数据,并行输出。
2.1.2系统逻辑功能划分
图2.2RS232串口通信控制器系统逻辑框图
如图2.2所示,系统可逻辑地划分为三个子系统:
一是分频子系统,包括为发送模块和接收模块提供波特率为9600bit的时钟和为按键防抖模块提供100Hz的时钟;二是发送子系统,包括发送模块和按键防抖模块;三是接收子系统,包括接收模块和7段数码管的译码显示模块。
2.1.3层次模块划分
系统划分为三层,自顶向下分别是顶层模块、控制器子模块、用户模式底层模块。
顶层描述了整个系统的功能和运行;控制器子模块实现系统中各个独立而完整的功能部分;第三层为第二层的子模块,用于扩展第二层的功能,具体负责用户模式的实现。
每个层次可用一个或多个具体模块加以实现。
图2.3RS232串口通信控制器层次模块划分框图
然后从逻辑上划分出各层次的具体模块,分述如下。
模块设计图见2.2.4小节。
A.顶层模块:
作用为集成子模块功能,控制子模块的连接和耦合信号。
定义实体为final,在其中例化子模块。
B.中层模块:
具体实现顶层模块的描述,包括的功能主要是产生时钟信号,发送模块和接收模块状态机的定义和状态的转移:
a)baud
时钟分频模块,将50MHz的时钟分频为9600×16Hz的时钟。
定义波特率为9600bit,波特因子为16。
输入:
50MHz晶振信号。
输出:
送往接收模块和发送模块的9600×16Hz的信号。
b)receiver
接收模块,检测接收端,若检测到低电平则开始接收数据,每隔16个周期接收一位数据,直到接满8bit。
输入:
9600×16Hz时钟信号、PC机发送的串行数据。
输出:
PC机发送的串行数据并行输出。
c)transfer
发送模块。
当按键确定有效时向PC发送自定义的字符串。
输入:
9600×16Hz时钟信号、发送数据按键。
输出:
字符串的串行输出。
以上模块分别生成符号文件,最后在顶层模块中进行连接。
C.底层模块:
a)sw
考虑到按键信号的抖动问题,由按键防抖模块对按键信号进行处理,由100Hz时钟信号对按键进行检测,并将其处理为9600×16Hz时钟信号的一个上升脉冲,作为按键有效信号给发送模块。
输入:
按键信号、9600×16Hz时钟信号、50MHz晶振时钟信号。
输出:
按键信号有效脉冲。
b)display
译码显示模块,可以译码显示20以内的整数。
输入:
接收数据的并行输出。
输出:
7段数码管按键译码。
2.1.4模块设计图
图2.4RS232串口通信控制器模块接口图
图2.4给出了详细的模块图,这些模块属于第二层和第三层。
模块输入输出信号的耦合是在顶层模块中进行描述的。
2.1.5发送模块设计
在本实验的具体设计中,发送模块的状态转移图由图2.5示出。
图2.5发送模块状态转移图
发送模块工作时有5个状态,分别为空闲(s_idle)、起始(s_start)、等待(s_wait)、移位(s_shift)和结束(s_stop)状态。
未检测到按键有效信号时,状态机处于空闲状态,一旦检测到按键信号则进入起始状态。
当定时计数器xcnt>=15时则进入等待状态,定时计数器归零。
定时计数器xcnt>=14时进入移位状态,只要移位计数器xbitcnt/=8,则一直往返于移位和等待状态之间,知道xbitcnt=framlent,即8位数据位发送完毕,则经过15个时钟周期进入结束状态,再经过15个时钟周期,返回到空闲状态,一帧数据10个bit发送完毕。
将发送模块生成符号文件。
其具体VHDL描述参见2.3.2小节。
2.1.6接收模块设计
与发送模块类似,接收模块分为开始(s_start)、找中(s_center)、等待(s_wait)、采样(s_sample)和停止(s_stop)5个状态。
没有检测到起始位时状态机一直处于开始状态,一旦检测到低电平的起始位(rxd_sync=’0’),则进入找中状态。
定时计数器count=8时则进入等待状态,此时处于16个周期的中间位置,即为码元中心,当定时计数器count=14时对数据进行采样,直到移位计数器rcnt=framlenr,即8bit数据采样完毕,此时进入停止状态,重新返回开始状态。
图2.6接收模块状态转移图
2.1.7分频模块设计
串行异步通信的关键在于准确地检测到起始位,因此接收端的时钟必须采用比波特率更高的时钟信号,以便提高采样的分辨率和抗干扰能力,从而更准确地判定起始位。
本实验中波特率为9600bit,取波特率因子为16。
接收端检测到传输线上电平由高到低的变化后便开始对接收时钟进行计数,当计到8个时钟以后,再次对传输线进行采样,如果仍为低电平,则确认是起始位,而不是干扰信号,之后每隔16个时钟进行一次采样,直到采样到停止位。
而且由于每一次采样都是在码元的中心进行的,这样还可以最大限度地避免收、发时钟偏差对数据传送造成的影响。
2.1.8按键防抖模块设计
为确保按键信号不干扰数据的发送,需要将初始按键信号进行处理。
用100Hz的时钟信号可以初步滤掉按键信号的抖动和瑕疵,之后再用9600×16Hz的时钟信号对滤过的信号进行处理,形成单脉冲。
2.1.9译码显示模块设计
图2.7BCD7段数码译码器真值表
由图2.7所示设计7段数码管的译码模块。
2.2系统硬件语言描述
2.2.1VHDL描述思路
第二部分“系统设计”中对系统的功能和逻辑结构、状态机和状态转移以及模块划分都有了详细的叙述,模块间的耦合和信号的传递关系已经清晰。
可以使用VHDL语言描述硬件电路了。
发送模块和接收模块的VHDL语言描述按照事先设计好的状态转移图进行编写可以使电路思想明晰化,避免编程过程中用软件的思维去描述硬件的电路系统,也可有效避免Multi-drive等错误。
由于进行了细致的模块划分,模块的间的耦合程度降到了很低,这就使得电路设计的可靠性大为增强,只要模块的输出满足了功能定义,整个系统就能正常工作,即便个别模块出现问题,也不会过多影响到其他模块。
实际测试过程中也印证了这一点。
2.2.2总体电路的描述
顶层符号文件描述如下:
图2.8顶层符号文件
2.2.3分块电路的描述
本节给出分块电路的硬件描述,对关键部分进行了分析。
由于代码长度较长,只给出部分代码,完整的代码请参见5.2节附录。
各模块的RTL图参考5.3附录。
1、发送模块的描述
定义状态:
TYPEstatesIS(s_idle,s_start,s_wait,s_shift,s_stop);
SIGNALstate:
states:
=s_idle;--初始状态为空闲状态
状态机描述如下
IF(resett='1')THEN--复位信号为1时处于空闲状态,并将输出信号赋值为1
state<=s_idle;
txd_done<='0';
txds:
='1';
ELSE
CASEstateIS
WHENs_idle=>--空闲状态
i:
=0;
IF(cmd_p='1')THEN--按键信号有效
state<=s_start;--进入起始状态
txd_done<='0';
ELSE
state<=s_idle;
txd_done<='1';
txds:
='1';--无数据发送时为高电平
ENDIF;
WHENs_start=>--起始状态
IF(i<8)THEN
CASEiIS
WHEN0=>txdbuf<="01001000";--txdbuf为发送数据缓冲器
WHEN1=>txdbuf<="01100101";
WHEN2=>txdbuf<="01101100";
WHEN3=>txdbuf<="01101100";
WHEN4=>txdbuf<="01101111";
WHEN5=>txdbuf<="01011110";
WHEN6=>txdbuf<="01011111";
WHEN7=>txdbuf<="01011110";--字符串“hello^_^”
WHENOTHERS=>txdbuf<="00000000";
ENDCASE;
ENDIF;
IF(xcnt16>="01111")THEN--xcnt为定时计数器
txds:
='0';
state<=s_wait;
xcnt16<="00000";
ELSE
xcnt16<=xcnt16+'1';
state<=s_start;
ENDIF;
WHENs_wait=>--等待状态
IF(xcnt16>="01110")THEN
IF(xbitcnt=framlent)THEN--xbitcnt为移位计数器
state<=s_stop;--framlent为数据长度,为8
xbitcnt:
=0;
ELSE
state<=s_shift;
ENDIF;
xcnt16<="00000";
ELSE
xcnt16<=xcnt16+'1';
state<=s_wait;
ENDIF;
WHENs_shift=>--移位发送
txds:
=txdbuf(xbitcnt);
xbitcnt:
=xbitcnt+1;
state<=s_wait;
WHENs_stop=>--停止状态,没进入一次完成对一个字符的发送
IF(xcnt16>="01111")THEN
IF(i<7)THEN--字符串未发送完毕则回到s_start状态
i:
=i+1;
state<=s_start;
ELSE
--i:
=0;
IF(cmd_p='0')THEN
state<=s_idle;
xcnt16<="00000";
ELSE
xcnt16<=xcnt16;
state<=s_stop;
ENDIF;
txd_done<='1';--数据发送完毕
ENDIF;
ELSE
xcnt16<=xcnt16+'1';
txds:
='1';
state<=s_stop;
ENDIF;
WHENOTHERS=>state<=s_idle;
ENDCASE;
ENDIF;
txd<=txds;--二级缓存
2、接收模块的描述
定义状态:
TYPEstatesIS(s_start,s_center,s_wait,s_sample,s_stop);
SIGNALstate:
states:
=s_start;
状态机描述如下:
IF(resetr='1')THEN--复位信号有效
state<=s_start;
count:
="0000";
ELSE
CASEstateIS
WHENs_start=>--开始状态
IF(rxd_sync='0')THEN--检测到数据起始位
state<=s_center;--进入找中状态
s_ready<='0';
rcnt:
=0;--为移位计数器
ELSE
state<=s_start;
s_ready<='0';
ENDIF;
WHENs_center=>--找中状态
IF(rxd_sync='0')THEN
IF(count="1000")THEN--定时计数器计到第八个周期
state<=s_wait;--进入等待状态
count:
="0000";
ELSE
count:
=count+1;
state<=s_center;
ENDIF;
ELSE
state<=s_start;
ENDIF;
WHENs_wait=>--等待状态
IF(count>="1110")THEN
IF(rcnt=framlenr)THEN--若数据接收8bit则进入停止状态
state<=s_stop;
ELSE
state<=s_sample;--数据未接收完毕进入取样状态
ENDIF;
count:
="0000";
ELSE
count:
=count+1;
state<=s_wait;
ENDIF;
WHENs_sample=>--取样状态
rbufs(rcnt):
=rxd_sync;
rcnt:
=rcnt+1;
state<=s_wait;
WHENs_stop=>--停止状态
s_ready<='1';
rbuf<=rbufs;
state<=s_start;
WHENOTHERS=>
state<=s_start;
ENDCASE;
ENDIF;
3、分频模块的描述
该模块将50MHz晶振时钟信号分频为9600×16Hz的时钟信号。
baud_set<="10100011";--分频参数,取163
IF(clk'eventANDclk='1')THEN
IF(resetb='1')THEN
cnt<="00000001";
bclk<='0';
ELSIF(cnt=baud_set)THEN
cnt<="00000000";
clk_tmp<=notclk_tmp;
ELSE
cnt<=cnt+1;
ENDIF;
ENDIF;
bclk<=clk_tmp;
4、按键防抖模块的描述
第一个process将50MHz晶振时钟信号分频为100Hz时钟信号,滤掉初始按键信号抖动。
pro1:
process(clk_in1)--clk_in1为50MHz时钟信号
begin
if(clk_in1'eventandclk_in1='1')then
ifcnt=249999then
cnt<=0;
clk_tmp<=notclk_tmp;
else
cnt<=cnt+1;
endif;
endif;
clk_out<=clk_tmp;
endprocess;
第二个process将滤过的按键信号生成为9600×16Hz时钟信号的脉冲。
pro2:
PROCESS(clk_out)--输入9600×16Hz时钟信号
BEGIN
IF(clk_out'EVENTANDclk_out='1')THEN
key_tmp0<=sw;
key_tmp1<=key_tmp0;
key_tmp2<=key_tmp0ANDkey_tmp1;
ENDIF;
ENDPROCESS;
pro3:
PROCESS(clk_in2)
BEGIN
IF(clk_in2'EVENTANDclk_in2='1')THEN
cmd_p<=key_tmp2;
ENDIF;
ENDPROCESS;
5、译码显示模块的描述
other1<='1';--不用的数码管置高电平
other2<='1';
other3<='1';
other4<='1';
tmp3:
="00001010";
IF(rxd>=tmp3)THEN--当数据值大于10时
tmp1:
=rxd-tmp3;--tmp1为个位数
tmp2:
=CONV_STD_LOGIC_VECTOR(CONV_INTEGER(rxd)/CONV_INTEGER(tmp3),8);
--tmp2为十位数
ELSE
tmp1:
=rxd;
tmp2:
="00000000";
ENDIF;
IF(bclkd='1')THEN
display1<='0';--两个数码管在同一时钟的不同段位上显示
display2<='1';
CASEtmp1IS
WHEN"00000000"=>rxd_out<="11111100";
WHEN"00000001"=>rxd_out<="01100000";
WHEN"00000010"=>rxd_out<="11011010";
WHEN"00000011"=>rxd_out<="11110010";
WHEN"00000100"=>rxd_out<="01100110";
WHEN"00000101"=>rxd_out<="10110110";
WHEN"00000110"=>rxd_out<="10111110";
WHEN"00000111"=>rxd_out<="11100000";
WHEN"00001000"=>rxd_out<="11111110";
WHEN"00001001"=>rxd_out<="11110110";--为7段数码管对应的BCD码
WHENOTHERS=>rxd_out<="00000000";
ENDCASE;
ELSE
CASEtmp2IS
WHEN"00000000"=>rxd_out<="11111100";
WHEN"00000001"=>rxd_out<="01100000";
WHEN"00000010"=>rxd_out<="11011010";
WHEN"00000011"=>rxd_out<="11110010";
WHEN"00000100"=>rxd_out<="01100110";
WHEN"00000101"=>rxd_out<="10110110";
WHEN"00000110"=>rxd_out<="10111110";
WHEN"00000111"=>rxd_out<="11100000";
WHEN"00001000"=>rxd_out<="11111110";
WHEN"00001001"=>rxd_out<="11110110";--7段数码管对应的BCD码
WHENOTHERS=>rxd_out<="00000000";
ENDCASE;
rxd_out<=tmp2;
display2<='0';
display1<='1';
ENDIF;
2.3系统仿真验证
系统仿真分为两个步骤进行,首先是关键模块的仿真,验证子系统功能的正确性,然后是综合仿真,验证整个系统的功能。
仿真环境是QuartusII9.0。
首先是分频模块的仿真。
由仿真结果可看出bclk的时钟周期为6.5us,与要求结果一致,频率为9600×16Hz。
图2.9分频模块仿真图(9600×16Hz分频)
然后是发送模块的仿真波形。
下图为发送模块第一个字符发送的波形,第一个字符h的ASCII码值为“01001000”,发送时低位先发送,因此仿真波形由时间的先后顺序,其序列应为“00010010”,每一帧数据为10个bit,第一个bit为起始位,最后一个bit为停止位,可以看出,中间的八位为“00010010”,说明了仿真波形的正确性。
图2.10发送模块仿真图
下图为接收模块仿真图。
设定的输入信号为“11101010”。
接收时将先接收到的数据作为低位,后接收到的数据作为高位,因此转化成的并行数据应为“01010111”,恰为W的ASCII码值,因此仿真波形正确。
图2.11接收模块仿真图
第三部分结果与分析
3.1实现功能说明
本实验完成的RS232串口通信控制器,完成了课题要求的所有功能(参看1.1节“课题要求”)。
由于顶层设计采用了有限状态机和子系统分模块描述的方法,使得控制器具有很好的扩展性。
可以在不改变系统框架和模块间电路连接关系,增加其他模块,实现其他功能。
本课题中并没有在数据帧中加入校验位,仅仅实现了1位起始位,1位停止位和8位数据位的传输。
初步实现了PC和CPLD之间的通信。
数字系统的设计,真正的重点在于把系统层面的设计做好,把系统状态机设计完整,系统架构清晰,层次明确,稳定性好,可扩展和可移植性好,这样的系统给到用户,只要添加用户模块或者稍作修改即可完成非特定的功能。
3.2器件资源分析
在设计阶段,对系统描述采用过多种方式,尝试了多种描述,代码的长度在增加,模块在增多,但系统稳定性和可扩展性也在增强,层次结构和模块设计也更加完善。
设计之初对资源使用没有多少概念,时常以软件的思想描述硬件,在描述中使用乘法等资源耗费严重的操作,也曾使用过嵌套多层的IF-ELSE语句,产生很长的选择器,降低了模块的可靠性,增加处理延时。
随着设计的深入,加上反复求精的过程磨练,逐步对硬件描述综合出的实际电路形式有了更多的理解,对延时和资源占用有了初步概念。
一个显见的结论是描述代码的长度和综合后的电路形式与资源占用无必然联系。
需要关注编译器在综合时如何将VHDL行为级描述转化为结构化的门级电路,才能分析清楚资源占用率和电路结构。
行为级描述可能与实际综合的电路产生不一致,需要谨慎对待。
图3.1是QuartusII编译工程后的综合报告。
逻辑单元占用率27%。
图3.1QuartusII编译综合报告
再进一步从图3.2进行分析,可以看出,资源占用最多的是UserMode子模块,这是很容易理解的,该模块定义了正方形色块,仅这一个定义就采用了四个与操作,每个与操作块内部又有10位的比较器,运动图形的产生运用了多处算数运算与比较,消耗的器件资源