IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32学习——串口 -> 正文阅读

[嵌入式]STM32学习——串口

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();
		}
	}
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-23 10:57:52  更:2021-07-23 10:58:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:51:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码