STM32学习——串口

2023-05-16

STM32学习——串口

USART通用同步异步收发器(Universal Synchronous Asynchronous Receiver Transmitter)是一串行通信设备,可以灵活地与外部设备进行进行**全双工信息交换**

UART(Universal Asynchronous Receiver Transmitter),它是在 USART 的基础上裁剪了同步通信功能,只保留异步通信功能我们平时使用的串口通信都是 UART

1.通信接口背景知识介绍

1.1通信双方的两种通信方式

  1. 串行通信

    • 传输原理:数据按位顺序传输
    • 优点:占用引脚资源少
    • 缺点:速度相对较慢
  2. 并行通信

    • 传输原理:数据各个位同时传输

    • 优点:速度快

    • 缺点:占用引脚资源多

1.2串行通信的分类

单工:数据只支持在一个方向上传输(看电视)

image-20210722155139811

**半双工:**同一时刻只允许在一个方向传输(对讲机)

image-20210722155152212

全双工:允许同时在两个方向上传输(电话)

image-20210722155205777

1.3常见的串行通信接口

image-20210722155600507

2.USART功能概述

任何 UART 双向通信至少需要 3 个引脚,数据发送引脚 TXD,数据接收引脚 RXD,数

据参考地 GND。串口连接必须共地!!!

这里解释下电平标准,根据使用使用的电平标准不同,可以分为 TTL 和 RS232 标准,因为控制器一般都是 TTL 标准,因此如果需要进行 RS232 通信时,一定要使用 R232转换器进行 TTL 和 RS232 的电平转换

image-20210428103209850

3.STM32串口资源

image-20210722155929235

  • 全双工异步通信。
  • 分数波特率发生器系统,提供精确的波特率。发送和接收共用的可编程波特率,最高可达4.5Mbits/s
  • 可编程的数据字长度(8位或者9位);
  • 可配置的停止位(支持1或者2位停止位);
  • 可配置的使用DMA多缓冲器通信。
  • 单独的发送器和接收器使能位。
  • 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
  • 多个带标志的中断源。触发中断。
  • 其他:校验控制,四个错误检测标志。

4.串口通信过程

image-20210722161223964

5.STM32串口通信使用方法

串口通信需要定义的参数

  1. 起始位
  2. 数据位(8位/9位)
  3. 奇偶校验位
  4. 停止位
  5. 波特率设置(收发两端要一致)

6.串口通信相关函数

  • 初始化函数 bsp_InitUart
    • 第1步: 配置GPIO
      • 打开 GPIO 时钟
      • 打开 UART 时钟
      • 配置 USART Tx 为复用功能
      • 配置 USART Rx 为复用功能
    • 第2步: 配置串口硬件参数
    • 第3步: Usart1 NVIC 配置
    • 第4步: 使能串口1
  • 中断服务函数 USART1_IRQHandler
  • 状态清零函数 Uart0_STA_Clr
  • 打印输出函数 fput
  • 数据发送函数 USART1_Send_Data

7.STM32F1的 USART 库

7.1对应库函数

  • 使能 USART 时钟

    下面的函数用于使能 USART1 的时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USARTx,ENABLE)

    下面的函数用于使能 USART2、USART3 UART4、UART5

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UARTx,ENABLE)

  • 使能 GPIO APB2 时钟(GPIO 是挂载在 APB2 高速总线上的)

  • 用函数 GPIO_Init()配置 GPIO 引脚,主要有以下两种配置方式,分别对应 RXD 和 TXD的引脚

     输入:浮空,上拉

     复用输出:复用推挽

  • 外设复用功能配置外设的复用功能

    • 对于其它外设(USART,SPI……)

      • 通过结构体 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_xx 配置为具体的 AF 模式(xx 对应的 OD 为开漏,PP 为推挽)
      • 调用函数 GPIO_Init()
    • 通 过 函 数 USART_Init 配 置 BaudRate , WordLength , StopBits , Parity ,HardwareFlowControl,USART_Mode

    • 如果需要使用中断,使用 NVIC_Init 函数进行配置,并通过 USART_ITConfig 使能相应中断。

    • 通过函数 USART_Cmd 使能对应串口

7.2初始化和配置函数

下面是串口 1 的初始化函数,主要完成串口的设置。

/**/
/*******************************************************************************
* 函数名   		:串口初始化函数
* 函数功能      :初始化USRAT1的硬件设备
* 形参          : 无
* 返回值        : 无
*******************************************************************************/
/**/
void bsp_InitUart1(uint32_t baud)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	USART_InitTypeDef	USART_InitStructure;
	
	#if	EN_USART1_RX	//如果使能了接收
		NVIC_InitTypeDef  NVIC_InitStructure;
	#endif
	
	/*TX=PA9 RX=PA10*/
	
	
	/*配置GPIO*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);	//打开GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);	//打开串口时钟
	
	/*配置PA9与PA10两个端口的复用功能*/
	//TX发送
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;					//PA9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;			//复用推挽输出

	GPIO_Init(GPIOA,&GPIO_InitStructure);		
	//RX接收	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;				//PA10	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;			
	GPIO_Init(GPIOA,&GPIO_InitStructure);	
	
	/*配置串口硬件参数*/
	//波特率
	USART_InitStructure.USART_BaudRate=baud;										//形参传入	
	//数据位字长
	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 ;					//收发模式
	//初始化串口1
	USART_Init(USART1,&USART_InitStructure);
	
	/*如果使能接收*/
	#if	EN_USART1_RX	
	/*配置串口中断*/
	//通道
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	//响应优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
	//通道使能
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
	
	NVIC_Init(&NVIC_InitStructure);//跟据指定参数初始化VIC寄存器
	
	//开启串口接收中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	//开启串口空闲中断    
	USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
	/*
		其实发送的两个字符之间间隔非常短,所以在两个字符之间不叫空闲。
		空闲的定义是总线上在一个字节的时间内没有再接收到数据,空闲中
		断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收
		到数据的时候发生的。
	*/
	#endif
	
	//使能串口
	USART_Cmd(USART1,ENABLE);	
}

主要完成串口波特率,数据长度,校验位,流控,模式的设置,在这个函数里没有对GPIO 引脚做宏定义,是因为 STM32F1 系列的串口 1 引脚序号都是一样的,这样本身就便于在移植时不做任何 GPIO 定义的更改。

下面是中断服务函数 USART1_IRQHandler,这里主要使用空闲中断,当接收到一帧数据后,总线空闲中断后,使用读取 USART1->SR 的方式再读取 DR 的方式来清除 IDLE 标记,同时将 ReceiveState 标志置 1。

/**/
/*******************************************************************************
* 函数名   		:USART1_IRQHandler
* 函数功能      :串口中断1中断服务函数
* 形参          : 无
* 返回值        : 无
*******************************************************************************/
/**/
void USART1_IRQHandler(void)
{
	uint8_t Res=Res;
	//接收到一个字节数据
	if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
	{
		USART_RX_BUF[RxCounter++]=USART1->DR;	//将接收到的字节保存
	}
	//接收到一帧数据
	if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
	{
		Res=USART1->SR;	//读SR寄存器
		Res=USART1->DR;	//读DR寄存器(先读SR再读DR可以清除IDLE)
		ReceiveState=1;	//接收状态标志位
	}	
}

下面是状态清零函数,这个函数比较简单,函数只是简单的将 RxCounter 和 ReceiveState 两个全局变量清零的操作做了一个封装,方便外部函数进行调用。

/**/
/*******************************************************************************
* 函数名   		:Uart0_STA_Clr
* 函数功能      :串口状态清除函数
* 形参          : 无
* 返回值        : 无
*******************************************************************************/
/**/
void Uart0_STA_Clr(void)
{
		RxCounter=0;	//串口Buf计数清零
		ReceiveState=0;	//接收状态清零
}

有过 C 语言基础的用户,都知道一个格式化打印输出的函数 printf。在 STM32 里我们要使用 printf 的方法,必须做一些操作,这里给出了一种方法,通过代码的方式实现printf,而不需要使用 MicroLIB。注意,要使用下面的文件,有些关键词是在 stdio.h 里申明的,我们在 bsp.h 里已经做了包含。

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#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 

这里解释一下 0x40,0x40 是 USART_FLAG_TC 的标志。(USART1->SR&0X40)==0 这句和 USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET 这句功能是一样的,一种是寄存器的写法,一种是使用库函数的方法。而实际在底层都是最终操作的寄存器。

使用库函数进行数据发送的函数如下,这个函数主要是便于发送 16 进制数据的。

/**/
/*******************************************************************************
* 函数名   		:USART1_Send_Data
* 函数功能      :USART1发送len个字
* 形参          : buf:发送区首地址
                  len:发送的字节数
* 返回值        : 无
*******************************************************************************/
/**/
void USART1_Send_Data(uint8_t *buf,uint8_t len)
{
	uint8_t t;
	for(t=0;t<len;t++)			//循环发送数据
	{
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
		USART_SendData(USART1,buf[t]);
	}
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
}

另外介绍一下 bsp_uart.h 里的内容,特别注意一下 extern 关键词。

#ifndef __BSP_UART_H
#define __BSP_UART_H
#include "sys.h"
#define USART_REC_LEN 1024 //定义最大接收字节数 1024
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口 1 接收
 
extern uint8_t USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大 USART_REC_LEN 个字节.末字节为换行符
extern uint8_t ReceiveState; //接收状态标记
extern uint16_t RxCounter;
void bsp_InitUart1(uint32_t baud);
#if EN_USART1_RX //如果使能了接收
void Uart0_STA_Clr(void);
#endif
#endif

如果某个 C 文件中定义的全局变量,需要被其它 C 文件使用时,函数实体定义在 C 文件里,而在.h 头文件里用 extern 申明该变量,且在申明时不能对该变量赋值,其它 C 文件调用时用 include 包含该头文件即可。

7.3主函数

/*
*********************************************************************************************************
*
*	模块名称 : 主程序入口
*	文件名称 : main.c
*	版    本 : V1.1
*	说    明 : 串口例子
*********************************************************************************************************
*/
#include "bsp.h"				/* 底层硬件驱动 */
/*
*********************************************************************************************************
*	函 数 名: main
*	功能说明: c程序入口
*	形    参:无
*	返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
	
	uint8_t Key_Code;		//按键代码
	uint16_t t;
	uint16_t len;
	//硬件初始化
	bsp_Init();	
	printf("DS0 正在闪烁(闪烁频率 = 1Hz)\r\n");
	printf("操作按键会打印按键事件\r\n");
	//启动软件定时器1自动重装500ms
	bsp_StartAutoTimer(0, 500); /* 启动 1 个 500ms 的自动重装的定时器 */
	
	while(1)
	{
		//喂狗
		bsp_Idle();
		/*LED*/
		if(bsp_CheckTimer(0))
		{
			bsp_LedToggle(1);
			printf("请输入数据,以回车键结束\r\n");
		}	
		/*按键处理*/
		Key_Code=bsp_GetKey();
		if(Key_Code!=KEY_NONE)
		{
			switch(Key_Code)
			{
				case	WKUP_DOWN:			//WKUP按下Led1亮,串口打印
					bsp_LedOn(1);
					printf("WKUP键按下\r\n");
					break;
				case	WKUP_UP:			//WKUP抬起,串口打印
					printf("WKUP键抬起\r\n");
					break;
				case	KEY0_DOWN:			//KEY0按下,串口打印
					printf("KEY0键按下\r\n");
					break;
				case	KEY0_UP:			//KEY0抬起 ,串口打印
					printf("KEY0键抬起\r\n");
					break;
				default  :
					break;
			}
		}
		//若收到数据
		if(ReceiveState)
		{
			len=RxCounter;					//得到此次接收数据的长度
			printf("\r\n发送的消息为\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART_SendData(USART1,USART_RX_BUF[t]);
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			}
			printf("\r\n\r\n");
			Uart0_STA_Clr();
		}
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32学习——串口 的相关文章

  • 51单片机 数码管中断操作

    实践目的 1 掌握中断的概念和思想 2 掌握51单片机中断系统和相关软硬件设计 实践内容 1 利用单片机的P0口接数码管的字段脚 P1 0脚接共阴极 P3 2 P3 3引脚接独立按键产生外部中断信号 编写程序 当程序正常运行时数码管显示H字
  • 处理器指令周期执行时间

    我的猜测是 no operation 内在 ARM 指令应花费 1 168 MHz 来执行 前提是每个NOP在一个时钟周期内执行 我想通过文档验证这一点 有关处理器指令周期执行时间的信息是否有标准位置 我试图确定 STM32f407IGh6
  • 以字符串形式接收数字(uart)

    我正在尝试通过 uart 接收一个包装为字符串的数字 我发送数字 1000 所以我得到 4 个字节 空字符 但是 当我使用 atoi 将数组转换为数字并将整数与 1000 进行比较时 我并不总是得到正确的数字 这是我用于接收号码的中断处理函
  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • 137-基于stm32单片机智能保温杯控制装置Proteus仿真+源程序

    资料编号 137 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DS18B20传感器 电机 制作一个基于stm32单片机智能保温杯控制装置Proteus仿真 2 通过DS18b20传感器检测当前保温杯水的温度 并且
  • 135-基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真+源程序

    资料编号 135 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 电机 超声波传感器 制作一个基于stm32单片机超声波非接触式感应水龙头控制系统Proteus仿真 2 通过DHT11传感器检测当前
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • 物联网网关

    物联网网关是 连接物联网设备和互联网的重要桥梁 它负责将物联网设备采集到的数据进行处理 存储和转发 使其能够与云端或其它设备进行通信 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互 物联网网关功能 数据采集 物联网网关可以从物联
  • STM32F103概要

    The STM32F103x4 STM32F103x6 STM32F103xC STM32F103xD and STM32F103xE are a drop in replacement for STM32F103x8 B medium d
  • SHT10温湿度传感器——STM32驱动

    实验效果 硬件外观 接线 3 3V供电 IIC通讯 代码获取 查看下方 END
  • [MM32硬件]搭建灵动微MM32G0001A6T的简易开发环境

    作为学习单片机的经典 自然是通过GPIO点亮LED 或者是响应按钮的外部中断例程 这我们看看SOP8封装的芯片MM32G0001A6T得引脚 除了VDD和GND固定外 我们可以使用PA14 PA1 PA13 PA15 PA2 PA3这六个G
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 擦除后无法写入闪存

    所以我不能在擦除后直接写入内部闪存 如果写操作之前没有擦除操作 那么我可以 有什么想法吗 编程函数返回 成功写入 值 但查看内存时 没有写入任何数据 这是代码 uint32 t pageAddress 0x08008000 uint16 t
  • 毕设开题分享 单片机智能教室系统(智能照明+人数统计)

    1 简介 Hi 大家好 今天向大家介绍一个学长做的单片机项目 单片机智能教室系统 智能照明 人数统计 大家可用于 课程设计 或 毕业设计 项目分享 https gitee com feifei1122 simulation project
  • Cortex-M3与M4权威指南

    处理器类型 所有的ARM Cortex M 处理器是32位的精简指令集处理器 它们有 32位寄存器 32位内部数据路径 32位总线接口 除了32位数据 Cortex M处理器也可以有效地处理器8位和16位数据以及支持许多涉及64位数据的操作
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • STM32内部时钟

    我对 STM32F7 设备 意法半导体的 Cortex M7 微控制器 上的时钟系统感到困惑 参考手册没有充分阐明这些时钟之间的差异 SYSCLK HCLK FCLK 参考手册中阅读章节 gt RCC 为 Cortex 系统定时器 SysT
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re

随机推荐