项目描述及要实现的功能:
-
实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
-
实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
-
定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
-
PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
-
音频文件播放完毕,OLED开始更新时间信息和当前的温度信息
完成的功能:(视频的演示部分已更新)
1、精确到分钟微秒的定时时钟、oled显示(RAM字库)、整点报警
2、温度采集、oled显示(RAM字库)
3、音乐播放(RAM读取)
4、串口接收温度数据、报警信号,发送oled初始化命令、字库信息
使用的资源:
这里将可以共用的寄存器全部合并了,并删除了微秒和秒的显示,暂且提高了资源利用率,后续还得多参考大佬们的代码,学习verilog的硬件编程的思路路漫漫其修远兮
1、一个pll:c0=1.8432mhz
c1=1mhz
c2=880khz
c3=115.2khz
2、RAM: 32768bit
3、LCS: 1591
4、寄存器使用:658
详细使用情况:
接下来是实现的思路
实现思路:
根据要求分析出需要的功能:
1、定时时钟
2、温度采集 + bcd转码
3、显示
4、uart接收、发送
5、上位机接收、发送
整体框架:
整个任务是围绕地三个功能来进行的,以第三个功能搭建框架:
上电后还要初始化显示屏:
接下来是细的步骤
初始化显示屏:
oled显示的细分步骤:
这里将要显示的数据放在专用寄存器内,然后通过地址加法将对应的字库地址换算出来:
对应字库地址 = (寄存器数据 << 4) + 12'd537 12'd537是‘0’字符的首地址
接下来直接上代码:
主循环 + 小任务
case (state)
UART_RECV:begin
ram_sw <= '1;
mode <=1'b0 ;
case (ck)
0:if(!rdy_r) begin EN <= '1; ck <= ck; end
else begin wren <= '1; ck <= ck + 1'b1; end
1:begin if(rdy_r) begin ck <= ck; end
else begin wren <= '0; ck <= ck + 1'b1; end
end
2:if(address == address_end)begin state_next <= state_back[state_pointer - 1'b1] ; state_pointer <= state_pointer - 1'd1; ck <= '0; EN <= '0; end
else begin address <= address + 1'b1 ; ck <= '0;end
endcase
end //UART_RECV
OLED_INIT:begin
case (ck)
0:begin oled_res_n <= '0;
delay_us <= 10'd1023 ; state_next <= DELAY ; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'd1;
ck <= ck + 1'b1;
end
1:begin
oled_res_n <= '1;
delay_us <= 10'd1023 ; state_next <= DELAY ; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'd1; //100ms
ck <= ck + 1'b1;
end
2:begin
oled_dc_n <= '0;
state_next <= SPI_SEND_RAM ; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'd1; ck <= ck + 1'b1;
send_cnt <= 16'd25 ; address <= '0 ;
end
3:begin R0<='0;R1<='0;R2<=8'd127;R3<=8'd3; spi_send(CMD , CMD_SET_X); ck <= ck + 1'b1; end
4:begin spi_send( CMD , R0 ) ; ck <= ck + 1'b1 ; end
5:begin spi_send( CMD , R2 ) ; ck <= ck + 1'b1 ; end
6:begin spi_send( CMD , CMD_SET_Y ) ; ck <= ck + 1'b1 ; end
7:begin spi_send( CMD , R1 ) ; ck <= ck + 1'b1 ; end
8:begin spi_send( CMD , R3 ) ; ck <= ck + 1'b1 ; end
9:begin spi_send_ram(DATA , 12'd25 , 512) ;ck <= ck + 1'b1;end
10:begin state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck <= '0;end
endcase
end
MAIN:begin
case (ck0)
0:begin address <= '0; address_end <= 12'd1122; state_next <= UART_RECV; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1; end
1:begin state_next <= UART_SEND; send_cnt <= 16'd1123; address <= '0; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1; end
2:begin state_next <= OLED_INIT; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1; end
3:begin gen_xy(R4 , 0 , SIZE8); spi_send(CMD , CMD_SET_X) ; R4 <= R4 + 8'd8 ; ck0 <= ck0 + 1'b1 ;end
4:begin spi_send(CMD , R0 ) ; ck0 <= ck0 + 1'b1 ; end
5:begin spi_send(CMD , R2 ) ; ck0 <= ck0 + 1'b1 ; end
6:begin spi_send(CMD , CMD_SET_Y) ; ck0 <= ck0 + 1'b1 ; end
7:begin spi_send(CMD , R1 ) ; ck0 <= ck0 + 1'b1 ; end
8:begin spi_send(CMD , R3 ) ; ck0 <= ck0 + 1'b1 ; end
9:begin spi_send_ram(DATA , clock[clock_ck], SIZE8_16) ; clock_ck <= clock_ck - 1'b1 ; ck0 <= ck0 + 1'b1 ; end
10:begin if(timer_alarm) begin state_next <= UART_SEND ; address <= 1093; send_cnt <= 16'd30; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1 ; ck <= '0; end else begin ck0 <= 5'd13; end end
11:begin state_next <= UART_RECV; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; address <= 12'd889; address_end <= 12'd1060; ck0 <= ck0 + 1'b1; end
//12:begin state_next <= UART_SEND; send_cnt <= 16'd172; address <= 12'd889; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1; end
12:begin state_next <= MUSIC; address <= 12'd889; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; ck0 <= ck0 + 1'b1; end
13:begin gen_xy(R6 , 2 , SIZE8); spi_send(CMD , CMD_SET_X); R6 <= R6 + 8'd8; ck0 <= ck0 + 1'b1; end
14:begin spi_send(CMD , R0 ) ; ck0 <= ck0 + 1'b1 ; end
15:begin spi_send(CMD , R2 ) ; ck0 <= ck0 + 1'b1 ; end
16:begin spi_send(CMD , CMD_SET_Y) ; ck0 <= ck0 + 1'b1 ; end
17:begin spi_send(CMD , R1 ) ; ck0 <= ck0 + 1'b1 ; end
18:begin spi_send(CMD , R3 ) ; ck0 <= ck0 + 1'b1 ; end
19:begin ram_write(12'd1093 , 2'd0) ; R0 <= ds18b20_out[15:8] ; t_ad <= bcd_buff[bcd_ad]; ck0 <= ck0 + 1'b1 ; end
20:begin ram_write(12'd1094 , 2'd0) ; R0 <= ds18b20_out[7:0] ; t_ad <= t_ad<<4; ck0 <= ck0 + 1'b1 ; end
21:begin address <= 12'd1092; send_cnt <= 16'd3; state_next <= UART_SEND; state_back[state_pointer] <= state ; state_pointer <= state_pointer + 1'b1 ; t_ad <= t_ad + 12'd537; ck0 <= ck0 + 1'b1 ; end
22:begin spi_send_ram(DATA , t_ad , SIZE8_16); bcd_ad <= bcd_ad - 1'd1; ck0 <= ck0 + 1'b1 ; end
23:begin if(bcd_ad == 2'd3) begin R6 <= '0; ck0 <= ck0 + 1'b1 ; end else begin ck0 <= 5'd13; end end
24:begin gen_xy(32 , 2 , SIZE16); spi_send(CMD , CMD_SET_X); ck0 <= ck0 + 1'b1 ; end
25:begin spi_send(CMD , R0 ) ; ck0 <= ck0 + 1'b1 ; end
26:begin spi_send(CMD , R2 ) ; ck0 <= ck0 + 1'b1 ; end
27:begin spi_send(CMD , CMD_SET_Y) ; ck0 <= ck0 + 1'b1 ; end
28:begin spi_send(CMD , R1 ) ; ck0 <= ck0 + 1'b1 ; end
29:begin spi_send(CMD , R3 ) ; ck0 <= ck0 + 1'b1 ; end
30:begin spi_send_ram(DATA , 12'd1061 , SIZE16_16); ck0 <= 5'd3; end
default:R4 = '0;
endcase
end
MUSIC:begin
if(address != 12'd1061)
case (ck)
0:begin ram_read(0) ; ck <= ck + 1'b1 ; music_ld <= '1; end
1:begin ram_read(1) ; address <= address + 1'b1; ck <= ck + 1'b1 ; end
2:begin time_fpn <= { R0 , R1 } ; ck <= ck + 1'b1 ; end
3:begin music_ld <= '0 ; ck <= ck + 1'b1 ; end
//5:begin if( address == MUSIC_LEN ) begin music_en <= '0 ; state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck <= 3'd0 ; end
// else begin ck <= 3'd6 ; end end
4:begin if(!upd ) begin music_ld <= '1 ; ck <= ck + 1'b1; end else begin ck <= ck; end end
5:begin if( upd ) begin music_ld <= '1 ; ck <= ck + 1'b1; end
else begin music_ld <= '1 ; ck <= ck ; address <= address; end end
6:begin address <= address + 1'd1 ; music_ld <= '1; ck <= '0; end
endcase
else begin state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; music_ld <= '0; ck <= '0; end
end
UART_SEND:begin
mode <= 1'd1 ;
case (ck)
0:begin rden <= 1'b1 ; ck <= ck + 1'b1 ; end
1:begin if(!rdy) begin EN <= 1'b1 ; end
else begin address <= address + 1'b1 ; ck <= ck + 1'b1 ; end
end
2:begin if( rdy) begin ck <= ck ; end
else begin send_cnt <= send_cnt - 1'b1 ; ck <= ck + 1'b1 ; end
end
3:begin if(send_cnt != 16'd0) begin ck <= '0; end
else begin
state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck <= '0 ;
EN <= 1'b0 ;
wren <= 1'b0 ; rden <= 1'b0 ; address <= 12'h000 ; end
end
endcase
end//MAIN
SPI_SEND_RAM:begin
case (ck_spi_send)
0:begin oled_cs_n <= '0 ; rden <= 1'b1 ; ck_spi_send <= ck_spi_send + 1'b1 ; end
1:begin if(!rdy_spi_tx) begin en <= 1'b1 ; end
else begin address <= address + 1'b1 ; ck_spi_send <= ck_spi_send + 1'b1 ; end
end
2:begin if( rdy_spi_tx) begin oled_cs_n <= '1 ; ck_spi_send <= ck_spi_send; end
else begin send_cnt <= send_cnt - 1'b1 ; ck_spi_send <= ck_spi_send + 1'b1 ; end
end
3:begin if(send_cnt == 16'd0) begin
state_next <= state_back[ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; rden <= 1'b0 ; address <= 12'h000 ;
send_cnt <= 16'd0 ;
en <= 1'b0 ;
ck_spi_send <= '0 ;
wren <= 1'b0 ; end
else begin ck_spi_send <= '0; end
end
endcase
end
SPI_SEND:begin
case (ck_spi_send)
0:if( ~rdy_spi_tx ) begin oled_cs_n <= '0 ; spi_sw <= '0; en <= '1 ; end
else begin ck_spi_send <= ck_spi_send + 1'b1 ; end
1:begin if( rdy_spi_tx ) begin oled_cs_n <= '1 ; ck_spi_send <= ck_spi_send ; end
else begin en <= '0 ; state_next <= state_back[ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck_spi_send <= '0 ; end
end
endcase
end
DELAY:begin
case ( ck1 )
0:begin timer_us_en <= '1 ; ck1 <= ck1 + 1'b1 ; end
1:if( timer_us < delay_us ) begin ck1 <= ck1 ; end
else begin timer_us_en <= '0 ; state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck1 <= '0; end
endcase
end
RAM_READ:begin
case ( ck2 )
0:begin rden <= '1 ; ck2 <= ck2+1'b1 ; end
1:begin
state_next <= state_back [ state_pointer - 1'b1 ] ; state_pointer <= state_pointer - 1'b1 ; ck2 <= '0;
rden <= '0 ;
case(R_)
0: R0 <= q ;
1: R1 <= q ;
2: R2 <= q ;
3: R3 <= q ;
default : ;
endcase
end
default : ;
endcase
end
RAM_WRITE:begin
case (ck2)
0:begin ram_sw <= '0 ; wren <= '1 ; ck2 <= ck2 + 1'b1 ;
case(R_)
0: ram_in <= R0 ;
1: ram_in <= R1 ;
2: ram_in <= R2 ;
3: ram_in <= R3 ;
default : ;
endcase
end
1:begin
state_next <= state_back[state_pointer - 1'b1] ; state_pointer <= state_pointer - 1'b1 ; ck2 <= '0;
wren <= '0 ;
end
endcase
end
default : begin ; end
endcase//STATE
end//end else
end//always
计时时钟、spi驱动、uart驱动、温度采集的代码见附件
遇到的困难:
1、还未领略硬件编程的精髓
2、起初蜂鸣器发烫,后来把输出寄存器的空闲状态调整为1‘b0,就能正常测到室温了
2、由于RAM读取的时序不清楚,导致前期,无法读出数据
改进计划:
1、下步计划重新优化结构,提升系统稳定性
2、优化状态机,减少多余状态,提高资源利用率
将来的打算与目前正在做的东西:
1、加入ps2接口,以便键盘输入命令(已实现)
2、设计基于spi协议的lcd驱动
3、基于lcd实现俄罗斯方块
对板卡的一点小建议:
1、加入外部ROM
2、丰富各种接口
3、加入LCD接口