专栏名称: 安信可科技
全球领先的联网模组、智能家居等物联网硬件方案提供商。
目录
相关文章推荐
高分子科学前沿  ·  剑桥大学申清臣/电子科技大学邓旭等《AM》: ... ·  9 小时前  
高分子科学前沿  ·  浙江大学的刘明研究员Angew:共晶策略“拼 ... ·  昨天  
淘股吧  ·  “史诗级”暴涨 ·  昨天  
固收彬法  ·  利率 | 春节期间海内外有哪些变化? ·  3 天前  
爱股君2020  ·  爱股君祝福来啦! ·  1 周前  
51好读  ›  专栏  ›  安信可科技

【电子DIY作品】基于FPGA的GPS授时日历时钟

安信可科技  · 公众号  ·  · 2024-11-04 12:04

正文



安信可技术论坛官方B站账号来啦
提前关注不迷路
在这里优先预告各类活动、教程





以下作品由安信可社区用户

一只呆头鹅制作


实验目的

使用安信可科技的 GP-01 模块实现卫星授时,显示在数码管上。


程序设计思路

GP01 模块有 24 个接口,写代码的时候只需要关注 TX 引脚,GP-01 模块获得的数据送到 FPGA,使用状态机获取我们想要的年月日、实时时间。此时得到的时间是 UTC 时间,并不是我们想要的北京时间,我们只需要在小时上加八,然后就把数据传到数码管显示就可以了。


代码

串口接收模块

把 GP01 的单比特数据转换成八比特的数据。

module  uart_rx#(    parameter   UART_BPS    =   'd9600,         //串口波特率    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率)(    input   wire            sys_clk     ,   //系统时钟50MHz    input   wire            sys_rst_n   ,   //全局复位    input   wire            rx          ,   //串口接收数据
output reg [7:0] po_data , //串转并后的8bit数据 output reg po_flag //串转并后的数据有效标志信号);
//********************************************************************////****************** Parameter and Internal Signal *******************////********************************************************************////localparam definelocalparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
//reg definereg rx_reg1 ;reg rx_reg2 ;reg rx_reg3 ;reg start_nedge ;reg work_en ;reg [12:0] baud_cnt ;reg bit_flag ;reg [3:0] bit_cnt ;reg [7:0] rx_data ;reg rx_flag ;
//********************************************************************////***************************** Main Code ****************************////********************************************************************////插入两级寄存器进行数据同步,用来消除亚稳态//rx_reg1:第一级寄存器,寄存器空闲状态复位为1always@(posedge sys_clk or negedge sys_rst_n) if (sys_rst_n == 1'b0) rx_reg1 <= 1'b1; else rx_reg1 <= rx;
//rx_reg2:第二级寄存器,寄存器空闲状态复位为1always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg2 <= 1'b1; else rx_reg2 <= rx_reg1;
//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg3 <= 1'b1; else rx_reg3 <= rx_reg2;
//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) start_nedge <= 1'b0; else if((~rx_reg2) && (rx_reg3)) start_nedge <= 1'b1; else start_nedge <= 1'b0;
//work_en:接收数据工作使能信号always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) work_en <= 1'b0; else if(start_nedge == 1'b1) work_en <= 1'b1; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) work_en <= 1'b0;
//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1;
//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,//此时拉高一个标志信号表示数据可以被取走always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == BAUD_CNT_MAX/2 - 1) bit_flag <= 1'b1; else bit_flag <= 1'b0;
//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)//都接收完成后计数器清零always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) bit_cnt <= 4'b0; else if(bit_flag ==1'b1) bit_cnt <= bit_cnt + 1'b1;
//rx_data:输入数据进行移位always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_data <= 8'b0; else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1)) rx_data <= {rx_reg3, rx_data[7:1]};
//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_flag <= 1'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) rx_flag <= 1'b1; else rx_flag <= 1'b0;
//po_data:输出完整的8位有效数据always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_data <= 8'b0; else if(rx_flag == 1'b1) po_data <= rx_data;
//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= rx_flag;
endmodule


获取日历、时间模块

因为 GP01 模块获取的数据不光只有日历和时间数据,所以需要用一个状态机来判断接收到的数据是不是日历、时间数据。


EMEA 协议中的时间与日历信息格式如图所示:

