简单的练习一下,题目要求如下:
- 用LCD屏显示温湿度
- 驱动电机转动
- 当湿度低于某个阈值时电机速度变快
- 用LCD屏显示电机转速
- 设置按钮A,使电机停止
- 设置按钮B,使电机速度进入手动控制模式
- 设置按钮C,每按一次电机速度发生变化
- 设置按钮D,再次进入自动模式
代码如下:
LCD12864:
module lcd12864_drive
(
input clock,
input reset,
input [63:0] data_buf,
output lcd12864_rs,
output lcd12864_rw,
output lcd12864_en,
output [7:0] lcd12864_data
);
wire [63:0] data_buf;
/**************************产生lcd12864时钟信号*************************/
reg clk_lcd12864;
reg [19:0]cnt;
always @(posedge clock or posedge reset)
begin
if (reset)
begin
cnt <= 20'b0;
clk_lcd12864 <= 0;
end
else if(cnt == 20'd20000) //时钟频率非常重要!!将近3k,经实测5k会在第0位出错。
begin
cnt <= 20'd0;
clk_lcd12864 <= ~clk_lcd12864;
end
else
cnt <= cnt +1'b1;
end
reg [1:0] clk_lcd12864_sync;
always @(posedge clock or posedge reset)
begin
if (reset)
clk_lcd12864_sync <= 2'b00;
else
clk_lcd12864_sync <= {clk_lcd12864_sync[0],clk_lcd12864};
end
assign clk_lcd12864_pos = (clk_lcd12864_sync == 2'b01);
//****************************lcd12864控制信号*****************************************/
reg [8:0] state; //State Machine code
parameter IDLE = 4'd0;
parameter CMD_WIDTH = 4'd1; //设置数据接口数量
parameter CMD_SET = 4'd2; //选择指令集
parameter CMD_CURSOR = 4'd3; //设置光标
parameter CMD_CLEAR = 4'd4; //清屏
parameter CMD_ACCESS = 4'd5; //输入方式设置:数据读写操作后,地址自动加一/画面不动
parameter CMD_DDRAM = 4'd6; //DDRAM行地址
parameter DATA_WRITE = 4'd7; //数据写入
parameter STOP = 4'd8; //
reg lcd12864_rs_r;
reg [7:0] lcd12864_data_r;
reg [7:0] data_buff;
reg [5:0] cnt_time;
//输出管教配置
assign lcd12864_rs = lcd12864_rs_r;
assign lcd12864_rw = 1'b0;
assign lcd12864_en = clk_lcd12864_sync[1]; //与lcd12864时钟相同
assign lcd12864_data = lcd12864_data_r;
always @(posedge clock or posedge reset)
begin
if(reset)
begin
lcd12864_rs_r <= 1'b0;
state <= IDLE;
// lcd12864_data_r <= 8'bzzzzzzzz; //高阻态
lcd12864_data_r <= 8'b11111111; //高阻态
cnt_time <= 6'd0;
end
else if(clk_lcd12864_pos)
begin
case(state)
IDLE:
begin
lcd12864_rs_r <= 1'b0;
cnt_time <= 6'd0;
state <= CMD_WIDTH;
// lcd12864_data_r <= 8'bzzzzzzzz;
lcd12864_data_r <= 8'b11111111;
end
CMD_WIDTH:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_SET;
lcd12864_data_r <= 8'h30; //8位数据口
end
CMD_SET:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_CURSOR;
lcd12864_data_r <= 8'h30; //基本指令集
end
CMD_CURSOR:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_CLEAR;
lcd12864_data_r <= 8'h0c; // 关光标
end
CMD_CLEAR:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_ACCESS;
lcd12864_data_r <= 8'h01; //清屏
end
CMD_ACCESS:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_DDRAM;
lcd12864_data_r <= 8'h06; //进入点设定
end
CMD_DDRAM: //行数命令
begin
lcd12864_rs_r <= 1'b0;
state <= DATA_WRITE;
case (cnt_time)
6'd0: lcd12864_data_r <= 8'h80;
6'd16: lcd12864_data_r <= 8'h90;
6'd32: lcd12864_data_r <= 8'h88;
6'd48: lcd12864_data_r <= 8'h98;
endcase
end
DATA_WRITE: //写数据
begin
lcd12864_rs_r <= 1'b1;
cnt_time <= cnt_time + 1'b1;
lcd12864_data_r <= data_buff;
case (cnt_time)
6'd15: state <= CMD_DDRAM;
6'd31: state <= CMD_DDRAM;
6'd47: state <= CMD_DDRAM;
6'd63: state <= STOP;
default: state <= DATA_WRITE;
endcase
end
STOP:
begin
lcd12864_rs_r <= 1'b0;
state <= CMD_DDRAM;
lcd12864_data_r <= 8'h80; //从第几行循环
cnt_time <= 6'd0;
end
default:
state <= IDLE;
endcase
end
end
always @(cnt_time)
begin
case (cnt_time)
6'd0: data_buff <= "W";
6'd1: data_buff <= ":";
6'd2: data_buff <= 8'h20;
6'd3: data_buff <= data_buf[15:8]+8'h30;//温湿度
6'd4: data_buff <= 8'h20;
6'd5: data_buff <= 8'h20;
6'd6: data_buff <= data_buf[7:0]+8'h30;
6'd7: data_buff <= 8'h20;
6'd8: data_buff <= 8'h20;
6'd9: data_buff <= "S";
6'd10: data_buff <= ":";
6'd11: data_buff <= 8'h20;
6'd12: data_buff <= data_buf[31:24]+8'h30;
6'd13: data_buff <= 8'h20;
6'd14: data_buff <= 8'h20;
6'd15: data_buff <= data_buf[23:16]+8'h30;
6'd16: data_buff <= 8'h20;//下一行开头
6'd17: data_buff <= 8'h20;
6'd18: data_buff <= data_buf[63:56]+8'h30;//速度
6'd19: data_buff <= 8'h20;
6'd20: data_buff <= data_buf[55:48]+8'h30;
6'd21: data_buff <= 8'h20;
6'd22: data_buff <= data_buf[47:40]+8'h30;
6'd23: data_buff <= 8'h20;
6'd24: data_buff <= data_buf[39:32]+8'h30;
6'd25: data_buff <= 8'h20;
6'd26: data_buff <= 8'h20;
6'd27: data_buff <= 8'h20;//数字0
6'd28: data_buff <= 8'h20;//数字1
6'd29: data_buff <= 8'h20;//数字2
6'd30: data_buff <= 8'h20;
6'd31: data_buff <= 8'h20;
6'd32: data_buff <= 8'h20;
6'd33: data_buff <= 8'h20;
6'd34: data_buff <= 8'h20;
6'd35: data_buff <= 8'h20;
6'd36: data_buff <= 8'h20;
6'd37: data_buff <= 8'h20;
6'd38: data_buff <= 8'h20;
6'd39: data_buff <= 8'h20;
6'd40: data_buff <= 8'h20;
6'd41: data_buff <= 8'h20;
6'd42: data_buff <= 8'h20;
6'd43: data_buff <= 8'h20;
6'd44: data_buff <= 8'h20;
6'd45: data_buff <= 8'h20;
6'd46: data_buff <= 8'h20;
6'd47: data_buff <= 8'h20;
6'd48: data_buff <= 8'h20;
6'd49: data_buff <= 8'h20;
6'd50: data_buff <= 8'h20;
6'd51: data_buff <= 8'h20;
6'd52: data_buff <= 8'h20;
6'd53: data_buff <= 8'h20;
6'd54: data_buff <= 8'h20;
6'd55: data_buff <= 8'h20;
6'd56: data_buff <= 8'h20;
6'd57: data_buff <= 8'h20;
6'd58: data_buff <= 8'h20;
6'd59: data_buff <= 8'h20;
6'd60: data_buff <= 8'h20;
6'd61: data_buff <= 8'h20;
6'd62: data_buff <= 8'h20;
6'd63: data_buff <= 8'h20;
default : data_buff <= 8'h02;
endcase
end
endmodule
button:
module button
(
input clock,
input reset,
input [3:0] row, //行
output [3:0] col, //列
output [3:0] key_value,
output key_out_flag
);
reg [3:0] col;
reg [3:0] key_value;
reg [31:0] count;
wire clk_20ms_flag;
reg [2:0] state; //状态标志
reg key_flag; //按键标志位
reg key_out_flag;
reg [3:0] col_reg; //寄存扫描列值
reg [3:0] row_reg; //寄存扫描行值
always @(posedge clock or posedge reset)
begin
if(reset)
count <= 0;
else
count <= count + 1;
end
assign clk_20ms_flag = (count[20:0] == 21'd2000000);
always @(posedge clock or posedge reset)
begin
if(reset)
begin
col <= 4'b0000;
state <= 0;
end
else if(clk_20ms_flag)
case (state)
0:
begin
col[3:0] <= 4'b0000;
key_flag <= 1'b0;
if(row[3:0] != 4'b1111)
begin
state <= 1;
col[3:0] <= 4'b1110;
end //有键按下,扫描第一行
else
state <= 0;
end
1:
begin
if(row[3:0] != 4'b1111)
state <= 5;//判断是否是第一行
else
begin
state <= 2;
col[3:0] <= 4'b1101;
end //扫描第二行
end
2:
begin
if(row[3:0] != 4'b1111)
state <= 5;//判断是否是第二行
else
begin
state <= 3;
col[3:0] <= 4'b1011;
end //扫描第三行
end
3:
begin
if(row[3:0] != 4'b1111)
state <= 5; //判断是否是第三一行
else
begin
state <= 4;
col[3:0] <= 4'b0111;
end //扫描第四行
end
4:
begin
if(row[3:0] != 4'b1111)
state <= 5;//判断是否是第一行
else
state <= 0;
end
5:
begin
if(row[3:0] != 4'b1111)
begin
col_reg <= col; //保存扫描列值
row_reg <= row; //保存扫描行值
state <= 5;
key_flag <= 1'b1; //有键按下
end
else
state <= 0;
end
endcase
end
always @(clock or col_reg or row_reg)
begin
if(key_flag == 1'b1)
begin
key_out_flag <= 1;
case ({col_reg,row_reg})
8'b1110_1110:key_value <= 4'b0001 ;
8'b1101_1110:key_value <= 4'b0010;
8'b1011_1110:key_value <= 4'b0110;
8'b0111_1110:key_value <= 4'b0000;
// 8'b1110_1101:key_value <= 4;
// 8'b1101_1101:key_value <= 5;
// 8'b1011_1101:key_value <= 6;
// 8'b0111_1101:key_value <= 7;
// 8'b1110_1011:key_value <= 8;
// 8'b1101_1011:key_value <= 9;
// 8'b1011_1011:key_value <= 10;
// 8'b0111_1011:key_value <= 11;
// 8'b1110_0111:key_value <= 12;
// 8'b1101_0111:key_value <= 13;
// 8'b1011_0111:key_value <= 14;
// 8'b0111_0111:key_value <= 15;
default: key_value <= 4'b0000;
endcase
end
else
key_out_flag <= 0;
end
endmodule
speed:
module speed
(
input clock,
input reset,
input pulse_from_motor,
output [19:0] speed_value
);
reg [32:0] cnt;
always @(posedge clock or posedge reset)
begin
if (reset)
cnt <= 0;
// else if(cnt == 33'd6000000000)
else if(cnt == 33'd300000000)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
wire start_flag;
wire end_flag;
assign start_flag = (cnt <= 33'd5);
//assign end_flag = (cnt == 33'd6000000000);
assign end_flag = (cnt == 33'd300000000);
reg [1:0] pulse_from_motor_sync;
always @(posedge clock or posedge reset)
begin
if (reset)
pulse_from_motor_sync <= 2'b00;
else
pulse_from_motor_sync <= {pulse_from_motor_sync[0],pulse_from_motor};
end
wire pulse_from_motor_pos;
assign pulse_from_motor_pos = (pulse_from_motor_sync == 2'b01);
wire [23 : 0] Q;
c_counter_binary_0 c_counter_binary_0_0
(
.CLK(clock), // input wire CLK
.CE(pulse_from_motor_pos), // input wire CE
.SCLR(start_flag), // input wire SCLR
.Q(Q) // output wire [23 : 0] Q
);
reg [23:0] pulse_cnt;
always @(posedge clock or posedge reset)
begin
if (reset)
pulse_cnt <= 0;
else if(end_flag)
pulse_cnt <= Q;
else
;
end
wire [15:0] turns_num;
//assign turns_num = pulse_cnt / 20;
assign turns_num = pulse_cnt[15:0];
assign speed_value [3:0] = turns_num % 10;
assign speed_value [7:4] = turns_num % 100 / 10;
assign speed_value [11:8] = turns_num % 1000 / 100;
assign speed_value [15:12] = turns_num % 10000 / 1000;
assign speed_value [19:16] = turns_num % 100000 / 10000;
endmodule
dht:
module dht11_drive
(
input clock,
input reset,
inout dht11,
output reg [31:0] dht11_value,
output [7:0] dht11_value_sd,
output [7:0] dht11_value_wd
);
parameter POWER_ON_NUM = 1000_000; //上电延时1s
parameter st_power_on_wait = 3'd0; //上电延时等待
parameter st_low_20ms = 3'd1; //主机发送20ms低电平
parameter st_high_13us = 3'd2; //主机释放总线13us
parameter st_rec_low_83us = 3'd3; //接收83us低电平响应
parameter st_rec_high_87us = 3'd4; //等待87us高电平(准备接收数据)
parameter st_rec_data = 3'd5; //接收40位数据
parameter st_delay = 3'd6; //延时等待,延时完成后重新操作DHT11
reg [2:0] cur_state; //当前状态
reg [2:0] next_state; //下一个状态
reg [5:0] clock_cnt; //分频计数器
reg clock_1M; //1MHz时钟
reg [20:0] us_cnt; //1微秒计数器
reg us_cnt_clr; //1微秒计数器清零信号
reg [39:0] data_temp; //缓存接收到的数据
reg step; //数据采集状态
reg [5:0] data_cnt; //接收数据用计数器
reg dht11_buffer; //DHT11输出信号
reg [1:0] clock_1M_sync;
wire clock_1M_pos;
wire clock_1M_neg;
reg [1:0] dht11_sync;
wire dht11_pos;
wire dht11_neg;
//1MHz分频时钟
always @ (posedge clock or posedge reset)
begin
if (reset)
clock_cnt <= 6'd0;
else if (clock_cnt == 6'd49)
clock_cnt <= 6'd0;
else
clock_cnt <= clock_cnt + 1'b1;
end
always @ (posedge clock or posedge reset)
begin
if (reset)
clock_1M <= 1'b0;
else if (clock_cnt == 6'd49)
clock_1M <= ~ clock_1M;
else
clock_1M <= clock_1M;
end
always @ (posedge clock or posedge reset)
begin
if (reset)
clock_1M_sync <= 2'b00;
else
clock_1M_sync <= {clock_1M_sync[0],clock_1M};
end
assign clock_1M_pos = (clock_1M_sync == 2'b01);
assign clock_1M_neg = (clock_1M_sync == 2'b10);
always @ (posedge clock or posedge reset)
begin
if (reset)
dht11_sync <= 2'b11;
else if(clock_1M_pos)
dht11_sync <= {dht11_sync[0],dht11};
end
assign dht11_pos = (dht11_sync == 2'b01);
assign dht11_neg = (dht11_sync == 2'b10);
//us计数器
always @ (posedge clock or posedge reset)
begin
if (reset)
us_cnt <= 21'd0;
else if (us_cnt_clr)
us_cnt <= 21'd0;
else if(clock_1M_pos)
us_cnt <= us_cnt + 1'b1;
end
//状态跳转
always @ (posedge clock or posedge reset)
begin
if (reset)
cur_state <= st_power_on_wait;
else
cur_state <= next_state;
end
//状态机读取DHT11数据
always @ (posedge clock or posedge reset)
begin
if(reset)
begin
next_state <= st_power_on_wait;
data_temp <= 40'd0;
step <= 1'b0;
us_cnt_clr <= 1'b0;
data_cnt <= 6'd0;
dht11_buffer <= 1'bz;
end
else if(clock_1M_pos)
begin
case (cur_state)
st_power_on_wait :
begin
if(us_cnt < POWER_ON_NUM)
begin
dht11_buffer <= 1'bz;
us_cnt_clr <= 1'b0;
end
else
begin
next_state <= st_low_20ms;
us_cnt_clr <= 1'b1;
end
end
st_low_20ms :
begin
if(us_cnt < 20000)
begin
dht11_buffer <= 1'b0;
us_cnt_clr <= 1'b0;
end
else
begin
dht11_buffer <= 1'bz;
next_state <= st_high_13us;
us_cnt_clr <= 1'b1;
end
end
st_high_13us :
begin
if (us_cnt < 20)
begin
us_cnt_clr <= 1'b0;
if(dht11_neg)
begin
next_state <= st_rec_low_83us;
us_cnt_clr <= 1'b1;
end
end
else
next_state <= st_delay;
end
st_rec_low_83us :
begin
if(dht11_pos)
next_state <= st_rec_high_87us;
end
st_rec_high_87us :
begin
if(dht11_neg)
begin
next_state <= st_rec_data;
us_cnt_clr <= 1'b1;
end
else
begin
data_cnt <= 6'd0;
data_temp <= 40'd0;
step <= 1'b0;
end
end
st_rec_data :
begin
case(step)
0 :
begin
if(dht11_pos)
begin
step <= 1'b1;
us_cnt_clr <= 1'b1;
end
else
us_cnt_clr <= 1'b0;
end
1 :
begin
if(dht11_neg)
begin
data_cnt <= data_cnt + 1'b1;
if(us_cnt < 60)
data_temp <= {data_temp[38:0],1'b0};
else
data_temp <= {data_temp[38:0],1'b1};
step <= 1'b0;
us_cnt_clr <= 1'b1;
end
else
us_cnt_clr <= 1'b0;
end
endcase
if(data_cnt == 40)
begin
next_state <= st_delay;
if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
dht11_value <= data_temp[39:8];
end
end
st_delay :
begin
if(us_cnt < 2000_000)
us_cnt_clr <= 1'b0;
else
begin
next_state <= st_low_20ms;
us_cnt_clr <= 1'b1;
end
end
default : ;
endcase
end
end
assign dht11 = dht11_buffer;
assign dht11_value_sd[3:0] = dht11_value[31:24] % 10;
assign dht11_value_sd[7:4] = dht11_value[31:24] / 10;
assign dht11_value_wd[3:0] = dht11_value[15:8] % 10;
assign dht11_value_wd[7:4] = dht11_value[15:8] / 10;
endmodule
Judge:(电机驱动,这里用的是步进电机)
module Judge(
input clk,
input rst,
input key_out_flag,
input [7:0]t_data,//温度
input [7:0]s_data,//湿度
input A,//清零
input B,//手动模式
input C,//速度变档
input D,//恢复到自动模式
output [3:0] motor_en
);
reg [3:0] motor_en_reg;
reg [31:0] cnt;
reg [1:0] s;
reg [1:0] state_speed;
always@(posedge key_out_flag)
begin
if(rst||A)
begin
s<=0;
end
else
begin
if(C)
s<=s+1;
end
end
always@(posedge clk)
begin
if(rst)
begin
state_speed<=cnt[21:20];
end
else
begin
case(s)
2'b00:
state_speed<=cnt[21:20];
2'b01:
state_speed<=cnt[20:19];
2'b10:
state_speed<=cnt[19:18];
2'b11:
state_speed<=cnt[18:17];//由于频率过快,该状态会强制暂停
default:;
endcase
end
end
always@(posedge clk)
begin
if(rst||A)
begin
motor_en_reg<=0;
end
else if(B)//手动模式
begin
case (state_speed)
2'b00 :
motor_en_reg <= 4'b0001;
2'b01 :
motor_en_reg <= 4'b0010;
2'b10 :
motor_en_reg <= 4'b0100;
2'b11 :
motor_en_reg <= 4'b1000;
endcase
end
else if(s_data[7:4]<4'd3)//湿度小于三十度
begin
case (cnt[19:18])
2'b00 :
motor_en_reg <= 4'b0001;
2'b01 :
motor_en_reg <= 4'b0010;
2'b10 :
motor_en_reg <= 4'b0100;
2'b11 :
motor_en_reg <= 4'b1000;
endcase
end
else
begin
case (cnt[21:20])
2'b00 :
motor_en_reg <= 4'b0001;
2'b01 :
motor_en_reg <= 4'b0010;
2'b10 :
motor_en_reg <= 4'b0100;
2'b11 :
motor_en_reg <= 4'b1000;
endcase
end
end
always @(posedge clk )
begin
if (rst)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
assign motor_en = motor_en_reg;
endmodule
实验效果: