2400|3

230

帖子

2

TA的资源

纯净的硅(初级)

楼主
 

基于ZX-2型FPGA开发板的串口示波器(三) [复制链接]

串口memory mapped 总线与配置系统子模块寄存器代码分析
CMDCMD模块为串口数据帧接收与解析模块,该模块负责串口接收到的每一的数据进行解码判断,从数据帧中提取出地址字节和数据字节最后地址字节和数据字节转换为类似于Avalon-MM形式的总线以实现对其它模块的控制寄存器的读写,从而实现通过串口控制FPGA各个模块工作目的
在工业应用中,串口指令大多以数据帧的格式出现,包含帧头、帧长、帧命令、帧内容校验和以及帧尾不会只是单纯的传输数据这个实验小梅哥也使用了数据帧的形式来通过上位机向FPGA发送命令,不过这里我使用的帧格式非常简单格式以帧头、帧长、帧内容以及帧尾组成,忽略了校验部分内容,帧头、帧长以及帧尾内容都是固定的,不固定只是帧内容,以下为小梅哥设计中一帧数据的格式:



帧头

帧长

地址

数据

数据



0xAA

0x03

0xXX

0xXX

0xXX

0x88
由于数据帧本身结构简单,因此数据帧的解析过程也相对简洁,以下为小梅哥的数据帧解析状态机设计该状态机分为帧头解析、帧长解析、数据接收以及帧尾解析。默认时,状态机处于帧头解析状态,一旦出现帧头数据,则跳转到帧长接收状态,若下一个字节为帧长数据(这里严格意义上并不能算作帧长,因为长度固定,充其量只能算作帧头,读者不须过分纠结),则开始连续接收三个字节的数据,若非指定的帧长内容,则表明这是无关传输,状态机将返回到帧头解析状态继续等待新的数据帧到来。帧尾解析状态若解析到的数据并非指定的尾数据,表明此次数据帧非有效帧,则将此帧已解析到的数据舍弃若为尾数据,则解析成功,产生命令有效标志信号(CMD_ValidMemory Mapped 总线进程在检测到此命令有效信号后,产生外设寄存器操作。



命令解析的状态机实现代码如下所示:

017     localparam

018         Header = 8'hAA, /*帧头*/

019         Length = 8'd3,      /*帧长*/

020         Tail   = 8'h88; /*帧尾*/

021

022 /*----------状态定义-----------------*/     

023     localparam

024         CMD_HEADER = 6'b00_0001,

025         CMD_LENGTH = 6'b00_0010,

026         CMD_DATAA  = 6'b00_0100,

027         CMD_DATAB  = 6'b00_1000,

028         CMD_DATAC  = 6'b01_0000,

029         CMD_TAIL   = 6'b10_0000;

030     

031     

032     always@(posedge Clk or negedge Rst_n)

033     if(!Rst_n)begin

034         reg_CMD_DATA <= 24'd0;

035         CMD_Valid <= 1'b0;

036         state <= CMD_HEADER;

037     end

038     else if(Rx_Int)begin

039         case(state)

040             CMD_HEADER: /*解码帧头数据*/

041                 if(Rx_Byte == Header)

042                     state <= CMD_LENGTH;

043                 else

044                     state <= CMD_HEADER;

045            

046             CMD_LENGTH: /*解码帧长数据*/

047                 if(Rx_Byte == Length)

048                     state <= CMD_DATAA;

049                 else

050                     state <= CMD_HEADER;

051            

052             CMD_DATAA:  /*解码数据A*/

053                 begin

054                     reg_CMD_DATA[23:16] <= Rx_Byte;

055                     state <= CMD_DATAB;

056                 end

057                 

058             CMD_DATAB:  /*解码数据B*/

059                 begin

060                     reg_CMD_DATA[15:8] <= Rx_Byte;

061                     state <= CMD_DATAC;            

062                 end

063                 

064             CMD_DATAC:  /*解码数据C*/

065                 begin

066                     reg_CMD_DATA[7:0] <= Rx_Byte;

067                     state <= CMD_TAIL;              

068                 end

069

070             CMD_TAIL:   /*解码帧尾数据*/

071                 if(Rx_Byte == Tail)begin

072                     CMD_Valid <= 1'b1;  /*解码成功,发送解码数据有效标志*/

073                     state <= CMD_HEADER;

074                 end

075                 else begin

076                     CMD_Valid <= 1'b0;

077                     state <= CMD_HEADER;

078                 end

079             default:;

080         endcase

081     end

082     else begin

083         CMD_Valid <= 1'b0;

084         reg_CMD_DATA <= reg_CMD_DATA;

085     end
23到第29为状态机编码,这里采用独热码的编码方式状态编码方式有很多种,包括二进制编码、独热码、格雷码等,二进制编码最接近我们的常规思维,但是FPGA内部,其译码电路较为复杂,容易出现竞争冒险,导致使用二进制编码的状态机最高运行速度相对较低独热码的译码电路最简单,因此采用独热码方式编码的状态机运行速度较二进制编码方式很多,但是编码会占用较多的数据位宽格雷码以其独特的编码特性,能够非常完美的解决竞争冒险的问题,使状态机综合出来的电路能够运行在很高的时钟频率,但是格雷码编码较为复杂,尤其对于位宽超过4的格雷码,编码实现二进制编码和独热码编码要复杂的多。这里详细的关于状态机的编码问题,小梅哥不做过多的讨论更加细致的内容,请大家参看夏宇闻老师经典书籍《Verilog数字系统设计教程12相关内容。
Memory Mapped 总线进程根据命令有效标志信号产生外设寄存器操作相关代码如下所示:

087 /*------驱动总线写外设寄存器--------*/    

088     always@(posedge Clk or negedge Rst_n)

089     if(!Rst_n)begin

090         m_wr <= 1'b0;

091         m_addr <= 8'd0;

092         m_wrdata <= 16'd0;

093     end

094     else if(CMD_Valid)begin

095         m_wr <= 1'b1;

096         m_addr <= reg_CMD_DATA[23:16];

097         m_wrdata <= reg_CMD_DATA[15:0];

098     end

099     else begin

100         m_wr <= 1'b0;

101         m_addr <= m_addr;

102         m_wrdata <= m_wrdata;   

103     end






本系统中,需要通过该Memory Mapped 总线配置的寄存器总共有12分别位于ADC采样速率控制模块(Sample_Ctrl)、串口发送控制模块(UART_Tx_Ctrl直接数字频率合成信号发生器模块(DDS,各寄存器地址分配及物理意义如下所示

地址

寄存器名称

寄存器宽度

寄存器功能

0x01

ADC_Sample_Cnt_Max_L

16

ADC采样率设置分频计数器计数最大值的低16

0x02

ADC_Sample_Cnt_Max_H

16

ADC采样率设置分频计数器计数最大值的高16

0x03

ADC_Sample_En

1

ADC采样使能寄存器

0x04

En_Tx

1

串口发送使能寄存器

0x05

reg_Baud_Set

2

串口发送波特率设置寄存器

0x06

DDS_En

1

DDS使能寄存器

0x07

reg_Fword_H

16

DDS频率控制字高16

0x08

reg_Fword_L

16

DDS频率控制字低16

0x09

reg_Pword

12

DDS相位控制字

0x0a

DDS_Sample_Cnt_Max_L

16

DDS采样率设置分频计数器计数最大值的低16

0x0b

DDS_Sample_Cnt_Max_H

16

DDS采样率设置分频计数器计数最大值的高16

0x0c

DDS_Sample_En

1

DDS采样使能寄存器



指令使用说明:



操作

指令

使能DDS生成数据

AA 03 06 00 01 88

停止DDS生成数据

AA 03 06 00 00 88

使能采样DDS数据

AA 03 0C 00 01 88

停止采样DDS数据

AA 03 0C 00 00 88

使能串口发送

AA 03 04 00 01 88

停止串口发送

AA 03 04 00 00 88

使能ADC采样

AA 03 03 00 01 88

停止ADC采样

AA 03 03 00 00 88

DDS频率控制字高16

AA 03 07 XX XX 88

DDS频率控制字16

AA 03 08 XX XX 88

XX_XX_XX_XX = 232*Fout/50_000_000



DDS相位控制字

AA 03 09 0X XX 88

采样DDS输出数据的采样速率控制高16

AA 03 0B XX XX 88

采样DDS输出数据的采样速率控制16

AA 03 0A XX XX 88

XX_XX_XX_XX = 50_000_000/Fs - 1



ADC采样速率控制高16

AA 03 02 XX XX 88

ADC采样速率控制16

AA 03 01 XX XX 88

XX_XX_XX_XX = 50_000_000/Fs - 1



设置串口波特率

AA 03 05 00 0X 88

X=

4’d0:9600bps;

4’d1:19200bps;

4’d2:38400bps;

4’d3:57600bps;

4’d4:115200bps;

4’d5:230400bps;

4’d6:460800bps;

4’d7:921600bps;


例如,系统在上电后,各个模块默认是没有工作的,要想在上位机上看到数据,就必须先通过上位机发送控制命令。因为系统上电后默认选择的数据通道为DDS生成的数据,为了最快的方式串口猎人上看到波形,一种可的控制顺序如下所示:
使能DDS生成数据AA 03 06 00 01 88> 使能采样DDS数据AA 03 0C 00 01 88>使能串口发送数据AA 03 04 00 01 88
这里,为了演示方便,因此在系统中对数据采样速率和DDS生成的信号的频率初始值都做了设置,因此不设置采样率和输出频率控制字这几个寄存器也能串口猎人上接收到数据。
经过此操作后,串口猎人的接收窗口中就会不断的接收到数据当然,这离我们最终显示波形还有一段距离,这部分内容我将放到文档最后,以一次具体的使用为例,来step by step的介绍给大家。


关于Memory Mapped 总线如何实现各模块寄存器的配置,这里小梅哥以ADC采样控制模块Sample_Ctrl三个寄存器的配置来进行介绍Sample_Ctrl三个寄存器的定义配置代码如下所示:

14      reg [15:0]ADC_Sample_Cnt_Max_L;/*采样分频计数器计数最大值的低16位,ADDR = 8'd1*/

15      reg [15:0]ADC_Sample_Cnt_Max_H;/*采样分频计数器计数最大值的高16位,ADDR = 8'd2*/

16      reg ADC_Sample_En;/*采样使能寄存器,ADDR = 8'd3*/

17

18  /*-------设置采样分频计数器计数最大值---------*/  

19      always@(posedge Clk or negedge Rst_n)

20      if(!Rst_n)begin

21          ADC_Sample_Cnt_Max_H <= 16'd0;

22          ADC_Sample_Cnt_Max_L <= 16'd49999;/*默认设置采样率为1K*/

23      end

24      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_L))//写采样分频计数器计数最大值的低16

25          ADC_Sample_Cnt_Max_L <= m_wrdata;

26      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_H))//写采样分频计数器计数最大值的高16