module Data_processing (    input   wire            sys_clk       ,    input   wire            sys_rst_n     ,     input   wire            uart_recv_done,    input   wire  [7:0]     uart_recv_data,
output reg [31:0] time_data , output reg [31:0] calendar_data , output reg led3 , output reg led2 , output reg led
); reg [8:0] state ; reg [31:0] time_data_temp; reg [31:0] calendar_temp ; wire [7:0] recv_time ; wire [7:0] recv_calendar ;
parameter WATI = 9'b0000_0000_0; parameter IDLE = 9'b0000_0000_1; parameter ONE = 9'b0000_0001_0; parameter TWO = 9'b0000_0010_0; parameter THREE= 9'b0000_0100_0; parameter FOUR = 9'b0000_1000_0; parameter FIVE = 9'b0001_0000_0; parameter SIX = 9'b0010_0000_0; parameter SIX_1 = 9'b0010_0000_1; parameter SIX_2 = 9'b0010_0001_0; parameter SIX_3 = 9'b0010_0001_1; parameter SIX_4 = 9'b0010_0010_0; parameter SIX_5 = 9'b0010_0010_1; parameter SIX_6 = 9'b0010_0011_0; parameter SIX_7 = 9'b0010_0011_1; parameter SIX_8 = 9'b0010_0100_0; parameter SIX_9 = 9'b0010_0100_1; parameter SEVEN = 9'b0100_0000_0; parameter SEVEN_1 = 9'b0100_0000_1; parameter SEVEN_2 = 9'b0100_0001_0; parameter SEVEN_3 = 9'b0100_0001_1; parameter SEVEN_4 = 9'b0100_0010_0; parameter SEVEN_5 = 9'b0100_0010_1; parameter SEVEN_6 = 9'b0100_0011_0; parameter SEVEN_7 = 9'b0100_0011_1; parameter SEVEN_8 = 9'b0100_0100_0; parameter SEVEN_9 = 9'b0100_0100_1; parameter SEVEN_10 = 9'b0100_1001_0; parameter EIGHT= 9'b1000_0000_0 ; parameter TEN = 9'b1000_0000_1 ; parameter CNT_MAX = 23'd7_500_000 ;
assign recv_time = uart_recv_data - 8'd48; //ascll码 0-9 assign recv_calendar = uart_recv_data - 8'd48;

always @(posedge sys_clk or negedge sys_rst_n) begin //这里的状态机用来接收日历和时间 if (sys_rst_n == 1'b0) begin state <= WATI; end else if( uart_recv_done == 1'd1) begin case (state) WATI : begin if ((uart_recv_data == 8'h24)) state <= IDLE ; else if ((uart_recv_data == 8'h47) ) state <= ONE; else state <= WATI; //固定格式 $GPZDA, end IDLE : state <= ((uart_recv_data == 8'h47)) ? ONE : WATI; ONE : state <= ((uart_recv_data == 8'h4E)) ? TWO : WATI; TWO : state <= ((uart_recv_data == 8'h5A)) ? THREE : WATI; THREE : state <= ((uart_recv_data == 8'h44)) ? FOUR : WATI; FOUR : state <= ((uart_recv_data == 8'h41)) ? FIVE : WATI; FIVE : state <= ((uart_recv_data == 8'h2C)) ? SIX : WATI; SIX : state <= ((uart_recv_data != 8'h2C)) ? SIX_1 : WATI; //时间 // SIX_1 : state <= ((uart_recv_data != 8'h2C)) ? SIX_2 : WATI; SIX_2 : state <= ((uart_recv_data != 8'h2C)) ? SIX_3 : WATI; SIX_3 : state <= ((uart_recv_data != 8'h2C)) ? SIX_4 : WATI; SIX_4 : state <= ((uart_recv_data != 8'h2C)) ? SIX_5 : WATI; SIX_5 : state <= ((uart_recv_data != 8'h2C)) ? SIX_6 : WATI; SIX_6 : state <= ((uart_recv_data == 8'h2E)) ? SIX_7 : WATI; //. SIX_7 : state <= ((uart_recv_data == 8'h30)) ? SIX_8 : WATI; //000 SIX_8 : state <= ((uart_recv_data == 8'h30)) ? SIX_9 : WATI; SIX_9 : state <= ((uart_recv_data == 8'h30)) ? SEVEN : WATI; SEVEN : state <= ((uart_recv_data == 8'h2C) ) ? SEVEN_1 : WATI; //, SEVEN_1 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_2 : WATI; //日 SEVEN_2 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_3 : WATI; SEVEN_3 : state <= ((uart_recv_data == 8'h2C) ) ? SEVEN_4 : WATI; SEVEN_4 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_5 : WATI; //月 SEVEN_5 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_6 : WATI; SEVEN_6 : state <= ((uart_recv_data == 8'h2C) ) ? SEVEN_7 : WATI; SEVEN_7 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_8 : WATI; //年 SEVEN_8 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_9 : WATI; SEVEN_9 : state <= ((uart_recv_data != 8'h2C) ) ? SEVEN_10 : WATI; SEVEN_10 : state <= ((uart_recv_data != 8'h2C) ) ? EIGHT : WATI; EIGHT : state <= ((uart_recv_data == 8'h2C) ) ? TEN : WATI; TEN : state <= ((uart_recv_data == 8'h30) ) ? WATI : WATI; default: state <= WATI; endcase end end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin led <= 1'b0; end else if((state == FIVE) && (uart_recv_done == 1'b1)) led <= ~led; else led <= led; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin led2 <= 1'b0; end else if((state == SEVEN) && (uart_recv_done == 1'b1)) led2 <= ~led2; else led2 <= led2; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin led3 <= 1'b0; end else if((state == EIGHT) && (uart_recv_done == 1'b1)) led3 <= ~led3; else led3 <= led3; end
always @(posedge sys_clk or negedge sys_rst_n) begin // if (sys_rst_n == 1'b0) time_data_temp <= 32'b0; else if(state == SIX ) time_data_temp <= {recv_time[3:0],time_data_temp[27:0]}; else if(state == SIX_1 ) time_data_temp <= {time_data_temp[31:28],recv_time[3:0],time_data_temp[23:0]}; else if(state == SIX_2 ) time_data_temp <= {time_data_temp[31:20],recv_time[3:0],time_data_temp[15:0]}; else if(state == SIX_3 ) time_data_temp <= {time_data_temp[31:16],recv_time[3:0],time_data_temp[11:0]}; else if(state == SIX_4 ) time_data_temp <= {time_data_temp[31:8],recv_time[3:0],time_data_temp[3:0]}; else if(state == SIX_5 ) time_data_temp <= {time_data_temp[31:4],recv_time[3:0]}; else time_data_temp <= {time_data_temp[31:24],4'd10,time_data_temp[19:12],4'd10,time_data_temp[7:0]}; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) calendar_temp <= 32'd0; else if(state == SEVEN_1) calendar_temp <= {calendar_temp[31:8],recv_calendar[3:0],calendar_temp[3:0]}; else if(state == SEVEN_2) calendar_temp <= {calendar_temp[31:4],recv_calendar[3:0]}; else if(state == SEVEN_4) calendar_temp <= {calendar_temp[31:16],recv_calendar[3:0],calendar_temp[11:0]}; else if(state == SEVEN_5) calendar_temp <= {calendar_temp[31:12],recv_calendar[3:0],calendar_temp[7:0]}; else if(state == SEVEN_7) calendar_temp <= {recv_calendar[3:0],calendar_temp[27:0]}; else if(state == SEVEN_8) calendar_temp <= {calendar_temp[31:28],recv_calendar[3:0],calendar_temp[23:0]}; else if(state == SEVEN_9) calendar_temp <= {calendar_temp[31:24],recv_calendar[3:0],calendar_temp[19:0]}; else if(state == SEVEN_10) calendar_temp <= {calendar_temp[31:20],recv_calendar[3:0],calendar_temp[15:0]}; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin calendar_data <= 32'b0; end else if((state == TEN) && (uart_recv_done == 1'b1) ) calendar_data <= calendar_temp; else calendar_data <= calendar_data; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin time_data <= 32'b0; end else if((state == SEVEN) && (uart_recv_done == 1'b1) ) time_data <= time_data_temp; else time_data <= time_data; end
endmodule //Data_processing


