18
2021
02

FPGA ALARM FPGA多功能闹钟 完整项目 内含上位机

一、项目简述

本项目使用苏州硬禾信息科技有限公司设计的小脚丫FPGA开发板设计了一个完成定时、测温、报警、控制的小项目,并通过上位机显示、下发音乐配置数据。


二、研究进展


(一)研究内容:

实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;

实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;

定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;

PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;

音频文件播放完毕,OLED开始更新时间信息和当前的温度信息


(二)研究方法

查找资料,从原理层研究如何实现功能。

比较、寻找能够符合项目需求的方案。

自顶向下编程实现功能。

综合测试系统性能和参数。

开发上位机软件,根据需求显示数据并下发音乐数据


(三)取得的成果

项目需求全部实现,软件开发也已完成。

image.png 

2.1 上位机助手截图

image.png 

2.2 开发板演示照片

烧录文件和上位机.7z

(四)核心代码展示

//==========================================================================
// Author: ChanRa1n
// Description: Alarm  for Step Training Board with Step-max10
// Web: www.myfpga.cn
//==========================================================================
module clock(clk_in,data_time_a,data_time_b,data_time_c,data_time_d,pause,tone_en,tone,rx_Sig,rx_data,key);//总程序
 
input clk_in;
 
//OLED
//时间按照AB:CD的格式
output reg [3:0] data_time_a;
output reg [3:0] data_time_b;
output reg [3:0] data_time_c;
output reg [3:0] data_time_d;
inout  reg pause;//暂停更新标志位
 
//PWM
output reg tone_en;  //蜂鸣器使能信号
output reg [4:0] tone; //蜂鸣器音节控制
 
//UART
input  [1:0] rx_Sig;//00内置音乐 11开始 10结束
input  [7:0] rx_data;
 
//private
reg[5:0] time_min;
reg[5:0] time_hour;
reg[8:0] time_sec;
reg[8:0] pwm_sec;
 
//KEY
input [3:0] key;//按键
 
initial
begin
 //OLED
 //时间按照AB:CD的格式
 data_time_a = 0;
 data_time_b = 5;
 data_time_c = 5;
 data_time_d = 9;
 
 //计时自05:59:50开始
 time_sec  = 250;
    time_min  = 59;
 time_hour = 5;
 pause = 0;
 
 //PWM
 tone_en=0;
 tone=1;
 pwm_sec  = 0;
 
end
 
always @(posedge clk_in)
begin
//过去了0.2s,增加计数
//时间按照AB:CD的格式
 time_sec<=time_sec+1;
 if(time_sec>=300)
 begin
 time_sec<=0;
 time_min<=time_min+1;
 end
 if(time_min>=60)
 begin
 time_min<=0;
 time_hour<=time_hour+1;
 end
 if(time_hour>=24)
 begin
 time_hour<=0;
 end
 
