数码时钟代码_至简设计系列_断电重加载时钟工程

--作者:小黑同学

本文为明德扬原创及录用文章,转载请注明出处!

1.1 总体设计

1.1.1 概述

在微机的发展初期,BIOS都存放在ROM(只读存储器)中。ROM内部的资料是在ROM的制造工序中,在工厂里用特殊的方法烧录进去的,其中的内容只能读不能改,一旦烧录进去,用户只能验证写入的资料是否正确,不能再做任何修改。如果发现资料有任何错误,则只有舍弃不用,重新订做一份。ROM是在生产线上生产的,由于成本高,一般只用在大批量应用的场合。

由于ROM制造和升级的不便,后来人们发明了PROM(Programmable ROM,可编程ROM)。最初从工厂中制作完成的PROM内部并没有资料,用户可以用专用的编程器将自己的资料写入,但是这种机会只有一次,一旦写入后也无法修改,若是出了错误,已写入的芯片只能报废。PROM的特性和ROM相同,但是其成本比ROM高,而且写入资料的速度比ROM的量产速度要慢,一般只适用于少量需求的场合或是ROM量产前的验证。

EPROM(Erasable Programmable ROM,可擦除可编程ROM)芯片可重复擦除和写入,解决了PROM芯片只能写入一次的弊端。EPROM芯片有一个很明显的特征,在其正面的陶瓷封装上,开有一个玻璃窗口,透过该窗口,可以看到其内部的集成电路,紫外线透过该孔照射内部芯片就可以擦除其内的数据,完成芯片擦除的操作要用到EPROM擦除器。EPROM内资料的写入要用专用的编程器,并且往芯片中写内容时必须要加一定的编程电压(VPP=12~24V,随不同的芯片型号而定)。EPROM的型号是以27开头的,如27C020(8*256K)是一片2M Bits容量的EPROM芯片。EPROM芯片在写入资料后,还要以不透光的贴纸或胶布把窗口封住,以免受到周围的紫外线照射而使资料受损。

由于EPROM操作的不便,后来出的主板上BIOS ROM芯片大部分都采用EEPROM(Electrically Erasable Programmable ROM,电可擦除可编程ROM)。EEPROM的擦除不需要借助于其它设备,它是以电子信号来修改其内容的,而且是以Byte为最小修改单位,不必将资料全部洗掉才能写入,彻底摆脱了EPROM Eraser和编程器的束缚。EEPROM在写入数据时,仍要利用一定的编程电压,此时,只需用厂商提供的专用刷新程序就可以轻而易举地改写内容,所以,它属于双电压芯片。借助于EEPROM芯片的双电压特性,可以使BIOS具有良好的防毒功能,在升级时,把跳线开关打至“on”的位置,即给芯片加上相应的编程电压,就可以方便地升级;平时使用时,则把跳线开关打至“off”的位置,防止CIH类的病毒对BIOS芯片的非法修改。所以,仍有不少主板采用EEPROM作为BIOS芯片并作为自己主板的一大特色。

1.1.2 设计目标

整个工程由FPGA、矩阵键盘/按键、数码管和AT93C46组成,实现一个上电后能重新加载,接着上次计数的数字时钟,详细功能如下。

1、 数码管显示时钟值,共使用了6个数码管,分别表示时十位、时个位、分十位、分个位、秒十位和秒个位。

2、 矩阵键盘或者按键可以对数字时钟进行时分秒的设置。

A、 上电后,时钟默认处于计时状态,当按键1按下,跳到时间设置状态,当按键1再次按下,回到计时状态。

B、 当处于时间设置状态时,默认此刻设置的是秒个位,当按键2按下,此刻设置秒十位,以此类推,一次设置为分个位、分十位、时个位和时十位。再按下按键2,则重新设置秒个位。

C、 当处于时间设置状态时,按下按键3,则设置位的值加1,如果溢出,则变成0。例如当目前小时显示05时,设置时十位,按下按键3,变成15,再按下按键3,则变成05.当目前小时显示为03时,设置时十位,按一下按键3,变成13,再按一下按键3,则变成23,再按则为03。

3、 AT93C46则用于保存时钟值,其具有断电保护功能,断电数据不丢失。

A、 AT93C46一共可以保存128字节的数据。工程将AT93C46分成空间1和空间2。空间1占用的地址为0~3,空间2占用的地址为4~7。

B、 每隔1秒,保存当前时钟值。第一次保存到空间1,第二次保存到空间2,第三次保存带空间1,依此类推。(如果只有一个空间,则可能出现写数据过程中断电,从而得不到完整数据情况)

C、 支持8位的CRC,生成多项式

ff4c1d5ac657843f3c01ed2021b4258b.png

,初始值为全1。

D、 每次保存的值,时十位、时个位、分十位、分个位、秒十位和秒个位各占4bit,共3个字节,加上1个字节的CRC,一共4个字节。

E、 上电后,FPGA将读取两个空间的数值,并作CRC检验。如果两组数据的CRC检验均失败,则不重新加载;如果有一组数据CRC检验失败,则加载正确的一组数据;如果两组数据CRC检验均正确,则加载数值较大的一组数据。

1.1.3 系统结构框图

系统结构框图如下所示:

c2d2b72785eca9239500b674be1c5d64.png

图一

1.1.4 模块功能

Ø 键盘(按键)扫描模块实现功能

1、将外来异步信号打两拍处理,将异步信号同步化。

2、实现20ms按键消抖功能。

3、实现矩阵键盘或者普通案件的检测功能,并输出有效按键信号。

Ø 时钟数据产生模块实现功能

负责产生数字时钟需要的时钟信息。包括:

1、 按数字时钟方式进行计数

2、 设置数字时钟的值

3、 将时钟数据输出给外部模块使用

4、 从数据处理模块得到时钟数据,并重新加载

Ø 数码管显示模块实现功能

1、 对时钟数据进行译码,然后发到数码管显示

2、 逐一显示时分秒的值

Ø 数据处理模块实现功能

负责写到AT93C46的数据,或者从AT93C46读到数据后的处理,包括:

1、 上电后,发送EWEN命令,打开AT93C46的写保护。

2、 发送EWEN命令后,开始读存储在AT93C46的两组时钟数据;对数据进行检验,然后选择适合的数据给时钟数据产生模块加载

3、 每隔1秒从时钟数据产生模块获取时分秒的值,并产生CRC值,最后写道AT93C46上

Ø CRC处理模块实现功能

负责CRC算法的模块,在数据处理模块内部使用

Ø AT93C46模块实现功能

根据上游模块的EWEN、WRITE和READ命令,产生AT93C46的相应时序,从而写数据或者读到数据。至于数据是什么、有什么用,不关心,只关心AT93C46的时序。

1.1.5 顶层信号

53f8053be83b5d88b66072dc0cc59d20.png

