1.硬件设计及调试结果
焊接与调试的感悟:一定要考虑焊接的顺序。在原理图,pcb设计以及打板完成之后,首先要解决的是有关一些基本元器件的(如阻值,电容值的选取)。焊接时,要先焊接贴片式的电阻和电容,先焊接小型的贴片元器件,然后焊接一些直插式的元器件,然后焊接usb以及ch340等。最后加入排针将核心板插入。
在后期调试每一个模块并验证其设计的正确性时,对于10路的DA,一定要满足R2R的关系,这对于后期调出波形发生器有很重要的影响。如果不满足此关系。对于后期正弦波信号,调试过程中,如果不满足此关系。波形就会不平滑甚至出现错误。
2.蜂鸣器发出音乐的设计流程
1.设计的代码
module Beeper
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
//input tone_en, //蜂鸣器使能信号
//input [4:0] tone, //蜂鸣器音节控制
output reg piano_out //蜂鸣器控制输出
);
wire clk2;
reg [2:0] jishu;
reg [4:0] tone;
reg [5:0] cnt;
reg [15:0] time_end;
parameter t1=1;
parameter t2=2;
parameter t3=3;
parameter t4=4;
divide #(.WIDTH(24),.N(3000000)) u1
(
.clk(clk_in),
.rst_n(rst_n_in),
.clkout(clk2)
);
always@(tone) begin
case(tone)
5'd1: time_end = 16'd22935; //L1,
5'd2: time_end = 16'd20428; //L2,
5'd3: time_end = 16'd18203; //L3,
5'd4: time_end = 16'd17181; //L4,
5'd5: time_end = 16'd15305; //L5,
5'd6: time_end = 16'd13635; //L6,
5'd7: time_end = 16'd12147; //L7,
5'd8: time_end = 16'd11464; //M1,
5'd9: time_end = 16'd10215; //M2,
5'd10: time_end = 16'd9100; //M3,
5'd11: time_end = 16'd8589; //M4,
5'd12: time_end = 16'd7652; //M5,
5'd13: time_end = 16'd6817; //M6,
5'd14: time_end = 16'd6073; //M7,
5'd15: time_end = 16'd5740; //H1,
5'd16: time_end = 16'd5107; //H2,
5'd17: time_end = 16'd4549; //H3,
5'd18: time_end = 16'd4294; //H4,
5'd19: time_end = 16'd3825; //H5,
5'd20: time_end = 16'd3408; //H6,
5'd21: time_end = 16'd3036; //H7,
default:time_end = 16'd65535;
endcase
end
reg [17:0] time_cnt;
//当蜂鸣器,计数器按照计数终值(分频系数)计数
always@(posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in)
begin
time_cnt <= 1'b0;
end
else
if(time_cnt>=time_end)
begin
time_cnt <= 1'b0;
end
else
begin
time_cnt <= time_cnt + 1'b1;
end
end
//根据计数器的周期,翻转蜂鸣器控制信号
always@(posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in)
begin
piano_out <= 1'b0;
end
else if(time_cnt==time_end)
begin
piano_out <= ~piano_out; //蜂鸣器控制输出翻转,两次翻转为1Hz
end
else begin
piano_out <= piano_out;
end
end
always @ (posedge clk2,negedge rst_n_in)
begin
if(!rst_n_in)
begin
tone<=5'd0;
cnt<=6'h0;
end
else
begin
if(cnt==6'd42)
cnt<=6'h0;
else
cnt<=cnt+1'b1;
case(cnt)
6'd1:tone<=5'd5;
6'd2:tone<=5'd5;
6'd3:tone<=5'd6;
6'd4:tone<=5'd6;
6'd5:tone<=5'd5;
6'd6:tone<=5'd5;
6'd7:tone<=5'd8;
6'd8:tone<=5'd8;
6'd9:tone<=5'd7;
6'd10:tone<=5'd7;
6'd11:tone<=5'd5;
6'd12:tone<=5'd5;
6'd13:tone<=5'd6;
6'd14:tone<=5'd6;
6'd15:tone<=5'd5;
6'd16:tone<=5'd5;
6'd17:tone<=5'd9;
6'd18:tone<=5'd9;
6'd19:tone<=5'd8;
6'd20:tone<=5'd8;
6'd21:tone<=5'd5;
6'd22:tone<=5'd5;
6'd23:tone<=5'd12;
6'd24:tone<=5'd12;
6'd25:tone<=5'd10;
6'd26:tone<=5'd10;
6'd27:tone<=5'd8;
6'd28:tone<=5'd8;
6'd29:tone<=5'd7;
6'd30:tone<=5'd7;
6'd31:tone<=5'd6;
6'd32:tone<=5'd6;
6'd33:tone<=5'd11;
6'd34:tone<=5'd11;
6'd35:tone<=5'd10;
6'd36:tone<=5'd10;
6'd37:tone<=5'd8;
6'd38:tone<=5'd8;
6'd39:tone<=5'd9;
6'd40:tone<=5'd9;
6'd41:tone<=5'd8;
6'd42:tone<=5'd8;
endcase
end
end
endmodule
always @ (posedge clk2,negedge rst_n_in)
begin
if(!rst_n_in)
begin
tone=5'd1;
end
else
if (tone==5'd21)
begin
tone=5'd1;
end
else
begin
tone=tone+1;
end
end
endmodule
2.调试结果
由于蜂鸣器能产生特定的音调,是由输入的pwm波的频率变化来控制其音调,于是设定21种频率来区分。分成三组,每一组对应七个音,分别是1,2,3,4,5,6,7。这三个组,分别是低音,中音和高音。通过计数器产生延时来翻转高低电平来产生不同频率的信号,最终通过滤波电路以及放大电路驱动蜂鸣器发声。
上面的verilog程序中自己根据乐谱设计了一首小的音乐,验证自己对于蜂鸣器这条支路设计正确。
3.温度传感器的实现的设计流程
1.代码实现
1)总模块
module Uart_Bus #
(
parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD
output rs232_tx, //FPGA中UART发送端,分配给UART模块中的接收端RXD
output [8:0] seg_led_1,
output [8:0] seg_led_2,
inout onewire
);
wire [15:0] data_out;
wire [11:0] bcd;
DS18B20Z u1
(
.clk_in(clk_in),
.rst_n_in(rst_n_in),
.one_wire(onewire),
.data_out(data_out)
);
bcd u2
(
.rst_n(rst_n_in),
.binary(data_out[10:4]),
.bcd(bcd)
);
//assign bcd1=bcd;
//assign bcd2=bcd1;
LED u3
(
.seg_data_1(bcd[3:0]),
.seg_data_2(bcd[7:4]),
.seg_led_1(seg_led_1),
.seg_led_2(seg_led_2)
);
/////////////////////////////////UART接收功能模块例化////////////////////////////////////
wire bps_en_rx,bps_clk_rx;
wire [7:0] rx_data;
assign rx_data=bcd[7:0];
/*
//UART接收波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_rx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ) //接收时钟输出
);
//UART接收数据模块 例化
Uart_Rx Uart_Rx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ), //接收时钟输入
.rs232_rx (rs232_rx ), //UART接收输入
.rx_data (rx_data ) //接收到的数据
);
*/
/////////////////////////////////UART发送功能模块例化////////////////////////////////////
wire bps_en_tx,bps_clk_tx;
//UART发送波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_tx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ) //发送时钟输出
);
//UART发送数据模块 例化
Uart_Tx Uart_Tx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ), //发送时钟输入
.rx_bps_en (bps_en_rx ), //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送
.tx_data (rx_data ), //需要发出的数据
.rs232_tx (rs232_tx ) //UART发送输出
);
endmodule
2)分模块
二进制数转BCD码
module bcd(rst_n,binary,bcd); //rst_n为使能端,binary为待转换的二进制数,bcd为转换后的BCD码
parameter B_SIZE=7; //B_SIZE为二进制数所占的位数,可根据需要进行扩展
input rst_n; //rst_n高电平有效,低电平时
input [B_SIZE-1:0] binary;
output reg [B_SIZE+4:0] bcd;
reg [B_SIZE-1:0] bin;
//reg [B_SIZE+3:0] bcd; //bcd的长度应根据实际情况进行修改
reg [B_SIZE+4:0] result; //result的长度=bcd的长度
always@(binary or rst_n)
begin
bin= binary;
result = 0;
if(rst_n == 0)
bcd <= 0;
else
begin
repeat(B_SIZE-1)//使用repeat语句进行循环计算
begin
result[0] = bin[B_SIZE-1];
if(result[3:0] > 4)
result[3:0]=result[3:0]+ 4'd3;
if(result[7:4] > 4)
result[7:4]=result[7:4]+4'd3;
if(result[11:8] > 4)
result[11:8] = result[11:8]+4'd3; //扩展时应参照此三条if语句续写
//if(result[15:12] > 4)
//result[15:12]= result[15:12]+ 4'd3;
result=result<<1;
bin=bin<<1; end
result[0]= bin[B_SIZE-1];
bcd<=result;
end
end
endmodule
产生稳定的波特率
module Baud #
(
parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input bps_en, //接收或发送时钟使能
output reg bps_clk //接收或发送时钟输出
);
reg [12:0] cnt;
//计数器计数满足波特率时钟要求
always @ (posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in)
cnt <= 1'b0;
else if((cnt >= BPS_PARA-1)||(!bps_en)) //当时钟信号不使能(bps_en为低电平)时,计数器清零并停止计数
cnt <= 1'b0; //当时钟信号使能时,计数器对系统时钟计数,周期为BPS_PARA个系统时钟周期
else
cnt <= cnt + 1'b1;
end
//产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收
always @ (posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in)
bps_clk <= 1'b0;
else if(cnt == (BPS_PARA>>1)) //BPS_PARA右移一位等于除2,因计数器终值BPS_PARA为数据更替时间点,所以计数器中值时为数据最稳定时间点
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
endmodule
温度传感器采集数据
module DS18B20Z
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
inout one_wire, //DS18B20Z传感器单总线,双向管脚
output reg [15:0] data_out //DS18B20Z有效温度数据输出
);
/*
本设计通过驱动DS18B20Z芯片获取温度数据,
需要了解inout类型的接口如何实现双向通信,
中间涉及各种不同的延时和寄存器指令操作,注释部分以作简要说明
*/
//wire [15:0] data_out
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam INIT = 3'd2;
localparam WRITE = 3'd3;
localparam READ = 3'd4;
localparam DELAY = 3'd5;
//计数器分频产生1MHz的时钟信号
reg clk_1mhz;
reg [2:0] cnt_1mhz;
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
cnt_1mhz <= 3'd0;
clk_1mhz <= 1'b0;
end else if(cnt_1mhz >= 3'd5) begin
cnt_1mhz <= 3'd0;
clk_1mhz <= ~clk_1mhz; //产生1MHz分频
end else begin
cnt_1mhz <= cnt_1mhz + 1'b1;
end
end
reg [2:0] cnt;
reg one_wire_buffer;
reg [3:0] cnt_main;
reg [7:0] data_wr;
reg [7:0] data_wr_buffer;
reg [2:0] cnt_init;
reg [19:0] cnt_delay;
reg [19:0] num_delay;
reg [3:0] cnt_write;
reg [2:0] cnt_read;
reg [15:0] temperature;
reg [7:0] temperature_buffer;
reg [2:0] state = IDLE;
reg [2:0] state_back = IDLE;
//使用1MHz时钟信号做触发完成下面状态机的功能
always@(posedge clk_1mhz or negedge rst_n_in) begin
if(!rst_n_in) begin
state <= IDLE;
state_back <= IDLE;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
temperature <= 16'h0;
end else begin
case(state)
IDLE:begin //IDLE状态,程序设计的软复位功能,各状态异常都会跳转到此状态
state <= MAIN; //软复位完成,跳转之MAIN状态重新工作
state_back <= MAIN;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
end
MAIN:begin //MAIN状态控制状态机在不同状态间跳转,实现完整的温度数据采集
if(cnt_main >= 4'd11) cnt_main <= 1'b0;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
4'd0: begin state <= INIT; end //跳转至INIT状态进行芯片的复位及验证
4'd1: begin data_wr <= 8'hcc;state <= WRITE; end //主设备发出跳转ROM指令
4'd2: begin data_wr <= 8'h44;state <= WRITE; end //主设备发出温度转换指令
4'd3: begin num_delay <= 20'd750000;state <= DELAY;state_back <= MAIN; end //延时750ms等待转换完成
4'd4: begin state <= INIT; end //跳转至INIT状态进行芯片的复位及验证
4'd5: begin data_wr <= 8'hcc;state <= WRITE; end //主设备发出跳转ROM指令
4'd6: begin data_wr <= 8'hbe;state <= WRITE; end //主设备发出读取温度指令
4'd7: begin state <= READ; end //跳转至READ状态进行单总线数据读取
4'd8: begin temperature[7:0] <= temperature_buffer; end //先读取的为低8位数据
4'd9: begin state <= READ; end //跳转至READ状态进行单总线数据读取
4'd10: begin temperature[15:8] <= temperature_buffer; end //后读取的为高8为数据
4'd11: begin state <= IDLE;data_out <= temperature; end //将完整的温度数据输出并重复以上所有操作
default: state <= IDLE;
endcase
end
INIT:begin //INIT状态完成DS18B20Z芯片的复位及验证功能
if(cnt_init >= 3'd6) cnt_init <= 1'b0;
else cnt_init <= cnt_init + 1'b1;
case(cnt_init)
3'd0: begin one_wire_buffer <= 1'b0; end //单总线复位脉冲拉低
3'd1: begin num_delay <= 20'd500;state <= DELAY;state_back <= INIT; end //复位脉冲保持拉低500us时间
3'd2: begin one_wire_buffer <= 1'bz; end //单总线复位脉冲释放,自动上拉
3'd3: begin num_delay <= 20'd100;state <= DELAY;state_back <= INIT; end //复位脉冲保持释放100us时间
3'd4: begin if(one_wire) state <= IDLE; else state <= INIT; end //根据单总线的存在检测结果判定是否继续
3'd5: begin num_delay <= 20'd400;state <= DELAY;state_back <= INIT; end //如果检测正常继续保持释放400us时间
3'd6: begin state <= MAIN; end //INIT状态操作完成,返回MAIN状态
default: state <= IDLE;
endcase
end
WRITE:begin //按照DS18B20Z芯片单总线时序进行写操作
if(cnt <= 3'd6) begin //共需要发送8bit的数据,这里控制循环的次数
if(cnt_write >= 4'd6) begin cnt_write <= 1'b1; cnt <= cnt + 1'b1; end
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end else begin
if(cnt_write >= 4'd8) begin cnt_write <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end
//对于WRITE状态中cnt_write来讲,执行过程为:0;[1~6]*8;7;8;
case(cnt_write)
//lock data_wr
4'd0: begin data_wr_buffer <= data_wr; end //将需要写出的数据缓存
//发送 1bit 数据的用时在60~120us之间,参考数据手册
4'd1: begin one_wire_buffer <= 1'b0; end //总线拉低
4'd2: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end //延时2us时间,保证15us以内
4'd3: begin one_wire_buffer <= data_wr_buffer[cnt]; end //先发送数据最低位
4'd4: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end //延时80us时间
4'd5: begin one_wire_buffer <= 1'bz; end //总线释放
4'd6: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end //延时2us时间
//back to main
4'd7: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end //延时80us时间
4'd8: begin state <= MAIN; end //返回MAIN状态
default: state <= IDLE;
endcase
end
READ:begin //按照DS18B20Z芯片单总线时序进行读操作
if(cnt <= 3'd6) begin //共需要接收8bit的数据,这里控制循环的次数
if(cnt_read >= 3'd5) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1; end
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end else begin
if(cnt_read >= 3'd6) begin cnt_read <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end
case(cnt_read)
//读取 1bit 数据的用时在60~120us之间,总线拉低后15us时间内读取数据,参考数据手册
3'd0: begin one_wire_buffer <= 1'b0; end //总线拉低
3'd1: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end //延时2us时间
3'd2: begin one_wire_buffer <= 1'bz; end //总线释放
3'd3: begin num_delay <= 20'd5;state <= DELAY;state_back <= READ; end //延时5us时间
3'd4: begin temperature_buffer[cnt] <= one_wire; end //读取DS18B20Z返回的总线数据,先收最低位
3'd5: begin num_delay <= 20'd60;state <= DELAY;state_back <= READ; end //延时60us时间
//back to main
3'd6: begin state <= MAIN; end //返回MAIN状态
default: state <= IDLE;
endcase
end
DELAY:begin //延时控制
if(cnt_delay >= num_delay) begin //延时控制,延时时间由num_delay指定
cnt_delay <= 1'b0;
state <= state_back; //很多状态都需要延时,延时后返回哪个状态由state_back指定
end else cnt_delay <= cnt_delay + 1'b1;
end
endcase
end
end
assign one_wire = one_wire_buffer;
endmodule
串口发送模块
module Uart_Tx
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
output reg bps_en, //发送时钟使能
input bps_clk, //发送时钟输入
input rx_bps_en, //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送
input [7:0] tx_data, //需要发出的数据
output reg rs232_tx //UART发送输出
);
//reg [7:0] seg_led_1,seg_led_2;
reg rx_bps_en_r;
//延时锁存接收时钟使能信号
always @ (posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) rx_bps_en_r <= 1'b0;
else rx_bps_en_r <= 1;
end
//检测接收时钟使能信号的下降沿,因为下降沿代表接收数据的完成,以此作为发送信号的激励
wire neg_rx_bps_en = rx_bps_en_r & (~rx_bps_en);
reg [3:0] num;
reg [9:0] tx_data_r;
//根据接收数据的完成,驱动发送数据操作
always @ (posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
bps_en <= 1'b0;
tx_data_r <= 8'd0;
end else if(1)begin
bps_en <= 1'b1; //当检测到接收时钟使能信号的下降沿,表明接收完成,需要发送数据,使能发送时钟使能信号
tx_data_r <= {1'b1,tx_data,1'b0};
end else if(num==4'd10) begin
bps_en <= 1'b0; //一次UART发送需要10个时钟信号,然后结束
end
end
//当处于工作状态中时,按照发送时钟的节拍发送数据
always @ (posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
num <= 1'b0;
rs232_tx <= 1'b1;
end else if(bps_en) begin
if(bps_clk) begin
num <= num + 1'b1;
rs232_tx <= tx_data_r[num];
end else if(num>=4'd10)
num <= 4'd0;
end
end
endmodule
数码管显示
module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2);
input [3:0] seg_data_1; //数码管需要显示0~9十个数字,所以最少需要4位输入做译码
input [3:0] seg_data_2; //小脚丫上第二个数码管
output [8:0] seg_led_1; //在小脚丫上控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
output [8:0] seg_led_2; //在小脚丫上第二个数码管的控制信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
reg [8:0] seg [9:0]; //定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽
initial //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
//initial和always不同,其中语句只执行一次
begin
seg[0] = 9'h3f; //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字 0
seg[1] = 9'h06; //7段显示数字 1
seg[2] = 9'h5b; //7段显示数字 2
seg[3] = 9'h4f; //7段显示数字 3
seg[4] = 9'h66; //7段显示数字 4
seg[5] = 9'h6d; //7段显示数字 5
seg[6] = 9'h7d; //7段显示数字 6
seg[7] = 9'h07; //7段显示数字 7
seg[8] = 9'h7f; //7段显示数字 8
seg[9] = 9'h6f; //7段显示数字 9
end
assign seg_led_1 = seg[seg_data_1]; //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出
assign seg_led_2 = seg[seg_data_2];
endmodule
设计结果与感悟
通过查看 DS18B20Z温度传感器的手册,了解内部不同状态切换,以及控制命令数据命令的时序逻辑,实现温度的采集。对于该设计,时序的要求特别高,如果没有 按照手册的时序编写程序,就会导致各种功能的出错。整体设计是利用温度传感器来采集温度并将温度通过串口发送并通过上位机实时的串口助手实时显示温度,除此之外将温度的整数部分显示的两位数码管上。在显示的过程中,由于存在十进制数与bcd码之间的转换,根据其转化原理设计其转化程序。
对于转化过程中,程序设计不能利用除法和取余,这种会导致生成电路时,产生大量的触发器和门电路,大量消耗内部资源,导致资源利用率降低。
4.信号发生器的实现
1)波形产生的总模块程序设计
module Uart_Bus #
(
parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD
output rs232_tx, //FPGA中UART发送端,分配给UART模块中的接收端RXD
output [7:0] seg_led_1,
output [7:0] seg_led_2,
output [9:0] dac_data_out
//output [7:0] led
);
reg [9:0] dac_data_out;
parameter s1=2'b00;
parameter s2=2'b01;
parameter s3=2'b10;
LED u1(
.seg_data_1( segdata1),
.seg_data_2(segdata2),
.seg_led_1(seg_led_1),
.seg_led_2(seg_led_2)
);
reg [1:0] leixing;
reg [5:0] pinlv;
reg [3:0] segdata1,segdata2;
wire [9:0] dac_data_out1,dac_data_out2,dac_data_out3;
//reg [7:0] led;
reg dds_en_in;
reg state1,state2,state3,state4;
/////////////////////////////////UART接收功能模块例化////////////////////////////////////
wire bps_en_rx,bps_clk_rx;
wire [7:0] rx_data;
//UART接收波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_rx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ) //接收时钟输出
);
//UART接收数据模块 例化
Uart_Rx Uart_Rx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ), //接收时钟输入
.rs232_rx (rs232_rx ), //UART接收输入
.rx_data (rx_data ) //接收到的数据
);
always @(posedge clk_in)
begin
leixing = rx_data[7:6];
pinlv=rx_data[5:0];
segdata1=rx_data[6];
segdata2=rx_data[7];
end
DDS u10
(
.clk_in(clk_in),
.rst_n_in(1),
.dds_en_in(state1),
.f_increment(pinlv),
.p_increment(0),
.dac_data_out(dac_data_out1)
);
sanjiaobo u11
(
.clk_in(clk_in),
.rst_n_in(1),
.dds_en_in(state2),
.f_increment(pinlv),
.p_increment(0),
.dac_data_out(dac_data_out2)
);
fangbom u12
(
.clk_in(clk_in),
.rst_n_in(1),
.dds_en_in(state3),
.f_increment(pinlv),
.p_increment(0),
// .dac_clk_out(clk_in),
.dac_data_out(dac_data_out3)
);
//assign dac_data_out=dac_data_out1;
always @(posedge clk_in)
begin
case(leixing)
s1: dac_data_out=dac_data_out1;
s2: dac_data_out=dac_data_out2;
s3: dac_data_out=dac_data_out3;
endcase
end
always @(posedge clk_in)
case (leixing)
s1:
begin
state1=1;
state2=0;
state3=0;
//state4=0;
end
s2:
begin
state1=0;
state2=1;
state3=0;
//state4=0;
end
s3:
begin
state1=0;
state2=0;
state3=1;
//state4=0;
end
endcase
// always @(posedge clk_in)
// begin
//led=dac_data_out1[9:2];
//end
/////////////////////////////////UART发送功能模块例化////////////////////////////////////
wire bps_en_tx,bps_clk_tx;
//UART发送波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_tx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ) //发送时钟输出
);
//UART发送数据模块 例化
Uart_Tx Uart_Tx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (rst_n_in ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ), //发送时钟输入
.rx_bps_en (bps_en_rx ), //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送
.tx_data (rx_data ), //需要发出的数据
.rs232_tx (rs232_tx ) //UART发送输出
);
endmodule
2)正弦波的产生
**
module DDS
(
input clk_in, //clock in
input rst_n_in, //reset, active low
input dds_en_in, //dds work enable
input [23:0] f_increment, //frequency increment
input [23:0] p_increment, //phase increment
output dac_clk_out, //clock out
output [9:0] dac_data_out //data out
);
reg [23:0] phase_accumulator;
wire [23:0] phase;
//wire [9:0] dac_data_out;
assign dac_clk_out = clk_in;
//next_phase = phase_accumulator + f_increment;
always @(posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in) phase_accumulator <= 23'b0;
else if(dds_en_in) phase_accumulator <= phase_accumulator + f_increment;
end
assign phase = phase_accumulator + p_increment; // phase is the high 8 bits
lookup_table lookup_table_uut
(
.phase(phase[23:16]),
.dac_data_out(dac_data_out)
);
endmodule
/**************************************************
module: lookup_table
**************************************************/
module lookup_table
(
input [7:0] phase,
output reg [9:0] dac_data_out
);
wire [5:0] address = phase[5:0];
wire [1:0] sel = phase[7:6];
wire [9:0] sine;
always@(sel or sine)
case (sel)
2'b00 : dac_data_out = {1'b1, sine[9:1]};
2'b01 : dac_data_out = {1'b1, sine[9:1]};
2'b10 : dac_data_out = {1'b0, 9'h1ff-sine[9:1]};
2'b11 : dac_data_out = {1'b0, 9'h1ff-sine[9:1]};
endcase
sine_table sine_table_uut
(
.sel(sel),
.address(address),
.sine(sine)
);
endmodule
/**************************************************
module: sine_table
**************************************************/
module sine_table
(
input [1:0] sel,
input [5:0] address,
output reg [9:0] sine
);
reg [5:0] table_addr;
always @(sel or address)
case (sel)
2'b00: table_addr = address;
2'b01: table_addr = 6'h3f - address;
2'b10: table_addr = address;
2'b11: table_addr = 6'h3f - address;
endcase
always @(table_addr)
case(table_addr)
6'h0: sine=10'h000;
6'h1: sine=10'h019;
6'h2: sine=10'h032;
6'h3: sine=10'h04B;
6'h4: sine=10'h064;
6'h5: sine=10'h07D;
6'h6: sine=10'h096;
6'h7: sine=10'h0AF;
6'h8: sine=10'h0C4;
6'h9: sine=10'h0E0;
6'ha: sine=10'h0F9;
6'hb: sine=10'h111;
6'hc: sine=10'h128;
6'hd: sine=10'h141;
6'he: sine=10'h159;
6'hf: sine=10'h170;
6'h10: sine=10'h187;
6'h11: sine=10'h19F;
6'h12: sine=10'h1B5;
6'h13: sine=10'h1CC;
6'h14: sine=10'h1E2;
6'h15: sine=10'h1F8;
6'h16: sine=10'h20E;
6'h17: sine=10'h223;
6'h18: sine=10'h238;
6'h19: sine=10'h24D;
6'h1a: sine=10'h261;
6'h1b: sine=10'h275;
6'h1c: sine=10'h289;
6'h1d: sine=10'h29C;
6'h1e: sine=10'h2AF;
6'h1f: sine=10'h2C1;
6'h20: sine=10'h2D3;
6'h21: sine=10'h2E5;
6'h22: sine=10'h2F6;
6'h23: sine=10'h307;
6'h24: sine=10'h317;
6'h25: sine=10'h326;
6'h26: sine=10'h336;
6'h27: sine=10'h344;
6'h28: sine=10'h353;
6'h29: sine=10'h360;
6'h2a: sine=10'h36D;
6'h2b: sine=10'h37A;
6'h2c: sine=10'h386;
6'h2d: sine=10'h392;
6'h2e: sine=10'h39C;
6'h2f: sine=10'h3A7;
6'h30: sine=10'h3B1;
6'h31: sine=10'h3BA;
6'h32: sine=10'h3C3;
6'h33: sine=10'h3CB;
6'h34: sine=10'h3D3;
6'h35: sine=10'h3DA;
6'h36: sine=10'h3E0;
6'h37: sine=10'h3E6;
6'h38: sine=10'h3EB;
6'h39: sine=10'h3F0;
6'h3a: sine=10'h3F3;
6'h3b: sine=10'h3F7;
6'h3c: sine=10'h3FA;
6'h3d: sine=10'h3FC;
6'h3e: sine=10'h3FE;
6'h3f: sine=10'h3FF;
endcase
endmodule
3)三角波的实现
module sanjiaobo
(
input clk_in, //clock in
input rst_n_in, //reset, active low
input dds_en_in, //dds work enable
input [23:0] f_increment, //frequency increment
input [23:0] p_increment, //phase increment
output dac_clk_out, //clock out
output [9:0] dac_data_out //data out
);
reg [23:0] phase_accumulator;
wire [23:0] phase;
//wire [9:0] dac_data_out;
assign dac_clk_out = clk_in;
//next_phase = phase_accumulator + f_increment;
always @(posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in) phase_accumulator <= 23’b0;
else if(dds_en_in) phase_accumulator <= phase_accumulator + f_increment;
end
assign phase = phase_accumulator + p_increment; // phase is the high 8 bits
a lookup_table_uut
(
.phase(phase[23:16]),
.dac_data_out(dac_data_out)
);
endmodule
/**************************************************
module: a
**************************************************/
module a
(
input [7:0] phase,
output reg [9:0] dac_data_out
);
wire [5:0] address = phase[5:0];
wire [1:0] sel = phase[7:6];
wire [9:0] sine;
always@(sel or sine)
case (sel)
2’b00 : dac_data_out = {1’b1, sine[9:1]};
2’b01 : dac_data_out = {1’b1, sine[9:1]};
2’b10 : dac_data_out = {1’b0, 9’h1ff-sine[9:1]};
2’b11 : dac_data_out = {1’b0, 9’h1ff-sine[9:1]};
endcase
b sine_table_uut
(
.sel(sel),
.address(address),
.sine(sine)
);
endmodule
/**************************************************
module: b
**************************************************/
module b
(
input [1:0] sel,
input [5:0] address,
output reg [9:0] sine
);
reg [5:0] table_addr;
always @(sel or address)
case (sel)
2’b00: table_addr = address;
2’b01: table_addr = 6’h3f - address;
2’b10: table_addr = address;
2’b11: table_addr = 6’h3f - address;
endcase
always @(table_addr)
case(table_addr)
6’h0: sine=10’h000;
6’h1: sine=10’h010;
6’h2: sine=10’h020;
6’h3: sine=10’h030;
6’h4: sine=10’h040;
6’h5: sine=10’h050;
6’h6: sine=10’h060;
6’h7: sine=10’h070;
6’h8: sine=10’h080;
6’h9: sine=10’h090;
6’ha: sine=10’h0a0;
6’hb: sine=10’h0b0;
6’hc: sine=10’h0c0;
6’hd: sine=10’h0d0;
6’he: sine=10’h0e0;
6’hf: sine=10’h0f0;
6’h10: sine=10’h100;
6’h11: sine=10’h110;
6’h12: sine=10’h120;
6’h13: sine=10’h130;
6’h14: sine=10’h140;
6’h15: sine=10’h150;
6’h16: sine=10’h160;
6’h17: sine=10’h170;
6’h18: sine=10’h180;
6’h19: sine=10’h190;
6’h1a: sine=10’h1a0;
6’h1b: sine=10’h1b0;
6’h1c: sine=10’h1c0;
6’h1d: sine=10’h1d0;
6’h1e: sine=10’h1e0;
6’h1f: sine=10’h1f0;
6’h20: sine=10’h200;
6’h21: sine=10’h210;
6’h22: sine=10’h220;
6’h23: sine=10’h230;
6’h24: sine=10’h240;
6’h25: sine=10’h250;
6’h26: sine=10’h260;
6’h27: sine=10’h270;
6’h28: sine=10’h280;
6’h29: sine=10’h290;
6’h2a: sine=10’h2a0;
6’h2b: sine=10’h2b0;
6’h2c: sine=10’h2c0;
6’h2d: sine=10’h2d0;
6’h2e: sine=10’h2e0;
6’h2f: sine=10’h2f0;
6’h30: sine=10’h300;
6’h31: sine=10’h310;
6’h32: sine=10’h320;
6’h33: sine=10’h330;
6’h34: sine=10’h340;
6’h35: sine=10’h350;
6’h36: sine=10’h360;
6’h37: sine=10’h370;
6’h38: sine=10’h380;
6’h39: sine=10’h390;
6’h3a: sine=10’h3a0;
6’h3b: sine=10’h3b0;
6’h3c: sine=10’h3c0;
6’h3d: sine=10’h3d0;
6’h3e: sine=10’h3e0;
6’h3f: sine=10’h3F0;
endcase
endmodule
4)方波的实现
module fangbom
(
input clk_in, //clock in
input rst_n_in, //reset, active low
input dds_en_in, //dds work enable
input [23:0] f_increment, //frequency increment
input [23:0] p_increment, //phase increment
output dac_clk_out, //clock out
output [9:0] dac_data_out //data out
);
reg [23:0] phase_accumulator;
wire [23:0] phase;
//wire [9:0] dac_data_out;
assign dac_clk_out = clk_in;
//next_phase = phase_accumulator + f_increment;
always @(posedge clk_in or negedge rst_n_in)
begin
if(!rst_n_in) phase_accumulator <= 23'b0;
else if(dds_en_in) phase_accumulator <= phase_accumulator + f_increment;
end
assign phase = phase_accumulator + p_increment; // phase is the high 8 bits
n lookup_table_uut
(
.phase(phase[23:16]),
.dac_data_out(dac_data_out)
);
endmodule
/**************************************************
module: lookup_table
**************************************************/
module n
(
input [7:0] phase,
output reg [9:0] dac_data_out
);
wire [5:0] address = phase[5:0];
wire [1:0] sel = phase[7:6];
wire [9:0] sine;
always@(sel or sine)
case (sel)
2'b00 : dac_data_out = {1'b1, sine[9:1]};
2'b01 : dac_data_out = {1'b1, sine[9:1]};
2'b10 : dac_data_out = {1'b0, 9'h1ff-sine[9:1]};
2'b11 : dac_data_out = {1'b0, 9'h1ff-sine[9:1]};
endcase
q sine_table_uut
(
.sel(sel),
.address(address),
.sine(sine)
);
endmodule
/**************************************************
module: sine_table
**************************************************/
module q
(
input [1:0] sel,
input [5:0] address,
output reg [9:0] sine
);
reg [5:0] table_addr;
always @(sel or address)
case (sel)
2'b00: table_addr = address;
2'b01: table_addr = 6'h3f - address;
2'b10: table_addr = address;
2'b11: table_addr = 6'h3f - address;
endcase
always @(table_addr)
case(table_addr)
6'h0: sine=10'h3ff;
6'h1: sine=10'h3ff;
6'h2: sine=10'h3ff;
6'h3: sine=10'h3ff;
6'h4: sine=10'h3ff;
6'h5: sine=10'h3ff;
6'h6: sine=10'h3ff;
6'h7: sine=10'h3ff;
6'h8: sine=10'h3ff;
6'h9: sine=10'h3ff;
6'ha: sine=10'h3ff;
6'hb: sine=10'h3ff;
6'hc: sine=10'h3ff;
6'hd: sine=10'h3ff;
6'he: sine=10'h3ff;
6'hf: sine=10'h3ff;
6'h10: sine=10'h3ff;
6'h11: sine=10'h3ff;
6'h12: sine=10'h3ff;
6'h13: sine=10'h3ff;
6'h14: sine=10'h3ff;
6'h15: sine=10'h3ff;
6'h16: sine=10'h3ff;
6'h17: sine=10'h3ff;
6'h18: sine=10'h3ff;
6'h19: sine=10'h3ff;
6'h1a: sine=10'h3ff;
6'h1b: sine=10'h3ff;
6'h1c: sine=10'h3ff;
6'h1d: sine=10'h3ff;
6'h1e: sine=10'h3ff;
6'h1f: sine=10'h3ff;
6'h20: sine=10'h3ff;
6'h21: sine=10'h3ff;
6'h22: sine=10'h3ff;
6'h23: sine=10'h3ff;
6'h24: sine=10'h3ff;
6'h25: sine=10'h3ff;
6'h26: sine=10'h3ff;
6'h27: sine=10'h3ff;
6'h28: sine=10'h3ff;
6'h29: sine=10'h3ff;
6'h2a: sine=10'h3ff;
6'h2b: sine=10'h3ff;
6'h2c: sine=10'h3ff;
6'h2d: sine=10'h3ff;
6'h2e: sine=10'h3ff;
6'h2f: sine=10'h3ff;
6'h30: sine=10'h3ff;
6'h31: sine=10'h3ff;
6'h32: sine=10'h3ff;
6'h33: sine=10'h3ff;
6'h34: sine=10'h3ff;
6'h35: sine=10'h3ff;
6'h36: sine=10'h3ff;
6'h37: sine=10'h3ff;
6'h38: sine=10'h3ff;
6'h39: sine=10'h3ff;
6'h3a: sine=10'h3ff;
6'h3b: sine=10'h3ff;
6'h3c: sine=10'h3ff;
6'h3d: sine=10'h3ff;
6'h3e: sine=10'h3ff;
6'h3f: sine=10'h3ff;
endcase
endmodule
总体设计及其感悟:总体设计是规定一种通信协议,以一个字节作为标准,最高两位作为本设计的波形的选择,剩余六位作为可调频率的设计。利用上位机根据协议利用串口下达指令利用DDS原理输出到DAC处,然后根据串口接收回来的协议数据,来控制输出的波形和频率。上面程序实现的就是波形信号发生器的设计。
在调试过程中特别注意DAC电阻网络的高低位,以及其对应关系,避免设计时分配引脚导致出错。最终导致波形出错。
5.oled液晶屏的实现
1)总模块的设计
module Uart_Bus #
(
parameter BPS_PARA = 1250 //当使用12MHz时钟时波特率参数选择1250对应9600的波特率
)
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD
inout one_wire,
output rs232_tx, //FPGA中UART发送端,分配给UART模块中的接收端RXD
output oled_csn, //OLCD液晶屏使能
output oled_rst, //OLCD液晶屏复位
output oled_dcn, //OLCD数据指令控制
output oled_clk, //OLCD时钟信号
output oled_dat,
output [8:0] seg_led_1,
output [8:0] seg_led_2
);
//input clk_in,rst_n_in;
//inout one_wire;
//output oled_csn,oled_rst,oled_dcn,oled_clk,oled_dat;
//output [8:0] seg_led_1,seg_led_2;
reg [7:0] chuankouxianshi;
wire [15:0] data_out;
wire [11:0] bcd;
DS18B20Z u1
(
.clk_in(clk_in),
.rst_n_in(rst_n_in),
.one_wire(one_wire),
.data_out(data_out)
);
bcd u2
(
.rst_n(rst_n_in),
.binary(data_out[10:4]),
.bcd(bcd)
);
LED u3
(
.seg_data_1(bcd[3:0]),
.seg_data_2(bcd[7:4]),
.seg_led_1(seg_led_1),
.seg_led_2(seg_led_2)
);
OLED12832_Driver u4
(
.clk(clk_in), //12MHz系统时钟
.rst_n(rst_n_in), //系统复位,低有效
.wendu(bcd[7:0]),
.chuankouxianshi(chuankouxianshi),
.oled_csn(oled_csn), //OLCD液晶屏使能
.oled_rst(oled_rst), //OLCD液晶屏复位
.oled_dcn(oled_dcn), //OLCD数据指令控制
.oled_clk(oled_clk), //OLCD时钟信号
.oled_dat(oled_dat) //OLCD数据信号
);
/////////////////////////////////UART接收功能模块例化////////////////////////////////////
wire bps_en_rx,bps_clk_rx;
wire [7:0] rx_data;
//UART接收波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_rx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (1 ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ) //接收时钟输出
);
//UART接收数据模块 例化
Uart_Rx Uart_Rx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (1 ), //系统复位,低有效
.bps_en (bps_en_rx ), //接收时钟使能
.bps_clk (bps_clk_rx ), //接收时钟输入
.rs232_rx (rs232_rx ), //UART接收输入
.rx_data (rx_data ) //接收到的数据
);
always@(posedge clk_in)
begin
chuankouxianshi<=rx_data;
end
/////////////////////////////////UART发送功能模块例化////////////////////////////////////
wire bps_en_tx,bps_clk_tx;
//UART发送波特率时钟控制模块 例化
Baud #
(
.BPS_PARA (BPS_PARA )
)
Baud_tx
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (1 ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ) //发送时钟输出
);
//UART发送数据模块 例化
Uart_Tx Uart_Tx_uut
(
.clk_in (clk_in ), //系统时钟
.rst_n_in (1 ), //系统复位,低有效
.bps_en (bps_en_tx ), //发送时钟使能
.bps_clk (bps_clk_tx ), //发送时钟输入
.rx_bps_en (bps_en_rx ), //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送
.tx_data (rx_data ), //需要发出的数据
.rs232_tx (rs232_tx ) //UART发送输出
);
endmodule
2)oled显示设计
module OLED12832_Driver
(
input clk, //12MHz系统时钟
input rst_n, //系统复位,低有效
input [7:0] wendu,
input [7:0] chuankouxianshi,
output reg oled_csn, //OLCD液晶屏使能
output reg oled_rst, //OLCD液晶屏复位
output reg oled_dcn, //OLCD数据指令控制
output reg oled_clk, //OLCD时钟信号
output reg oled_dat //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:32];
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;
reg [7:0] chuankouxianshi_ge,chuankouxianshi_shi;
always @(posedge clk)
begin
chuankouxianshi_shi=chuankouxianshi[7:4]+48;
chuankouxianshi_ge=chuankouxianshi[3:0]+48;
end
reg [7:0] wendu_shi,wendu_ge;
always @(posedge clk)
begin
wendu_shi=wendu[7:4]+48;
wendu_ge=wendu[3:0]+48;
end
always@(posedge clk 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;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= IDLE; state_back <= IDLE;
//chuankouxianshi_shi<=chuankouxianshi[7:4]+48;chuankouxianshi_ge<=chuankouxianshi[3:0]+48;
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;
oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
state <= MAIN; state_back <= MAIN;
end
MAIN:begin
if(cnt_main >= 5'd4) cnt_main <= 5'd1;
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[(8*21-1):(8*21-6*40)]<="wendu:"; char[(8*21-6*40-1):(8*21-7*40)]=mem[wendu_shi];
//char[(8*21-7*40-1):(8*21-8*40)]<=mem[wendu_ge];
// char <= {"wendu:",wendu_shi,wendu_ge," "};
char <= {"chuankou:",chuankouxianshi_shi,chuankouxianshi_ge," "};
//char <= "Signal Generator";
state <= SCAN; end
5'd2: begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16;
//char <= {"chuankou:",chuankouxianshi_shi,chuankouxianshi_ge," "};
char <= {"wendu:",wendu_shi,wendu_ge," "};
state <= SCAN; end
5'd3: begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16;
char <= "freq: 000000Hz";state <= SCAN; end
5'd4: begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16;
char <= "ampl: . Vpp";state <= SCAN; end
default: state <= IDLE;
endcase
end
INIT:begin //初始化状态
case(cnt_init)
5'd0: begin oled_rst <= 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 oled_rst <= 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;
oled_dcn <= 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 oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end //定位列页地址
5'd 1: begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end //定位行地址低位
5'd 2: begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end //定位行地址高位
5'd 3: begin num <= num - 1'b1;end
5'd 4: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 5: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 6: begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end //将5*8点阵编程8*8
5'd 7: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
5'd 8: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
5'd 9: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
5'd10: begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
5'd11: begin oled_dcn <= 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 oled_csn <= LOW; end //9位数据最高位为命令数据控制位
5'd 1: begin oled_clk <= LOW; oled_dat <= char_reg[7]; end //先发高位数据
5'd 2: begin oled_clk <= HIGH; end
5'd 3: begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
5'd 4: begin oled_clk <= HIGH; end
5'd 5: begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
5'd 6: begin oled_clk <= HIGH; end
5'd 7: begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
5'd 8: begin oled_clk <= HIGH; end
5'd 9: begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
5'd10: begin oled_clk <= HIGH; end
5'd11: begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
5'd12: begin oled_clk <= HIGH; end
5'd13: begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
5'd14: begin oled_clk <= HIGH; end
5'd15: begin oled_clk <= LOW; oled_dat <= char_reg[0]; end //后发低位数据
5'd16: begin oled_clk <= HIGH; end
5'd17: begin oled_csn <= 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[ 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
**
2)总体设计与感悟:通过利用上位机以及利用温度传感器,根据SPI总线原理,将温度传感器采集到的温度显示到OLED屏以及通过串口发送数据,将数据显示到OLED屏,当温度达到设定温度时,通过触发蜂鸣器实现报警。
上述的设计过程中最重要的是三种总线协议,uart,I^2C,以及SPI三种总线的工作原理以及数据通信的协议格式来分别完成串口通信,传感器的数据采集,oled显示屏的显示等。
最重要的是利用数据手册去查看对应模块的时序要求以及其控制原理,根据时序的严格要求来完成底层设计电路,进而通过编程来控制并实现整个的流程。