//检测按键
if(!key[0])time_hour<=time_hour+1;
if(!key[1])time_hour<=time_hour-1;
if(!key[2])time_min<=time_min+1;
if(!key[3])time_min<=time_min-1;
//刷新显示
//时间按照AB:CD的格式
data_time_a <= time_hour/10;
data_time_b <= time_hour%10;
data_time_c <=  time_min/10;
data_time_d <=  time_min%10;
//整点到
if(time_min==0 && time_sec==1)pause<=1;//如果分钟为0,并且秒数为0
if(pause && rx_Sig==2'b0)//使用内置音乐模式
begin
if(pause)
begin
tone_en<=1;//准备唱歌了
pwm_sec<=pwm_sec+1;//增加0.2秒
end
if(pwm_sec%5==0 && tone_en==1)//过去一秒
begin
tone<=(tone<=5'd21)?tone+1:1;//瞧这美丽动听的音乐
//咱也是有音乐细菌的
end
else if(pwm_sec>=35 && tone_en==1)//过去七秒,想要唱多久,此处就改为秒数*5
begin
tone_en<=0;//咱别唱了
pause  <=0;//继续计时
pwm_sec<=0;//为下次计时做准备
end
end
if(pause && rx_Sig==2'b11)//串口开始
begin
tone_en<=1;
tone[4:0]<=rx_data[4:0];
end
if(pause && rx_Sig==2'b10)//串口结束
begin
tone_en<=0;
tone<=0;
pause<=0;
end
end
Endmodule


该代码主要用于计时,并与蜂鸣器、串口、温度传感器进行通信。并没有采用严格的面向对象和模块化开发,降低了开发的难度和周期。

module uart_t(
           clk,   //12Mhz C1
           tx,    //ouptut  
  TX_En_Sig, 
  data_tx,
  TX_Done_Sig
);
input clk;
output reg tx;
input [7:0]data_tx;
reg [7:0]data_temp;
input    TX_En_Sig;
output  reg TX_Done_Sig;
 
//-------------------波特率控制------------
wire bps_start;
reg [3:0] tx_num;         // 2^4-1=15
always@(posedge clk)
begin
 if(tx_num==10)
         TX_Done_Sig<=1'b0; 
end
assign bps_start = TX_En_Sig;
//------------------产生波特率--------------
//9600
parameter bps_cnt=1250;    //12Mhz / 9600 = 12.5
parameter bps_cnt_half=625;
reg [12:0] cnt;
always@(posedge clk)
begin
    if(cnt==bps_cnt) cnt<=13'b0;
    else if(bps_start) cnt<=cnt+1'b1;
    else cnt<=13'b0;
end 
 
//---------------------发送数据--------------
//transport
always@(posedge clk)
begin 
if(cnt==bps_cnt_half)
       begin
if(TX_En_Sig && data_temp!=data_tx)
begin
 tx_num<=tx_num+1'b1;
 case(tx_num)
 0:tx<=1'b0;
 1:tx<=data_tx[0];
 2:tx<=data_tx[1];
 3:tx<=data_tx[2];
 4:tx<=data_tx[3];
 5:tx<=data_tx[4];
 6:tx<=data_tx[5];
 7:tx<=data_tx[6];
 8:tx<=data_tx[7];
 9:tx<=1'b1;
  endcase
 end
else tx<=1'b1;
    end
 else if(tx_num==10) begin tx_num<=4'b0; data_temp<=data_tx; end
end
 
endmodule


串口发送模块,将时间、温度信息发送给上位机。

module uart_r(
           clk,   //12Mhz
           rst_n, //reset
           rx,    //input     
           rx_data,
  rx_Sig
);
input clk,rst_n;
input rx;
output reg [7:0] rx_data;
output reg [1:0] rx_Sig;//00内置音乐 11开始 10结束
initial
 begin
rx_Sig<=2'b0;
 end
//-----------------检测是否有数据来--------
//边沿检测  
wire rx_start;
reg rx1;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) rx1<=1'b0;
    else rx1<=rx;
end
assign rx_start = ~rx & rx1;
 
//-------------------波特率控制------------
wire bps_start;
reg bps_start_rx;
reg [3:0] rx_num; 
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
       begin 
        bps_start_rx<=1'b0; 
       end
    else if(rx_start) bps_start_rx<=1'b1;
    //接收完数据后 开始发送数据
    else if(rx_num==10) 
       begin  
         bps_start_rx<=1'b0; 
       end
end
assign bps_start = bps_start_rx;
//------------------产生波特率--------------
//9600
parameter bps_cnt=1250;    //50Mhz / 9600 = 5208.3......
parameter bps_cnt_half=625;
reg [12:0] cnt;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) cnt<=13'b0;
    else if(cnt==bps_cnt) cnt<=13'b0;
    else if(bps_start) cnt<=cnt+1'b1;
    else cnt<=13'b0;
end 
 
//---------------------接收数据--------------
//receive
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n) 
     begin 
      rx_num<=4'b0; 
      rx_data<=8'b0; 
     end
    else if(cnt==bps_cnt_half)
     begin
if(bps_start_rx)
begin
 rx_num<=rx_num+1'b1;
 case(rx_num)
 1:rx_data[0]<=rx;
 2:rx_data[1]<=rx;
 3:rx_data[2]<=rx;
 4:rx_data[3]<=rx;
 5:rx_data[4]<=rx;
 6:rx_data[5]<=rx;
 7:rx_data[6]<=rx;
 8:rx_data[7]<=rx;
 9:begin if(rx_data==0) rx_Sig<=2'b10;else rx_Sig<=2'b11; end//如果收到了串口信息,则代表使用串口音乐
 endcase
end
 end
 else if(rx_num==10) rx_num<=4'b0;
 
end
endmodule


串口接收模块,用于将上位机发送的音乐配置信息发送至Clock模块,并产生声音。

module oled
(
inputClkIn,//12MHz系统时钟
inputrst_n,//系统复位,低有效
 
input[3:0]data_time_a,
input[3:0]data_time_b,
input[3:0]data_time_c,
input[3:0]data_time_d,
input[3:0]data_temp_a,
input[3:0]data_temp_b,
input[3:0]data_temp_c,
inputpause,//暂停更新标志位
//outputoled_busy,//没有设置显示忙标志位,因为刷新频率很高,看不到影响
//利用OLED RAM暂存,依次刷新数据
 
outputregOledCs,//OLCD液晶屏使能
outputregOledRes,//OLCD液晶屏复位
outputregOledDc,//OLCD数据指令控制
outputregOledClk,//OLCD时钟信号
outputregOledDin//OLCD数据信号
);
 
localparam INIT_DEPTH = 16'd25; //LCD初始化的命令的数量
localparam IDLE = 6'h1, MAIN = 6'h2, INIT = 6'h4, SCAN = 6'h8, WRITE = 6'h10, DELAY = 6'h20;
localparam HIGH= 1'b1, LOW = 1'b0;
localparam DATA= 1'b1, CMD = 1'b0;
 
reg [7:0] cmd [24:0];
reg [39:0] mem [122:0];
reg[7:0]y_p, x_ph, x_pl;
reg[(8*21-1):0] char;
reg[7:0]num, char_reg;//
reg[4:0]cnt_main, cnt_init, cnt_scan, cnt_write;
reg[15:0]num_delay, cnt_delay, cnt;
reg[5:0] state, state_back;
always@(posedge ClkIn or negedge rst_n) begin
if(!rst_n) 
begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
OledCs <= HIGH; OledRes <= HIGH; OledDc <= CMD; OledClk <= HIGH; OledDin <= LOW;
state <= IDLE; state_back <= IDLE;
end 
else
begin
case(state)
IDLE:begin
cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
OledCs <= HIGH; OledRes <= HIGH; OledDc <= CMD; OledClk <= HIGH; OledDin <= LOW;
state <= MAIN; state_back <= MAIN;
end
MAIN:begin
if(cnt_main >= 5'd11) cnt_main <= 5'd5;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)//MAIN状态
5'd0:begin state <= INIT; end
5'd1:begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "   FPGA ALARM   ";state <= SCAN; end
5'd2:begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "       :        ";state <= SCAN; end
5'd3:begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "       . *      ";state <= SCAN; end
5'd4:begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "MyFPGA  ChanRa1n";state <= SCAN; end
 
5'd5:begin if(!pause) begin y_p <= 8'hb1; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd 1; char <= data_time_a; state <= SCAN; end end
5'd6:begin if(!pause) begin y_p <= 8'hb1; x_ph <= 8'h13; x_pl <= 8'h00; num <= 5'd 1; char <= data_time_b; state <= SCAN; end end
5'd7:begin if(!pause) begin y_p <= 8'hb1; x_ph <= 8'h14; x_pl <= 8'h00; num <= 5'd 1; char <= data_time_c; state <= SCAN; end end
5'd8:begin if(!pause) begin y_p <= 8'hb1; x_ph <= 8'h15; x_pl <= 8'h00; num <= 5'd 1; char <= data_time_d; state <= SCAN; end end
5'd9:begin if(!pause) begin y_p <= 8'hb2; x_ph <= 8'h12; x_pl <= 8'h00; num <= 5'd 1; char <= data_temp_a; state <= SCAN; end end
5'd10:begin if(!pause) begin y_p <= 8'hb2; x_ph <= 8'h13; x_pl <= 8'h00; num <= 5'd 1; char <= data_temp_b; state <= SCAN; end end
5'd11:begin if(!pause) begin y_p <= 8'hb2; x_ph <= 8'h14; x_pl <= 8'h00; num <= 5'd 1; char <= data_temp_c; state <= SCAN; end end
 
default: state <= IDLE;
endcase
end
INIT:begin//初始化状态
case(cnt_init)
5'd0:begin OledRes <= LOW; cnt_init <= cnt_init + 1'b1; end//复位有效
5'd1:begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end//延时大于3us
5'd2:begin OledRes <= HIGH; cnt_init <= cnt_init + 1'b1; end//复位恢复
5'd3:begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end//延时大于220us
5'd4:begin 
if(cnt>=INIT_DEPTH) begin//当25条指令及数据发出后,配置完成
cnt <= 1'b0;
cnt_init <= cnt_init + 1'b1;
end else begin
cnt <= cnt + 1'b1; num_delay <= 16'd5;
OledDc <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
end
end
5'd5:begin cnt_init <= 1'b0; state <= MAIN; end//初始化完成,返回MAIN状态
default: state <= IDLE;
endcase
end
SCAN:begin//刷屏状态,从RAM中读取数据刷屏
if(cnt_scan == 5'd11) begin
if(num) cnt_scan <= 5'd3;
else cnt_scan <= cnt_scan + 1'b1;
end else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
else cnt_scan <= cnt_scan + 1'b1;
case(cnt_scan)
5'd 0:begin OledDc <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end//定位列页地址
5'd 1:begin OledDc <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end//定位行地址低位
5'd 2:begin OledDc <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end//定位行地址高位
 
5'd 3:begin num <= num - 1'b1;end
5'd 4:begin OledDc <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end//将5*8点阵编程8*8
5'd 5:begin OledDc <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end//将5*8点阵编程8*8
5'd 6:begin OledDc <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end//将5*8点阵编程8*8
5'd 7:begin OledDc <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
5'd 8:begin OledDc <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
5'd 9:begin OledDc <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
5'd10:begin OledDc <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
5'd11:begin OledDc <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
5'd12:begin state <= MAIN; end
default: state <= IDLE;
endcase
end
WRITE:begin//WRITE状态,将数据按照SPI时序发送给屏幕
if(cnt_write >= 5'd17) cnt_write <= 1'b0;
else cnt_write <= cnt_write + 1'b1;
case(cnt_write)
5'd 0:begin OledCs <= LOW; end//9位数据最高位为命令数据控制位
5'd 1:begin OledClk <= LOW; OledDin <= char_reg[7]; end//先发高位数据
5'd 2:begin OledClk <= HIGH; end
5'd 3:begin OledClk <= LOW; OledDin <= char_reg[6]; end
5'd 4:begin OledClk <= HIGH; end
5'd 5:begin OledClk <= LOW; OledDin <= char_reg[5]; end
5'd 6:begin OledClk <= HIGH; end
5'd 7:begin OledClk <= LOW; OledDin <= char_reg[4]; end
5'd 8:begin OledClk <= HIGH; end
5'd 9:begin OledClk <= LOW; OledDin <= char_reg[3]; end
5'd10:begin OledClk <= HIGH; end
5'd11:begin OledClk <= LOW; OledDin <= char_reg[2]; end
5'd12:begin OledClk <= HIGH; end
5'd13:begin OledClk <= LOW; OledDin <= char_reg[1]; end
5'd14:begin OledClk <= HIGH; end
5'd15:begin OledClk <= LOW; OledDin <= char_reg[0]; end//后发低位数据
5'd16:begin OledClk <= HIGH; end
5'd17:begin OledCs <= HIGH; state <= DELAY; end//
default: state <= IDLE;
endcase
end
DELAY:begin//延时状态
if(cnt_delay >= num_delay) begin
cnt_delay <= 16'd0; state <= state_back; 
end else cnt_delay <= cnt_delay + 1'b1;
end
default:state <= IDLE;
endcase
end
end
 
//OLED配置指令数据
always@(posedge rst_n)
begin
cmd[ 0] = {8'hae}; 
cmd[ 1] = {8'h00}; 
cmd[ 2] = {8'h10}; 
cmd[ 3] = {8'h00}; 
cmd[ 4] = {8'hb0}; 
cmd[ 5] = {8'h81}; 
cmd[ 6] = {8'hff}; 
cmd[ 7] = {8'ha1}; 
cmd[ 8] = {8'ha6}; 
cmd[ 9] = {8'ha8}; 
cmd[10] = {8'h1f}; 
cmd[11] = {8'hc8};
cmd[12] = {8'hd3};
cmd[13] = {8'h00};
cmd[14] = {8'hd5};
cmd[15] = {8'h80};
cmd[16] = {8'hd9};
cmd[17] = {8'h1f};
cmd[18] = {8'hda};
cmd[19] = {8'h00};
cmd[20] = {8'hdb};
cmd[21] = {8'h40};
cmd[22] = {8'h8d};
cmd[23] = {8'h14};
cmd[24] = {8'haf};
end 
 
//5*8点阵字库数据
always@(posedge rst_n)
begin
mem[  0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
mem[  1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
mem[  2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
mem[  3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
mem[  4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
mem[  5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
mem[  6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
mem[  7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
mem[  8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
mem[  9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F
 
mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00};   // 32  sp 
mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00};   // 33  !  
mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00};   // 34  
mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14};   // 35  #
mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12};   // 36  $
mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23};   // 37  %
mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50};   // 38  &
mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00};   // 39  '
mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00};   // 40  (
mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00};   // 41  )
mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14};   // 42  *
mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08};   // 43  +
mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00};   // 44  ,
mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08};   // 45  -
mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00};   // 46  .
mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02};   // 47  /
mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00};   // 58  :
mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00};   // 59  ;
mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00};   // 60  <
mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14};   // 61  =
mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08};   // 62  >
mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06};   // 63  ?
mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E};   // 64  @
mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F
mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A};   // 71  G
mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F};   // 72  H
mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00};   // 73  I
mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01};   // 74  J
mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41};   // 75  K
mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40};   // 76  L
mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F};   // 77  M
mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F};   // 78  N
mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E};   // 79  O
mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06};   // 80  P
mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E};   // 81  Q
mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46};   // 82  R
mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31};   // 83  S
mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01};   // 84  T
mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F};   // 85  U
mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F};   // 86  V
mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F};   // 87  W
mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63};   // 88  X
mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07};   // 89  Y
mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43};   // 90  Z
mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00};   // 91  [
mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55};   // 92  .
mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00};   // 93  ]
mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04};   // 94  ^
mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40};   // 95  _
mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00};   // 96  '
mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78};   // 97  a
mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38};   // 98  b
mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20};   // 99  c
mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F};   // 100 d
mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18};   // 101 e
mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02};   // 102 f
mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C};   // 103 g
mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78};   // 104 h
mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00};   // 105 i
mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00};   // 106 j
mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00};   // 107 k
mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00};   // 108 l
mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78};   // 109 m
mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78};   // 110 n
mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38};   // 111 o
mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18};   // 112 p
mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC};   // 113 q
mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08};   // 114 r
mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20};   // 115 s
mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20};   // 116 t
mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C};   // 117 u
mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C};   // 118 v
mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C};   // 119 w
mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44};   // 120 x
mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C};   // 121 y
mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44};   // 122 z
end
 
endmodule


        Oled代码根据电子森林内提供的示例代码进行了少许的修改,能够正常、稳定地显示数据。

三、项目实施经验

    串口模块非常容易产生误码,并且不适宜进行长时间的持续通信,我认为可以采用的方法有两种,一种是增加冗余信息,校验数据。另外一种是降低通信频率,减少积累效应。

    我采用的方法是降低通信频率并重复发送数据,每0.2秒发送一次数据,降低了积累的延迟导致的数据错误问题。事实证明这种方法非常有效,数据非常稳定。


« 上一篇 下一篇 »

返回顶部
请先 登录 再评论,若不是会员请先 注册