STM32F407——串口通信
前言
本文将对串口通信的分类和基于 stm32 的串口配置进行介绍,以及如何使用串口调试助手进行串口收发功能的调试,旨在帮助还不会使用 stm32 单片机串口资源进行通信的家人们快速学会如何使用串口来进行通信。
(纯干货、快速上手、零基础也能会!!!)
一、串口通信的概念及分类
1、串口及串口通信概念
(1)串口,即串行接口,是一种可以将接收来自CPU的并行数据字符转换为连续的串行数据流发送出去,同时可将接收的串行数据流转换为并行的数据字符供给CPU的器件。
(2)串口通信(Serial Communications)的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。
2、串口分类
常用的有串口有三种:
(1)RS232标准的串口
这一类的串口采用的是 RS232 电平标准,其中高电平为 +15V ,低电平为 -15V ,这类串口抗干扰性强,但是对我这种平平无奇大学生来说用不上。就是下面这玩意:
(2)USB转串口
这类串口十分常用,主要用来调试串口(下面会将如何用)。在 STM32F407 中也有一个 USB 串口,如下图所示:
板载 CH340 USB 转 TTL 芯片,可以说是个串口调试的工具。
(3)原生的串口
这一类主要用于控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用 TTL 电平(高电平 3.3V 或 5V,低电平 0V)通信。大家平常用的蓝牙、传感器等很多都是采用这个。
二、串口配置与调试
1、配置步骤
以 STM32F407 的串口3为例,跟大家讲下如何配置一个串口,使其能进行数据收发。
(1)串口初始化
在这一步中,需要:
① 对该串口使用到的 IO 口进行初始化和使能相应的时钟树,并对端口进行复用映射;
② 中断配置 (主要是一个优先级);
③ 串口初始化配置(奇偶校验、收发配置等);
④ 串口使能;
按上述步骤编写函数,代码如下:
void USART3_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟
//串口3对应引脚复用映射
GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3);
//USART3端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStructure);
//Usart3 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART3 初始化设置
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(USART3, &USART_InitStructure); //初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART3, ENABLE); //使能串口3
TIM7_Int_Init(100-1,8400-1); //10ms中断一次
USART3_RX_STA=0; //清零
TIM_Cmd(TIM7,DISABLE); //关闭定时器7
}
(2)编写发送与接收函数
这个其实不用编写,库里就有了,接收与发送分别是:
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
/* Receive Data */
return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
/* Check the parameters */
assert_param(IS_USART_ALL_PERIPH(USARTx));
assert_param(IS_USART_DATA(Data));
/* Transmit Data */
USARTx->DR = (Data & (uint16_t)0x01FF);
}
这个大家知道一下这两个的功能和如何调用函数即可。如果要接收串口3的信息,这样调用:Receive = USART_ReceiveData(USART3);如果要通过串口3发送消息,则:USART_SendData(USART3,data)。等下在中断函数和串口打印函数中会用到。
(3)编写中断服务函数
void USART3_IRQHandler(void) //串口1中断服务程序
{
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART3);//(USART3->DR); //读取接收到的数据
if((USART3_RX_STA&0x8000)==0)//接收未完成
{
if(USART3_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART3_RX_STA=0;//接收错误,重新开始
else USART3_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART3_RX_STA|=0x4000;
else
{
USART3_RX_BUF[USART3_RX_STA&0X3FFF]=Res ;
USART3_RX_STA++;
if(USART3_RX_STA>(USART3_MAX_RECV_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
中断函数是在发生中断时间后,主程序自动进入中断函数运行,运行结束后在退出中断函数,返回到进入中断函数之前的运行状态。我愿把他成为一个时光静止器,执行到这个函数时,外界暂停,此刻整个世界只有自己,我们可以利用这段时间尽情地做我们想做的事,对我们所接收的信息进行分析处理等等。
(4)编写串口打印函数
这一函数与 C 语言中的 printf 其实是类似的,可将接收到的信息打印到电脑上,主要用于调试,函数如下:
//串口3,printf 函数
//确保一次发送数据不超过USART3_MAX_SEND_LEN字节
void u3_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
完成这几步后,一个串口就算配置好了,那如何检测配好的串口是否能进行正常的信息收发呢?请继续往下看。
2、串口收发功能测试
要测试的话,咱们需要自费5块钱左右买来一个 USB 转 TTL 模块(我20岁,现在能全款买下一个 USB 转 TTL 模块,大家应该也可以)。然后按照模块 5V 端口接单片机 5V 电源,模块 GND 端口接单片机 GND,模块 TX 端口接单片机 RX 端,模块 RX 端口接单片机 TX 端(发接收,收接发,不能接错,否则通信不起来)这样的连接方式将该模块连接到单片机上串口用到的引脚,然后插入电脑,打开串口调试助手(个人推荐正点原子的 XCOM),打开后的界面如下所示:
接下来,我们要在串口选择栏选择对应串口,设置对应波特率,如果你在配置的时候设置的是115200,那这里就选择115200,一定不能出错,否则会造成输出乱码或者无法输出。然后奇偶校验这些咱们就按默认来,不需要设置,因为在配置中也没设置奇偶校验,然后点击打开串口,此时该栏的黑点会变成红点。如图所示:
之后我们就可以进行收发测试了,收发测试需要在中断函数或者主函数中加上这一行代码:
u3_printf("收到的消息:%c/r/n",Res);
我们发个字符 ‘1’ 试试:
发 ‘1’ 收 ‘1’ ,说明我们串口收发没问题,这样我们就可以放心大胆地在接收中断中做文章了。
如果要改成串口4的话,只需要改个引脚,把 3 改成 4 就行了。移植代码如下:
#include "uart4.h"
#include "delay.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
u8 UART4_RX_BUF[UART4_MAX_RECV_LEN]; //接收缓冲,最大UART4_MAX_RECV_LEN个字节.
u8 UART4_TX_BUF[UART4_MAX_SEND_LEN]; //发送缓冲,最大UART4_MAX_SEND_LEN字节
vu16 UART4_RX_STA=0;
void uart4_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE); //使能GPIOC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);//使能USART4时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_UART4); //GPIOC11复用为UART4
GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_UART4); //GPIOC10复用为UART4
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOC11和GPIOC10初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10
//USART1 初始化设置
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(UART4, &USART_InitStructure); //初始化串口4
USART_Cmd(UART4, ENABLE); //使能串口4
//USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//开启中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
}
void UART4_IRQHandler(void) //串口4中断服务程序
{
u8 Res;
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(UART4);//(USART1->DR); //读取接收到的数据
if((UART4_RX_STA&0x8000)==0)//接收未完成
{
if(UART4_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)UART4_RX_STA=0;//接收错误,重新开始
else UART4_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)UART4_RX_STA|=0x4000;
else
{
UART4_RX_BUF[UART4_RX_STA&0X3FFF]=Res ;
UART4_RX_STA++;
if(UART4_RX_STA>(UART4_MAX_RECV_LEN-1))UART4_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
void u4_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)UART4_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)UART4_TX_BUF);//此次发送数据的长度
for(j=0;j<i;j++)//循环发送数据
{
while(USART_GetFlagStatus(UART4,USART_FLAG_TC)==RESET); //等待上次传输完成
USART_SendData(UART4,(uint8_t)UART4_TX_BUF[j]); //发送数据到串口3
}
}
结语
本文主要讲了串口的配置步骤和注意事项以及串口的调试。 关于 STM32F407 的串口串口通信我就讲这么多,个人认为纯干货,拿来开发(不是很难的)也够用了。一样的,欢迎各位批评指正,联系微信:Cyy15880234628
版权声明:本文为CSDN博主「平平无奇的大学生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Cyy0807/article/details/122828007
暂无评论