USART介绍
USART(通用同步异步收发器)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个UART,它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。
USART主要特性
● 全双工的,异步通信
● 分数波特率发生器系统
─ 发送和接收共用的可编程波特率,最高达4.5Mbits/s
● 可编程数据字长度(8位或9位)
● 可配置的停止位-支持1或2个停止位
● 发送方为同步传输提供时钟
● 单独的发送器和接收器使能位
● 检测标志
─ 接收缓冲器满
─ 发送缓冲器空
─ 传输结束标志
● 校验控制
─ 发送校验位
─ 对接收数据进行校验
● 四个错误检测标志
USART功能
任何USART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。
● 总线在发送或接收前应处于空闲状态
● 一个起始位
● 一个数据字(8或9位),最低有效位在前
● 0.5,1.5,2个的停止位,由此表明数据帧的结束
● 使用分数波特率发生器 —— 12位整数和4位小数的表示方法。
● 一个状态寄存器(USART_SR)
● 数据寄存器(USART_DR)
● 一个波特率寄存器(USART_BRR),12位的整数和4位小数
USART代码配置
void usart_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//GPIO配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//规定推挽复用输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//规定浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART配置
USART_InitStructure.USART_BaudRate=9600; //波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件数据流控制,一般设置为无(不使用)
USART_InitStructure.USART_Mode= USART_Mode_Tx|USART_Mode_Rx; //都使能
USART_InitStructure.USART_Parity=USART_Parity_No; //奇偶校验
USART_InitStructure.USART_WordLength=USART_WordLength_8b; //字长
USART_InitStructure.USART_StopBits=USART_StopBits_1; //停止位
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);//开启USART串口
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收区中断
//NVIC配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_Init(&NVIC_InitStructure);
}
由于使用的库函数版所以可直接调用函数。
1.要使能GPOA和USART1,
2.根据数据手册,配置PA9(TX)必须是GPIO_Mode_AF_PP(推挽复用输出),配置PA10(RX)必须是GPIO_Mode_IN_FLOATING(规定浮空输入),至于引脚速度没有必须要求。
3.USART配置
我们使用的是异步的通信所以要提前配置好双方的波特率USART_InitStructure.USART_BaudRate=9600; (在串口助手上也要调制9600否则可能出现乱码)
USART_InitStructure.USART_Mode= USART_Mode_Tx|USART_Mode_Rx; 使能收发数据
串口我们可以配置奇偶校验,有8位数据区,加一个停止位
USART_InitStructure.USART_Parity=USART_Parity_No; //无奇偶校验
USART_InitStructure.USART_WordLength=USART_WordLength_8b; //字长
USART_InitStructure.USART_StopBits=USART_StopBits_1; //停止位
一定要记得开启USART串口和接收区中断
USART_Cmd(USART1,ENABLE);//开启USART串口
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收区中断
4.EXTI中断配置,最好是在主函数提前声明一下NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);中断分组,这样可以使代码更加稳定。
USART服务函数
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清除中断标志
Res=USART_ReceiveData(USART1); //读取接收到的数据
USART_SendData(USART1,Res);//回传接收到的数据
LED2_OFF;
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送完毕
Delay_1ms(10);
LED2_ON; //LED灯闪烁,接收成功发送完成
if(Res=='1')//接收到1,点亮LED
{
LED1_ON;
}
else//其他情况熄灭LED
{
LED1_OFF;
}
}
}
此服务函数代表,进入中断,每次发送数据,LED2可以闪烁一次,当发送’1’时LED1亮,发送其他数据LED1灭。
单片机通过USART1发送数据
USART的配置还是上述代码,只不过是服务函数有所变更
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
while( (USART_GetFlagStatus(USART1,USART_FLAG_TC)) !=SET);//等待发送完毕
USART_SendData(USART1,*str++);
}
}
可以将此函数发在主函数的while(1)循环中,他就可以不停的向串口发送数据。
在这里注意:等待发送完毕有两种写法while( (USART_GetFlagStatus(USART1,USART_FLAG_TC)) !=SET);和while( (USART_GetFlagStatus(USART1,USART_FLAG_TXE)) !=SET);
USART_FLAG_TC是干嘛用的呢?
当发送移位寄存器中的1字节数据已经通过TX脚一位一位的移出去后,该标志位就会被置1,从而引发该事件的中断。所以,其实USART_FLAG_TC就是用来标志“发送移位寄存器中的数据有没有全部发送出去”这件事。
USART_FLAG_TXE是干嘛用的呢?
当发送数据寄存器中的数据已经取完了,该标志位就会被置1,从而引发该事件的中断。所以,其实USART_FLAG_TXE就是用来标志一个事件的,通过这个标志可以知道该事件有没有发生(即发送数据寄存器中的数据有没有被取走)。
下面讲述在不同代码写法下,得到不同实验效果【调试助手接收数据】:
1.常见写法一
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
while( (USART_GetFlagStatus(USART1,USART_FLAG_TXE)) !=SET);//等待发送完毕
USART_SendData(USART1,*str++);
}
}
这种写法在不是特殊情况下,问题不大,USART数据会成功发送出去。 但是在上面说的特殊情况下,问题就来了,代码只将数据放到了发送缓冲区,而没有发送出去就掉电或待机了,这个时候其实最后两个字符是没有发送出去的。
2.常见写法二
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
while( (USART_GetFlagStatus(USART1,USART_FLAG_TC)) !=SET);//等待发送完毕
USART_SendData(USART1,*str++);
}
}
这种写法达到的效果和上面存在不同的就是倒数第二个数据发送出去了,也就是只有最后一个字符是没有发送出去的。
3.常见写法三
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
USART_SendData(USART1,*str++);
while( (USART_GetFlagStatus(USART1,USART_FLAG_TXE)) !=SET);//等待发送完毕
}
}
这种写法达到的效果和上面两种写法有不一样,发送了10个字符。
4.写法四
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
USART_SendData(USART1,*str++);
while( (USART_GetFlagStatus(USART1,USART_FLAG_TC)) !=SET);//等待发送完毕
}
}
这种写法按理说可以实现功能,但实际多次试验结果确实第一字节数据丢失了。
5.写法五(正确写法)
void usart1_sendstr(char *str)
{
while( (*str) != '\0')
{
while( (USART_GetFlagStatus(USART1,USART_FLAG_TXE)) !=SET);//等待发送完毕
USART_SendData(USART1,*str++);
while( (USART_GetFlagStatus(USART1,USART_FLAG_TC)) !=SET);//等待发送完毕
}
}
![在这里插入图
这种写法是比较确保发送完整数据的