/* ds1302驱动; 单字节传输模式,输入时钟是25Mhz,状态机使用的时钟是8us/周期, 输出到ds1302的时钟是16us/周期.该驱动模块儿使用的是同步复位;我以前测试的时候, 使用的异步复位。但是,异步复位给我带来了一些麻烦,就是当我下载或者上电时, 都会自动重新将ds1302复位。但是,实际应用中,ds1302不应该上电复位。我使用了同步复位后, 就不出现上电复位了。 */ module ds1302_drive(data_in,adr_seg,clock,rst,rst_n,sclk,data_io,dat_o_TM,dat_o_C); input [3:0] data_in;// input[3:0]adr_seg;// input rst;//外部复位按键重新设置数据 input clock; //ds1302端口信号 output rst_n,sclk;//ds1302的工作时钟 inout data_io; //dat_o_TM信号接六个数码管,分别显示小时,分钟和秒 //dat_o_C信号送到12864显示年,月,日和星期 output[23:0] dat_o_TM;//数码管显示时间 output[31:0]dat_o_C;//送到液晶,液晶显示日历 reg rst_n; reg data;//data_io的缓存, reg clk_us;//状态机的时钟8us/period reg clk_2us;//ds1302的工作时钟 //因为data_io是双向口,所以我设计了个开关link_write。它来管理数据的出入 //link_write为高时,允许输出,为低电平是高阻 reg link_write; reg flag123;//重新设置日历标志位 reg [4:0]step1;//WR_SET和RD_T任务的状态 reg [4:0]step2; reg [23:0] data_out_reg_time; reg [31:0] data_out_reg_calender; reg F; reg FF;//读写任务的标志 reg [7:0]reg_CMD=0;//command reg [7:0]reg_SEC=0;//时间/日历 reg [7:0]reg_MIN=8'h40;//分钟 reg [7:0]reg_H=8'h19;//小时 reg [7:0]reg_DAY=8'h29;//天 reg [7:0]reg_MONTH=8'h04;//月 reg [7:0]reg_X=8'h05;//星期 reg [7:0]reg_YEAR=8'h11; reg [7:0]register1;//command reg [7:0]register3; reg [7:0]register4;//读进数据寄存器 reg [3:0]state;//状态机 //=============================================== //初始设置的状态参数 //写状态 parameter IDLE =4'b0000, WR_S =4'b0001, WR_Min =4'b0010, WR_H =4'b0011, WR_X =4'b0100, WR_D =4'b0101, WR_M =4'b0110, WR_Y =4'b0111; //=============================================== //=============================================== parameter //状态读 RD_S =4'b1000, RD_Min =4'b1001, RD_H =4'b1010, RD_X =4'b1011, RD_D =4'b1100, RD_M =4'b1101, RD_Y =4'b1110, CLOSE_W =4'b1111; //=============================================== //WR_SET任务的参数 parameter step1_f0 =5'b00000, step1_f1 =5'b00001, step1_f2 =5'b00010, step1_f3 =5'b00011, step1_f4 =5'b00100, step1_f5 =5'b00101, step1_f6 =5'b00110, step1_f7 =5'b00111, step1_f8 =5'b01000, step1_f9 =5'b01001, step1_fa =5'b01010, step1_fb =5'b01011, step1_fc =5'b01100, step1_fd =5'b01101, step1_fe =5'b01110, step1_ff =5'b01111, step1_f10 =5'b10000; //=============================================== //RD_T任务的参数 parameter step2_f0 =5'b00000, step2_f1 =5'b00001, step2_f2 =5'b00010, step2_f3 =5'b00011, step2_f4 =5'b00100, step2_f5 =5'b00101, step2_f6 =5'b00110, step2_f7 =5'b00111, step2_f8 =5'b01000, step2_f9 =5'b01001, step2_fa =5'b01010, step2_fb =5'b01011, step2_fc =5'b01100, step2_fd =5'b01101, step2_fe =5'b01110, step2_ff =5'b01111, step2_f10=5'b10000; //++++++++++++++++++++++++++++++++++++++++++++++ //本模块的时钟40ns X 100 X 2=8us/period, reg[8:0]counter; always@(posedge clock ) if(counter<=100) counter<=counter+9'b1; else begin clk_us<=~clk_us; counter<=0; end //clk_2us的时钟是ds1302使用的 16us/period always @ (negedge clk_us)// or negedge rst_n) begin if(!rst_n) clk_2us<=0; else clk_2us<=~clk_2us; end //++++++++++++++++++++++++++++++++++++++++++++++++ //此always模块的功能是将外部的时间接收进寄存器 //我本想设计个接受数据的模块,但是由于时间的问题。我没有完成。 //输入的时间是4位的 /* always@ (posedge clk_us) begin case(adr_seg) 4'b0000:reg_H[7:4]<=4'h1;//data_in; 4'b0001:reg_H[3:0]<=4'h7;//data_in; 4'b0010:reg_MIN[7:4]<=4'h4;//;data_in; 4'b0011:reg_MIN[3:0]<=4'h2;//data_in; 4'b0100:reg_SEC[7:4]<=0;//data_in; 4'b0101:reg_SEC[3:0]<=0;//data_in; 4'b0110:reg_YEAR[7:4]<=1;//data_in; 4'b0111:reg_YEAR[3:0]<=1;//data_in; 4'b1000:reg_MONTH[7:4]<=0;//data_in; 4'b1001:reg_MONTH[3:0]<=4;//data_in; 4'b1010:reg_DAY[7:4]<=4'h2;//data_in; 4'b1011:reg_DAY[3:0]<=4'h9;//data_in; 4'b1100:reg_X[7:4]<=0; 4'b1101:reg_X[3:0]<=4'b0101; default: begin reg_SEC<=0;//时间/日历 reg_MIN<=0;//分钟 reg_H<=0;//小时 reg_DAY<=0;//天 reg_MONTH<=0;//月 reg_X<=0;//星期 reg_YEAR<=0; end endcase end */ //***************主状态机************************ //状态机默认的状态是RD_S(读秒)。状态机正常工作时,只读时间。 //只有复位按键按下时,执行一次写,而后循环的读时间 always @(posedge clk_us ) begin if(rst==0) begin //初始化操作时,link_write<=0;所以输出呈现高阻态 rst_n<=0; data<=0;//输出缓存器被清空 FF<=0;//WR_SET任务的标志位 F<=0;//RD_T任务标志位 state<=IDLE; link_write<=0; register1<=8'b10001110;//关闭写保护 data_out_reg_time<=0; data_out_reg_calender<=0; flag123<=1; step1<=0; step2<=0; end else begin case(state) //IDLE状态选择工作状态还是初始设置状态 IDLE: begin //flag123==1时,重新设置数据 if(flag123==1) begin if(FF==0) begin WR_SET(reg_CMD); state<=IDLE; end else begin flag123<=0; rst_n<=0; link_write<=0; FF<=0; register1<=8'b1000_0000;////写时钟/日历寄存器 state<=WR_S; end end else if(flag123==0) begin register4<=8'b1000_0001; link_write<=0; rst_n<=0; F<=0; state<=RD_S; end else state<=IDLE; end WR_S: begin if(FF==0) WR_SET(reg_SEC);//写秒 else begin state<=WR_Min; link_write<=0; FF<=0; register1<=8'b1000_0010; end end WR_Min: begin if(FF==0) WR_SET(reg_MIN); else begin state<=WR_H; link_write<=0; FF<=0; register1<=8'b1000_0100; end end WR_H: begin if(FF==0) WR_SET(reg_H); else begin state<=WR_X; link_write<=0; FF<=0; register1<=8'b1000_1010; end end WR_X: begin if(FF==0) WR_SET(reg_X); else begin state<=WR_D; link_write<=0; FF<=0; register1<=8'b1000_0110; end end WR_D: begin if(FF==0) WR_SET(reg_DAY); else begin state<=WR_M; link_write<=0; FF<=0; register1<=8'b1000_1000; end end WR_M: begin if(FF==0) WR_SET(reg_MONTH); else begin state<=WR_Y; link_write<=0; FF<=0; register1<=8'b1000_1100; end end WR_Y: begin if(FF==0) WR_SET(reg_YEAR); else begin state<=CLOSE_W; link_write<=0; FF<=0; register1<=8'b10001110; end end RD_S : if(F==0) RD_T; else begin data_out_reg_time[7:0]<=register3; rst_n<=0; F<=0; register4<=8'b1000_0011; state<=RD_Min; end RD_Min : if(F==0) RD_T; else begin data_out_reg_time[15:8]<=register3; rst_n<=0; F<=0; register4<=8'b1000_0101; state<=RD_H; end RD_H : if(F==0) RD_T; else begin data_out_reg_time[23:16]<=register3; rst_n<=0; F<=0; register4<=8'b1000_1011; state<=RD_X; end RD_X : if(F==0) RD_T; else begin data_out_reg_calender[7:0]<=register3; rst_n<=0; F<=0; register4<=8'b1000_0111; state<=RD_D; end RD_D : if(F==0) RD_T; else begin data_out_reg_calender[15:8]<=register3; rst_n<=0; F<=0; register4<=8'b1000_1001; state<=RD_M; end RD_M : if(F==0) RD_T; else begin data_out_reg_calender[23:16]<=register3; rst_n<=0; F<=0; register4<=8'b1000_1101; state<=RD_Y; end RD_Y : if(F==0) RD_T; else begin data_out_reg_calender[31:24]<=register3; rst_n<=0; F<=0; register4<=8'b1000_0001; state<=RD_S; //flag_initial<=0; end CLOSE_W: begin if(FF==0) WR_SET(8'h10); else begin state<=IDLE; link_write<=0; FF<=0; F<=0; flag123<=0; end end default: begin flag123<=0; rst_n<=0; data<=0; FF<=0; F<=0; state<=RD_S; register3<=0;//接受数据的寄存器 register4<=8'b1000_0001;//读任务的指令寄存器 reg_CMD<=0; end endcase end end //******************end of machine********************** //该任务用于读时间 task RD_T; begin case(step2) step2_f0: begin if(!clk_2us) begin link_write<=1; rst_n<=1; data<=register4[0]; step2<=step2_f1; end else begin rst_n<=0; step2<=step2_f0; data<=register4[0]; link_write<=1; end end step2_f1: if(!clk_2us) begin data<=register4[1]; step2<=step2_f2; end else begin data<=data; step2<=step2; end step2_f2: if(!clk_2us) begin data<=register4[2]; step2<=step2_f3; end else begin data<=data; step2<=step2; end step2_f3: if(!clk_2us) begin data<=register4[3]; step2<=step2_f4; end else begin data<=data; step2<=step2; end step2_f4: if(!clk_2us) begin data<=register4[4]; step2<=step2_f5; end else begin data<=data; step2<=step2; end step2_f5: if(!clk_2us) begin data<=register4[5]; step2<=step2_f6; end else begin data<=data; step2<=step2; end step2_f6: if(!clk_2us) begin data<=register4[6]; step2<=step2_f7; end else begin data<=data; step2<=step2; end step2_f7: if(!clk_2us) begin data<=register4[7]; step2<=step2_f8; end else begin data<=data; step2<=step2; end step2_f8: if(!clk_2us) begin link_write<=0; step2<=step2_f9; end else step2<=step2; step2_f9: if(!clk_2us) begin step2<=step2_fa; end else begin step2<=step2; end step2_fa: if(!clk_2us) begin step2<=step2_fb; end else step2<=step2; step2_fb: if(!clk_2us) begin step2<=step2_fc; end else step2<=step2; step2_fc: if(!clk_2us) begin step2<=step2_fd; end else step2<=step2; step2_fd: if(!clk_2us) begin step2<=step2_fe; end else step2<=step2; step2_fe: if(!clk_2us) begin step2<=step2_ff; end else step2<=step2; step2_ff: if(!clk_2us) begin step2<=step2_f10; end else begin step2<=step2_ff; end step2_f10: if(!clk_2us) begin//该begin end块不执行 F<=1; rst_n<=0; step2<=step2_f0;//do not run end else begin F<=1; rst_n<=0; step2<=step2_f0; end default: begin step2<=step2_f0; end endcase end endtask //&&&&&&&&&&&&&&&&&&&&&The TASK of WRITE&&&&&&&&&&&&&&& always @( posedge clk_2us or negedge rst) if(!rst) register3<=0; else begin case(step2) step2_f9 :register3[0]<=data_io; step2_fa :register3[1]<=data_io; step2_fb :register3[2]<=data_io; step2_fc :register3[3]<=data_io; step2_fd :register3[4]<=data_io; step2_fe :register3[5]<=data_io; step2_ff :register3[6]<=data_io; step2_f10 :register3[7]<=data_io; default:register3<=register3; endcase end //===================================================== task WR_SET; input [7:0]reg_store; begin case(step1) step1_f0: if(!clk_2us) begin link_write<=1; rst_n<=1;//低电平时才可以将rst_n拉高 data<=register1[0]; step1<=step1_f1; end else begin rst_n<=0; step1<=0; data<=register1[0]; link_write<=0; end step1_f1: if(!clk_2us) begin link_write<=1; data<=register1[1]; step1<=step1_f2; end else step1<=step1; step1_f2: if(!clk_2us) begin link_write<=1; data<=register1[2]; step1<=step1_f3; end else step1<=step1; step1_f3: if(!clk_2us) begin data<=register1[3]; step1<=step1_f4; end else step1<=step1; step1_f4: if(!clk_2us) begin data<=register1[4]; step1<=step1_f5; end else step1<=step1; step1_f5: if(!clk_2us) begin data<=register1[5]; step1<=step1_f6; end else step1<=step1; step1_f6: if(!clk_2us) begin data<=register1[6]; step1<=step1_f7; end else step1<=step1; step1_f7: if(!clk_2us) begin data<=register1[7]; step1<=step1_f8; end else step1<=step1; step1_f8: begin if(!clk_2us) begin data<=reg_store[0]; step1<=step1_f9; end else step1<=step1; end step1_f9: begin if(!clk_2us) begin rst_n<=1; data<=reg_store[1]; step1<=step1_fa; end else begin step1<=step1; end end step1_fa: if(!clk_2us) begin data<=reg_store[2]; step1<=step1_fb; end else step1<=step1; step1_fb: if(!clk_2us) begin data<=reg_store[3]; step1<=step1_fc; end else step1<=step1; step1_fc: if(!clk_2us) begin data<=reg_store[4]; step1<=step1_fd; end else step1<=step1; step1_fd: if(!clk_2us) begin data<=reg_store[5]; step1<=step1_fe; end else step1<=step1; step1_fe: if(!clk_2us) begin data<=reg_store[6]; step1<=step1_ff; end else step1<=step1; step1_ff: if(!clk_2us) begin data<=reg_store[7]; step1<=step1_f10; end else begin step1<=step1; end step1_f10: if(!clk_2us) begin step1<=step1_f0; FF<=1; rst_n<=0; end else begin step1<=step1;//读完数据不能马上把rst_n拉低,切记!!!!!!!!!!!!! end default: begin step1<=step1_f0; end endcase end endtask //==============END of TASK========================== assign dat_o_TM=data_out_reg_time; assign dat_o_C=data_out_reg_calender; assign data_io=(link_write)? data:1'bz; assign sclk=clk_2us; endmodule