集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 2991|回复: 7

基于ZX-2型FPGA开发板的串口示波器(三)

[复制链接]
小梅哥 发表于 2015-4-9 11:21:01 | 显示全部楼层 |阅读模式
基于ZX-2型FPGA开发板的串口示波器(三)

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

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

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

  1. 017     localparam
  2. 018         Header = 8'hAA, /*帧头*/
  3. 019         Length = 8'd3,      /*帧长*/
  4. 020         Tail   = 8'h88; /*帧尾*/
  5. 021
  6. 022 /*----------状态定义-----------------*/     
  7. 023     localparam
  8. 024         CMD_HEADER = 6'b00_0001,
  9. 025         CMD_LENGTH = 6'b00_0010,
  10. 026         CMD_DATAA  = 6'b00_0100,
  11. 027         CMD_DATAB  = 6'b00_1000,
  12. 028         CMD_DATAC  = 6'b01_0000,
  13. 029         CMD_TAIL   = 6'b10_0000;
  14. 030     
  15. 031     
  16. 032     always@(posedge Clk or negedge Rst_n)
  17. 033     if(!Rst_n)begin
  18. 034         reg_CMD_DATA <= 24'd0;
  19. 035         CMD_Valid <= 1'b0;
  20. 036         state <= CMD_HEADER;
  21. 037     end
  22. 038     else if(Rx_Int)begin
  23. 039         case(state)
  24. 040             CMD_HEADER: /*解码帧头数据*/
  25. 041                 if(Rx_Byte == Header)
  26. 042                     state <= CMD_LENGTH;
  27. 043                 else
  28. 044                     state <= CMD_HEADER;
  29. 045            
  30. 046             CMD_LENGTH: /*解码帧长数据*/
  31. 047                 if(Rx_Byte == Length)
  32. 048                     state <= CMD_DATAA;
  33. 049                 else
  34. 050                     state <= CMD_HEADER;
  35. 051            
  36. 052             CMD_DATAA:  /*解码数据A*/
  37. 053                 begin
  38. 054                     reg_CMD_DATA[23:16] <= Rx_Byte;
  39. 055                     state <= CMD_DATAB;
  40. 056                 end
  41. 057                 
  42. 058             CMD_DATAB:  /*解码数据B*/
  43. 059                 begin
  44. 060                     reg_CMD_DATA[15:8] <= Rx_Byte;
  45. 061                     state <= CMD_DATAC;            
  46. 062                 end
  47. 063                 
  48. 064             CMD_DATAC:  /*解码数据C*/
  49. 065                 begin
  50. 066                     reg_CMD_DATA[7:0] <= Rx_Byte;
  51. 067                     state <= CMD_TAIL;              
  52. 068                 end
  53. 069
  54. 070             CMD_TAIL:   /*解码帧尾数据*/
  55. 071                 if(Rx_Byte == Tail)begin
  56. 072                     CMD_Valid <= 1'b1;  /*解码成功,发送解码数据有效标志*/
  57. 073                     state <= CMD_HEADER;
  58. 074                 end
  59. 075                 else begin
  60. 076                     CMD_Valid <= 1'b0;
  61. 077                     state <= CMD_HEADER;
  62. 078                 end
  63. 079             default:;
  64. 080         endcase
  65. 081     end
  66. 082     else begin
  67. 083         CMD_Valid <= 1'b0;
  68. 084         reg_CMD_DATA <= reg_CMD_DATA;
  69. 085     end
复制代码

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

  1. 087 /*------驱动总线写外设寄存器--------*/   
  2. 088     always@(posedge Clk or negedge Rst_n)
  3. 089     if(!Rst_n)begin
  4. 090         m_wr <= 1'b0;
  5. 091         m_addr <= 8'd0;
  6. 092         m_wrdata <= 16'd0;
  7. 093     end
  8. 094     else if(CMD_Valid)begin
  9. 095         m_wr <= 1'b1;
  10. 096         m_addr <= reg_CMD_DATA[23:16];
  11. 097         m_wrdata <= reg_CMD_DATA[15:0];
  12. 098     end
  13. 099     else begin
  14. 100         m_wr <= 1'b0;
  15. 101         m_addr <= m_addr;
  16. 102         m_wrdata <= m_wrdata;   
  17. 103     end
复制代码

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

指令使用说明:

例如,系统在上电后,各个模块默认是没有工作的,要想在上位机上看到数据,就必须先通过上位机发送控制命令。因为系统上电后默认选择的数据通道为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中三个寄存器的定义及配置代码如下所示:

  1. 14      reg [15:0]ADC_Sample_Cnt_Max_L;/*采样分频计数器计数最大值的低16位,ADDR = 8'd1*/
  2. 15      reg [15:0]ADC_Sample_Cnt_Max_H;/*采样分频计数器计数最大值的高16位,ADDR = 8'd2*/
  3. 16      reg ADC_Sample_En;/*采样使能寄存器,ADDR = 8'd3*/
  4. 17
  5. 18  /*-------设置采样分频计数器计数最大值---------*/  
  6. 19      always@(posedge Clk or negedge Rst_n)
  7. 20      if(!Rst_n)begin
  8. 21          ADC_Sample_Cnt_Max_H <= 16'd0;
  9. 22          ADC_Sample_Cnt_Max_L <= 16'd49999;/*默认设置采样率为1K*/
  10. 23      end
  11. 24      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_L))//写采样分频计数器计数最大值的低16位
  12. 25          ADC_Sample_Cnt_Max_L <= m_wrdata;
  13. 26      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_H))//写采样分频计数器计数最大值的高16位
  14. 27          ADC_Sample_Cnt_Max_H <= m_wrdata;
  15. 28      else begin
  16. 29          ADC_Sample_Cnt_Max_H <= ADC_Sample_Cnt_Max_H;
  17. 30          ADC_Sample_Cnt_Max_L <= ADC_Sample_Cnt_Max_L;
  18. 31      end
  19. 32      
  20. 33  /*---------写采样使能寄存器-------------*/
  21. 34      always@(posedge Clk or negedge Rst_n)
  22. 35      if(!Rst_n)
  23. 36          ADC_Sample_En <= 1'b0;
  24. 37      else if(m_wr && (m_addr == `ADC_Sample_En))
  25. 38          ADC_Sample_En <= m_wrdata[0];
  26. 39      else
  27. 40          ADC_Sample_En <= ADC_Sample_En;
复制代码


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

这里,小梅哥使用自己设计的一个山寨版Memory Mapped 总线来配置各个寄存器,该总线包含三组信号,分别为:
写使能信号:m_wr;
写地址信号:m_addr;
写数据信号:m_wrdata;
那么,这三组信号是如何配合工作的呢?我们以配置ADC_Sample_Cnt_Max_H和ADC_Sample_Cnt_Max_L这两个寄存器来进行介绍,这里再贴上这部分代码:

  1. 18  /*-------设置采样分频计数器计数最大值---------*/  
  2. 19      always@(posedge Clk or negedge Rst_n)
  3. 20      if(!Rst_n)begin
  4. 21          ADC_Sample_Cnt_Max_H <= 16'd0;
  5. 22          ADC_Sample_Cnt_Max_L <= 16'd49999;/*默认设置采样率为1K*/
  6. 23      end
  7. 24      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_L))//写采样分频计数器计数最大值的低16位
  8. 25          ADC_Sample_Cnt_Max_L <= m_wrdata;
  9. 26      else if(m_wr && (m_addr == `ADC_S_Cnt_Max_H))//写采样分频计数器计数最大值的高16位
  10. 27          ADC_Sample_Cnt_Max_H <= m_wrdata;
  11. 28      else begin
  12. 29          ADC_Sample_Cnt_Max_H <= ADC_Sample_Cnt_Max_H;
  13. 30          ADC_Sample_Cnt_Max_L <= ADC_Sample_Cnt_Max_L;
  14. 31      end
复制代码

复位时,让{ ADC_Sample_Cnt_Max_H,ADC_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寄存器中。其他寄存器的配置原理与此相同,因此不再做阐述,相信大家举一反三,便可理解了。
小梅哥
2015年4月8日 于至芯科技

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
 楼主| 小梅哥 发表于 2015-4-9 11:44:18 | 显示全部楼层
基于ZX-2型FPGA开发板的串口示波器(一)
http://www.fpgaw.com/forum.php?m ... 0&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(二)
http://www.fpgaw.com/forum.php?m ... 1&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(三)
http://www.fpgaw.com/forum.php?m ... 2&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(四)
http://www.fpgaw.com/forum.php?m ... 3&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(五)
http://www.fpgaw.com/forum.php?m ... 4&fromuid=28191
(出处: fpga论坛|fpga设计论坛)


基于ZX-2型FPGA开发板的串口示波器(六)
http://www.fpgaw.com/forum.php?m ... 5&fromuid=28191
(出处: fpga论坛|fpga设计论坛)
 楼主| 小梅哥 发表于 2015-4-9 11:48:29 | 显示全部楼层
需要工程的请关注至芯官方微信号

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
寻你灯火阑珊处 发表于 2015-4-12 10:00:41 | 显示全部楼层
工程工程快到我的兜里来
telewuhun 发表于 2015-4-17 16:56:21 | 显示全部楼层
太牛逼了,值得学习啊
同人于郊 发表于 2015-8-1 16:36:27 | 显示全部楼层
感谢小梅哥的无私分享,小弟邮箱     759896891@qq.com    麻烦了
金英俊 发表于 2015-9-7 23:06:55 | 显示全部楼层
小梅哥,如果一串数据为AA 03 AA 03 XX  AA 03 XX XX XX  88,就解析不了吧,是不是应该加个定时器呢
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

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

QQ|小黑屋|手机版|Archiver|集成电路技术分享 ( 京ICP备20003123号-1 )

GMT+8, 2024-5-5 00:15 , Processed in 0.091065 second(s), 30 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表