一、UART帧格式详解
1.UART(串口控制器)
是总线协议,通信协议
通用异步收发器,是一种通用的串行、异步通信总线。该总线有两条数据线,可以实现全双工的发送和接收,在嵌入式系统中常用于主机与辅助设备之间的、芯片与外围设备通信
2.通信基础-并行和串行
总线-导线,传递高低电频信号,
并行通信:通过总线将不同数据通过不同线一次性发送
串行通信:一根线传递”8“位,逐次传递
3.通信基础-单工 和双工
单工:单向通信
双工:双向通信
半双工:A与B不能同时发送数据,一根总线;全双工:A和B可以同时发送数据,两根总线。
4.波特率
波特率用于描述UART通信时的通信速度,其单位为 bps(bit per second)即每秒钟传送的bit(二进制位)的数量(不是字节),8 bit =1 byte
UART帧格式.
规定:空闲状态数据线上状态是高电平
A0x55-> B 0 10101010
发送前要发送一个信号告诉对方要开始发送,所以起始位 低-位
串行总线:先发低位再发高位,最多8位,避免累计误差
奇偶校验,校验位1表示偶数个1,不能修正错位
串口只能发一个字节,多字节不能连续发送,发完一个需要停止位后再发
如何区分发送的是01还是0011 :
用波特率,一秒传输的二进制位,发送方和接收方双发都要掐表计算(比如发送一个1是1秒)
异步通信,所以有误差
UART硬件连接
UART 控制器
一般情况下处理器中都会集成UART控制器 我们使用UART进行通信时候只需对其内部的相关寄存器进行设置即可
二、Exynos4412下的UART控制器
只用了两个接口
可以看到一个引脚很多功能,使用串口的时候需要选择相应的功能
引脚的功能设置
设置引脚功能的实质是让引脚在芯片内部连接到某一个对应的控制器上
1.4412拥有四个独立通道,提供一专用通道与GPS通信Ch4,5个串口;最大波特率4M;先进先出(队列)
2.发送数据,先进先出(类似队列)TXD
3.接受数据,RXD
4.UART包括:
- 波特率可修改
- 红外线(无线)通信
- 1/2的停止位
- 5.6.7.8位的数据宽度和 校验位
每个串口控制器包括
- 波特率发生器,控制收发速率SCLK_UART:时钟,100M的速度
- 发送器和接收器。
- 发送数据,数据先进入发送队列,再被到拷贝到发送移位器,数据通过发送数据的引脚TXDn发送给出去;接受数据,数据从接收数据的引脚RXDn引入到接收移位器中,拷贝至接收移位器
- 控制单元
发送数据:发送54321,
接收数据
3. UART寄存器详解
- 设置引脚功能
-
串口相关寄存器
- ULCONn:控制串口功能,n表示使用第几个串口,这里是2
-
/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/ GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
- UCONn
- 设置收发模式
/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/ UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);
- 轮寻:cpu不断主动查看FIFO是否有数据来
- 中断:串口控制器去通知CPU
- DMA:直接将接收的数据放到内存中不经过cpu
- UTRSTATn
-
/*等待发送寄存器为空,即上一个数据已经发送完成 UTRSTAT2[1]*/ while(!(UART2.UTRSTAT2 & (1 << 1)));
- UTXHn和URXHn
- 接收和发送
-
//将要发送的数据写入发射寄存器 UTXH2就能发送过去 UART2.UTXH2 = 'A'; //SecureCRT默认只能显示字符不能显示数字
- 设置波特率
如何设置波特率
/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
UART2.UBRDIV2 = 53;
UART2.UFRACVAL2 = 4;
四、 UART编程
#include "exynos_4412.h"
int main()
{
/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/
GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
/*2.设置UART2的帧格式 8位数据位 1位停止位 无校验 正常模式 ULCON2[6:0]*/
UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0); //7位清零 0111 1111 = ox7F
/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/
UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);
/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
UART2.UBRDIV2 = 53; //(100000000 / (115200 x 16))-1 = 53.25
UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
while(1)
{
//将要发送的数据写入发射寄存器 UTXH2就能发送过去
UART2.UTXH2 = 'A'; //SecureCRT默认只能显示字符不能显示数字
//UART2.UTXH2 = 'B'; //SecureCRT默认只能显示字符不能显示数字
//UART2.UTXH2 = 'C'; //SecureCRT默认只能显示字符不能显示数字
//UART2.UTXH2 = 'D'; //SecureCRT默认只能显示字符不能显示数字
}
return 0;
}
现象:终端循环发送
原因:cpu写入寄存器TXH速度比发送速率115200快很多,发送一次cpu写入可能上万次,所以当TXH发完一个后再去获取CPU发来的数据,无法确定具体的数据
解决办法:通过读取状态寄存器的值来判断是否写入THX中
void UART_Send_Byte(char Dat)
{
/*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
while(!(UART2.UTRSTAT2 & (1 << 1))); //(UART2.UTRSTAT2 & (1 << 1)这个条件表示这个寄存器第一位的状态,不为空,等待
{
//将要发送的数据写入发射寄存器 UTXH2就能发送过去
UART2.UTXH2 = Data; //SecureCRT默认只能显示字符不能显示数字
}
}
#include "exynos_4412.h"
void UART_Init(void)
{
/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/
GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
/*2.设置UART2的帧格式 8位数据位 1位停止位 无校验 正常模式 ULCON2[6:0]*/
UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0); //7位清零 0111 1111 = ox7f
/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/
UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);
/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
UART2.UBRDIV2 = 53; //(100000000 / (115200 x 16))-1 = 53.25
UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
}
void UART_Send_Byte(char Dat)
{
/*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
{
//将要发送的数据写入发射寄存器 UTXH2就能发送过去
UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
}
}
char UART_Rec_Byte(void)
{
char Dat = 0;
/*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
if(UART2.UTRSTAT2 & 1) // 0位不需要移动
{
/*从接收寄存器中读取接收到的数据 URXH2*/
Dat = UART2.URXH2;
return Dat;
}
else
return 0;
}
int main()
{
UART_Init();
char RecDat = 0;
while(1)
{
RecDat = UART_Rec_Byte();
if(RecDat == 0)
{
}
else
{
RecDat = RecDat+1;
UART_Send_Byte(RecDat);
}
}
return 0;
}
五、输入输出重定向
#include "exynos_4412.h"
void UART_Init(void)
{
/*1.将GPA1_0和GPA1_1设置成UART2的接收和发送引脚 GPA1CON[7:0]*/
GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);
/*2.设置UART2的帧格式 8位数据位 1位停止位 无校验 正常模式 ULCON2[6:0]*/
UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0); //7位清零 0111 1111 = ox7f
/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:0]*/
UART2.UCON2 = UART2.UCON2 &( ~(0xF << 0)) | (0x5 << 0);
/*4.设置UART2的波特率为115200 UBRDIV2/UFRACVAL2*/
UART2.UBRDIV2 = 53; //(100000000 / (115200 x 16))-1 = 53.25
UART2.UFRACVAL2 = 4; // 0.25 X 16 = 4
}
void UART_Send_Byte(char Dat)
{
/*等待发送寄存器为空,即上一个数据已经发送完 UTRSTAT2[1]*/
while(!(UART2.UTRSTAT2 & (1 << 1))); //这个条件表示这个寄存器第一位的状态
{
//将要发送的数据写入发射寄存器 UTXH2就能发送过去
UART2.UTXH2 = Dat; //SecureCRT默认只能显示字符不能显示数字
}
}
char UART_Rec_Byte(void)
{
char Dat = 0;
/*判断接收寄存器是否接收到了数据 UTRSTAT2[0]*/
if(UART2.UTRSTAT2 & 1) // 0位不需要移动
{
/*从接收寄存器中读取接收到的数据 URXH2*/
Dat = UART2.URXH2;
return Dat;
}
else
return 0;
}
//
//发送字符串
void UART_Send_Str(char * pstr)
{
while(*pstr != '\0')
UART_Send_Byte(*pstr++);
}
int main()
{
UART_Init();
char RecDat = 0;
while(1)
{
UART_Send_Str("Hello World\n");
}
return 0;
}
现象: 终端输出不是顶个换行的hello world
使用printf
函数,资料中的printf 已由写好;以前所用的printf 是c库中的printf,
自己写的printf和c库的printf输出定向不同:c库是定向到显卡,显卡再控制屏幕,再屏幕上显示出来;自己写的是定向到串口
int main()
{
UART_Init();
char RecDat = 0;
while(1)
{
printf("你好");
}
return 0;