小梅哥串口部分学习part2
- 串口通信接收原理
- 串口通信接收程序设计与调试
- 巧用位操作优化串口接收逻辑设计
- 串口接收模块的项目应用案例
串口通信接收原理
在采样的时候没有必要一直判断一个clk内全部都是高/低电平,如果采用直接对中间点进行判断的话,很有可能出现中间点恰好电力失常等等,因此可以采集多次样本,其中样本数据频率高的值就是该段电平的值
**基本原理:**采样
**技巧是:**一位数据采多次,统计得到高电平出现的次数,次数多的就是该位的电平值。采样8次,0,1,2,3低电平,4,5,6,7为高电平
**起始位检测:**通过边沿检测电路
串口通信接收程序设计与调试
波特率是指串口通信中,单位时间传输的二进制位数eg:115200对应的就是1s传输115200位,即传输一位需要1000000000/115200,若进行采样频率为波特率的16倍则需要再除以16对应于每次的采样的时间,由于内部时钟20ns的频率进行变化,所以想要计算对应的采样次数就需要再除以20~
源代码
module uart_byte_rx(input Clk,input Reset,input [2:0]Baud_Set,input uart_rx,output reg[7:0] Data,output reg RxDone);//边沿检测reg [1:0]uart_rx_r;always@(posedge Clk)beginuart_rx_r[0]<=uart_rx;uart_rx_r[1]<=uart_rx_r[0];end//上升沿wire pedge_uart_rx;//assign pedge_uart_rx=((uart_rx_r[0]==0)&&(uart_rx_r[1]==1));assign pedge_uart_rx=(uart_rx_r==2'b01);//下降沿wire nedge_uart_rx;//assign pedge_uart_rx=((uart_rx_r[0]==1)&&(uart_rx_r[1]==0));assign nedge_uart_rx=(uart_rx_r==2'b10);//采样需要计数的位数reg [8:0] Bps_DR;always@(*)case(Baud_Set)0:Bps_DR = 1000000000/9600/16/20 - 1;1:Bps_DR = 1000000000/19200/16/20 - 1;2:Bps_DR = 1000000000/38400/16/20 - 1;3:Bps_DR = 1000000000/57600/16/20 - 1;4:Bps_DR = 1000000000/115200/16/20 - 1;default:Bps_DR = 1000000000/9600/16/20 - 1;endcasewire bps_clk_16x;assign bps_clk_16x = (div_cnt == Bps_DR / 2); reg [8:0]div_cnt;always@(posedge Clk or negedge Reset)beginif(!Reset)div_cnt<=0;else if(RX_EN)beginif(div_cnt==Bps_DR)div_cnt<=0;elsediv_cnt<=div_cnt+1;endelsediv_cnt<=0; end//每位被分成16次频率采样,所以一共检测10位则需要160位reg [7:0]bps_cnt;always@(posedge Clk or negedge Reset)beginif(!Reset)bps_cnt<=0;else if(RX_EN)beginif(bps_clk_16x)beginif(bps_cnt==159)bps_cnt<=0;elsebps_cnt<=bps_cnt+1;endelsebps_cnt<=bps_cnt; endelsebps_cnt<=0;endreg[2:0]r_data[7:0];reg [2:0]sta_bit;reg [2:0]sto_bit;reg RX_EN; always@(posedge Clk or negedge Reset)beginif(!Reset)RX_EN<=0;else if(nedge_uart_rx)RX_EN<=1;else if(RxDone || (sta_bit >= 4))RX_EN<=0;end//用于对数据赋值 always@(posedge Clk or negedge Reset)beginif(!Reset)beginsta_bit<=0;sto_bit<=0;r_data[0]<=0;r_data[1]<=0;r_data[2]<=0;r_data[3]<=0;r_data[4]<=0;r_data[5]<=0;r_data[6]<=0;r_data[7]<=0;endelse if(bps_clk_16x)//中间位置取结果16次里面的5,6,7,8,9,10,11次数据begincase(bps_cnt)0:beginsta_bit<=0;sto_bit<=0;r_data[0]<=0;r_data[1]<=0;r_data[2]<=0;r_data[3]<=0;r_data[4]<=0;r_data[5]<=0;r_data[6]<=0;r_data[7]<=0;end5,6,7,8,9,10,11:sta_bit<=sta_bit+uart_rx;21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;149,150,151,152,153,154,155: sto_bit <= sto_bit + uart_rx;default:;endcaseendendalways@(posedge Clk or negedge Reset)if(!Reset) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))beginData[0] <= (r_data[0] >= 4)?1'b1:1'b0;Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;end always@(posedge Clk or negedge Reset)beginif(!Reset)RxDone<=0;else if((div_cnt==Bps_DR/2)&&(bps_cnt==159))RxDone<=1;elseRxDone<=0;endendmodule
测试模块
`timescale 1ns / 1ns
module uart_byte_rx_tb();reg Clk;reg Reset;wire [2:0]Baud_Set;reg uart_rx;wire[7:0] Data;wire RxDone;assign Baud_Set=4;uart_byte_rx uart_byte_rx(Clk,Reset,Baud_Set,uart_rx,Data,RxDone);initial Clk=0;always #10 Clk=!Clk;initial beginReset=0;uart_rx=1;#201;
// Reset=1;
// uart_tx_byte(8'h54);
// @(posedge RxDone);
// #50000;
// uart_tx_byte(8'h32);
// @(posedge RxDone);
// #50000;
// uart_tx_byte(8'h89);
// @(posedge RxDone);
// #50000;Reset = 1;#200; uart_tx_byte(8'h5a);#90000;uart_tx_byte(8'ha5);#90000;uart_tx_byte(8'h86);#90000;$stop;$stop;endtask uart_tx_byte;input [7:0]tx_data;beginuart_rx=1;#20;uart_rx=0;#8680;uart_rx=tx_data[0];#8680;uart_rx=tx_data[1];#8680;uart_rx=tx_data[2];#8680;uart_rx=tx_data[3];#8680;uart_rx=tx_data[4];#8680;uart_rx=tx_data[5];#8680;uart_rx=tx_data[6];#8680;uart_rx=tx_data[7];#8680;uart_rx=1;#8680;endendtask
endmodule
仿真截图
巧用位操作优化串口接收逻辑设计
解释:3’b000 3’b001 3’b010 3’b011 3’b100 3’b101 3’b110 3’b111判断是否大于等于4可以直接对第2位进行判断,为1则大于等于,为0则不大于
always@(posedge Clk or negedge Reset)if(!Reset) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))beginData[0] <= (r_data[0] >= 4)?1'b1:1'b0;Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;end //可以达到和上面同样的功能
// always@(posedge Clk or negedge Reset)
// if(!Reset)
// Data <= 0;
// else if(bps_clk_16x && (bps_cnt == 159))begin
// Data[0] <= r_data[0][2];
// Data[1] <= r_data[1][2];
// Data[2] <= r_data[2][2];
// Data[3] <= r_data[3][2];
// Data[4] <= r_data[4][2];
// Data[5] <= r_data[5][2];
// Data[6] <= r_data[6][2];
// Data[7] <= r_data[7][2];
// end
串口接收模块的项目应用案例
使用串口来控制LED工作状态
题目:使用串口发送指令到FPGA开发板,来控制第7课第4个实验的开发板上的LED灯的工作状态
让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机指定。8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择
如何使用串口接收8个字节的数据
收获:
1:上板调试时,对于时钟计时问题,最初counter=0,发现不满足,counter就会一直自加,直到加到32位的’hFFFFFFFF’才会清零
在实际板级运行的时候,当我们的time值更新时(25000000),counter的值已经大于该值,所以无法通过计数比较的方式清零,只能一直自加下去,直到32位计满了,溢出清零,然后才能正常的循环计数清零
这里涉及到一种编写技巧判断
if(i>=32)
a=0;
和if(i==32)
a=0;
虽然结界点都是32,但是对于第一种情况可以有效地避免当不满足条件时的及时清零,对于第二种有的时候或许会有些小问题
2:对于reset这种外部模块最好全部都定义成大写,并且统一这样赋值的时候不容易出错,模块内部的变量定义成小写
3:在顶层模块中几乎除了输入输出以外的内部变量都要定义成wire类型,代表内部的连线,输入输出还是采用和以往相同的方法,若底层是reg型,则上层直接定义成output就可,不用再定义成reg,测试文件直接写出wire~
//counter_led_4中
always@(posedge Clk or negedge Reset_n)if(!Reset_n)counter <= 0;else if(counter >= Time - 1)//这里由==改成了>=counter <= 0;elsecounter <= counter + 1'b1;
源代码
module uart_rx_ctrl_led(input Clk,input reset,input uart_rx,output Led);wire [7:0]Ctrl;wire [31:0]Time;wire [7:0]Data;wire RxDone;counter_led_4 counter_led_4(.Clk(Clk),.Reset_n(reset),.Ctrl(Ctrl),.Time(Time),.Led(Led));uart_byte_rx uart_byte_rx(.Clk(Clk),.Reset(reset),.Baud_Set(3'd4),.uart_rx(uart_rx),.Data(Data),.RxDone(RxDone));uart_cmd uart_cmd(.clk(Clk),.reset(reset),.rx_data(Data),.rx_done(RxDone),.ctrl(Ctrl),.time_set(Time));
endmodule
module counter_led_4(Clk,Reset_n,Ctrl,Time,Led
);input Clk;input Reset_n;input [7:0]Ctrl;input [31:0]Time;output reg Led;reg [31:0]counter;always@(posedge Clk or negedge Reset_n)if(!Reset_n)counter <= 0;else if(counter >= Time - 1)counter <= 0;elsecounter <= counter + 1'b1;reg [2:0]counter2;always@(posedge Clk or negedge Reset_n)if(!Reset_n) counter2 <= 0; else if(counter == Time - 1)counter2 <= counter2 + 1'b1;always@(posedge Clk or negedge Reset_n)if(!Reset_n)Led <= 0;else case(counter2)0:Led <= Ctrl[0];1:Led <= Ctrl[1];2:Led <= Ctrl[2];3:Led <= Ctrl[3];4:Led <= Ctrl[4];5:Led <= Ctrl[5];6:Led <= Ctrl[6];7:Led <= Ctrl[7];default:Led <= Led;endcaseendmodule
module uart_byte_rx(input Clk,input Reset,input [2:0]Baud_Set,input uart_rx,output reg[7:0] Data,output reg RxDone);//边沿检测reg [1:0]uart_rx_r;always@(posedge Clk)beginuart_rx_r[0]<=uart_rx;uart_rx_r[1]<=uart_rx_r[0];end//上升沿wire pedge_uart_rx;//assign pedge_uart_rx=((uart_rx_r[0]==0)&&(uart_rx_r[1]==1));assign pedge_uart_rx=(uart_rx_r==2'b01);//下降沿wire nedge_uart_rx;//assign pedge_uart_rx=((uart_rx_r[0]==1)&&(uart_rx_r[1]==0));assign nedge_uart_rx=(uart_rx_r==2'b10);//采样需要计数的位数reg [8:0] Bps_DR;always@(*)case(Baud_Set)0:Bps_DR = 1000000000/9600/16/20 - 1;1:Bps_DR = 1000000000/19200/16/20 - 1;2:Bps_DR = 1000000000/38400/16/20 - 1;3:Bps_DR = 1000000000/57600/16/20 - 1;4:Bps_DR = 1000000000/115200/16/20 - 1;default:Bps_DR = 1000000000/9600/16/20 - 1;endcasewire bps_clk_16x;assign bps_clk_16x = (div_cnt == Bps_DR / 2); reg [8:0]div_cnt;always@(posedge Clk or negedge Reset)beginif(!Reset)div_cnt<=0;else if(RX_EN)beginif(div_cnt==Bps_DR)div_cnt<=0;elsediv_cnt<=div_cnt+1;endelsediv_cnt<=0; end//每位被分成16次频率采样,所以一共检测10位则需要160位reg [7:0]bps_cnt;always@(posedge Clk or negedge Reset)beginif(!Reset)bps_cnt<=0;else if(RX_EN)beginif(bps_clk_16x)beginif(bps_cnt==159)bps_cnt<=0;elsebps_cnt<=bps_cnt+1;endelsebps_cnt<=bps_cnt; endelsebps_cnt<=0;endreg[2:0]r_data[7:0];reg [2:0]sta_bit;reg [2:0]sto_bit;reg RX_EN; always@(posedge Clk or negedge Reset)beginif(!Reset)RX_EN<=0;else if(nedge_uart_rx)RX_EN<=1;else if(RxDone || (sta_bit >= 4))RX_EN<=0;end//用于对数据赋值 always@(posedge Clk or negedge Reset)beginif(!Reset)beginsta_bit<=0;sto_bit<=0;r_data[0]<=0;r_data[1]<=0;r_data[2]<=0;r_data[3]<=0;r_data[4]<=0;r_data[5]<=0;r_data[6]<=0;r_data[7]<=0;endelse if(bps_clk_16x)//中间位置取结果16次里面的5,6,7,8,9,10,11次数据begincase(bps_cnt)0:beginsta_bit<=0;sto_bit<=0;r_data[0]<=0;r_data[1]<=0;r_data[2]<=0;r_data[3]<=0;r_data[4]<=0;r_data[5]<=0;r_data[6]<=0;r_data[7]<=0;end5,6,7,8,9,10,11:sta_bit<=sta_bit+uart_rx;21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;149,150,151,152,153,154,155: sto_bit <= sto_bit + uart_rx;default:;endcaseendendalways@(posedge Clk or negedge Reset)if(!Reset) Data <= 0; else if(bps_clk_16x && (bps_cnt == 159))beginData[0] <= (r_data[0] >= 4)?1'b1:1'b0;Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;end //可以达到和上面同样的功能
// always@(posedge Clk or negedge Reset)
// if(!Reset)
// Data <= 0;
// else if(bps_clk_16x && (bps_cnt == 159))begin
// Data[0] <= r_data[0][2];
// Data[1] <= r_data[1][2];
// Data[2] <= r_data[2][2];
// Data[3] <= r_data[3][2];
// Data[4] <= r_data[4][2];
// Data[5] <= r_data[5][2];
// Data[6] <= r_data[6][2];
// Data[7] <= r_data[7][2];
// end always@(posedge Clk or negedge Reset)beginif(!Reset)RxDone<=0;else if((div_cnt == Bps_DR/2)&&(bps_cnt==159))RxDone<=1;elseRxDone<=0;endendmodule
//这里养成一个习惯,在模块内部的信号用小写
module uart_cmd(input clk,input reset,input [7:0]rx_data,input rx_done,output reg [7:0]ctrl,output reg [31:0]time_set);reg [7:0] reg_data[7:0];always@(posedge clk)beginif(rx_done)beginreg_data[7]<=rx_data;reg_data[6]<=reg_data[7];reg_data[5]<=reg_data[6];reg_data[4]<=reg_data[5];reg_data[3]<=reg_data[4];reg_data[2]<=reg_data[3];reg_data[1]<=reg_data[2];reg_data[0]<=reg_data[1];endendreg rx_rx_done;always@(posedge clk)rx_rx_done<=rx_done;always@(posedge clk or negedge reset)beginif(!reset)begintime_set<=0;ctrl<=0;endelse if(rx_rx_done)beginif((reg_data[0]==8'h55)&&(reg_data[1]==8'ha5)&&(reg_data[7]==8'hf0))begintime_set[7:0]<=reg_data[2];time_set[15:8]<=reg_data[3];time_set[23:16]<=reg_data[4];time_set[31:24]<=reg_data[5];ctrl<=reg_data[6];endendend
endmodule
测试文件
`timescale 1ns / 1psmodule uart_rx_ctrl_led_tb();reg Clk;reg reset;reg uart_rx;wire Led;uart_rx_ctrl_led uart_rx_ctrl_led(Clk,reset,uart_rx,Led);initial Clk = 1;always#10 Clk = ~Clk;initial beginreset = 0;uart_rx = 1;#201;reset = 1;#200; uart_tx_byte(8'h55);#90000;uart_tx_byte(8'ha5);#90000;uart_tx_byte(8'h55);#90000;uart_tx_byte(8'ha5);#90000;uart_tx_byte(8'h12);#90000;uart_tx_byte(8'h34);#90000;uart_tx_byte(8'h56);#90000;uart_tx_byte(8'h78);#90000; uart_tx_byte(8'h9a);#90000; uart_tx_byte(8'hf0);#90000; uart_tx_byte(8'h55);#90000;uart_tx_byte(8'ha5);#90000;uart_tx_byte(8'h9a);#90000;uart_tx_byte(8'h78);#90000;uart_tx_byte(8'h56);#90000;uart_tx_byte(8'h34);#90000; uart_tx_byte(8'h12);#90000; uart_tx_byte(8'hf1);#90000; $stop;endtask uart_tx_byte;input [7:0]tx_data;beginuart_rx = 1;#20;uart_rx = 0;#8680;uart_rx = tx_data[0];#8680;uart_rx = tx_data[1];#8680;uart_rx = tx_data[2];#8680;uart_rx = tx_data[3];#8680;uart_rx = tx_data[4];#8680;uart_rx = tx_data[5];#8680;uart_rx = tx_data[6];#8680;uart_rx = tx_data[7];#8680;uart_rx = 1;#8680; endendtask
endmodule
仿真截图