RS232原理
注意:以下仅为学习笔记,借鉴网络各个知识点汇聚而成,如有错误请留言。
原理:RS232协议是UART的一种,只有两根数据线,分别是rx和tx,分别用来接收数据和发送数据,数据收发基于帧结构,每次按照8bit的大小来接收和发送数据。数据线空闲状态下为高电平,开始发送数据后将电平拉低一帧作为起始位,随后8帧的数据为数据内容,发送完毕后将数据线电平拉高一帧作为停止位,然后一直拉高回到空闲状态。
RS232缺点:1.传输数据的距离进; 2.传输数据慢。
优点:1.很多传感器CPU都带有串口功能,方便调试;
2.只有两条数据线,减少IO口。
串口RS232协议:位宽都是1bit。通过串口调试助手发送一个8bit的数据(一位一位的发送和接收);RS232串口在发送数据时将并行数据转换成串行数据来传输;在接收数据时将接收到的串行数据转换成并行数据。
串口发送接收1bit数据时间称一个波特,如:波特率9600,波特是1/9600s,系统时钟50Mhz,时钟周期是20ns,在9600波特率下,一个波特等于5208个系统时钟周期((1s * 10^9ns)ns/9600)/20ns,也就是说每个bit数据在50Mhz时钟下保持5208个时钟周期,起始位和停止位都是一个波特时间,本次实例采用9600波特率。
串口数据RX发送模块
该模块的功能是将并行数据转化为串行数据。
RX接收模块框图:
理想波形设计:
RX接收模块编程代码:
module uart_rx
#(
parameter UART_BPS = 'd9600, //波特率9600
CLK_FREQ = 'd50_000_000 //时钟频率50MHZ
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx ,
output reg [7:0] po_data ,
output reg po_flag
);
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //接受1bit数据所需多少个脉冲周期
reg rx_reg1 ; //打一拍,同步时钟
reg rx_reg2 ; //打两拍,减小亚状态
reg rx_reg3 ; //打三拍,在次减小亚状态
reg start_flag ; //开始标志信号
reg work_en ; //计数使能信号
reg [15:0] baud_cnt ; //波特率计数器
reg bit_flag ; //bit计数信号
reg [3:0] bit_cnt ; //bit计数
reg [7:0] rx_data ; //传输移位bit
reg rx_flag ; //传输1帧完成信号
//rx_reg: 进行打拍操作,消除亚稳态
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
begin
rx_reg1 <= 1'b1;
rx_reg2 <= 1'b1;
rx_reg3 <= 1'b1;
end
else
begin
rx_reg1 <= rx ;
rx_reg2 <= rx_reg1 ;
rx_reg3 <= rx_reg2 ;
end
//start_nedge:开始传输数据的标志
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
start_flag <= 1'b0;
else if((rx_reg2 == 1'b0 && rx_reg3 == 1'b1 ) && (work_en == 1'b0))
start_flag <= 1'b1;
else
start_flag <= 1'b0;
//work_en:高电平传输数据使能
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
work_en <= 1'b0;
else if (start_flag == 1'b1)
work_en <= 1'b1;
else if ((bit_flag == 1'b1) && (bit_cnt == 4'd8))
work_en <= 1'b0;
else
work_en <= work_en;
//传输一位数据需要花 5208 脉冲周期
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
baud_cnt <= 16'd0;
else if (baud_cnt == BAUD_CNT_MAX - 1'b1)
baud_cnt <= 16'd0;
else if (work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 16'd0;
//bit_flag:接收数据标志
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
bit_flag <= 1'd0;
else if (( baud_cnt == (BAUD_CNT_MAX/2) - 1 ))
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
//bit_cnt:bit计数器,计数传输bit数据
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
bit_cnt <= 4'd0;
else if ((bit_flag == 1'b1) && (bit_cnt == 4'd8))
bit_cnt <= 4'd0;
else if (bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
//rx_fata:将数据传输移位
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
rx_data <= 1'b0;
else if ((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {rx_reg3, rx_data[7:1]};
// rx_flag:数据传输后的标志位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
//把传输移位的数据输出到po_data,并设置一个输出标志位
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
po_data <= 1'b0;
po_flag <= 1'b0;
end
else if (rx_flag == 1'b1)
begin
po_data <= rx_data;
po_flag <= 1'b1;
end
else
po_flag <= 1'b0;
endmodule
RX接收模块仿真代码:
`timescale 1ns/1ns
module tb_uart_rx();
reg sys_clk;
reg sys_rst_n;
reg rx;
wire [7:0] po_data;
wire po_flag;
always #10 sys_clk = ~sys_clk;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
rx <= 1'b1;
#20;
sys_rst_n <= 1'b1;
end
//模拟发送8次数据,分别为0~7
initial begin
#200
rx_bit(8'd0); //任务的调用,任务名+括号中要传递进任务的参数
rx_bit(8'd1);
rx_bit(8'd2);
rx_bit(8'd3);
rx_bit(8'd4);
rx_bit(8'd5);
rx_bit(8'd6);
rx_bit(8'd7);
end
task rx_bit(input [7:0] data);
integer i; //定义一个变量
//integer类型也是一种寄存器数据类型,integer类型的变量为有符号数
for(i=0; i<10; i=i+1) begin //不可用i=i++的方式
case(i)
0: rx <= 1'b0; //起始位
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7];
9: rx <= 1'b1;
endcase
#(5208*20); //每发送1位数据延时5208个时钟周期
end
endtask //任务以endtask结束
uart_rx uart_rx_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.po_data (po_data ),
.po_flag (po_flag )
);
endmodule
RX仿真图像显示:
串口数据TX发送模块
该模块的功能是将串行数据转化为并行数据。
TX接收模块框图:
理想波形图像:
TX发送模块编程代码:
module uart_tx
#(
parameter UART_BPS = 'd9600 ,
parameter CLK_FREQ = 'd50_000_000
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,
input wire pi_flag ,
output reg tx
);
reg work_en;
reg [15:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
work_en <= 1'b0;
else if(pi_flag == 1'b1)
work_en <= 1'b1;
else if((bit_flag == 1'b1) && (bit_cnt == 4'd9) )
work_en <= 1'b0;
//传输一位数据需要花 5208 脉冲周期
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
baud_cnt <= 1'b0;
else if (baud_cnt == BAUD_CNT_MAX - 1'b1)
baud_cnt <= 1'b0;
else if (work_en == 1'b1)
baud_cnt <= baud_cnt + 1'b1;
else
baud_cnt <= 1'b0;
//bit_flag:接收数据标志
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
bit_flag <= 1'b0;
else if (( baud_cnt == (BAUD_CNT_MAX/2) - 1 ))
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
bit_cnt <= 1'b0;
else if ((bit_flag == 1'b1) && (bit_cnt < 4'd9))
bit_cnt <= bit_cnt + 1'b1;
else if ((bit_flag == 1'b1) && (bit_cnt == 4'd9))
bit_cnt <= 1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n )
tx <= 1'b1;
else if(bit_flag == 1'b1)
case(bit_cnt)
0 : tx <= 1'b0; //起始位
1 : tx <= pi_data[bit_cnt - 1];
2 : tx <= pi_data[bit_cnt - 1];
3 : tx <= pi_data[bit_cnt - 1];
4 : tx <= pi_data[bit_cnt - 1];
5 : tx <= pi_data[bit_cnt - 1];
6 : tx <= pi_data[bit_cnt - 1];
7 : tx <= pi_data[bit_cnt - 1];
8 : tx <= pi_data[bit_cnt - 1];
9 : tx <= 1'b1 ; //结束位
default : tx <= 1'b1;
endcase
endmodule
TX发送模块仿真代码:
`timescale 1ns/1ns
module tb_uart_tx();
reg sys_clk;
reg sys_rst_n;
reg [7:0] pi_data;
reg pi_flag;
wire tx;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20;
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
initial begin
pi_data <= 8'b0;
pi_flag <= 1'b0;
#200
//发送数据0
pi_data <= 8'd0;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧数据为10bit
//所以需要数据延时(5208*20*10)后再产生下一个数据
#(5208*20*10);
//发送数据1
pi_data <= 8'd1;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据2
pi_data <= 8'd2;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据3
pi_data <= 8'd3;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据4
pi_data <= 8'd4;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据5
pi_data <= 8'd5;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据6
pi_data <= 8'd6;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
#(5208*20*10);
//发送数据7
pi_data <= 8'd7;
pi_flag <= 1'b1;
#20
pi_flag <= 1'b0;
end
uart_tx uart_tx_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_data (pi_data ),
.pi_flag (pi_flag ),
.tx (tx )
);
endmodule
RX和TX仿真波形图:
顶层模块代码:
module rs232
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire rx , //串口接收数据
output wire tx //串口发送数据
);
parameter UART_BPS = 20'd9600 , //波特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
wire [7:0] po_data;
wire po_flag;
//串口接受数据模块
uart_rx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_rx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.po_data (po_data ),
.po_flag (po_flag )
);
//串口发送数据模块
uart_tx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_tx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_data (po_data ),
.pi_flag (po_flag ),
.tx (tx )
);
endmodule
顶层仿真:
`timescale 1ns/1ns
module tb_rs232();
wire tx ;
reg sys_clk ;
reg sys_rst_n ;
reg rx ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
rx <= 1'b1;
#20;
sys_rst_n <= 1'b1;
end
//初始化任务函数
initial begin
#200
rx_byte(); //调用任务rx_byte
end
always #10 sys_clk = ~sys_clk;
//创建任务rx_byte,本次任务调用rx_bit任务,发送8次数据,分别为0~7
task rx_byte(); //因为不需要外部传递参数,所以括号中没有输入
integer j;
for(j=0; j<8; j=j+1) //调用8次rx_bit任务,每次发送的值从0变化7。for 括号中最后执行的内容不可写成i= i++
rx_bit(j);
endtask
//创建任务rx_bit,每次发送的数据有10位,data的值分别为0到7由j的值传递进来
task rx_bit(input [7:0] data);
integer i;
for(i=0; i<10; i=i+1) begin
case(i)
0: rx <= 1'b0; //起始位为0
1: rx <= data[0];
2: rx <= data[1];
3: rx <= data[2];
4: rx <= data[3];
5: rx <= data[4];
6: rx <= data[5];
7: rx <= data[6];
8: rx <= data[7];
9: rx <= 1'b1; //终止位为1
endcase
#(5208*20); //每发送1位数据延时5208个时钟周期
end
endtask
rs232 rs232_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.rx (rx ),
.tx (tx )
);
endmodule
仿真波形: