RS-232是常用的传输接口,是硬件学习的入门级接口。
一、接口特性
常见的9脚接口管脚分配如下图,参考链接:http://zh.wikipedia.org/wiki/RS-232
DE-9 Male (Pin Side) DE-9 Female (Pin Side)
------------- -------------
\ 1 2 3 4 5 / \ 5 4 3 2 1 /
\ 6 7 8 9 / \ 9 8 7 6 /
--------- ---------
信号 | DB-25 | DE-9 | EIA/TIA 561 | Yost |
---|---|---|---|---|
公共接地 | 7 | 5 | 4 | 4,5 |
发送数据(TD、TXD) | 2 | 3 | 6 | 3 |
接受数据(RD、RXD) | 3 | 2 | 5 | 6 |
数据终端准备(DTR) | 20 | 4 | 3 | 2 |
数据准备好(DSR) | 6 | 6 | 1 | 7 |
请求发送(RTS) | 4 | 7 | 8 | 1 |
清除发送(CTS) | 5 | 8 | 7 | 8 |
数据载波检测(DCD) | 8 | 1 | 2 | 7 |
振铃指示(RI) | 22 | 9 | 1 | - |
RS-232设计之初是用来连接调制解调器做传输之用,也因此它的脚位意义通常也和调制解调器传输有关,而我们做硬件与PC接口通常只用到两个管脚即可:TXD,RXD.
由于发送接收数据分别采用两条串行总线进行传输,因此rs232接口是可以全双工工作的,最大速率可达10KB/s.
根据美国电子工业学会(EIA)的标准。在TxD和RxD上:
逻辑1(MARK) =-3V~-15V
逻辑0(SPACE)=+3~+15V
二、异步通信协议
S-232使用异步通讯协议。也就是说数据的传输没有时钟信号。接收端必须有某种方式,使之与接收数据同步。
对于RS-232来说,是这样处理的:
1、串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等)
2、当没有数据传输的时候,发送端向数据线上发送"1"
3、每传输一个字节之前,发送端先发送一个"0"来表示传输已经开始。这样接收端便可以知道有数据到来了。
4、开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步
5、每次传输完成一个字节之后,都在其后发送一个停止位("1")
1、串行线缆的两端事先约定好串行传输的参数(传输速度、传输格式等)
2、当没有数据传输的时候,发送端向数据线上发送"1"
3、每传输一个字节之前,发送端先发送一个"0"来表示传输已经开始。这样接收端便可以知道有数据到来了。
4、开始传输后,数据以约定的速度和格式传输,所以接收端可以与之同步
5、每次传输完成一个字节之后,都在其后发送一个停止位("1")
与我前一篇博客中的ps2接口类似的是,数据都是通过一条总线传输,传输有效数据之前会先发送一个"0“,然后是一个或多个字节的有效数据(低位在前,高位在后),
接着发送的是校验位,与ps2接口不同的是该校验位可以是奇校验也可以是偶校验也可以无校验位。最后发送的值是高电平。
不同之处是,ps2接口有发送时钟,设备发送的数据在下降沿有效,而串口是没有发送时钟的。不过我们可以通过提取数据下降沿的方式构造出有效采样时钟。
数据的传输速度是用波特来描述的,亦即每秒钟传输的数据位,例如1000波特表示每秒钟传输1000比特的数据, 或者说每个数据位持续1毫秒。
波特率不是随意的,必须服从一定的标准,常用的串行传输速率值包括以下几种:
1200 波特.
9600 波特.
38400 波特.
115200 波特 (通常情况下是你可以使用的最高速度)
波特率不是随意的,必须服从一定的标准,常用的串行传输速率值包括以下几种:
1200 波特.
9600 波特.
38400 波特.
115200 波特 (通常情况下是你可以使用的最高速度)
三、接收模块设计
接收模块设计主要包括了有三个部分:
波特率产生部分:这一部分主要是收到串口传输有效信号rs_ena后,每间隔一定时间(波特率时间间隔)就产生一个时钟周期的(系统时钟)高电平rs_clk,
使得高电平时刻落在可采用串口数据的中间时刻点,以供接收时提供采样时刻。
数据接收部分:这一部分的主要功能是对接收串行信号进行处理,提取到串行信号的下降沿,在下降沿处启动一个计数器。该计数器范围为0-11,在计数器有效范围
内令rs_ena为高电平传给第一部分,然后利用第一部分输出的rs_clk,在rs_clk有效位置将接收数据传给缓存区,首先接收的数据放在低存储位,等计数器计满后将buff
里的值传给输出寄存器。同时rs_ena,计数器等清零。等待下一次传输的到来。
数码管显示部分:这一部分是前面工作的内容,可以参加博客:http://blog.csdn.net/baijingdong/article/details/20220363
第一部分代码:
module rs_clk_gen(
clk,rst,rs_clk,rs_ena
);
input clk;//系统时钟
input rst;//复位信号
input rs_ena; //串口通信允许信号
output rs_clk; //输出允许的波特率信号时钟
parameter N1=5207;//5208,9600bps 50M的系统时钟;
parameter N2=2603;//2604,这里是0-2603,共计数2604次
reg rs_clk='b0;
reg [13:0] count='d0;//计数器
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
count<='d0; //复位信号到来时,count计数器清零
end
else
if(count==N1 || !rs_ena) count<='d0;//当count计满或者无串口通信使能时count都不计数
else count<=count+'b1;//当且仅当count不为0,通信使能时count计数。
end
always @(posedge clk or negedge rst)
begin
if (!rst)
rs_clk<='b0;
else
if(count==N2&&rs_ena)//当且仅当count计数到一半且通信使能时允许时钟翻转
rs_clk<='b1; //
else
rs_clk<='b0;//使得rs_clk是一个小波峰的时钟信号,在有效信号的数据位为高电平。
end
endmodule
第一部分的仿真结果:由于分频系数太大,不容易观察,这里采用N1=3,N2=2来仿真。仿真之后要改回不然会出错。
接收模块代码
module rs_receive(
input rx_data,//接收到的串口数据
input clk,
input rst,
output [7:0] byte_data_out,//接收完成的一字节数据输出
input rs_clk,//输入的数据有效采样时刻时钟
output reg rs_ena='b0//有效数据到来标志
);
//++++++++++++++++++++++++++++++++++++++++++++++
reg rx_data0='b0;
reg rx_data1='b0;
reg rx_data2='b0;//声明寄存器用于提取有效数据的下降沿;
wire neg_rx_data=~rx_data1 & rx_data2;//下降沿时该信号电平为高
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
rx_data0<='b0;
rx_data1<='b0;
rx_data2<='b0;
end
else
begin
rx_data0<=rx_data;
rx_data1<=rx_data0;
rx_data2<=rx_data1;
end
end
//+++++++++++++++++++++++++++++++++++++++++++++
reg rx_int='b0;//接收中断信号
//reg rs_ena;
reg [3:0]num='d0;
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
rx_int<='b0;
rs_ena<='bz;
end
else
if(neg_rx_data)
begin
rx_int<='b1;//下降沿到来时,产生接收中断--此期间即使有下降沿到来也不理会,此时使能模块1,即rs_ena为高电平。
rs_ena<='b1;
end
else
if(num=='d10)
begin
rx_int<='b0;//num==10,即数据接收完成时,中断结束。
rs_ena<='b0;
end
end
reg [7:0]rx_data_buf='d0;//接收信号缓存区,由于数据一位一位传输,不能立刻赋值输出,用于缓存数据,到接收完成再赋值给输出
reg [7:0]data_byte_r='d0;//寄存器型,用于完成赋值,最后赋给输出。
assign byte_data_out=data_byte_r;
always @(posedge clk or negedge rst)
begin
if(!rst)
num<='d0;
else
if(rx_int)
begin
if(rs_clk)//在接收中断的情况下,到来一次采样时钟进行一次如下运算,
begin
num<=num+1'b1;
case(num)
'd1:rx_data_buf[0]<=rx_data;
'd2:rx_data_buf[1]<=rx_data;
'd3:rx_data_buf[2]<=rx_data;
'd4:rx_data_buf[3]<=rx_data;//数据依次加入到缓存区
'd5:rx_data_buf[4]<=rx_data;
'd6:rx_data_buf[5]<=rx_data;
'd7:rx_data_buf[6]<=rx_data;
'd8:rx_data_buf[7]<=rx_data;
default:;
endcase
end
else
if(num=='d10)
begin//数据接收完成,将缓存数据交给寄存器,同时计数器清零
num<='d0;
data_byte_r<=rx_data_buf;
end
end
end
endmodule
顶层模块代码:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: bupt abs_lab
// Engineer: mr.bai
//
// Create Date: 18:44:58 03/03/2014
// Design Name: rs232_receive_module
// Module Name: rs232_top
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module rs232_top(
input clk,
input rst,
input rs232_rx,
output reg [7:0] sm_seg,
output reg [1:0]sm_bit='b01
);
wire rs_clk,rs_ena;
rs_clk_gen M1(
.clk(clk),.rst(rst),.rs_clk(rs_clk),.rs_ena(rs_ena)
);
wire [7:0] byte_data_out;
rs_receive M2(
.rx_data(rs232_rx),
.clk(clk),
.rst(rst),
.byte_data_out(byte_data_out),
.rs_clk(rs_clk),
.rs_ena(rs_ena)
);
//====================数码管显示接收数据部分===================================
parameter N2=50000;
reg clk3=1'b0;
reg [16:0]count3=17'd0;
//assign clk_out=clk3;
always @(posedge clk or negedge rst)
begin
if (!rst)
begin
count3<=17'd0;
clk3<=1'b0;
end
else
if(count3<N2-1)
begin
count3<=count3+1'b1;
if(count3<(N2/2-1))
clk3<=1'b0;
else
clk3<=1'b1;
end
else
begin
count3<=17'd0;
clk3<=1'b0;
end
end
//==================state select================
reg[3:0] Num;
always @(posedge clk3)
begin
case (sm_bit)
'b01: begin
Num<=byte_data_out[7:4];
sm_bit<='b10;
end
'b10: begin
Num<=byte_data_out[3:0];
sm_bit<='b01;
end
default:
Num<='d0;
endcase
end
//=========================================================
always @ (Num)//
begin
case (Num)
4'h0 : sm_seg = 8'h3f; // "0"
4'h1 : sm_seg = 8'h06; // "1"
4'h2 : sm_seg = 8'h5b; // "2"
4'h3 : sm_seg = 8'h4f; // "3"
4'h4 : sm_seg = 8'h66; // "4"
4'h5 : sm_seg = 8'h6d; // "5"//共阴极数码管表
4'h6 : sm_seg = 8'h7d; // "6"
4'h7 : sm_seg = 8'h07; // "7"
4'h8 : sm_seg = 8'h7f; // "8"
4'h9 : sm_seg = 8'h6f; // "9"
4'ha : sm_seg = 8'h77; // "a"
4'hb : sm_seg = 8'h7c; // "b"
4'hc : sm_seg = 8'h39; // "c"
4'hd : sm_seg = 8'h5e; // "d"
4'he : sm_seg = 8'h79; // "e"
4'hf : sm_seg = 8'h71; // "f"
endcase
end
//==============================================
endmodule
综合仿真之后效果:在pc端输入 A, B, C, 1, 2, 3等数据,串口调试器传输出的是该数据对应的16进制ASCII码值;
A对应的16进制ascii码为41H,数码管显示输出正确