STM32串口通信

本文详细介绍了STM32的USART串口通信原理及应用,包括同步与异步通信的区别、不同电平的连接方式及其特点,并提供了串口初始化、发送及接收中断的编程实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32串口通信


注意:本文参考STM32F10XXX数据手册

串口通信简介

USART为通用同步异步接收/发送器,我们常用的是异步通信,下面也重点讲解异步通信

同步异步有什么区别呢?

具体区别可以问度娘,大致记住同步是要求时钟同步,那怎么保证时钟同步呢?撇开网络不谈,意法的解决办法是专门提供一个叫做USARTy_CK的引脚接口,也就是说如果采用同步通信这个引脚必须要和设备连上,异步通信就简单了,只需接受RX与发送TX两条线就可以了

异步通信怎样连线?

直接连接:


在这里插入图片描述

注意:USART只是一种通信协议根据不同电平分为TTL、RS232、RS485等等

直接连接,采用TTL电平通信,距离十分受限,最远通信距离大概2M

转RS232连接:


在这里插入图片描述

RS232连接方式,通信协议依旧没变,实际就是将TTL电平转换成了232电平,目的自然是为了获得更远的通信距离,最远通信距离大概20M

采用RS232电平的连线接头如下:


在这里插入图片描述

相信大家也不陌生,生活中还是用得比较多

转RS485连接:


在这里插入图片描述

采用485电平连接方式,与232类似,通信距离大幅度提升,多用在工业上,最远通信距离大概1000M

对于STM32F10XXX来说USART1位于高速总线APB2上,其通信速率高达4.5兆位/秒,快于其他串口,且支持DMA操作(对于DMA后续会详说,开始坑!)

编程代码

/*
 * @Author: ExclusiveTP 
 * @Date: 2021-01-26 21:15:15 
 * @Last Modified by:   ExclusiveTP 
 * @Last Modified time: 2021-01-26 21:15:15 
  
 */

#include "usart.h"
#include "stdio.h"
//printf输出与USART2关联,能够传参
#if 1
#pragma import(__use_no_semihosting)       /* 确保没有从 C 库链接使用半主机的函数 */
//标准库需要的支持函数                 
struct __FILE 
{ 
int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); 
        USART_SendData(USART2,(uint8_t)ch);   
return ch;
}
#endif 
//串口初始化函数
void Usart_Init(u32 Baudrate){
	
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	//GPIO初始化 
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_2;//TX复用推挽输出
    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_3;//RX浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
	//Usart2初始化
	USART_InitStructure.USART_BaudRate =Baudrate;//波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//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(USART2, &USART_InitStructure);
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//使能串口接收中断
	USART_Cmd(USART2, ENABLE);//使能串口
	
	//Usart2 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//响应优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 
	
 }
//串口打印函数,不能传参
void Usart_Printf(u8 *str){
		
		u8 data=0;
		do{
	
		  USART_SendData(USART2,str[data]);//发送一个字
		  while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待单字发送完成
		  data++;
		}
		while(str[data]!=0);  //判断数据是否发送完成                                                  
		
 }
//接收中断服务函数	
void USART2_IRQHandler(void){ //串口2中断服务程序(固定的函数名不能修改)	
	    u8 a;
	    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){  //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
		  a =USART_ReceiveData(USART2);//读取接收到的数据
		  printf("%c",a); //把收到的数据发送回电脑		  
	   } 
 } 

简单说下代码,上面代码大致分为四块:printf与USART关联、串口初始化、Usart_Printf函数、串口中断服务函数

(1)printf是C标准库函数,必须将其与相应的串口关联才能使用,优点是能够传参,缺点是只能关联一个串口,当有多个串口使用时其中一个使用了printf函数,剩下的就只有老老实实写了

#if 1
#pragma import(__use_no_semihosting)       /* 确保没有从 C 库链接使用半主机的函数 */
//标准库需要的支持函数                 
struct __FILE 
{ 
int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
while(USART_GetFlagStatus(USARTy,USART_FLAG_TC)==RESET); 
        USART_SendData(USARTy,(uint8_t)ch);   
return ch;
}
#endif 

这部分关联的代码没什么技巧,更换USARTy就行了,不多说

(2)串口初始化也都给了备注,大致捋一下:

①定义GPIO和USART两个结构体,这是必不可少的,至于NVIC中断优先级配置如果没有用到中断就不需要

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//使能串口接收中断

也就是说不要上面这句代码就不用配置NVIC

②配置TX引脚为复用推挽输出、RX为浮空或上拉输入,至于为什么直接上图,记住就好了
在这里插入图片描述
③进行协议的配置,Baudrate通过调用函数传参实现设置,8位数据位、1位停止位、不进行奇偶校验、无硬件流控制、接收发送模式,这是比较常用的,可以根据需要自行修改(这里不再详说了,因为每一点展开来都可以写成一篇文章了)

④中断优先级配置

NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器 

这里注意不要把中断 USART2_IRQn入口地址写错了,当然你用到哪个中断入口就写哪个,然后就是中断优先级,抢占优先级高于响应优先级(具体什么关系,怎样设置后面说中断再详说,有一坑!)一般在不用到多个中断或者中断嵌套的情况下可以不管,这里都写0就行了

(3)串口打印函数Usart_Printf,这个函数是自己写的,就是当前面说的printf函数已经和其他串口关联后,我们就得自己写串口打印函数了,这个函数缺点是不能够传参,也就只能像这样用用

Usart_Printf("ExclusiveTP is handsome!")

具体代码是怎样实现的,我后面会专门来详说(再一坑!)

(4)串口接收中断

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//使能串口接收中断

前面要有了这句代码,串口在接收到数据后才会进入此中断,至于进入中断干什么完全由你自己书写,我给的代码例子是将串口接收到的东西再发送回去

几乎所有的中断服务函数都是这个大致的框架

void XXXXX_IRQHandler(void){ 	
	    if(USART_GetITStatus(XXX, XXX) != RESET){  
		 
	   } 
	   XXX_ClearITPendingBit(XXX,XXX);//清楚中断标志位
 } 
加油!拼命干才会百分百成功,下一篇再见!

下一篇:自写Usart_Printf()串口发送函数实现方法详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值