FPGA-数字时钟
FPGA-数字时钟
总体设计
用FPGA驱动数码管按照HH-MM-SS
的格式显示时间,每秒用串口向上位机发送当前时间,当串口收到@HH:MM:SS
,对时间进行校准。由于年月要考虑到大小月,闰年等。为了简单起见,只考虑时分秒。
数码管模块驱动
数码管硬件电路
数码管分为段选SEG
端和位选SEL
端,其中SEG
是一个八位的字段,每一位用于abcdefgh
所表式的灯条在使能时是否亮起;SEL
端也是一个八位的字段,用于使能对应数码管,在循环扫描时,字段中八位只有一位是有效的。(一次只使能一个数码管)
同时显示八个数码管的基本原理是,用一定频率依次单独控制一个数码管显示内容,当刷新频率在人眼余辉效应之内,肉眼看到的现象就是八个数码管同时亮起。(若用帧率比较高的摄像头去拍,则可以看到数码管是依次循环亮起的)
为了节约FPGA的端口资源,使用74HC595
串转并模块,使需要的控制线的数量减少到三根。其工作原理就是,将SEG[7:0]
和SEL[7:0]
共十六位数据,通过Data[15:0]
输入。但这十六位数据,只需通过FPGA的三根引脚(两根时钟线和一根数据线),串行的数据经过十六个时钟周期转存到两个74HC595
模块的十六个输出引脚中。这样就不需要FPGA引出16根线来输出SEG
和SEL
的十六位数据。
前面提到使用74HC595
模块需要两根时钟线,其中SHCP
控制数据在移位寄存器上移位,而当移位完成后,通过控制STCP
将数据转入到输出寄存器中。假如移位寄存器初始值为0000_0000
有个8位数据1100_1100
,则在第1-8个时钟信号的上升沿,移位寄存器的值为0000_0000
0000_0000
1000_0000
1100_0000
0110_0000
0011_0000
1001_1000
1100_1100
,在数据准备完成时,控制STCP
,则1100_1100
这个数据才能被送到74HC595
的输出端口。
数码管软件设计
根据数码管硬件电路的原理,将驱动数码管的过程分为两大模块。
前面提到,显示八个数码管的基本原理是,用一定频率依次单独控制一个数码管显示内容。其本质就是以一定频率去改变SEG
字段和SEL
字段的值,而这也是我们第一个模块需要完成的功能。(hex8.v
)
而为了节约FPGA的端口资源用到了74HC595
串转并模块。它由两根时钟线和一根数据线作为输入。首先,将第一个模块生产的SEL
和SEG
值,封装成{SEG, SEL}
的形式,再生成SHCP
时钟信号对数据进行移位,当数据准备完成时生成STCP
将数据送到输出端口。这就是我们第二个模块需要完成的功能。(HC595_Driver.v
)
hex8.v
// 八位数码管 扫描周期 20ms / 8 晶振为50MHz
module hex8(Clk,Reset_n,Data_IN[31: 0], // 要显示的内容 Sel[7:0], // 位选Seg[7:0], // 段选Ch_Flag // {SEG, SEL}值发送改变);input Clk;input Reset_n;input [31: 0] Data_IN;output reg [7: 0] Sel;output reg [7: 0] Seg;output reg Ch_Flag;// 计数器 1000000/8 20ms/8reg [31: 0] cnt;reg [2: 0] sel_idx;always@(posedge Clk or negedge Reset_n)if(!Reset_n)begincnt <= 0;sel_idx <= 0;endelse if(cnt == 1000000/8- 1)begincnt <= 0;sel_idx <= sel_idx + 1;endelse begincnt <= cnt + 1;end// 位选always@(posedge Clk or negedge Reset_n)if(!Reset_n)Sel <= 0;else begincase(sel_idx)0: Sel <= 8'b0000_0001;1: Sel <= 8'b0000_0010;2: Sel <= 8'b0000_0100;3: Sel <= 8'b0000_1000;4: Sel <= 8'b0001_0000;5: Sel <= 8'b0010_0000;6: Sel <= 8'b0100_0000;7: Sel <= 8'b1000_0000;endcaseend// 数据缓存reg [3: 0] data_temp;always@(posedge Clk or negedge Reset_n)if(!Reset_n)data_temp <= 0;else begincase(sel_idx)0: data_temp <= Data_IN[3: 0];1: data_temp <= Data_IN[3+4: 0+4];2: data_temp <= Data_IN[3+4*2: 0+4*2];3: data_temp <= Data_IN[3+4*3: 0+4*3];4: data_temp <= Data_IN[3+4*4: 0+4*4];5: data_temp <= Data_IN[3+4*5: 0+4*5];6: data_temp <= Data_IN[3+4*6: 0+4*6];7: data_temp <= Data_IN[3+4*7: 0+4*7];endcaseend// 段选always@(posedge Clk or negedge Reset_n)if(!Reset_n)Seg <= 0;else begincase(data_temp)4'h0: Seg <= 8'b1100_0000; // 0xc04'h1: Seg <= 8'b1111_1001; // 0xf94'h2: Seg <= 8'b1010_0100; // 0xa44'h3: Seg <= 8'b1011_0000; // 0xb04'h4: Seg <= 8'b1001_1001; // 0x994'h5: Seg <= 8'b1001_0010; // 0x924'h6: Seg <= 8'b1000_0010; // 0x824'h7: Seg <= 8'b1111_1000; // 0xf84'h8: Seg <= 8'b1000_0000; // 0x804'h9: Seg <= 8'b1001_0000; // 0x904'ha: Seg <= 8'b1000_1000; // 0x884'hb: Seg <= 8'b1000_0011; // 0x834'hc: Seg <= 8'b1100_0110; // 0xc64'hd: Seg <= 8'b1010_0001; // 0xa14'he: Seg <= 8'b1000_0110; // 0x86
// 4'hf: Seg <= 8'b1000_1110; // 0x8e4'hf: Seg <= 8'b1011_1111; // 显示 -endcaseend// Ch_Flagalways@(posedge Clk or negedge Reset_n)if(!Reset_n)Ch_Flag <= 0;else if(cnt == 2) // Seg 成功赋值需要两个机器周期Ch_Flag <= 1;elseCh_Flag <= 0;
endmodule
HC595_Driver.v
// 移动数据的频率为12.5MHz 则需要脉冲频率为25MHz
module HC595_Driver(Clk,Reset_n,Data[15: 0],En,SHCP, // SHCP 移位时钟DS, // 数据线,SHCP下降沿时改变数据STCP // STCP 停止时钟 );input Clk;input Reset_n;input [15: 0] Data;input En;output reg SHCP;output reg DS;output reg STCP;parameter MCNT = 2;// 缓存数据reg [15:0] r_data;always@(posedge Clk)if(En)beginr_data <= Data;end// 生成脉冲信号reg [31:0] div_counter;always@(posedge Clk or negedge Reset_n)if(!Reset_n)div_counter <= 0;else if(En)begindiv_counter <= 0; endelse if(div_counter == MCNT - 1)div_counter <= 0;else div_counter <= div_counter + 1;wire plus;assign plus = (div_counter == MCNT - 1);// SHCP 移位时钟边缘计数 每周期32个边缘reg [5: 0] shcp_edge_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n)shcp_edge_cnt <= 0;else if(En)beginshcp_edge_cnt <= 0; endelse if(plus)begin// 16比特的数据 要16个周期才能准备完,共32个边缘 还有一个边缘等待将移位寄存器中的数据转移到输出端口if(shcp_edge_cnt == 32)shcp_edge_cnt <= 0;elseshcp_edge_cnt <= shcp_edge_cnt + 1;end// 根据SHCP时钟边缘 产生对应时序always@(posedge Clk or negedge Reset_n)if(!Reset_n)beginSHCP <= 0;DS <= 0;STCP <= 0;endelse if(En)beginSTCP <= 0;DS <= 0;SHCP <= 0;endelse begin// 根据时序图 生产时钟信号 在下降沿改变DS的数据(上升沿时,芯片会读取数据)// 16比特的数据 要16个周期才能准备完,共32个边缘 还有一个边缘等待将移位寄存器中的数据转移到输出端口case(shcp_edge_cnt)0: begin SHCP <= 0; DS <= r_data[15]; STCP <= 0; end1: begin SHCP <= 1; end2: begin SHCP <= 0; DS <= r_data[14]; end3: begin SHCP <= 1; end4: begin SHCP <= 0; DS <= r_data[13]; end5: begin SHCP <= 1; end6: begin SHCP <= 0; DS <= r_data[12]; end7: begin SHCP <= 1; end8: begin SHCP <= 0; DS <= r_data[11]; end9: begin SHCP <= 1; end10: begin SHCP <= 0; DS <= r_data[10]; end11: begin SHCP <= 1; end12: begin SHCP <= 0; DS <= r_data[9]; end13: begin SHCP <= 1; end14: begin SHCP <= 0; DS <= r_data[8]; end15: begin SHCP <= 1; end16: begin SHCP <= 0; DS <= r_data[7]; end17: begin SHCP <= 1; end18: begin SHCP <= 0; DS <= r_data[6]; end19: begin SHCP <= 1; end20: begin SHCP <= 0; DS <= r_data[5]; end21: begin SHCP <= 1; end22: begin SHCP <= 0; DS <= r_data[4]; end23: begin SHCP <= 1; end24: begin SHCP <= 0; DS <= r_data[3]; end25: begin SHCP <= 1; end26: begin SHCP <= 0; DS <= r_data[2]; end27: begin SHCP <= 1; end28: begin SHCP <= 0; DS <= r_data[1]; end29: begin SHCP <= 1; end30: begin SHCP <= 0; DS <= r_data[0]; end31: begin SHCP <= 1; end32: begin STCP <= 1; enddefault:begin STCP <= 0;DS <= 0;SHCP <= 0;endendcaseend
endmodule
UART串口模块驱动
UART通信原理
UART通信基本原理不再赘述,注意以下要点即可:
- 不发送时保持停止位
- 发送时先发送一个起始位
- 起始位后接八个Bit的数据
- 以停止位结束
- 发送一个字节的数据实际有10位(包含起始位和停止位)
- 异步通信,通信双方需要实现约定通信速率(波特率)
对于0基础读者通信原理可以参考STM32入门笔记10_USART串口通信+案例:上位机控制LED亮灭(USART串口通信、TIM定时器、EXTI综合案例)-CSDN博客
UART发送模块
uart_tx_byte.v
UART发送一个字节,重点讲下波特率计数值的计算方法:晶振为50MHz,则一个时钟周期为20ns;若波特率为9600,即将1s也就是1000000000ns分为9600份,每份时间为1000000000/9600,而计数1次是20ns,所以需要计数的次数为1000000000/9600/20=5208
module uart_tx_byte(Clk,Reset_n,Data[7:0],send_en,baud_set[2:0],START_BIT,STOP_BIT,uart_tx,tx_done,uart_state);input Clk;input Reset_n;input [7:0] Data;input send_en;input [2:0] baud_set;input START_BIT;input STOP_BIT;output reg uart_tx;output reg tx_done;output reg uart_state;// 波特率时钟设置reg [16:0] bps_DR; // 13位always@(baud_set)case(baud_set)3'd0: bps_DR <= 5208 - 1; // 96003'd1: bps_DR <= 2604 - 1; // 192003'd2: bps_DR <= 1302 - 1; // 384003'd3: bps_DR <= 868 -1; // 576003'd4: bps_DR <= 434 - 1; // 115200default: bps_DR <= 5208 - 1;endcase// 波特率时钟生成reg [16:0] div_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n)div_cnt <= 0;else if(uart_state)beginif(div_cnt == bps_DR)div_cnt <=0;else div_cnt <= div_cnt + 1;endelsediv_cnt <= 0;// bps_clk genreg bps_clk;always@(posedge Clk or negedge Reset_n)if(!Reset_n)bps_clk <= 0;else if(div_cnt == 1)bps_clk <=1;else bps_clk <= 0;// bps counterreg [3:0] bps_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n)bps_cnt <= 4'd0;else if(bps_cnt == 4'd11)bps_cnt <= 4'd0;else if(bps_clk)bps_cnt <= bps_cnt + 1; else bps_cnt <= bps_cnt;// 传送结束标志always@(posedge Clk or negedge Reset_n)if(!Reset_n)tx_done <= 0;else if(bps_cnt == 4'd11)tx_done <= 1;else tx_done <= 0;// 传输状态always@(posedge Clk or negedge Reset_n)if(!Reset_n)uart_state <= 0;else if(send_en)uart_state <= 1;else if(bps_cnt == 4'd11) // bps_cnt 计数到11, 确保有十个完整的波形uart_state <= 0;elseuart_state <= uart_state;// 数据缓存reg [7:0] data_byte_reg;always@(posedge Clk or negedge Reset_n)if(!Reset_n)data_byte_reg <= 8'd0;else if(send_en)data_byte_reg <= Data;elsedata_byte_reg <= data_byte_reg; // 发送数据 always@(posedge Clk or negedge Reset_n)if(!Reset_n)uart_tx = STOP_BIT;else begincase(bps_cnt)0:uart_tx <= STOP_BIT;1:uart_tx <= START_BIT;2:uart_tx <= data_byte_reg[0];3:uart_tx <= data_byte_reg[1];4:uart_tx <= data_byte_reg[2];5:uart_tx <= data_byte_reg[3];6:uart_tx <= data_byte_reg[4];7:uart_tx <= data_byte_reg[5];8:uart_tx <= data_byte_reg[6];9:uart_tx <= data_byte_reg[7];10:uart_tx <= STOP_BIT;default:uart_tx <= STOP_BIT;endcaseend
endmodule
uart_tx_data
若想发送多个字节的数据,则需事先确定需要发送数据的字节数,然后使用状态机的方法,在不同状态下执行不同操作。
module uart_tx_data(Clk,Reset_n,send_go,Data8[8*9-1: 0],uart_tx,trans_done);input Clk;input Reset_n;input send_go;input [8*9-1:0] Data8;output uart_tx;output reg trans_done;reg [7: 0] Data;reg [8*9-1: 0] Data8_reg;wire tx_done;wire uart_state;reg send_en;uart_tx_byte uart_tx_byte_instance(.Clk(Clk),.Reset_n(Reset_n),.Data(Data),.send_en(send_en),.baud_set(0),.START_BIT(0),.STOP_BIT(1),.uart_tx(uart_tx),.tx_done(tx_done),.uart_state(uart_state));reg [3:0] state;// 状态改变always@(posedge Clk or negedge Reset_n)if(!Reset_n)state <= 0;else if(state == 10)state <= 0;else if(state == 0)begin // 等待使能信号if(send_go)state <= state + 1;endelse if(tx_done) // 若发送完成state <= state + 1;// 发送使能always@(posedge Clk or negedge Reset_n)if(!Reset_n)send_en <= 0;else if(state == 0)beginif(send_go)send_en <= 1;endelse if(state == 10)send_en <= 0;else beginif(tx_done)send_en <= 1; // 发送完成 send_en 置 1,开始发送下一个字节elsesend_en <= 0; end// Data_reg 缓存数据always@(posedge Clk or negedge Reset_n)if(!Reset_n)Data8_reg <= 0;else if(state == 0)Data8_reg <= Data8;else if(tx_done) // 每发送完一个字节向右移动八位Data8_reg <= Data8_reg >> 8;elseData8_reg <= Data8_reg;// 根据state更新Data中的数据always@(posedge Clk or negedge Reset_n)if(!Reset_n)Data <= 0;else if(state == 10)Data <= 0;else if(state == 0)Data <= Data;else Data <= Data8_reg[7: 0];// trans_donealways@(posedge Clk or negedge Reset_n)if(!Reset_n)trans_done <= 0;else if(state == 0)trans_done <= 0;else if(state == 10)trans_done <= 1; // 所有数据发送完成 trans_done 置 1
endmodule
UART接收模块
uart_rx_byte
接收模块,只需接收一个字节即可,在上层模块中完成对多个字节数据的接收
// uart 接收模块, 每个比特数据采样16次,取中间八次进行统计判断高低电平
module uart_rx_byte(Clk,Reset_n,uart_rx,baud_set[2:0],Data[7:0],rx_done,rx_state);input Clk;input Reset_n;input uart_rx;input [2:0] baud_set;output reg[7:0] Data;output reg rx_done;output reg rx_state;reg [15:0] bsp_DR; // 设置波特率always@(baud_set)case(baud_set)0: bsp_DR <= 5208; // 96001: bsp_DR <= 2604; // 192002: bsp_DR <= 1302; // 384003: bsp_DR <= 868; // 576004: bsp_DR <= 434; // 115200default:bsp_DR <= 5208;endcase// 下降沿检测器和上升沿检测器reg [1:0] edge_reg;always@(posedge Clk or negedge Reset_n)if(!Reset_n)beginedge_reg <= 0;endelse beginedge_reg[0] <= uart_rx;edge_reg[1] <= edge_reg[0];endwire pos_reg;assign pos_reg = (edge_reg == 2'b01);wire neg_reg;assign neg_reg = (edge_reg == 2'b10);// rx_statealways@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_state <= 0;else if(neg_reg) // 检测到下降沿rx_state <= 1; // 正在接收else if(rx_done) // 检测到发送结束rx_state <= 0; // 空闲中// 采样时钟生成reg [8: 0] bsp_clk;always@(posedge Clk or negedge Reset_n)if(!Reset_n)bsp_clk <= 0; else if(rx_state)beginif(bsp_clk == (bsp_DR / 16)-1)bsp_clk <= 0;else bsp_clk <= bsp_clk + 1;endelse bsp_clk <= 0;// 采样计数器 reg [5: 0] bsp_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n)bsp_cnt <= 0;else if(rx_state)beginif(bsp_cnt == 15+1)bsp_cnt <= 0;// 这里要减1 若忘记减1 波特率会有比较大的误差 导致不能连续收到数据else if(bsp_clk == (bsp_DR / 16)-1)bsp_cnt <= bsp_cnt + 1;endelsebsp_cnt <= 0;// 收到的第几个字节的个数 data_idxreg [3: 0] data_idx;always@(posedge Clk or negedge Reset_n)if(!Reset_n)data_idx <= 0;else if(rx_state)beginif(bsp_cnt == 15+1)data_idx <= data_idx + 1;endelse if(rx_done)data_idx <= 0;elsedata_idx <= 0;// 统计采样到的数据reg [3: 0] StartBit;reg [3: 0] StopBit;reg [3: 0] bsp_Data_reg[7:0]; always@(posedge Clk or negedge Reset_n)if(!Reset_n)beginStartBit <= 0;bsp_Data_reg[0] <= 0;bsp_Data_reg[1] <= 0;bsp_Data_reg[2] <= 0;bsp_Data_reg[3] <= 0;bsp_Data_reg[4] <= 0;bsp_Data_reg[5] <= 0;bsp_Data_reg[6] <= 0;bsp_Data_reg[7] <= 0;StopBit <= 0;endelse if(rx_done) begin // 接收完清零StartBit <= 0;bsp_Data_reg[0] <= 0;bsp_Data_reg[1] <= 0;bsp_Data_reg[2] <= 0;bsp_Data_reg[3] <= 0;bsp_Data_reg[4] <= 0;bsp_Data_reg[5] <= 0;bsp_Data_reg[6] <= 0;bsp_Data_reg[7] <= 0;StopBit <= 0;endelse if(rx_state)begin // 如果正在接收// 这里要减1 若忘记减1 波特率会有比较大的误差 导致不能连续收到数据if(bsp_clk == (bsp_DR / 16 / 2)-1)begin // 采样时间到case(bsp_cnt)4,5,6,7,8,9,10,11:if(data_idx == 0)StartBit <= StartBit + uart_rx;else if(data_idx == 9)StopBit <= StopBit + uart_rx;elsebsp_Data_reg[data_idx-1] <= bsp_Data_reg[data_idx-1] + uart_rx;default:; endcase end end// 当data_idx == 11时, 发送rx_done信号always@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_done <= 0;else if(rx_done)rx_done <= 0;else if(data_idx == 10)beginif(rx_state)rx_done <= 1;end// 根据统计结果保存数据always@(posedge Clk or negedge Reset_n)if(!Reset_n)Data <= 0;else if((neg_reg) && (!rx_state))Data <= 0;else if(bsp_cnt == 15)if((data_idx != 0) || (data_idx != 9)) // 不是起始位或停止位Data[data_idx - 1] <= (bsp_Data_reg[data_idx - 1] >= 4);endmodule
顶层模块设计
顶层模块中,对时分秒进行运算,例化hex8
和HC595_Driver
用于将数据显示到数码管,例化uart_rx_byte
接收数据,例化uart_tx_data
发送数据
digital_clock.v
hour
minute
second
为时分秒对应的十进制数据,通过s_cnt
计时器,每秒对 second
进行自增,每60秒对minute
进行自增,每60分对hour
进行自增;
hour_hex
minute_hex
second_hex
为十分秒对应的十进制数据,这些数据位宽为八位,高低四位分别存储十进制下的十位和个位值。该数据最终在hex8
模块中,被用于生成段选数据;
hour_str_x
minute_str_x
second_str_x
字符串形式的时间量,每位通过一个字节的字符串表示 如10:11:12
,有效数值部分共6个字节。该数据用:
分隔打包好后,通过uart_tx_data
模块,发送到上位机;
数据接受状态用rx_flag
表示,接收时判断包头和分隔符,从而保证接收数据正确;
rx_hour
rx_minute
rx_second
用于保存接收到的校准数据,根据rx_flag
接收到的数据是时分或秒,若接收过程出错则不会对数据更新,只有当成功接收到正确数据时(rx_flag==9
),才会对 hour
minute
和second
执行赋值操作。
// 数字时钟 串口校时
module digital_clock(Clk,Reset_n,uart_tx,uart_rx,SHCP,DS,STCP);input Clk;input Reset_n;input uart_rx;output uart_tx;output SHCP;output DS;output STCP;reg [4:0] hour;reg [7:0] hour_hex;reg [5:0] minute;reg [7:0] minute_hex;reg [5:0] second;reg [7:0] second_hex;reg [3:0] rx_flag;reg [4:0] rx_hour;reg [5:0] rx_minute;reg [5:0] rx_second;// 1s计时器parameter MCNT = 50000000;reg [31: 0] s_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n)s_cnt <= 0;else if(rx_flag == 9)s_cnt <= 0;else if(s_cnt == MCNT) s_cnt <= 0;elses_cnt <= s_cnt + 1;// second always@(posedge Clk or negedge Reset_n)if(!Reset_n)second <= 0;else if(rx_flag == 9)second <= rx_second;else if(second == 60)second <= 0;else if(s_cnt == MCNT - 1)second <= second + 1;// minutealways@(posedge Clk or negedge Reset_n)if(!Reset_n) minute <= 0;else if(rx_flag == 9)minute <= rx_minute;else if(minute == 60)minute <= 0;else if(second == 60)minute <= minute + 1;// hour always@(posedge Clk or negedge Reset_n)if(!Reset_n)hour <= 0;else if(rx_flag == 9)hour <= rx_hour; else if(hour == 24)hour <= 0;else if(minute == 60)hour <= hour + 1;// second_hexalways@(posedge Clk or negedge Reset_n)if(!Reset_n)second_hex <= 0;else beginsecond_hex[7: 4] <= second / 10;second_hex[3: 0] <= second % 10;end// minute_hexalways@(posedge Clk or negedge Reset_n)if(!Reset_n)minute_hex <= 0;else beginminute_hex[7: 4] <= minute / 10;minute_hex[3: 0] <= minute % 10;end// hour_hexalways@(posedge Clk or negedge Reset_n)if(!Reset_n)hour_hex <= 0;else beginhour_hex[7: 4] <= hour / 10;hour_hex[3: 0] <= hour % 10;end// hex8 showwire [31: 0] Data_IN;wire [7: 0] Sel;wire [7:0] Seg;wire Ch_Flag;assign Data_IN = {hour_hex, 4'hf, minute_hex, 4'hf, second_hex};hex8 hex8_ins(.Clk(Clk),.Reset_n(Reset_n),.Data_IN(Data_IN),.Sel(Sel), // 位选.Seg(Seg), // 段选.Ch_Flag(Ch_Flag));HC595_Driver HC595_Driver_ins(.Clk(Clk),.Reset_n(Reset_n),.Data({Seg, Sel}),.En(Ch_Flag),.SHCP(SHCP), // SHCP 移位时钟.DS(DS), // 数据线,SHCP下降沿时改变数据.STCP(STCP) // STCP 停止时钟 );// 串口发送当前时间reg [7: 0] Data;reg send_go;wire [7: 0] second_str_h;wire [7: 0] second_str_l;wire [7: 0] minute_str_h;wire [7: 0] minute_str_l;wire [7: 0] hour_str_h;wire [7: 0] hour_str_l;wire [7: 0] div_sig;wire [7: 0] end_sig;wire [8*9-1: 0] Data_Send;wire trans_done;assign second_str_l = second % 10 + "0";assign second_str_h = second / 10 + "0";assign minute_str_l = minute % 10 + "0";assign minute_str_h = minute / 10 + "0";assign hour_str_l = hour % 10 + "0";assign hour_str_h = hour / 10 + "0";assign div_sig = ":"; // 分隔符assign end_sig = "\n"; // 结束符 assign Data_Send = {end_sig, second_str_l, second_str_h, div_sig, minute_str_l, minute_str_h, div_sig, hour_str_l, hour_str_h};// 串口发送uart_tx_data uart_tx_data_ins(.Clk(Clk),.Reset_n(Reset_n),.send_go(send_go),.Data8(Data_Send),.uart_tx(uart_tx),.trans_done(trans_done));// send_goreg uart_tx_state;always@(posedge Clk or negedge Reset_n)if(!Reset_n)send_go <= 0;else if(s_cnt == MCNT - 1)begin if(uart_tx_state == 0)send_go <= 1; elsesend_go <= 0;endelse send_go <= 0;// uart_tx_statealways@(posedge Clk or negedge Reset_n)if(!Reset_n)uart_tx_state <= 0;else if(s_cnt == MCNT - 1)beginif(uart_tx_state == 0)uart_tx_state <= 1;endelse if(trans_done) // 数据全部发送完成 进入空闲态uart_tx_state <= 0;// 串口接收并授时wire [7: 0] Rx_Data_Reg;wire rx_done;wire rx_state;uart_rx_byte uart_rx_byte_ins(.Clk(Clk),.Reset_n(Reset_n),.uart_rx(uart_rx),.baud_set(0),.Data(Rx_Data_Reg),.rx_done(rx_done),.rx_state(rx_state));// rx_flag 状态机always@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_flag <= 0;else if(rx_flag == 9)rx_flag <= 0;else if(rx_done)begincase(rx_flag)0:begin if(Rx_Data_Reg == "@") rx_flag <= rx_flag + 1;end1: rx_flag <= rx_flag + 1;2: rx_flag <= rx_flag + 1;3: beginif(Rx_Data_Reg == ":")rx_flag <= rx_flag + 1;else rx_flag <= 0;end4: rx_flag <= rx_flag + 1;5: rx_flag <= rx_flag + 1;6:beginif(Rx_Data_Reg == ":")rx_flag <= rx_flag + 1;else rx_flag <= 0;end7: rx_flag <= rx_flag + 1;8: rx_flag <= rx_flag + 1;default:rx_flag <= 0;endcaseend// rx_houralways@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_hour <= 0;else if(rx_flag == 0)rx_hour <= 0;else if(rx_flag == 1)beginif(rx_done)rx_hour <= rx_hour + (Rx_Data_Reg - "0") * 10;endelse if(rx_flag == 2)beginif(rx_done)rx_hour <= rx_hour + (Rx_Data_Reg - "0");end// rx_minutealways@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_minute <= 0;else if(rx_flag == 0)rx_minute <= 0;else if(rx_flag == 4)beginif(rx_done)rx_minute <= rx_minute + (Rx_Data_Reg - "0") * 10;endelse if(rx_flag == 5)beginif(rx_done)rx_minute <= rx_minute + (Rx_Data_Reg - "0");end // rx_secondalways@(posedge Clk or negedge Reset_n)if(!Reset_n)rx_second <= 0;else if(rx_flag == 0)rx_second <= 0;else if(rx_flag == 7)beginif(rx_done)rx_second <= rx_second + (Rx_Data_Reg - "0") * 10;endelse if(rx_flag == 8)beginif(rx_done)rx_second <= rx_second + (Rx_Data_Reg - "0");end
endmodule
digital_clock_tb.v
要看子模块的数据波形,自行Add to Wave
`timescale 1ns / 1nsmodule digital_clock_tb();reg Clk;reg Reset_n;wire uart_tx;reg uart_rx;wire SHCP;wire DS;wire STCP;digital_clock digital_clock_ins(.Clk(Clk),.Reset_n(Reset_n),.uart_tx(uart_tx),.uart_rx(uart_rx),.SHCP(SHCP),.DS(DS),.STCP(STCP));initial Clk = 1;always #10 Clk = !Clk;initial beginReset_n = 0;#201;Reset_n = 1;#20;uart_tx_byte_sim("@");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("2");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("3");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim(":");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("5");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("9");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim(":");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("5");@(posedge digital_clock_ins.rx_done);uart_tx_byte_sim("5");@(posedge digital_clock_ins.rx_done);#1000000000;#1000000000;$stop;endtask uart_tx_byte_sim;input [7: 0] tx_data;beginuart_rx = 1;#20;uart_rx = 0;#52083;#52083;uart_rx = tx_data[0];#52083;#52083;uart_rx = tx_data[1];#52083;#52083;uart_rx = tx_data[2];#52083;#52083;uart_rx = tx_data[3];#52083;#52083;uart_rx = tx_data[4]; #52083;#52083;uart_rx = tx_data[5];#52083;#52083;uart_rx = tx_data[6]; #52083;#52083;uart_rx = tx_data[7];#52083;#52083;uart_rx =1;endendtask
endmodule
实验现象
FPGA-数字时钟
开发板型号
小梅哥FPGA ACX720
芯片型号: XC7A35TFGG484ABX2l
相关文章:
FPGA-数字时钟
FPGA-数字时钟 总体设计 用FPGA驱动数码管按照HH-MM-SS的格式显示时间,每秒用串口向上位机发送当前时间,当串口收到HH:MM:SS,对时间进行校准。由于年月要考虑到大小月,闰年等。为了简单起见,只考虑时分秒。 数码管…...
数据结构 RBT 插入操作的 Python 代码实现
目录 一、红黑树的性质二、红黑树的插入1. 插入根节点或根节点变红2. 双亲节点 P 为黑色3. 双亲结点 P 和叔伯结点 U 均为红色4. 双亲结点 P 为红色,叔伯结点 U 为黑色或缺失1)情形一2)情形二 三、插入的 Python 代码实现 红黑树动画演示网站…...
颖儿生活提案:用海信璀璨505U6真空冰箱重建都市鲜食自由
热播剧《六姊妹》中,演员颖儿饰演的何家艺以泼辣坚韧的形象深入人心,一双手撑起家庭的"烟火气";戏外,她平衡事业与家庭,以自律姿态书写鲜活人生。 近日,颖儿向公众展示家中厨房,意外…...
JQuery 使用技巧
文章目录 隐藏/显示淡入淡出滑动追加新元素删除元素/内容设置 CSS 样式尺寸遍历Ajax根据 input 控件中的值 实时改变另一个值 $()是jQuery()的简写getElementByTagName();如: $(“div”)getElementByTagName(“div”); $()的作用是用于查找出 HTML 的标签、属性、样…...
光流法:从传统方法到深度学习方法
1 光流法简介 光流(Optical Flow)是指图像中像素灰度值随时间的变化而产生的运动场。 简单来说,它描述了图像中每个像素点的运动速度和方向。 光流法是一种通过分析图像序列中像素灰度值来计算光流的方法。对于图像数据计算出来的光流是一个二…...
如何选择合适的RFID手持终端设备?
一、明确核心需求,锁定关键参数 选购RFID手持终端的首要任务是明确应用场景的核心需求。若用于仓储物流或零售盘点,推荐选择上海岳冉超高频RFID手持终端设备,支持1-20米远距离批量读取;若用于医疗耗材或图书管理,岳冉高…...
Axios 传参与 Spring Boot 接收参数完全指南
Axios 传参与 Spring Boot 接收参数完全指南 本文详细说明前端 Axios 传参与后端 Spring Boot 接收参数的各类场景,包括 GET/POST/PUT/DELETE 请求、路径参数/查询参数/请求体参数 的传递方式,以及如何接收 List、Map 等复杂类型。通过代码示例和对比表…...
NdrpPointerUnmarshallInternal函数分析之pStubMsg--pAllocAllNodesContext的由来
第一部分: // // Check if this is an allocate all nodes pointer AND that were // not already in an allocate all nodes context. // if ( ALLOCATE_ALL_NODES(pFormat[1]) && ! pStubMsg->pAllocAllNodesContext …...
人脑、深思考大模型与其他大模型的区别科普
文章目录 大模型的基本概念与特点深思考大模型的独特之处深思考大模型与其他大模型的对比架构与技术训练数据应用场景提示词编写 大模型给出答案的方式:基于概率还是真的会分析问题?人脑的思考过程基本单位与网络大脑结构与功能分区信息处理流程思维模式…...
Unity-粒子系统:萤火虫粒子特效效果及参数
萤火虫特效由两部分组成。萤火虫粒子底色粒子面片。萤火虫的旋转飞动主要由 Noise参数和Color over Lifetime模块控制。 贴图:中间实周边虚的圆,可随意自行制作 Shader:Universal Render Pipeline/2D/Sprite-Lit-Default 以下是粒子详细参…...
Java垃圾收集器与内存分配策略深度解析
在 Java 与 C 的世界里,内存动态分配与垃圾收集技术仿佛筑起了一道高墙。墙外的人渴望进入,享受自动内存管理的便利;而墙内的人却试图突破,追求更高的性能与控制力。今天,就让我们深入探讨 Java 的垃圾收集器与内存分配…...
优化MySQL性能:主从复制与读写分离实践指南
目录 一、知识介绍 1.MySQL主从复制原理 2.MySQL读写分离原理 二、资源清单 三、案例实施 1.修改主机名 2.搭建MySQL主从复制 3.搭建MySQL读写分离 一、知识介绍 1.MySQL主从复制原理 MySQL支持的复制类型 基于语句的复制基于行的复制混合模型复制 工作过程 主&#…...
Foupk3systemX5OS系统产品设备
Foupk3systemX5OS TXW8(基于Foupk3systemX5OS系统19.62正式版开发的智能移动设备由Foupk3systemX5OS系统与FOUPK3云服务平台共同自主研发) Foupk3systemX5OS TX6(Foupk3systemX5OS TX6基于Foupk3systemX5OS系统19.60正式版开发的智能平板设备…...
【计网】认识跨域,及其在go中通过注册CORS中间件解决跨域方案,go-zero、gin
一、跨域(CORS)是什么? 跨域,指的是浏览器出于安全限制,前端页面在访问不同源(协议、域名、端口任一不同)的后端接口时,会被浏览器拦截。 比如: 前端地址后端接口地址是…...
关于 【Spring Boot Configuration Annotation Processor 未配置问题】 的详细分析、解决方案及代码示例
以下是关于 Spring Boot Configuration Annotation Processor 未配置问题 的详细分析、解决方案及代码示例: 1. 问题描述 当使用 Spring Boot 的配置注解(如 ConfigurationProperties、Value、ConditionalOnProperty 等)时,若未…...
MySQL 的ANALYZE与 OPTIMIZE命令
MySQL 的ANALYZE与 OPTIMIZE命令 一、ANALYZE TABLE - 更新统计信息 1. 基本语法与功能 ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ...作用:收集表统计信息用于优化器生成更优的执行计划,主要更新: 索引基数&am…...
【机器学习】人工智能在电力电子领域的应用
摘要: 本文概述了电力电子系统的人工智能 (AI) 应用。设计、控制和维护这三个独特的生命周期阶段与人工智能要解决的一项或多项任务相关,包括优化、分类、回归和数据结构探索。讨论了专家系统、模糊逻辑、元启发法和机器学习四类人工智能的应用。我们对…...
InferType和_checked_type的区别?
在 TVM 的 Relay IR 中,relay.frontend.common.infer_shape(node) 和 node.checked_type.shape 都与**形状(Shape)**信息相关,但它们的用途、实现机制和性能特点有显著区别。以下是详细对比: 1. 功能区别 特性node.ch…...
Flutter 学习之旅 之 flutter 作为 module ,在 Android 端主动唤起 Flutter 开发的界面 简单的整理
Flutter 学习之旅 之 flutter 作为 module ,在 Android 端主动唤起 Flutter 开发的界面 简单的整理 目录 Flutter 学习之旅 之 flutter 作为 module ,在 Android 端主动唤起 Flutter 开发的界面 简单的整理 一、简单介绍 二、Android 端唤起 Flutter …...
vue3 css模拟语音通话不同语音、正在加载等的效果
实现效果如下: 在不同的时间,显示不一样的效果(大小是一样的,截图时尺寸发生了变化) 具体实现代码如下: <script setup> import {ref} from "vue";const max_hight ref(40px) const min…...
【Machine Learning Q and AI 读书笔记】- 01 嵌入、潜空间和表征
Machine Learning Q and AI 中文译名 大模型技术30讲,主要总结了大模型相关的技术要点,结合学术和工程化,对LLM从业者来说,是一份非常好的学习实践技术地图. 本文是Machine Learning Q and AI 读书笔记的第1篇,对应原…...
[Agent]AI Agent入门02——ReAct 基本理论与实战
ReAct介绍 ReAct(Reasoning and Acting)是一种通过协同推理(Reasoning)与行动(Acting)提升大语言模型(LLM)任务解决能力的技术。其核心思想是在解决复杂问题时交替生成推理和动作&a…...
uniapp自定义头部(兼容微信小程序(胶囊和状态栏),兼容h5)
很早之前就写过自定义头部,但是那时偷懒写死了,现在用插槽重新写了个 有两种形式: type1是完全自定义的,可以自己去组件改也可以用插槽改 type2是正常的返回标题和右边按钮,使用就是 title"标题" rightClic…...
mybatis的xml ${item}总是更新失败
场景 代码如下 void updateStatus(Param("deviceSerialIdCollection") Collection<String> deviceSerialIdCollection, Param("status") Integer status);<update id"updateStatus">UPDATE gb_monitor SET online#{status} WHERE d…...
数据库- JDBC
标题目录 JDBC基本概念JDBC 接口JDBC 工作原理 JDBC APIJDBC工作过程Driver 接口及驱动加载Connection 接口Statemen 接口ResultSet 接口PreparedStatement 接口 JDBC 基本概念 Java Database Connectivity:java访问数据库的解决方案希望用相同的方式访问不同的数…...
[26] cuda 应用之 nppi 实现图像格式转换
[26] cuda 应用之 nppi 实现图像格式转换 讲述 nppi 接口定义通过nppi实现 bayer 格式转rgb格式官网参考信息:http://gwmodel.whu.edu.cn/docs/CUDA/npp/group__image__color__debayer.html#details1. 接口定义 官网关于转换的原理是这么写的: Grayscale Color Filter Array …...
MYSQL-OCP官方课程学习截图
第一节 介绍...
医院信息管理系统全解析
目录 一、医院信息管理系统是什么 1. 概念阐释 2. 核心功能概述 二、医院信息管理系统的种类 1. 医院信息系统(HIS) 2. 电子病历系统(EMR) 3. 实验室信息管理系统(LIS) 三、医院信息管理系统的实际…...
模型上下文协议(MCP):技术解析与生态发展
一、概念与目标 模型上下文协议(Model Context Protocol,MCP)是由Anthropic于2024年11月推出的开源协议,旨在为大语言模型(LLM)与外部工具、数据源提供标准化的双向通信框架。其核心目标是打破数据孤岛&am…...
laravel中layui的table翻页不起作用问题的解决
本地测试是好的,部署的时候就发现,翻页不起作用了。但lay_num序号是可以变化的,查看api接口传递的数据,发现数据没有变化,加上page2等翻页,也是不起作用,看来是url参数返回给后台,后…...
python上测试neo4j库
安装完了neo4j库后,如何使用。用python来小试牛刀 1.从其他博客上找来demo #coding:utf-8 from py2neo import Graph,Node,Relationship##连接neo4j数据库,输入地址、用户名、密码 graph Graph(bolt://xx.xx.xx.xx:7687,userneo4j,passwordneo4j1234)…...
云原生周刊:Kubernetes v1.33 正式发布
开源项目推荐 Robusta Robusta 是一个开源的 K8s 可观测性与自动化平台,旨在增强 Prometheus 告警的智能化处理能力。它通过规则和 AI 技术对告警进行丰富化处理,自动附加相关的 Pod 日志、图表和可能的修复建议,支持智能分组、自动修复和高…...
网络安全入门综述
引言 在数字化时代,网络安全(Cybersecurity)已成为保护个人、企业和政府机构免受数字威胁的关键领域。随着互联网的普及、云计算的兴起以及物联网(IoT)设备的激增,网络攻击的频率和复杂性不断增加。从数据…...
LLaMA-Factory部署以及大模型的训练(细节+新手向)
LLaMA-Factory 经过一段时间的探索,从手动编写训练代码到寻求框架辅助训练,遇到了各种各样的问题。前面我介绍了dify的部署,但是并没有详细介绍使用方式,是因为我在尝试利用dify的时候碰到了很多困难,总结下来首先就是…...
ASP.NET MVC 入门指南四
21. 高级路由配置 21.1 自定义路由约束 除了使用默认的路由约束,你还可以创建自定义路由约束。自定义路由约束允许你根据特定的业务逻辑来决定一个路由是否匹配。例如,创建一个只允许特定年份的路由约束: csharp public class YearRouteCo…...
rabbitmq-集群部署
场景:单个pod,部署在主节点,基础版没有插件,进阶版多了一个插件 基础版本: --- apiVersion: v1 kind: PersistentVolume metadata:name: rabbitmq-pv spec:capacity:storage: 5GiaccessModes:- ReadWriteOncestorage…...
明远智睿SSD2351开发板:开启工业控制新征程
在工业控制领域,对开发板的性能、稳定性和扩展性有着极高的要求。明远智睿的SSD2351开发板凭借其卓越的特性,为工业控制带来了全新的解决方案。 SSD2351开发板搭载四核1.4GHz处理器,强大的运算能力使其在处理工业控制中的复杂任务时游刃有余。…...
RISCV学习(5)GD32VF103 MCU架构了解
RISCV学习(5)GD32VF103 MCU架构了解 1、芯片内核功能简介 GD32VF103 MCU架构,采用Bumblebee内核,芯来科技(Nuclei System Technology)与台湾晶心科技(Andes Technology)联合开发&am…...
IDEA2022.3开启热部署
1、开启IDEA的自动编译 1.1 具体步骤:打开顶部工具栏 File -> Settings -> Build,Execution,Deployment -> Compiler 然后勾选 Build project automatically 。 1.2 打开顶部工具栏 File -> Settings -> Advanced Settings -> Compiler -> 然…...
《算法吞噬幻想乡:GPT-4o引发的艺术平权运动与版权核爆》
一、引言:现象级AI艺术事件的社会回响 GPT - 4o吉卜力风格刷屏现象 在当今数字化浪潮中,GPT - 4o吉卜力风格的作品在网络上掀起了一阵刷屏热潮。吉卜力工作室以其独特的水彩质感、奇幻氛围和孤独美学,在全球范围内拥有大量粉丝。而GPT - 4o强…...
yolov5 源码 +jupyter notebook 笔记 kaggle
YOLOv5 | Kaggle 直接用的githuab的源码,git clone 后output才有文件 直接gitclone他的源码用Vscode看 好久没见过16g了 怎么这么便宜 https://gadgetversus.com/graphics-card/nvidia-tesla-p100-pcie-16gb-vs-nvidia-geforce-rtx-4060/#google_vignette 好的&am…...
聊天室系统:多任务版TCP服务端程序开发详细代码解释
1. 需求 目前我们开发的TCP服务端程序只能服务于一个客户端,如何开发一个多任务版的TCP服务端程序能够服务于多个客户端呢? 完成多任务,可以使用线程,比进程更加节省内存资源。 2. 具体实现步骤 编写一个TCP服务端程序,循环等…...
Python(15)迭代器和生成器
在 Python 编程领域中,迭代器和生成器是两个强大且独特的概念,它们为处理数据序列提供了高效且灵活的方式。这篇博客将结合菜鸟教程内容,通过丰富的代码示例,深入学习 Python3 中的迭代器与生成器知识,方便日后复习回顾…...
无刷空心杯电机及机器人灵巧手的技术解析与发展趋势
一、无刷空心杯电机结构与技术解析 1. 核心结构设计 无刷空心杯电机的核心设计突破在于无铁芯转子与电子换向系统的结合。其结构由以下关键部分构成: 定子组件:采用印刷电路板(PCB)或柔性电路板(FPC)作为绕组载体,通过三维绕线技术形成空心杯状绕组,彻底消除齿槽效应…...
如何修复卡在恢复模式下的 iPhone:简短指南
Apple 建议使用恢复模式作为最后的手段,以便在 iPhone 启动循环或显示 Apple 标志时恢复 iPhone。这是解决持续问题的简单方法,但您很少使用。但是,当您的 iPhone 卡住恢复模式本身时,您会怎么做?虽然 iPhone 卡在这种…...
蒋新松:中国机器人之父
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 蒋新松:中国机器人之父 一、生平简介 1. 早年经历与求学道路 蒋新松出生…...
[Windows] MousePlus 5.5.9
[Windows] MousePlus 链接:https://pan.xunlei.com/s/VOOwKJ281kDaZV5_MpP1COd_A1?pwdn69c# MousePlus是一款轻便小巧的鼠标右键增强工具,使用鼠标右键拖动即可唤醒鼠标轮盘,这个功能界面和quicker的轮盘软件界面一样,操作逻辑…...
BT131-ASEMI无人机专用功率器件BT131
编辑:ll BT131-ASEMI无人机专用功率器件BT131 型号:BT131 品牌:ASEMI 封装:TO-92 批号:最新 引脚数量:3 特性:双向可控硅 工作温度:-40℃~150℃ 在智能化浪潮中,…...
ETL架构、数据建模及性能优化实践
ETL(Extract, Transform, Load)和数据建模是构建高性能数据仓库的核心环节。下面从架构设计、详细设计、数据建模方法和最佳实践等方面系统阐述如何优化性能。 一、ETL架构设计优化 1. 分层架构设计 核心分层: 数据源层:对接O…...
30分钟上架鸿蒙原生应用,即时通信IM UI组件库全面适配HarmonyOS 原
自去年 10 月 8 日鸿蒙5开启公测以来,鸿蒙操作系统不断迭代,生态趋向稳健。当前,支持HarmonyOS操作系统的设备数量已超过 10 亿,上架HarmonyOS 5 应用市场的鸿蒙原生应用和元服务已超过2万个。这无疑为广大开发者提供了丰富的应用…...