1. 概述
1.1 处理器与外部设备通信方式
1.1.1并行通信
- 传输原理:数据各个位同时传输。
- 优点:速度快
- 缺点:占用引脚资源多
1.1.2 串行通信(本文重点讲解此通信方式)
- 传输原理:数据按位顺序传输
- 优点:占用引脚资源少
- 缺点:速度相对较慢
1.2 串行通信的数据传输方向
1.2.1 单工
数据传输只支持数据在一个方向上的传输
1.2.2 半双工
允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,他实际上是一种切换方向的单工通信
1.2.3 全双工
允许数据同时在两个方向上传输,因此,全双工通信时两个单工通信方式的结合,他要求发送设备和接收设备都有独立的接收和发送能力
1.3 串行通信的通信方式
1.3.1 同步通信
- 带时钟同步信号传输(SPI,IIC通信接口)
1.3.2 异步通信
- 不带时钟同步信号(UART:通用异步收发器,单总线)
在STM32中有UART和USART两种收发器,其中UART为通用异步收发器,USART为通用同步异步收发器(即可用与同步或者异步收发)
1.4 UART异步通信
1.4.1 引脚连接方法
1.4.2 特点
- 全双工异步通信。
- 分数波特率发生器系统,提供精确的波特率。
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s - 可编程的数据字长度(8位或者9位);
- 可配置的停止位(支持1或者2位停止位);
- 可配置的使用DMA多缓冲器通信。
- 单独的发送器和接收器使能位。
- 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
- 多个带标志的中断源。触发中断。
- 其他:校验控制,四个错误检测标志。
1.4.3 通信过程
2.思路
异步通信在通信时需要先约定好波特率,才能够确保发送和接收数据的完整。
在程序中需要将串口初始化并且使能串口,若需要接收到数据后产生中断则需要加上中断使能函数。
参考正点原子的教程,其还增加了一个校验的功能(其实正常的通信都应该要加上起始位和结束位,确保数据的正确性),定义了一个函数:
bit13~0:接收到的有效数据个数,bit14:接收到0X0D标志,bit15:接收0X0A完成标志。
3.寄存器说明
3.1 状态寄存器(USART_SR)
RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式: 1)读 USART_SR,写USART_DR。 2)直接向该位写 0。
3.2 数据寄存器(USART_DR)
DR(8:0):包含了发送或接收的数据。
3.2 波特比率寄存器(USART_BRR)
4.库函数说明
项目 | 功能 |
---|---|
void USART_Init() | 串口初始化:波特率,数据字长奇偶校验,硬件流控以及收发使能 |
void USART_Cmd() | 使能串口 |
void USART_ITConfig() | 使能相关中断 |
void USART_SendData() | 发送数据到串口,DR |
uint16_t USART_ReceiveData() | 接受数据,从DR读取接受到的数据 |
FlagStatus USART_GetFlagStatus() | 获取状态标志位 |
void USART_ClearFlag() | 清除状态标志位 |
ITStatus USART_GetITStatus() | 获取中断状态标志位 |
void USART_ClearITPendingBit() | 清除中断状态标志位 |
5. 串口配置代码
- 串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
- 串口复位:USART_DeInit(); 这一步不是必须的
- GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
- 串口参数初始化:USART_Init();
- 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init();
USART_ITConfig(); - 使能串口:USART_Cmd();
- 编写中断处理函数:USARTx_IRQHandler();
- 串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据 - 串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
void My_USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrue;
USART_InitTypeDef USART_InitStrue;
NVIC_InitTypeDef NVIC_InitStrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStrue);
USART_InitStrue.USART_BaudRate=115200;
USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStrue.USART_Parity=USART_Parity_No;
USART_InitStrue.USART_StopBits=USART_StopBits_1;
USART_InitStrue.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStrue);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStrue);
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE))
{
res= USART_ReceiveData(USART1);
USART_SendData(USART1,res);
}
}
加上“USART_RX_STA”校验
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
}
}
}
}
参考:
1.正点原子库函数版本实现