本文采用了正点原子STM32F1mini开发板, 依据正点原子F103的教学视频进行学习笔记的整理和自己对串口通信的一些理解进行知识的整合。参考资料是《STM32不完全手册-库函数版本》的第8章串口实验和5.3usart串口文件夹介绍,官方资料参考《STM32中文参考手册V10》-第25张通用同步异步收发器(USART)。
按照数据传送方向,分为:
单工:数据传输只支持数据在一个方向上传输
半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接受设备都有独立的接受和发送能力。
上面是比较常见的一些串行通信接口,其中UART与单总线是属于异步通信,也就是不需要SCL同步时钟引脚,区别在于UART是全双工通信,分别有TXD与RXD引脚,而单总线输入与输出共用一个DQ引脚;
SPI与I2C属于同步通信,区别同样在于通信方向上,这里SPI是全双工即包含一个MISO输出和一个MOSI输入两个引脚,而I2C为半双工也就是输入输入共用一个SDA引脚。
大容量STM32F10x系列芯片,包含3个USART和2个UART。也就是说如果单论异步收发器,F10x系列芯片有5个UART可供使用,而这5个当中有2个USART可供同步通信使用。
—RXD:数据输入引脚,数据接收
—TXD:数据发送引脚,数据发送
若两端设备无转化器,则两端输入输出都为TTL电平,引脚连接需要反向连接,RXD连TXD,TXD连RXD。而当STM32芯片与电脑连接时,相互间则需要RS232转换器对两端引脚的输入输出进行转换,此时TxD与TxD分别通过转换器对应相连,这里不可以直接相连,因为两端电平是不兼容的。
单片机与单片机之间如果需要通过DB9相连接的话同样需要通过串口电平转换,都转换为232电平后才能进行连接。
1. 全双工异步通信
2. 分数波特率发生器系统,提供精确的波特率
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s
3. 可编程的数据字长度(8位或者9位)
4. 可配置的使用DMA多缓冲器通信
5. 单独的发送器和接收器使能位
6. 检测标志:①接受缓冲器②发送缓冲器空③传输结束标志
7. 多个带标志的中断源。触发中断
8. 其他:校验控制,四个错误检测标志
这里串口通信的原理图里,数据发送与接受经过的寄存器类似于GPIO原理图里的寄存器,不同的是这里输入输出数据寄存器是在MCU内核的第一个经过位置,而在GPIO里数据首先从MCU出发要通过位设置/清除寄存器。
①起始位
②数据位(8位或者9位)
③奇偶校验位(第9位)
④停止位(1,15,2位)
⑤波特率设置
①RX接收:数据从RX引脚进来通过编解码模块沿下方电路到接收移位寄存器,寄存器一位一位接收完数据后会一次性写到接收数据存储器(RDR),然后CPU进入RDR读取数据。
②TX发送:数据从CPU出发一次性到达发送数据寄存器(TDR),数据一次性发送给发送移位寄存器,发送移位寄存器按照事先约定好的波特率一位一位的将数据移出。
发送和接收都需要事先需要约定好波特率来控制,发送和接受的波特率分别由下方的发送器控制和接收器控制两个模块定义。两个模块的时钟共用一个波特率发生器。
大容量的f103芯片有五个USART串口,串口1时钟来源是PCLK2,串口2到5时钟来源是PCK1,相应的时钟会经过USARTDIV值,该值由USART_BRR来配置,USART_BRR的作用是可以用来配置小数或整数分频的波特率。由USART_BRR输出的值经过USARTDIV对小数进行16分之1整数倍的除法运算配置后再除以16得到最终波特率。
发送器控制与接收器控制连接着两个寄存器,分别是CR1、SR和中断控制。
SR寄存器主要用来存储一些传输过程中的状态标志位;
CR1寄存器一半用来对接收和发送进行使能,一半是中断使能,下面连接着中断控制可以产生中断。
1.2 波特率计算方法
上式中,是给串口的时钟;USARTDIV是一个无符号定点数。我们只要得到USARTDIV的值,就可以得到串口波特率寄存器USART1->BRR的值,反之亦然。
下面介绍如何通过USARTDIV得到串口USART_BRR寄存器的值。假设串口1要设置为115200的波特率,而PCLK2的时钟为72M。这样根据上面的公式有:
USARTDIV=72000000/(115200*16) = 39.0625
那么得到:
DIV_Fraction = 16*0.0625 = 1 = 0X01;
DIV_Mantissa = 39 = 0X27;
这样就得到了USART1->BRR的值为0X0271。只要设置串口1的BRR寄存器值为0X0271就可以得到115200的波特率。
(1)使能串口、中断及串口初始化
void USART_Init();//串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能。
void USART_Cmd();//使能串口
(2)从DR发送、接受数据
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
(3)标志位操作
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
②串口复位:USART_DeInit();//这一步不是必须的
③GPIO端口模式设置:GPIO_Init();//模式设置为GPIO_Mode_AF_PP
④串口参数初始化:USART_Init();
⑤开启中断并且初始化NVIC//(如果需要开启中断才需要这个步骤)
⑥使能串口: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);
第一步串口使能,在项目中首先需要引入stm32f10x_rcc.c与相关头文件,新建一个main函数文件,在文件里编写一个串口的定义函数,由于这里使用的是串口1(USART1),在头文件中找到使能函数分别对GPIOA和USART1进行使能。
void My_USART1_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
随后对GPIO进行初始化模式设置,创建一个初始化结构体变量对GPIO端口的相关参数进行赋值,分别对端口9输出与端口10输入进行配置。
GPIO_InitTypeDef GPIO_InitStrue;
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);
第三步对串口进行初始化,这里同样需要创建一个结构体变量对参数进行赋值,方法与上面GPIO配置大体相同,只是在结构体变量的参数上有些区别,这里串口参数包括波特率、硬件流控制、模式(发送或接受模式)、奇偶校验、停止位与字长。串口配置完后使能即可。
USART_InitTypeDef USART_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);
}
串口初始化完毕后接下来我们用到了中断,首先需要设置中断优先级分组,相关函数定义在misc.c文件中,先在main.c文件里新建一个main函数。设置中断优先级分组,随后在串口初始化函数开启中断并且初始化NVIC。NVIC参数包括通道选择、开启通道、抢占优先级、指令优先级,相应定义在stm32f10x.h顶层头文件里。
void My_USART1_Init(void)
{
NVIC_InitTypeDef NVIC_InitStrue;
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口接收中断
NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStrue.NVIC_IRQChannelRreemptionPriority=1;
NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStrue);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
接下来编写中断服务函数,函数本身定义在stm32f10x启动文件中,对应查阅即可。在中断服务函数里需要先进行if判断中断的类型,若是对应的中断则在if里对中断内容进行操作,这里我们接收从串口发送来的数据,简单的对数据进行发送即结束函数。在main函数里调用串口初始化函数,随后进入死循环等待单片机接收到数据进入中断状态。
void USART1_IRQHandler(void)
{
if(USART_GetStatus(USART1,USART_IT_RXNE)) //根据中断类型进行分析,调用中断的状态
{
res = USART_ReceiveData(USART1);
USART_SendData(USART1,res); //处理接收数据
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
My_USART1_Init();
while(1);
//初始化后程序一直在此等待,当电脑发送数据给单片机后单片机进入中断,
//进入中断后单片机把数据发送给电脑
}
系统文件夹里的usart.c文件包括了中断服务函数USART1_IRQHandler(),编译过程可能存在重复定义,这里将usart.c文件删除即可。
下面是串口通信实验的具体操作内容,这里协议与协议要对应一致,将选项配置完后发送数据可以看到串口将数据原封不动的传输回来,与程序实现功能相同。
在system文件夹中存在一个usart.c文件,这里我们将上面写好的uart_init(u32 bound)初始化函数带到这里来,在main.c文件中调用该函数。这里我们将串口中断服务程序USART1_IRQHander(void)更改一下带到usart.c文件中,以下是更改后的中断服务程序代码。
#if EN_USART1_RX //如果使能了接收
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS
OSIntEnter();
#endif
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;
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS
OSIntExit();
#endif
}
#endif
在USART串口协议文件usart.c里定义了三个不同的变量,第一个宏变量USART_REC_LEN是最大接收数,协议中限制了它最大是200。余下两个分别是8位无符号整型数据接收数组USART_RX_BUF[USART_REC_LEN],16位无符号整型接收状态标记USART_RX_STA。
协议里规定接收状态标记变量最高位bit15为接收完成标志,bit14是判断是否接收到回车0X0D标志。在接收数据的过程中,若单片机接收到0X0D后,会将bit14位置一,接下来进入下一次接收循环,如果下一次接收数据为0X0A,默认本段数据接收完毕,进行数据最终处理,最后程序跳出数据接收循环,并且标志位bit0~bit15置零,如果没有接收到0X0A则接收错误,接收完成标志设为0重新开始。
实验效果同上,发送前需要勾选发送新行,这样能够接收到回车,默认以回车键结束接收。