27          ADC_Sample_Cnt_Max_H <= m_wrdata;

28      else begin

29          ADC_Sample_Cnt_Max_H <= ADC_Sample_Cnt_Max_H;

30          ADC_Sample_Cnt_Max_L <= ADC_Sample_Cnt_Max_L;

31      end

32      

33  /*---------写采样使能寄存器-------------*/

34      always@(posedge Clk or negedge Rst_n)

35      if(!Rst_n)

36          ADC_Sample_En <= 1'b0;

37      else if(m_wr && (m_addr == `ADC_Sample_En))

38          ADC_Sample_En <= m_wrdata[0];

39      else

40          ADC_Sample_En <= ADC_Sample_En;




采样率的控制采用定时器的方式实现。使用一个计数器持续对系统时钟进行计数一旦计数满设定时间,则产生一个时钟周期的高脉冲信号,作为ADC采样使能信号。这里,系统时钟周期20ns,因此如果要实现采样1K采样率(采样周期为1ms),则需系统时钟计数50000次;若实现20K的采样率(采样周期为50us则需要对系统时钟计数2500以此类推,可知改变采样率的实质就是改变计数器计数最大值,因此,我们要改变采样速率,也只需要改变采样率控制计数器的计数最大值即可所以这里,我们设计了两个16的寄存器,分别存储采样率控制计数器计数最大16161415所示。我们需要修改ADC的采样率时,直接通过串口发指令,修改这两个寄存器中的内容即可。


这里小梅哥使用自己设计的一个山寨版Memory Mapped 总线来配置各个寄存器,该总线包含三组信号,分别为

使能信号m_wr

地址信号:m_addr

数据信号:m_wrdata
那么,这三组信号如何配合工作的呢?我们配置ADC_Sample_Cnt_Max_HADC_Sample_Cnt_Max_L这两个寄存器来进行介绍这里再贴上这部分代码:

18  /*-------设置采样分频计数器计数最大值---------*/  

19      always@(posedge Clk or negedge Rst_n)

20      if(!Rst_n)begin

21          ADC_Sample_Cnt_Max_H <= 16'd0;

22          ADC_Sample_Cnt_Max_L <= 16'd49999;/*默认设置采样率为1K*/

23      end

24      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_L))//写采样分频计数器计数最大值的低16

25          ADC_Sample_Cnt_Max_L <= m_wrdata;

26      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_H))//写采样分频计数器计数最大值的高16

27          ADC_Sample_Cnt_Max_H <= m_wrdata;

28      else begin

29          ADC_Sample_Cnt_Max_H <= ADC_Sample_Cnt_Max_H;

30          ADC_Sample_Cnt_Max_L <= ADC_Sample_Cnt_Max_L;

31      end



复位时,让{ ADC_Sample_Cnt_Max_HADC_Sample_Cnt_Max_L }49999,即设置默认采样率为1K每当m_wr高且m_addr等于ADC_Sample_Cnt_Max_H寄存器的地址时,就m_wrdata数据更新到ADC_Sample_Cnt_Max_H寄存器中,同理,若m_wr高且m_addr等于ADC_Sample_Cnt_Max_L寄存器的地址时,就m_wrdata数据更新到ADC_Sample_Cnt_Max_L寄存器。其他寄存器的配置原理此相同,因此不再做阐述,相信大家举一反三便可理解了。

小梅哥


201548 于至芯科技




此帖出自FPGA/CPLD论坛

最新回复

小梅哥,太厉害了,求分享  详情 回复 发表于 2015-4-13 15:06
点赞 关注
 

回复
举报

132

帖子

0

TA的资源

一粒金砂(中级)

沙发
 
太牛逼了!
工程分享一下呗
此帖出自FPGA/CPLD论坛
 
 

回复

1891

帖子

2

TA的资源

纯净的硅(中级)

板凳
 
有点厉害。。。。
此帖出自FPGA/CPLD论坛
个人签名
分享铸就美好未来。。。




 
 
 

回复

28

帖子

0

TA的资源

一粒金砂(初级)

4
 
小梅哥,太厉害了,求分享
此帖出自FPGA/CPLD论坛
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表