文章目录[隐藏]
STM32之串口通信 - USART(含串口实验详细解析)
- 开发环境:Window 10
- 开发工具:Keil uVision5 MDK
- 硬件:STM32F103
资料参考:
【正点原子】STM32F103开发板资料(A盘);
- STM32F1开发指南(精英版)-库函数版本.pdf
- STM32固件库使用手册的中文翻译版.pdf
- STM32中文参考手册.pdf
注意:本文主要介绍 USART 异步串行模式下的收发器。
STM32F103ZE 简介
https://www.keil.com/dd2/stmicroelectronics/stm32f103ze/
内核:ARM Cortex-M3,72MHz
内存:64kB RAM,512kB ROM
时钟和电源:2.00V—3.60V,72 MHz
通信 :SPI、I2C、UART、I2S、CAN、USART、USB、Device
定时器/计数器/PWM :8 × 16 位定时器
模拟:2通道 12位DAC,21通道 12位ADC
I/O 和封装: -40℃—85℃,144-QFP,144-BGA
USART简介
1. 串口通信
串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式,如SPI通信、USART通信、EEPROM通信等。简单讲,串口通信实现了上位机(PC)与下位机(如STM32)之间的信息交互。
上位机(PC)通过串口调试助手等实现数据的接收和发送;
下位机(STM32)通过printf()、getchar()等函数实现字符或字符串的接收和发送。
2. 串口通信的分类:
处理器与外部设备通信有两种方式:
2.1 串行通信:数据按位顺序依次传输,如8个数据位依次传输,速度慢,但占用引脚资源少
按照数据传送方向,又分为:
- 单工:数据传输只支持数据在一个方向上传输。(只收不发或者只发不收,模式固定)
- 单双工:允许数据在两个方向上传输,但是在某一时刻,只允许数据在一个方向上传输。(能发能收,但不能同时进行)
- 全双工:允许数据同时在两个方向上传输。(能发能收,且能同时进行)
2.2 并行通信:数据各个位同时传输,如8个数据位同时传输,占用引脚资源慢,但速度快。
3. 串行通信
串行通信按通信的方式可分为:
- 同步通信:带时钟同步信号传输,如SPI、IIC通信等
- 异步通信:不带时钟同步信号,如UART(通用异步收发器)、单总线等。
4.STM32的串口通信接口
- UART:通用异步收发器
- USART:通用同步/异步收发器(两种模式可切换)
STM32F103系列提供5路串口,包含3个 USART 和2个 UART 。
串口的引脚如下图所示:
数据传输的格式/通信协议
串行通信一定要有适合的通信协议。
通信协议指通信双方之间为完成信息交互所必须遵守的一种规则和约定。比如两个人约定在何时交流、用中文还是英文交流、交流什么内容。
1.起始位
当未有数据发送时,数据线处于逻辑“1”状态;先发出一个逻辑“0”信号,表示开始传输字符。
2.数据位
紧随起始位之后,数据位表示真正要发送或接收的信息,位数一般有8位或9位
3.奇偶校验位
数据位末尾可以选择是否添加奇偶校验位,用于检测数据传输是否正确
4.停止位
代表信息传输结束的标志位,可以是1位,1.5位或2位。停止位的位数越多,数据传输的速率也越慢。
5.波特率设置
波特率表示每秒钟传输码元的个数,是衡量数据传输速率的指标,单位Baud。另外有个名词叫比特率,比特率表示每秒钟传输二进制位bit的个数,单位 bit/s。
比特(bit)就是指一位信息,当用二进制表示数据时,0是一位,1也是一位信息,它是固定不变的,一个比特就代表二进制下的一位。
通常描述码元,我们会说M进制的码元。比如八进制,我们知道八进制包含0~7共八种数据,而计算机是只识别0,1两种的,我们若是想将这八种数据发送给计算机,可以用3个比特为一组的形式来表示,即000,001,…,111共八组,因而一个八进制的码元就表示携带了3个比特,这时的比特率也就是波特率的3倍。那么,一个M进制的码元,就携带log2 M个比特。
如果还不明白可以看一下这篇文章:波特、码元与比特的关系
6.波特率的计算
波特率是由USART_BRR波特率寄存器控制的,TR/Tx的波特率 = 串口时钟 / BRR的值。
一般我们先确定波特率,通过计算得到BRR的值,并将其赋给USART_BRR寄存器。如串口时钟72M,选用波特率115200,BRR的值为72M÷115200=625(16进制0x0271),BRR寄存器的值就为0x0271。
USART的使用步骤
串口设置的一般步骤可以总结为如下几个步骤:
1)串口时钟使能 GPIO 时钟使能
2)串口复位
3)GPIO 端口模式设置
4)串口参数初始化
5)开启中断 并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6)使能串口
7)编写中断处理函数
以正点原子的实例-串口实验对串口时钟的使用步骤进行详细分析。
程序功能是STM32通过 串口1 和上位机(PC)对话,STM32收到上位机发送的数据(字符串)后,将数据原原本本的返回给上位机。采用USART1的全双工异步串行收发模式。
在例程的usart.c中,引入了一段使程序支持printf函数的代码,直接复制使用即可。
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
1)串口时钟使能 GPIO 时钟使能
GPIO口引脚复用,需要开启复用时钟;使用串口外设也要开启时钟
串口挂载、复用时钟在APB2下面的外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
2)串口复位
库函数中,串口的复位通过USART_DeInit()实现,选择待复位的串口USART1 进行设置即可。
USART_DeInit(USART1); // 复位串口 1
3)GPIO 端口模式设置
PA9是发送引脚,需要发送数字量上表示为0,1的高低电平,若使用开漏输出要建立外部电路,接上拉电阻。故采用推挽输出模式,直接输出0V、3.3V的电压。
PA10是接收引脚,需要接收高低电平,设置上拉会将引脚电平限制在高电平,下拉会限制在低电平,模拟输入则将信号传输到其他外设,只有浮空输入能实现高低电平的接收。
同时,STM32复用功能对端口的配置也有以下三条标准(详见参考手册P107 8.1.4 复用功能(AF)):
- 对于复用的输入功能,端口必须配置成输入模式(浮空、上拉或下拉)且输入引脚必须由外部驱动;
- 对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏);
- 对于双向复用功能,端口位必须配置复用功能输出模式(推挽或开漏),这时,输入驱动器被配置成浮空输入模式。
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
4)串口参数初始化
库函数中,使用USART_Init()对串口进行初始化
void USART_Init(USART_TypeDef* USARTx, USART_I nitTypeDef* USART_InitStruct)
第一个参数:待初始化的串口标号
第二个参数:配置串口参数的结构体指针
typedef struct
{
u32 USART_BaudRate;
u16 USART_WordLength;
u16 USART_StopBits;
u16 USART_Parity;
u16 USART_HardwareFlowControl;
u16 USART_Mode;
u16 USART_Clock;
u16 USART_CPOL;
u16 USART_CPHA;
u16 USART_LastBit;
} USART_InitTypeDef;
- USART_BaudRate:波特率,常用9600,115200等,不用换算成BRR寄存器的值
- USART_WordLength:数据位的长度,取值8位或9位,USART_WordLength_8b,USART_WordLength_9b
- USART_StopBits:停止位的位数,取值为0.5,1,1.5,2位,常用1位USART_StopBits_1
- USART_Parity:奇偶校验位使能,取值有奇偶失能USART_Parity_No,偶校验USART_Parity_Even,奇校验USART_Parity_Odd
- USART_HardwareFlowControl:硬件流控制模式使能,用于处理数据丢失,这里选失能不启用就好
- USART_Mode:发送接收模式使能,取值有接收使能USART_Mode_Rx,发送使能USART_Mode_Tx
以上是异步模式的配置参数,同步模式还需要配置7~10。
-
USART_Clock:时钟使能
-
USART_CPOL:时钟输出极性
-
USART_CPHA:时钟输出相位
-
USART_LastBit:最后一位数据的时钟脉冲输出方式
详见固件库使用手册P346 Table707
对串口1 初始化范例:
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
5)开启中断 并且初始化 NVIC
如果要使用中断服务,便要对中断优先级进行配置,如果只有一个中断服务,随意配置顺序即可。
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
6)使能串口
在移植代码或自己编写的过程中,都不能忘记开启串口功能
USART_Cmd(USART1, ENABLE); //使能串口1
7)编写中断处理函数
其中,SYSTEM_SUPPORT_OS 的作用是实现STM32和操作系统共用STM32内部SysTick定时器,使二者拥有相同的时钟周期。
中断服务过程:
-
USART_GetITStatus():用于获取接收状态,即是否开启接收
-
USART_ReceiveData():获得当前接收到的数据,数据长度8位
-
USART_RX_STA:接收的状态标志,数据长度16位,第14位、15位对接收结束的判断,第0~13位是接收数据的长度。
-
USART_RX_BUF[]:数据存储栈,将接受到的数据依次存储
当 USART_GetITStatus读取到起始位时触发更新请求,代表数据开始接收。用USART_ReceiveData()读取当前接收的数据,如果不是0x0d,则将数据保存到BUF中,如果连续接收到了0x0d(回车),0x0a(换行)表示数据接收结束,STA的第15位置1,在主程序main()中执行相关操作并等待开启下一次的数据接收(当STA=0时表示数据可以被接收存储),详细流程图如下。
扩展:回车、换行的区别
回车 \r 本义是光标重新回到本行开头,
换行 \n 本义是光标往下一行(不一定到下一行行首)
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) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
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
}
版权声明:本文为CSDN博主「Aries.Pig」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/AriesPIG/article/details/119840979
暂无评论