UTC 处理模块

这里是将 UTC 时间转换成北京时间,日历与时间每隔五秒切换一次。

module display (    input   wire            sys_clk       ,    input   wire            sys_rst_n     ,     input   wire    [31:0]  time_data     ,    input   wire    [31:0]  calendar_data ,
output wire [31:0] display_data ); localparam COUNT_MAX = 50_000_000 ; localparam CNT_10S_MAX = 6'd5 ;
reg [26:0] count ; reg [3:0] cnt_500ms ; reg display_flag ; reg [31:0] time_data_temp ;
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin count <= 0; end else begin if (count == COUNT_MAX - 1) begin count <= 0; end else begin count <= count + 27'd1; end end end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin time_data_temp <= 32'd0; end else if (display_flag == 1'd1 && ((time_data[27:24] + 4'd8) >= 4'd10 ))begin //因为获取的时间是UTC时间转换成北京时间加上八就可以了 time_data_temp[31:28] <= time_data[31:28] + 4'd1; time_data_temp[27:24] <= (time_data[27:24] + 4'd8) % 4'd10; end else if (display_flag == 1'd1 && time_data[27:24] + 4'd8 < 4'd10 )begin time_data_temp[31:28] <= time_data[31:28]; time_data_temp[27:24] <= time_data[27:24]+ 4'd8; end else time_data_temp <= {time_data_temp[31:24],time_data[23:0]}; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin cnt_500ms <= 4'd0; end else if (cnt_500ms == CNT_10S_MAX) //5S cnt_500ms <= 4'd0; else if (count == COUNT_MAX - 1) cnt_500ms <= cnt_500ms + 4'd1; end
always @(posedge sys_clk or negedge sys_rst_n) begin if (sys_rst_n == 1'b0) begin display_flag <= 1'd0; end else if (cnt_500ms == CNT_10S_MAX) begin display_flag <= ~display_flag; end else display_flag <= display_flag; end
assign display_data = display_flag ? calendar_data :time_data_temp;endmodule //display


数码管显示模块

module smg (    input   wire            sys_clk     ,    input   wire            sys_rst_n   ,    input   wire  [31:0]    smg_seg_in  ,
output reg [7:0] smg_sel , output reg [7:0] smg_seg); localparam CNT_1MS_MAX = 16'd50_000 ;
reg [15:0] cnt_1ms ; reg [3:0] flag_1ms ; reg [3:0] data_disp ; always @(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n==1'b0) cnt_1ms<=16'b0; else if (cnt_1ms==CNT_1MS_MAX-1) cnt_1ms<=16'b0; else cnt_1ms<=cnt_1ms+16'b1; end





请到「今天看啥」查看全文