什么是UART串口
全称通用异步收发器,是一种串行异步的通信协议,该协议规定了传输数据时数据的传输方式及其所使用的信号。
RS232标准:+3~+15低电平 -3~-15高电平
RS485标准:差分信号D+D-,D+ - D- +2~+6低电平 -2~-6高电平
TTL电平信号:工作在5v电压下,输出>2.4v高电平,<0.5v低电平,输入>2.0v高电平,<0.8v低电平。2.4v与5v有较大差距
LVTTL:工作在3.3v电压下,输出>2.4v高电平,<0.5v低电平,输入>2.0v高电平,<0.8v低电平。2.4v与5v有较大差距
串口细节梳理
UART:串口协议
UART控制器:实现UART协议收发数据的逻辑电路
RS232/RS485:电气接口标准,规定了电气标准和接线顺序
DB9接口:用于RS232电气接口通信的物理接线连接器
USB转TLL串口:将UART协议数据转换为USB接口信号进行传输的桥接芯片
我们是遵循,使用协议;电气接口属于硬件电路设计范畴;我们要做的是实现UART逻辑控制器
UART发数据
若UART发送器没有数据发送,处于空闲态,此时TX保持高电平,需要发送数据时,先将TX信号拉低,保持一个位时间。一位起始位时间标志着数据开始传输,紧接着传输数据,每一位bit数据传输时间和起始位位时间相同,当最后一位数据发送完成后,TX拉高并保持一个位时间的长度,标志着一轮数据发送完。发送一个8位数据,包括起始位和结束位,则一轮数据加两位标志位是10个bit,即10个码元。
波特率:每秒中传输的码元个数,每个码元占用时间为(1/波特率)秒
奇偶检验:通过在数据位后接一位校验位来确保数据+校验位中的”1“的个数位奇偶个。
UART串口发送逻辑设计要点分析
设计任务
设计一个串口发送模块,发送用户输入的数据给电脑,要求:
- 波特率位9600
- 8位数据位
- 1位停止位
- 无校验位
- 每1秒发送一次当前8位拨码开关的值
- 每次发送完成后将led0的状态翻转
模块端口
uart_tx:用来发送的数据信号
led:发送一次让led翻转
data[7:0]:8位拨码开关的值,由用户输入
波特率:9600,每一码元发送的最小时间单位是1/9600 秒,需要计数一轮耗费时间为 1/9600 的计数器
发送的数据8位:先发一位起始位,再发送8位数据,再发送一个停止位
- 要求1:串口每1秒发送一次数据:
思路:设计一个1秒的计数器,此计数器不会停止,每计数到1秒,让串口发送一次数据,再回到初始0继续计数。
-
要求2:串口发送内容为启动发送时,8位拨码开关的值
启动信号发送时,此时八位开关的状态被寄存下来,不会因输入改变而改变。
思路:使用8位的D触发器对拨码开关的值进行缓存
-
要求3:串口每发送完一次,翻转led状态
-
思路:通过线性序列机,当串口发送完成时,翻转led
UART发数模块主要功能点
//最小位时间,波特率计数器
//最小位时间,波特率计数器 1/9600 * 1_000_000_000 /20 -1
always@(posedge clk or negedge rst_n)
if(!rst_n)
en_counter_bit <= 0;
else if(counter1 == maxcnt1 - 1'd1) //计满1s,发送开始时刻
en_counter_bit <= 1;
else if((counter_10 == 9) && (counter_bit == maxcntbit - 1)) //发送结束时刻
en_counter_bit <= 0;
else
en_counter_bit <= en_counter_bit;
always@(posedge clk or negedge rst_n)
if(!rst_n)
counter_bit <= 0;
else if(en_counter_bit) begin
if(counter_bit == 13'd5208 - 1'd1)
counter_bit <= 0;
else
counter_bit <= counter_bit + 1'd1;
end
else
counter_bit <= 0;
添加使能信号,en_counter_bit,使能信号为0时,标志还未发送数据,发送处于空闲态。
//位计数器,发送哪十位中的哪一位数据由该计数器确定
//延时计数器,1秒钟不停止
//延时计数器,1秒钟不停止
always@(posedge clk or negedge rst_n)
if(!rst_n)
counter1 <= 0;
else if(counter1 == maxcnt1 - 1'd1)
counter1 <= 0;
else
counter1 <= counter1 + 1'd1;
//位计数器,记到10
//位计数器,发送哪十位中的哪一位数据由该计数器确定
always@(posedge clk or negedge rst_n)
if(!rst_n)
counter_10 <= 0;
else if(counter_bit == 13'd5208 -1'd1) begin
if(counter_10 == 10 - 1'd1)
counter_10 <= 0;
else
counter_10 <= counter_10 + 1'd1;
end
else
counter_10 <= counter_10;
//位 发送/选择 逻辑
//位发送逻辑
always@(posedge clk or negedge rst_n)
if(!rst_n)
r_data <= 0;
else if(counter1 == maxcnt1 - 1)
r_data <= data;
always@(posedge clk or negedge rst_n)
if(!rst_n)
uart_tx <= 1; //空闲态为高电平
else if(en_counter_bit == 0)
uart_tx <= 1;
else begin
case(counter_10)
0:uart_tx <= 0;
1:uart_tx <=r_data[0];
2:uart_tx <=r_data[1];
3:uart_tx <=r_data[2];
4:uart_tx <=r_data[3];
5:uart_tx <=r_data[4];
6:uart_tx <=r_data[5];
7:uart_tx <=r_data[6];
8:uart_tx <=r_data[7];
9:uart_tx <= 1;
default:uart_tx <= uart_tx;
endcase
end
//led翻转逻辑
//led翻转逻辑
always@(posedge clk or negedge rst_n)
if(!rst_n)
led <= 0;
else if((counter_10 == 9) && (counter_bit == 5207))
led <= ~led;
仿真截图
在en_counter_bit使能有效,即开始发数据的时刻,r_data中存储的数据8‘d0101_0101,由低到高依次传输,起始位低电平,结束位高电平
逻辑优化
发送信号由外部逻辑提供
定义一个输入端口 input send_go;
定义一个输出端口 tx_done; 当counter109 && counter_bit5208时,拉高该信号,标志当前发数完成。
位发送逻辑中 else if(counter1 == maxcnt1 - 1) r_data <= data;
表示延时计数器计数到1秒,即发数开始时刻,锁存data到r_data。修改逻辑为
else if(send_go)
assign tx_done = (counter109 && counter_bit5208);