1.1.6 参考代码

 1. `define KEY_SCAN  
 2. module at93c46_top(  
 3.     clk      ,  
 4.     rst_n    ,  
 5.     key_col  ,  
 6.     mo       ,  
 7.     cs       ,  
 8.     mi       ,  
 9.     sk       ,  
 10.     key_row  ,  
 11.     seg_sel  ,  
 12.     seg_data   
 13.     );  
 14.  
 15.     parameter    TIME_1S = 50_000_000;  
 16.     input               clk          ;  
 17.     input               rst_n        ;  
 18.     input [3:0]         key_col      ;  
 19.     input               mo           ;  
 20.  
 21.     output              cs           ;  
 22.     output              mi           ;  
 23.     output              sk           ;  
 24.     output[3:0]         key_row      ;  
 25.     output[5:0]         seg_sel      ;  
 26.     output[7:0]         seg_data     ;  
 27.  
 28.     wire                rdy          ;  
 29.     wire                rdata_vld    ;  
 30.     wire  [3:0]         key_en       ;  
 31.     wire  [23:0]        data_load    ;  
 32.     wire                data_load_vld;  
 33.     wire  [23:0]        clk_data_out ;  
 34.     wire  [6:0]         addr         ;  
 35.     wire  [1:0]         mode         ;  
 36.     wire                start        ;  
 37.     wire  [7:0]         wdata        ;  
 38.     wire  [7:0]         rdata        ;  
 
 39.  
 40.  
 41.  `ifdef KEY_SCAN
 42.  key_scan u_key_scan(
 43.  .clk (clk ),
 44.  .rst_n (rst_n ),
 45.  .key_col (key_col),
 46.  .key_row (key_row),
 47.  .key_en (key_en )
 48.  );
 49.  `else 
 50.  key_module u_key_module(
 51.  .clk (clk ),
 52.  .rst_n (rst_n ),
 53.  .key_in (~key_col),
 54.  .key_vld (key_en )
 55.  );
 56.  
 57.  `endif
 58.    clock_data#(.TIME_1S(TIME_1S)) u_clock_data(  
 
 59.                 .clk           (clk           ),  
 60.                 .rst_n         (rst_n         ),  
 61.                 .data_load     (data_load     ),  
 62.                 .data_load_vld (data_load_vld ),  
 63.                 .key_en        (key_en        ),  
 64.                 .data_out      (clk_data_out  )   
 65.     );  
 66.  
 67.     seg_disp#(.SEG_NUM(6)) u_seg_disp(  
 68.                 .rst_n       (rst_n       ),  
 69.                 .clk         (clk         ),  
 70.                 .din         (clk_data_out),  
 71.                 .din_vld     ({6{1'b1}}   ),  
 72.                 .seg_sel     (seg_sel     ),  
 73.                 .segment     (seg_data    )   
 74.              );  
 75.  
 76.  
 77.    data_processor#(.TIME_1S(TIME_1S)) u_data_pro(  
 78.                       .clk        (clk        )  ,  
 79.                       .rst_n      (rst_n      )  ,  
 80.                       .din        (clk_data_out)  ,  
 81.                       .start      (start      )  ,  
 82.                       .mode       (mode       )  ,  
 83.                       .addr       (addr       )  ,  
 84.                       .wdata      (wdata      )  ,  
 85.                       .rdata      (rdata      )  ,  
 86.                       .rdata_vld  (rdata_vld  )  ,  
 87.                       .rdy        (rdy        )  ,  
 88.                       .dout       (data_load  )  ,  
 89.                       .dout_vld   (data_load_vld )      
 90.  
 91.     );  
 92.  
 93.  
 94.     at93c46_mix u_at93c46_mix(  
 95.      .clk        (clk        )  ,  
 96.     .rst_n      (rst_n      )  ,  
 97.     .start      (start      )  ,  
 98.     .mode       (mode       )  ,  
 99.      .addr       (addr       )  ,  
 100.                    .wdata      (wdata      )  ,  
 101.                    .rdata      (rdata      )  ,  
 102.                    .rdata_vld  (rdata_vld  )  ,  
 103.                    .rdy        (rdy        )  ,  
 104.                    .do         (mo         )  ,  
 105.                    .di         (mi         )  ,  
 106.                    .cs         (cs         )  ,  
 107.                    .sk         (sk         )      
 108.     );  
 109. endmodule  
 

本工程会应用于不同的开发板,主要区别在于使用普通按键还是矩阵键盘,顶层代码中针对这一点进行了设计,如何开发板使用的是矩阵键盘,则顶层代码不需要改,如果使用的是普通按键,只需要将顶层代码最上面的一行删除或者注释掉就可以了。

1ba96a555784123dd06b13c803ecfe5f.png

.2 键盘(按键)扫描模块设计

1.2.1 接口信号

下面为使用矩阵键盘时的接口信号:

695f3e9a0f9271b2d082e020cf22f3b2.png

下面是使用普通按键时的接口信号:

56ba6849168167f1a620047577a62dbd.png

1.2.2 设计思路

在前面的按键控制数字时钟的案例中已经有介绍,所以这里不在过多介绍

1.2.3 参考代码

 1. //矩阵键盘
 
 2. always @(posedge clk or negedge rst_n)begin
 3.  if(rst_n==1'b0)begin
 4.  key_col_ff0 <= 4'b1111;
 5.  key_col_ff1 <= 4'b1111;
 6.  end
 7.  else begin
 8.  key_col_ff0 <= key_col ;
 9.  key_col_ff1 <= key_col_ff0;
 10.  end
 11. end
 12.  
 13.  
 14. always @(posedge clk or negedge rst_n) begin 
 15.  if (rst_n==0) begin
 16.  shake_cnt <= 0; 
 17.  end
 18.  else if(add_shake_cnt) begin
 19.  if(end_shake_cnt)
 20.  shake_cnt <= 0; 
 21.  else
 22.  shake_cnt <= shake_cnt+1 ;
 23.  end
 24. end
 25. assign add_shake_cnt = key_col_ff1!=4'hf;
 26. assign end_shake_cnt = add_shake_cnt && shake_cnt == TIME_20MS-1 ;
 27.  
 28.  
 29. always @(posedge clk or negedge rst_n)begin
 30.  if(rst_n==1'b0)begin
 31.  state_c <= CHK_COL;
 32.  end
 33.  else begin
 34.  state_c <= state_n;
 35.  end
 36. end
 37.  
 38. always @(*)begin
 39.  case(state_c)
 40.  CHK_COL: begin
 41.  if(col2row_start )begin
 42.  state_n = CHK_ROW;
 43.  end
 44.  else begin
 45.  state_n = CHK_COL;
 46.  end
 47.  end
 48.  CHK_ROW: begin
 49.  if(row2del_start)begin
 50.  state_n = DELAY;
 51.  end
 52.  else begin
 53.  state_n = CHK_ROW;
 54.  end
 55.  end
 56.  DELAY : begin
 57.  if(del2wait_start)begin
 58.  state_n = WAIT_END;
 59.  end
 60.  else begin
 61.  state_n = DELAY;
 62.  end
 63.  end
 64.  WAIT_END: begin
 65.  if(wait2col_start)begin
 66.  state_n = CHK_COL;
 67.  end
 68.  else begin
 69.  state_n = WAIT_END;
 70.  end
 71.  end
 72.  default: state_n = CHK_COL;
 73.  endcase
 74. end
 75. assign col2row_start = state_c==CHK_COL && end_shake_cnt;
 76. assign row2del_start = state_c==CHK_ROW && row_index==3 && end_row_cnt;
 77. assign del2wait_start= state_c==DELAY && end_row_cnt;
 78. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;
 79.  
 80. always @(posedge clk or negedge rst_n)begin
 81.  if(rst_n==1'b0)begin
 82.  key_row <= 4'b0;
 83.  end
 84.   else if(state_c==CHK_ROW)begin
 85.  key_row <= ~(1'b1 << row_index);
 86.  end
 87.  else begin
 88.  key_row <= 4'b0;
 89.  end
 90. end
 91.  
 92.  
 93. always @(posedge clk or negedge rst_n) begin 
 94.  if (rst_n==0) begin
 95.  row_index <= 0; 
 96.  end
 97.  else if(add_row_index) begin
 98.  if(end_row_index)
 99.  row_index <= 0; 
 100.  else
 101.  row_index <= row_index+1 ;
 102.  end
 103.  else if(state_c!=CHK_ROW)begin
 104.  row_index <= 0;
 105.  end
 106. end
 107. assign add_row_index = state_c==CHK_ROW && end_row_cnt;
 108. assign end_row_index = add_row_index && row_index == 4-1 ;
 109.  
 110.  
 111. always @(posedge clk or negedge rst_n) begin 
 112.  if (rst_n==0) begin
 113.  row_cnt <= 0; 
 114.  end
 115.  else if(add_row_cnt) begin
 116.  if(end_row_cnt)
 117.  row_cnt <= 0; 
 118.  else
 119.  row_cnt <= row_cnt+1 ;
 120.  end
 121. end
 122. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
 123. assign end_row_cnt = add_row_cnt && row_cnt == 16-1 ;
 124.  
 125.  
 126. always @(posedge clk or negedge rst_n)begin
 127.  if(rst_n==1'b0)begin
 128.  key_col_get <= 0;
 129.  end
 130.  else if(state_c==CHK_COL && end_shake_cnt ) begin
 131.  if(key_col_ff1==4'b1110)
 132.  key_col_get <= 0;
 133.  else if(key_col_ff1==4'b1101)
 134.  key_col_get <= 1;
 135.  else if(key_col_ff1==4'b1011)
 136.  key_col_get <= 2;
 137.  else 
 138.  key_col_get <= 3;
 139.  end
 140. end
 141.  
 142.  
 143. always @(posedge clk or negedge rst_n)begin
 144.  if(rst_n==1'b0)begin
 145.  key_out <= 0;
 146.  end
 147.  else if(state_c==CHK_ROW && end_row_cnt)begin
 148.  key_out <= {row_index,key_col_get};
 149.  end
 150.  else begin
 151.  key_out <= 0;
 152.  end
 153. end
 154.  
 155. always @(posedge clk or negedge rst_n)begin
 156.  if(rst_n==1'b0)begin
 157.  key_vld <= 1'b0;
 158.  end
 159.  else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin
 160.  key_vld <= 1'b1;
 161.  end
 162.  else begin
 163.  key_vld <= 1'b0;
 164.  end
 165. end
 166.  
 167.  
 168. always @(*)begin
 169.  if(rst_n==1'b0)begin
 170.  key_en = 0;
 171.  end
 172.  else if(key_vld && key_out==0)begin
 173.  key_en = 4'b0001;
 174.  end
 175.  else if(key_vld && key_out==1)begin
 176.  key_en = 4'b0010;
 177.  end
 178.  else if(key_vld && key_out==2)begin
 179.  key_en = 4'b0100;
 180.  end
 181.  else begin
 182.  key_en = 0;
 183.  end
 184. End
 185.  
 186. /*********************
 187. 普通按键
 188. *********************/
 
 189. always @(posedge clk or negedge rst_n)begin
 190.  if(rst_n==1'b0)begin
 191.  cnt <= 20'b0;
 192.  end
 193.  else if(add_cnt)begin
 194.  if(end_cnt)
 195.  cnt <= 20'b0;
 196.  else
 197.  cnt <= cnt + 1'b1;
 198.  end
 199.  else begin
 200.  cnt <= 0;
 201.  end
 202. end
 203.  
 204. assign add_cnt = flag_add==1'b0 && (&key_in_ff1==0);
 205. assign end_cnt = add_cnt && cnt == TIME_20MS - 1;
 206.  
 207.  
 208. always @(posedge clk or negedge rst_n)begin
 209.  if(rst_n==1'b0)begin
 210.  flag_add <= 1'b0;
 211.  end
 212.  else if(end_cnt)begin
 213.  flag_add <= 1'b1;
 214.  end
 215.  else if(&key_in_ff1==1)begin
 216.  flag_add <= 1'b0;
 217.  end
 218. end
 219.  
 220.  
 221. always @(posedge clk or negedge rst_n)begin
 222.  if(rst_n==1'b0)begin
 223.  key_in_ff0 <= {{KEY_W}{1'b1}};
 224.  key_in_ff1 <= {{KEY_W}{1'b1}};
 225.  end
 226.  else begin
 227.  key_in_ff0 <= key_in ;
 228.  key_in_ff1 <= key_in_ff0;
 229.  end
 230. end
 231.  
 232.  
 233. always @(posedge clk or negedge rst_n)begin
 234.  if(rst_n==1'b0)begin
 235.  key_vld <= 0;
 236.  end
 237.  else if(end_cnt)begin
 238.  key_vld <= ~key_in_ff1;
 239.  end
 240.  else begin
 241.  key_vld <= 0;
 242.  end
 243. end
 

1.3 时间数据产生模块设计

1.3.1 接口信号

182707d18a25ce4ca0fdb7d893e3fe92.png

1.3.2 设计思路

本模块相对于前面的按键控制数字时钟案例中的时间数据产生模块来说,总体的设计思路是相同的,只是增加了一个重载的时钟信号,对于此信号的设计也比较简单,只需要在时分秒的个位和十位计数器中增加一句:在重载的时钟数据有效的时候,使计数器输出重载的时钟对应的数据即可,比如秒个位计数器应该输出重载时钟数据的第0到第3位数据,秒十位计数器应该输出重载时钟数据的第4到第7位数据,以此类推。

其他详细的设计思路可以看一下往期按键控制数字时钟的文章:

1.3.3 参考代码

1. module clock_data(
 2.  clk ,
 3.  rst_n ,
 4.  data_load ,
 5.  data_load_vld,
 6.  key_en ,
 7.  data_out 
 8.  );
 9.  
 10.  parameter TIME_1S = 50_000_000 ;
 11.  
 12.  input clk ;
 13.  input rst_n ;
 14.  input data_load_vld;
 15.  input [23:0] data_load ;
 16.  input [ 3:0] key_en ;
 17.  output[23:0] data_out ;
 18.  wire [23:0] data_out ;
 19.  
 20.  
 21.  reg [25:0]  cnt_1s ;
 22.  reg [3:0] miao_ge ;
 23.  reg [3:0] miao_shi ;
 24.  reg [3:0] fen_ge ;
 25.  reg [3:0] fen_shi ;
 26.  reg [3:0] shi_ge ;
 27.  reg [3:0] shi_shi ;
 28.  reg [2:0] set_sel ;
 29.  reg set_flag ;
 30.  wire add_set_sel ;
 31.  wire add_cnt_1s ;
 32.  wire add_miao_ge ;
 33.  wire add_miao_shi ;
 34.  wire  add_fen_ge ;
 35.  wire add_fen_shi ;
 36.  wire add_shi_ge ;
 37.  wire add_shi_shi ;
 38.  wire end_cnt_1s ;
 39.  wire end_set_sel ;
 40.  wire end_miao_ge  ;
 41.  wire end_miao_shi ;
 42.  wire end_fen_ge ;
 43.  wire end_fen_shi ;
 44.  wire end_shi_ge ;
 45.  wire end_shi_shi ;
 46.  wire set_miao_ge ;
 47.  wire set_miao_shi ;
 48.  wire set_fen_ge ;
 49.  wire set_fen_shi ;
 50.  wire set_shi_ge ;
 51.  wire set_shi_shi ;
 52.  
 53.  reg [ 3:0] x ;
 54.  reg [ 2:0] y ;
 55.  
 56.  always @(posedge clk or negedge rst_n)begin
 57.  if(rst_n==1'b0)begin
 58.  set_flag <= 1'b0;
 59.  end
 60.  else if(key_en[0]) begin
 61.  set_flag <= ~ set_flag;
 62.  end
 63.  end
 64.  
 65.  
 66.  
 67.  always @(posedge clk or negedge rst_n)begin
 68.  if(!rst_n)begin
 69.  set_sel <= 0;
 70.  end
 71.  else if(add_set_sel)begin
 72.  if(end_set_sel)
 73.  set_sel <= 0;
 74.  else
 75.   set_sel <= set_sel + 1;
 76.  end
 77.  else if(set_flag==0)begin
 78.  set_sel <= 0;
 79.  end
 80.  end
 81.  
 82.  assign add_set_sel = set_flag && key_en[1]; 
 83.  assign end_set_sel = add_set_sel && set_sel==6-1 ; 
 84.  
 85.  always @(posedge clk or negedge rst_n)begin
 86.  if(!rst_n)begin
 87.  cnt_1s <= 0;
 88.  end
 89.  else if(add_cnt_1s)begin
 90.  if(end_cnt_1s)
 91.  cnt_1s <= 0;
 92.  else
 93.  cnt_1s <= cnt_1s + 1;
 94.  end
 95.  end
 96.  
 97.  assign add_cnt_1s = set_flag==0 ; 
 98.  assign end_cnt_1s = add_cnt_1s && cnt_1s==TIME_1S-1 ; 
 99.  
 100.  
 101.  always @(posedge clk or negedge rst_n)begin
 102.  if(!rst_n)begin
 103.  miao_ge <= 0;
 104.  end
 105.  else if(add_miao_ge)begin
 106.  if(end_miao_ge)
 107.  miao_ge <= 0;
 108.  else
 109.  miao_ge <= miao_ge + 1;
 110.  end
 111.  else if(data_load_vld)begin
 112.  miao_ge <= data_load[3:0];
 113.  end
 114.  end
 115.  
 116.  assign add_miao_ge = (end_cnt_1s || set_miao_ge) ; 
 117.  assign end_miao_ge = add_miao_ge && miao_ge==10-1 ;
 118.  assign set_miao_ge = set_flag && set_sel==0 && key_en[2]; 
 119.  
 120.  
 121.  always @(posedge clk or negedge rst_n)begin
 122.  if(!rst_n)begin
 123.  miao_shi <= 0;
 124.  end
 125.  else if(add_miao_shi)begin
 126.  if(end_miao_shi)
 127.  miao_shi <= 0;
 128.  else
 129.  miao_shi <= miao_shi + 1;
 130.  end
 131.  else if(data_load_vld)begin
 132.  miao_shi <= data_load[7:4];
 133.  end
 134.  end
 135.  
 136.  assign add_miao_shi = (end_miao_ge || set_miao_shi); 
 137.  assign end_miao_shi = add_miao_shi && miao_shi==6-1;
 138.  assign set_miao_shi = set_flag && set_sel==1 && key_en[2]; 
 139.  
 140.  
 141.  always @(posedge clk or negedge rst_n)begin
 142.  if(!rst_n)begin
 143.  fen_ge <= 0;
 144.  end
 145.  else if(add_fen_ge)begin
 146.  if(end_fen_ge)
 147.  fen_ge <= 0;
 148.  else
 149.  fen_ge <= fen_ge + 1;
 150.  end
 151.  else if(data_load_vld)begin
 152.  fen_ge <= data_load[11:8];
 153.  end
 154.  end
 155.  
 156.  assign add_fen_ge = (end_miao_shi || set_fen_ge); 
 157.  assign end_fen_ge = add_fen_ge && fen_ge==10-1;
 158.  assign set_fen_ge = set_flag && set_sel==2 && key_en[2]; 
 159.  
 160.  
 161.  always @(posedge clk or negedge rst_n)begin
 162.  if(!rst_n)begin
 163.  fen_shi <= 0;
 164.  end
 165.  else if(add_fen_shi)begin
 166.   if(end_fen_shi)
 167.  fen_shi <= 0;
 168.  else
 169.  fen_shi <= fen_shi + 1;
 170.  end
 171.  else if(data_load_vld)begin
 172.  fen_shi <= data_load[15:12];
 173.  end
 174.  end
 175.  
 176.  assign add_fen_shi = (end_fen_ge || set_fen_shi); 
 177.  assign end_fen_shi = add_fen_shi && fen_shi==6-1;
 178.  assign set_fen_shi = set_flag && set_sel==3 && key_en[2]; 
 179.  
 180.  always @(posedge clk or negedge rst_n)begin
 181.  if(!rst_n)begin
 182.  shi_ge <= 0;
 183.  end
 184.  else if(add_shi_ge)begin
 185.  if(end_shi_ge)
 186.  shi_ge <= 0;
 187.  else
 188.  shi_ge <= shi_ge + 1;
 189.  end
 190.  else if(data_load_vld)begin
 191.  shi_ge <= data_load[19:16];
 192.  end
 193.  end
 194.  
 195.  assign add_shi_ge = (end_fen_shi || set_shi_ge); 
 196.  assign end_shi_ge = add_shi_ge && shi_ge==x-1;
 197.  assign set_shi_ge = set_flag && set_sel==4 && key_en[2]; 
 198.  
 199.  
 200.  always @(posedge clk or negedge rst_n)begin
 201.  if(!rst_n)begin
 202.  shi_shi <= 0;
 203.  end
 204.  else if(add_shi_shi)begin
 205.  if(end_shi_shi)
 206.  shi_shi <= 0;
 207.  else
 208.  shi_shi <= shi_shi + 1;
 209.  end
 210.  else if(data_load_vld)begin
 211.  shi_shi <= data_load[23:20];
 212.  end
 213.  end
 214.  
 215.  assign add_shi_shi = (end_shi_ge || set_shi_shi); 
 216.  assign end_shi_shi = add_shi_shi && shi_shi==y-1;
 217.  assign set_shi_shi = set_flag && set_sel==5 && key_en[2]; 
 218.  
 219.  always @(*)begin
 220.  if(shi_shi<2)
 221.  x = 10;
 222.  else
 223.  x = 4;
 224.  end
 225.  
 226.  always @(*)begin
 227.  if(set_flag && set_sel==5 && shi_ge>=4) 
 228.  y = 2;
 229.  else
 230.  y = 3;
 231.  end
 232.  assign data_out = {shi_shi,shi_ge,fen_shi,fen_ge,miao_shi,miao_ge};
 233.  
 234. endmodule
 

1.4 数码管显示模块设计

1.4.1 接口信号

1fa83a2bcb8004ff91bb49a8ea23fe9d.png

1.4.2 设计思路

数码管显示在前面的案例文章已经有讲述,这里不再进行介绍,想了解的可以看一下往期文章:

1.4.3 参考代码

235. always @(posedge clk or negedge rst_n)begin
 236.  if(!rst_n)begin
 237.  cnt_2ms <= 0;
 238.  end
 239.  else if(add_cnt_2ms)begin
 240.  if(end_cnt_2ms)
 241.  cnt_2ms <= 0;
 242.  else
 243.  cnt_2ms <= cnt_2ms + 1;
 244.  end
 245. end
 246.  
 247. assign add_cnt_2ms = 1; 
 248. assign end_cnt_2ms = add_cnt_2ms && cnt_2ms==TIME_2MS-1 ; 
 249.  
 250. always @(posedge clk or negedge rst_n)begin
 251.  if(!rst_n)begin
 252.  cnt_sel <= 0;
 253.  end
 254.  else if(add_cnt_sel)begin
 255.  if(end_cnt_sel)
 256.  cnt_sel <= 0;
 257.  else
 258.  cnt_sel <= cnt_sel + 1;
 259.  end
 260. end
 261.  
 262. assign add_cnt_sel = end_cnt_2ms; 
 263. assign end_cnt_sel = add_cnt_sel && cnt_sel== SEG_NUM-1; 
 264.  
 265.  
 266. always @(posedge clk or negedge rst_n)begin
 267.  if(rst_n==1'b0)begin
 268.  seg_sel <= {SEG_NUM{1'b1}};
 269.  end
 270.  else begin
 271.  seg_sel <= ~(1'b1 << cnt_sel);
 272.  end
 273. end
 274.  
 275. always @(posedge clk or negedge rst_n)begin
 276.  if(rst_n==1'b0)begin
 277.  din_ff0 <= 0;
 278.  end
 279.  else begin
 280.  for(ii=0;ii<SEG_NUM;ii=ii+1)begin
 281.  if(din_vld[ii]==1'b1)begin
 282.  din_ff0[(ii+1)*4-1 -:4] <= din[(ii+1)*4-1 -:4];
 283.  end
 284.  else begin
 285.  din_ff0[(ii+1)*4-1 -:4] <= din_ff0[(ii+1)*4-1 -:4];
 286.  end
 287.  end
 288.  end
 289. end
 290.  
 291. always @(*)begin
 292.  seg_tmp = din_ff0[(cnt_sel+1)*4-1 -:4]; 
 293. end
 294.  
 295.  
 296. always@(posedge clk or negedge rst_n)begin
 297.  if(rst_n==1'b0)begin
 298.  segment<=NUM_0;
 299.  end
 300.  else begin
 301.  case(seg_tmp)
 302.  4'd0:segment <= NUM_0;
 303.  4'd1:segment <= NUM_1;
 304.  4'd2:segment <= NUM_2;
 305.  4'd3:segment <= NUM_3;
 306.  4'd4:segment <= NUM_4;
 307.  4'd5:segment <= NUM_5;
 308.  4'd6:segment <= NUM_6;
 309.  4'd7:segment <= NUM_7;
 310.  4'd8:segment <= NUM_8;
 311.  4'd9:segment <= NUM_9;
 312.  default:begin
 313.  segment <= NUM_ERR
 314.  end
 315.   endcase
 316.  end
 317. End
 

1.5 数据处理模块设计

1.5.1 接口信号

70622f3624d41f870a60c12fb2ab4594.png

1.5.2 设计思路

本模块主要负责写到AT93C46的数据或者读出的数据的处理,上电后,发送EWEN指令给AT93C46接口模块,打开AT93C46的写保护;发送EWEN命令后,开始读取存储在AT93C46保存的两组时钟数据;每隔1秒读取输入时钟数据的值,并产生CRC值,写到AT93C46上。

根据上面的功能描述,该模块采用状态机进行架构,可划分四个状态:打开写保护状态(S_EWEN)、读数据状态(S_READ)、空闲状态(S_IDLE)和写数据状态(S_WRIT),状态的跳转图如下:

20d1cf655189ebec56ee0199deb96255.png

由于功能要求只在刚上电或者复位的时候才读取AT93C46中的数据,因此刚上电就是写保护打开状态,或者复位有效时,进入写保护打开状态。由于复位是由按键控制的,因此在按下的时候会产生抖动,可能会导致产生很多个start,因此延时一段时间之后,如果AT93C46接口模块准备好,便进入读数据状态。数据读完之后,就进入空闲状态,等待计时1秒之后,开始将输入的时钟数据写入AT93C46中,写完四个字节数据之后重新回到空闲状态,等待计时1秒,如此循环。

下面介绍一下该模块中其他信号的设计思路:

时钟计数器time_cnt:该计数器是计数1秒的时间,从写保护打开状态跳转到读数据状态需要的延时和空闲状态跳转写数据状态需要的1秒的时间可使用此计数器表示;加一条件为1,表示一直计数;结束条件为数50000000个,表示1秒的时间,数完就清零。

写数据计数器wr_cnt:该计数器用于对要写入AT93C46的数据进行计数;加一条件为state_c==S_WRIT && rdy,表示在写数据状态的时候,如果AT93C46接口模块准备好,就开始计数;结束条件为数4个,3个字节的时钟数据加上1个字节的CRC校验,共四个字节,数完就清零。

读数据计数器:该计数器数的是从AT93C46读出,并经过CRC处理的数据字节数;加一条件为state_c==S_READ && crc_dout_vld,表示在读数据状态的时候,CRC处理完就计数一个;结束条件为数8个,AT93C46两个区域内共存有8个字节的数据,数完就清零。

写区间选择信号write_sel:初始状态为0,表示选择区间0~3,当写操作完成之后,该信号取反,变为1,表示选择区间4~7。

读写地址信号addr:初始状态为0,根据下方的表格(AT93C46的指令集),当处于写数据状态的时候,地址为7bit,由于本工程只会使用区间0~7来存储数据,因此地址为4bit0加上写区间选择信号write_sel加上写数据计数器;当处于写保护打开状态的时候,地址应为7’b11xxxxx,其中“x”表示不关注,工程中设为0即可;当处于读数据状态的时候,根据读数据计数器的变化选择地址即可,即地址为4’b0加上rd_cnt

4cd256d3ce5506ac9b01987e5d4b91dc.png

AT93C46指令集

AT93C46开始命令start:初始状态为0,表示AT93C46不工作,当(add_wr_cnt || start_read),也就是在写数据、读数据或者写保护打开状态的时候,该信号拉高,指示AT93C46工作。

写数据wdata:该信号表示要往AT93C46中写入的数据,初始状态为0。前三个字节写入输入的时钟数据,第四个字节写入CRC。

AT93C46返回数据dout_temp:从AT93C46读出的8个字节数据,经过串并转换之后保存在该信号中。

第一区间CRC错误指示信号dout0_err:初始状态为0,表示没有错误;当读数据计数器数到第4个的时候,表示CRC模块已经完成了第一区间数据的校验,如果校验结果为0,表示正确,如果校验结果不等于0,表示输出错误。

第二区间CRC错误指示信号dout1_err:为了使错误指示信号跟数据能对齐,采用组合逻辑设计,当CRC模块输出数据为0,表示没有错误,该信号为0,如果CRC模块输出数据不为0,表示有错误,该信号为1。

用于加载的时钟数据dout:初始状态为0;当读数据计数器数完的时候,如果区间1和区间2的检验都没有错误,则比较AT93C46返回数据的高4字节和低4字节的大小,输出大的;如果区间1检验正确,区间2检验错误,则输出高4字节数据,反之则输出低4字节数据。

时钟加载有效指示信号dout_vld:初始状态为0,表示时钟数据无效;当读数据计数器数完的时候,如果第一区间和第二区间有最少一个正确,该信号就拉高,表示待加载的时钟数据有效,其他情况不拉高。

1.5.3 参考代码

318.   always @(posedge clk or negedge rst_n)begin
 319.  if(rst_n==1'b0)begin
 320.  state_c <= S_EWEN;
 321.  end
 322.  else begin
 323.  state_c <= state_n;
 324.  end
 325.  end
 326.  
 327.  always @(*)begin
 328.  case(state_c)
 329.  S_EWEN : begin
 330.  if(ewen2read_start)begin
 331.  state_n = S_READ ;
 332.  end
 333.  else begin
 334.  state_n = state_c;
 335.  end
 336.  end
 337.  S_READ : begin
 338.  if(read2idle_start)begin
 339.  state_n = S_IDLE;
 340.  end
 341.  else begin
 342.  state_n = state_c;
 343.  end
 344.  end
 345.  S_IDLE : begin
 346.  if(idle2write_start)begin
 347.  state_n = S_WRIT;
 348.  end
 349.  else begin
 350.  state_n = state_c;
 351.  end
 352.  end
 353.  S_WRIT : begin
 354.  if(write2idle_start)begin
 355.  state_n = S_IDLE;
 356.  end
 357.  else begin
 358.  state_n = state_c;
 359.  end
 360.  end
 361.  default : begin
 362.  state_n = S_EWEN;
 363.  end
 364.  endcase
 365.  end
 366.  
 367.  
 368.  assign ewen2read_start = state_c==S_EWEN && add_time_cnt && time_cnt==1000-1 && rdy;
 369.  assign read2idle_start = state_c==S_READ && end_rd_cnt ;
 370.  assign idle2write_start = state_c==S_IDLE && end_time_cnt ;
 371.  assign write2idle_start = state_c==S_WRIT && end_wr_cnt ;
 372.  
 373.  
 374.  always @(posedge clk or negedge rst_n)begin
 375.  if(!rst_n)begin
 376.  time_cnt <= 0;
 377.  end
 378.  else if(add_time_cnt)begin
 379.  if(end_time_cnt)
 380.  time_cnt <= 0;
 381.  else
 382.  time_cnt <= time_cnt + 1;
 383.  end
 384.  end
 385.  
 386.  assign add_time_cnt = 1; 
 387.  assign end_time_cnt = add_time_cnt && time_cnt==TIME_1S-1 ; 
 388.  
 389.  
 390.  always @(posedge clk or negedge rst_n)begin
 391.  if(!rst_n)begin
 392.  wr_cnt <= 0;
 393.  end
 394.  else if(add_wr_cnt)begin
 395.  if(end_wr_cnt)
 396.  wr_cnt <= 0;
 397.  else
 398.  wr_cnt <= wr_cnt + 1;
 399.  end
 400.   end
 401.  
 402.  assign add_wr_cnt = state_c==S_WRIT && rdy; 
 403.  assign end_wr_cnt = add_wr_cnt && wr_cnt == WR_NUM-1 ;
 404.  
 405.  
 406.  always @(posedge clk or negedge rst_n)begin
 407.  if(!rst_n)begin
 408.  rd_cnt <= 0;
 409.  end
 410.  else if(add_rd_cnt)begin
 411.  if(end_rd_cnt)
 412.  rd_cnt <= 0;
 413.  else
 414.  rd_cnt <= rd_cnt + 1;
 415.  end
 416.  end
 417.  
 418.  assign add_rd_cnt = state_c==S_READ && crc_dout_vld; 
 419.  assign end_rd_cnt = add_rd_cnt && rd_cnt == RD_NUM-1 ;
 420.  
 421.  
 422.  assign start_read = state_c==S_READ && flag_wait_crc==0 && rdy;
 423.  
 424.  always @(posedge clk or negedge rst_n)begin
 425.  if(rst_n==1'b0)begin
 426.  flag_wait_crc <= 0;
 427.  end
 428.  else if(start_read)begin
 429.  flag_wait_crc <= 1;
 430.  end
 431.  else if(crc_dout_vld)begin
 432.  flag_wait_crc <= 0;
 433.  end
 434.  end
 435.  
 436.  always @(posedge clk or negedge rst_n)begin
 437.  if(rst_n==1'b0)begin
 438.  mode <= EWEN; 
 439.  end
 440.  else if(state_c==S_EWEN) begin
 441.  mode <= EWEN; 
 442.  end
 443.  else if(state_c==S_WRIT)begin
 444.  mode <= WRITE;
 445.  end
 446.  else if(state_c==S_READ)begin
 447.  mode <= READ;
 448.  end
 449.  end
 450.  
 451.  
 452.  
 453.  always @(posedge clk or negedge rst_n)begin
 454.  if(rst_n==1'b0)begin
 455.  crc_din <= 0;
 456.  end
 457.  else if(add_wr_cnt && end_wr_cnt==0) begin 
 458.  crc_din <= din[8*(D_LEN-wr_cnt)-1 -:8];
 459.  end
 460.  else begin
 461.  crc_din <= rdata;
 462.  end
 463.  end
 464.  
 465.  always @(posedge clk or negedge rst_n)begin
 466.  if(rst_n==1'b0)begin
 467.  crc_din_vld <= 1'b0;
 468.  end
 469.  else if(add_wr_cnt && end_wr_cnt==0) begin
 470.  crc_din_vld <= 1'b1;
 471.  end
 472.  else begin
 473.  crc_din_vld <= rdata_vld;
 474.  end
 475.  end
 476.  
 477.  always @(posedge clk or negedge rst_n)begin
 478.  if(rst_n==1'b0)begin
 479.  crc_clr <= 1'b0;
 480.  end
 481.  else if((add_rd_cnt && rd_cnt==4-1)|| read2idle_start || write2idle_start) begin 
 482.  crc_clr <= 1'b1;
 483.  end
 484.  else begin
 485.  crc_clr <= 1'b0;
 486.  end
 487.  end
 488.  
 489.  always @(posedge clk or negedge rst_n)begin
 490.  if(rst_n==1'b0)begin
 491.  addr <= 0;
 492.  end
 493.  else if(state_c==S_WRIT) begin
 494.  addr <= {4'b0,write_sel,wr_cnt[1:0]};
 495.  end
 496.  else if(state_c==S_EWEN) begin
 497.  addr <= 7'b1100000;
 498.  end
 499.  else begin
 500.  addr <= {4'b0,rd_cnt};
 501.  end
 502.  end
 503.  
 504.  always @(posedge clk or negedge rst_n)begin
 505.  if(rst_n==1'b0)begin
 506.  write_sel <= 1'b0;
 507.  end
 508.  else if(write2idle_start) begin
 509.  write_sel <= ~write_sel;
 510.  end
 511.  end
 512.  
 513.  always @(posedge clk or negedge rst_n)begin
 514.  if(rst_n==1'b0)begin
 515.  din_ff0 <= 0;
 516.  end
 517.  else if(write2idle_start) begin
 518.   din_ff0 <= din;
 519.  end
 520.  end
 521.  
 522.  
 523.  always @(posedge clk or negedge rst_n)begin
 524.  if(rst_n==1'b0)begin
 525.  wdata <= 0;
 526.  end
 527.  else if(wr_cnt==WR_NUM-1) begin
 528.  wdata <= crc_dout;
 529.  end
 530.  else begin
 531.  wdata <= din[8*(D_LEN-wr_cnt)-1 -:8];
 532.  end
 533.  end
 534.  
 535.  always @(posedge clk or negedge rst_n)begin
 536.  if(rst_n==1'b0)begin
 537.  start <= 1'b0;
 538.  end
 539.  else if(add_wr_cnt || start_read || ewen2read_start)begin
 540.  start <= 1'b1;
 541.  end
 542.  else begin
 543.  start <= 1'b0;
 544.  end
 545.  end
 546.  
 547.  always @(posedge clk or negedge rst_n)begin
 548.  if(rst_n==1'b0)begin
 549.  dout_temp<= 0;
 550.  end
 551.  else if(rdata_vld) begin
 552.  dout_temp[(8-rd_cnt)*8-1 -:8] <= rdata;
 553.  end
 554.  end
 555.  
 556.  always @(posedge clk or negedge rst_n)begin
 557.  if(rst_n==1'b0)begin
 558.  dout0_err <= 1'b0;
 559.  end
 560.  else if(add_rd_cnt && rd_cnt==4-1) begin
 561.  if(crc_dout!=0)
 562.  dout0_err <= 1'b1;
 563.  else
 564.  dout0_err <= 1'b0;
 565.  end
 566.  end
 567.  
 568.  always @(*)begin
 569.  if(crc_dout!=0)
 570.  dout1_err = 1'b1;
 571.  else
 572.  dout1_err = 1'b0;
 573.  end
 574.  
 575.  
 576.  assign dout0_temp = dout_temp[8*8-1 -:24];
 577.  assign dout1_temp = dout_temp[4*8-1 -:24];
 578.  
 579.  
 580.  
 581.  
 582.  always @(posedge clk or negedge rst_n)begin
 583.  if(rst_n==1'b0)begin
 584.  dout <= 0;
 585.  end
 586.  else if(end_rd_cnt) begin
 587.  if(dout0_err==1'b0 && dout1_err==1'b0)begin
 588.  if(dout0_temp > dout1_temp)
 589.  dout <= dout0_temp;
 590.  else
 591.  dout <= dout1_temp;
 592.   end
 593.  else if(dout0_err==1'b0)begin
 594.  dout <= dout0_temp;
 595.  end
 596.  else begin
 597.  dout <= dout1_temp;
 598.  end
 599.  end
 600.  end
 601.  
 602.  
 603.  always @(posedge clk or negedge rst_n)begin
 604.  if(rst_n==1'b0)begin
 605.  dout_vld <= 1'b0;
 606.  end
 607.  else if(end_rd_cnt) begin
 608.  if(dout0_err && dout1_err)begin
 609.  dout_vld <= 1'b0;
 610.  end
 611.  else begin
 612.  dout_vld <= 1'b1;
 613.  end
 614.  end
 615.  else begin
 616.  dout_vld <= 1'b0;
 617.  end
 618.  end
 619.  
 620.  
 621.  crc8_d8 u_crc8_d8(
 622.  .clk (clk ),
 623.  .rst_n (rst_n ),
 624.  .clr (crc_clr ),
 625.  .din_vld (crc_din_vld ),
 626.  .din (crc_din ),
 627.  .dout_vld (crc_dout_vld ),
 628.  .dout (crc_dout )  
 629.  ); 
 

1.6 CRC处理模块设计

1.6.1 接口信号

4963e88d57e1175d09626bedf0958f20.png

1.6.2 设计思路

该模块主要的作用是负责CRC运算,在数据处理模块内部使用,多项式为 ,本模块代码不需要设计,使用网上的生成工具(https://www.easics.com/crctool/

),输入多项式即可生成,具体设置请看下图。

cbf7acd5c0606d6df2c6a818f2a89edc.png

关于CRC的原理和并行、串行实现的方法,有另外的视频进行讲解,这里不在进行介绍。

1.6.3 参考代码

630.  assign d = din ;
 631.  assign c = dout ;
 632.  
 633.  
 634.  always @(posedge clk or negedge rst_n)begin
 635.  if(rst_n==1'b0)begin
 636.  dout <= 0;
 637.  end
 638.  else if(clr)begin
 639.  dout <= 0;
 640.  end
 641.  else if(din_vld) begin
 642.  dout[0] <= d[7] ^ d[6] ^ d[0] ^ c[0] ^ c[6] ^ c[7];
 643.  dout[1] <= d[6] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[6];
 644.  dout[2] <= d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[2] ^ c[6];
 645.  dout[3] <= d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[1] ^ c[2] ^ c[3] ^ c[7];
 646.  dout[4] <= d[4] ^ d[3] ^ d[2] ^ c[2] ^ c[3] ^ c[4];
 647.  dout[5] <= d[5] ^ d[4] ^ d[3] ^ c[3] ^ c[4] ^ c[5];
 648.   dout[6] <= d[6] ^ d[5] ^ d[4] ^ c[4] ^ c[5] ^ c[6];
 649.  dout[7] <= d[7] ^ d[6] ^ d[5] ^ c[5] ^ c[6] ^ c[7];
 650.  end
 651.  end
 652.  
 653.  always @(posedge clk or negedge rst_n)begin
 654.  if(rst_n==1'b0)begin
 655.  dout_vld <= 0;
 656.  end
 657.  else begin
 658.  dout_vld <= din_vld;
 659.  end
 660.  end
 

1.7 AT93C46接口模块设计

1.7.1 接口信号

8d1ff9a286b21e46398e31d336a72568.png

1.7.2 设计思路

参考数据手册,本模块主要实现三个命令:打开写保护(EWEN)、读数据(READ)和写数据(WRITE)。

下面时EWEN命令的时序图,结合上文提到的AT93C46的指令集,打开写保护指令的时序应该是写10bit数据之后,等待TCS时间,然后结束。

97f2a72b3993a147d05c048b5f4f1e3f.png

下面是READ命令的时序图。结合上文提到的AT93C46的指令集,读数据命令对应的时序应该是写10bit数据钟后,读8bit数据,等待TCS时间,然后结束。

641c61cecf38880a8dc8d10704a81d6a.png

下面是WRITE命令的时序图。结合上文提到的AT93C46的指令集,写数据命令对应的时序应该是写18bit数据,cs拉低TCS时间,等待TWP(5ms)时间,然后结束。

d0f4b793b5e412a512ada4bccdba38b6.png

根据上述的时序介绍,本模块采用3个计数器的架构,下面是计数器的架构图。

19daf9c1dacba6a6bb32cb057ae6476d.png

架构中的三个计数器分别为时钟计数器cnt0、比特计数器cnt1和阶段计数器cnt2,flag_work为工作状态指示信号。

时钟计数器cnt0:该计数器用来计数时钟的个数。加一条件为flag_work,表示进入工作状态就开始计数。结束条件为数x个,根据不同的工作模式和所处的阶段不同而变化。包括SK的时钟周期数、等待时间TCS、等待5ms时间。

比特计数器cnt1:该计数器用来数有多少bit数据,加一条件为end_cnt0,表示每数完1bit,就加1;结束条件为数y个,y分别为10(EWEN)、18(READ和WRITE)、1(等待TCS和5ms)。

阶段计数器cnt2:该计数器用来对每个指令需要的阶段进行计数。加一条件为end_cnt1,表示发送完一组数据就加一;结束条件为数u个,u分别为2(EWEN和READ)、3(WRITE)。

除了上述的计数器之外,还有一些比较重要的信号,我们来分析一下如何进行设计。

工作状态指示信号flag_work:初始状态为0,表示处于空闲状态;当收到开始命令start的时候,变化变为1,由空闲状态转为工作状态;当前指令的时序结束之后,也就是阶段计数器cnt2数完,就变为0,进入空闲状态。

待发送数据dout:当接收到开始命令的时候,根据AT93C46的指令集,将SB、Opcode、Address和data按照顺序拼接到一起。

模式暂存器mode_reg:为保证在发送时序期间保持不变,在接收到开始命令的时候,将操作模式指示信号进行暂存。

AT93C46时钟sk:时钟频率为200KHz,工程的系统时钟为50MHz,因此sk一个完整的周期需要250个系统时钟周期,初始状态设为低电平,当时钟计数器数到125个的时候置为高电平,时钟计数器数完,在置为低电平。

AT93C46数据输入di:在每个指令时序的第一阶段,也就是cnt2=1-1的时候,根据比特计数器要数的个数,将待发送的数据dout送给di。

AT93C46片选信号cs:在写比特数据、等待5ms期间为高,其他时候都为低,所以该信号拉高的条件为(start_vld==1 || (add_cnt2 && cnt2==2-1 && end_cnt2==0))。其他时候片选信号都为低,所以拉低的条件为((add_cnt2 && cnt2==1-1) || end_cnt2)。

读取数据rdata:在读模式下,处于第一阶段,并且在第11~18bit的时候,将AT93C46输出数据do保存到rdata里面。

准备好信号rdy:由组合逻辑产生,当接收到开始命令,或者处于工作状态的时候,为低电平,表示没有准备好;其他时刻为高电平,表示准备好。

1.7.3 参考代码

 1.  assign start_vld = flag_work==0 && start;
 2.  
 3.  always @(*)begin
 4.  if(mode==EWEN)
 5.  opcode = 3'b100;
 6.  else if(mode==WRITE)
 7.  opcode = 3'b101;
 8.  else
 9.  opcode = 3'b110;
 10.  end
 11.  
 12.  always @(posedge clk or negedge rst_n)begin
 13.  if(rst_n==1'b0)begin
 14.  dout <= 0;
 15.  end
 16.  else if(start_vld) begin
 17.  dout <={opcode,addr,wdata}; 
 18.  end
 19.  end
 20.  
 21.  
 22.  always @(posedge clk or negedge rst_n)begin
 23.  if(rst_n==1'b0)begin
 24.  mode_reg <= 0;
 25.  end
 26.  else if(start_vld) begin
 27.  mode_reg <= mode;
 28.  end
 29.  end
 30.  
 31.  always @(posedge clk or negedge rst_n)begin
 32.  if(rst_n==1'b0)begin
 33.  flag_work <= 0;
 34.  end
 35.  else if(start_vld) begin
 36.  flag_work <= 1;
 37.  end
 38.  else if(end_cnt2)begin
 39.  flag_work <= 0;
 40.  end
 41.  end
 42.  
 43.  
 44.  
 45.  always @(posedge clk or negedge rst_n)begin
 46.  if(!rst_n)begin
 47.  cnt0 <= 0;
 48.  end
 49.  else if(add_cnt0)begin
 50.  if(end_cnt0)
 51.  cnt0 <= 0;
 52.  else
 53.  cnt0 <= cnt0 + 1;
 54.  end
 55.  end
 56.  
 57.  assign add_cnt0 = flag_work; 
 58.  assign end_cnt0 = add_cnt0 && cnt0== x-1; 
 59.  
 60.  always @(posedge clk or negedge rst_n)begin
 61.  if(!rst_n)begin
 62.  cnt1 <= 0;
 63.  end
 64.  else if(add_cnt1)begin
 65.  if(end_cnt1)
 66.  cnt1 <= 0;
 67.  else
 68.  cnt1 <= cnt1 + 1;
 69.  end
 70.  end
 71.  
 72.  assign add_cnt1 = end_cnt0; 
 73.  assign end_cnt1 = add_cnt1 && cnt1==y-1 ;
 74.  
 75.  
 76.  always @(posedge clk or negedge rst_n)begin
 77.  if(!rst_n)begin
 78.  cnt2 <= 0;
 79.  end
 80.  else if(add_cnt2)begin
 81.  if(end_cnt2)
 82.  cnt2 <= 0;
 83.  else
 84.  cnt2 <= cnt2 + 1;
 85.  end
 86.  end
 87.  
 88.  assign add_cnt2 = end_cnt1; 
 89.  assign end_cnt2 = add_cnt2 && cnt2==u-1; 
 90.  
 91.  
 92.  assign en_sk1 = add_cnt0 && cnt0==x/2-1 && cnt2==1-1;
 93.  assign en_sk0 = end_cnt0 ;
 94.  always @(posedge clk or negedge rst_n)begin
 95.   if(rst_n==1'b0)begin
 96.  sk <= 0;
 97.  end
 98.  else if(add_cnt0 && cnt0==x/2-1 && cnt2==1-1)begin
 99.  sk <= 1;
 100.  end
 101.  else if(end_cnt0)begin
 102.  sk <= 0;
 103.  end
 104.  end
 105.  
 106.  assign en_di = add_cnt0 && cnt0==1-1 && cnt2==1-1;
 107.  
 108.  always @(posedge clk or negedge rst_n)begin
 109.  if(rst_n==1'b0)begin
 110.  di <= 0;
 111.  end
 112.  else if(en_di) begin
 113.  di <= dout[17-cnt1];
 114.  end
 115.  end
 116.  
 117.  
 118.  always @(posedge clk or negedge rst_n)begin
 119.  if(rst_n==1'b0)begin
 120.  cs <= 0;
 121.  end
 122.  else if(start_vld==1 || (add_cnt2 && cnt2==2-1 && end_cnt2==0)) begin
 123.  cs <= 1;
 124.  end
 125.  else if((add_cnt2 && cnt2==1-1) || end_cnt2)begin
 126.  cs <= 0;
 127.  end
 128.  end
 129.  
 130.  
 131.  always @(posedge clk or negedge rst_n)begin
 132.  if(rst_n==1'b0)begin
 133.  rdata <= 0;
 134.  end
 135.  else if(end_cnt0 && cnt1 >=10 && cnt2==1-1 && mode_reg==READ ) begin
 136.  rdata[17-cnt1] <= do;
 137.  end
 138.  end
 139.  
 140.  always @(posedge clk or negedge rst_n)begin
 141.  if(rst_n==1'b0)begin
 142.  rdata_vld <= 0;
 143.  end
 144.  else begin
 145.  rdata_vld <= end_cnt2 && mode_reg==READ;
 146.  end
 147.  end
 148.  
 149.  always @(*)begin
 150.  if(start || flag_work)
 151.  rdy = 1'b0;
 152.  else
 153.  rdy = 1'b1;
 154.  end
 155.  
 156.  
 157.  always @(*)begin
 158.  if(mode_reg==WRITE && cnt2==1-1)begin
 159.  x = 250;
 160.  y = 18;
 161.  u = 3; 
 162.  end
 163.  else if(mode_reg==WRITE && (cnt2==2-1 ))begin
 164.  x = 250 ;
 165.  y = 1 ;
 166.  u = 3 ; 
 167.  end
 168.  else if(mode_reg==WRITE && cnt2==3-1)begin
 169.  x = 500000; 
 170.   y = 1 ;
 171.  u = 3 ; 
 172.  end
 173.  else if(mode_reg==READ && cnt2==1-1)begin
 174.  x = 250;
 175.  y = 18;
 176.  u = 2; 
 177.  end
 178.  else if(mode_reg==READ && cnt2==2-1)begin
 179.  x = 250 ; 
 180.  y = 1 ;
 181.  u = 2 ; 
 182.  end
 183.  else if(mode_reg==EWEN && cnt2==1-1)begin
 184.  x = 250;
 185.  y = 10;
 186.  u = 2; 
 187.  end
 188.  else begin
 189.  x = 250 ; 
 190.  y = 1 ;
 191.  u = 2 ; 
 192.  end
 193.  end
 

1.8 效果和总结

本工程上板之后,可通过复位来验证现象,若要通过断电来进行验证,需要将工程烧录进开发板才行。

1.8.1 db603开发板

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。

b5bca489d55a06ae092edcf4b9d30d93.png

1.8.2 mp801开发板

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。

46a001d0717a29695aba2eef6d0b9daa.png

1.8.3 ms980试验箱

由于本工程现象是一个动态的过程,所以从下面图片中看不出具体实现的效果,想要看上板效果的话可以看一下工程上板的视频。

43c381715ad84f009c090964ab869f17.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值