STM32学习笔记一一USART

前言:

为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

在这里插入图片描述



githubmy github


注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
本文设计的文件包含:
(1)drvuart.c:UART 驱动实现
(2)头文件:
drvuart.h :UART;


1. 串口的基本概念

通用同步异步收发器 (USART) 提供了一种灵活的方法与使用工业标准 NRZ 异步串行数据格式的外部设备之间进行全双工数据交换。 USART 利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持 LIN (局部互连网),智能卡协议和 IrDA (红外数据组织) SIR ENDEC 规范,以及调制解调器 (CTS/RTS )操作。它还允许多处理器通信。使用多缓冲器配置的 DMA 方式,可以实现高速数据通信。

2. 串口通信连接

接口通过三个引脚与其他设备连接在一起。任何 USART 双向通信至少需要两个脚:接收数据输入 (RX) 和发送数据输出 (TX)。

RX: 接收数据串行输。通过过采样技术来区别数据和噪音,从而恢复数据。

TX: 发送数据输出。当发送器被禁止时,输出引脚恢复到它的 I/O 端口配置。当发送器被激活,并且不发送数据时, TX 引脚处于高电平。在单线和智能卡模式里,此 I/O 口 被同时用于数据的发送和接收。

3. 串口设置的一般步骤

对于复用功能的 IO,我们首先要使能 GPIO 时钟,然后使能复用功能时钟,同时要把 GPIO 模式设置为复用功能对应的模式,串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成后就是使能串口。同时,如果开启了串口的中断,当然要初始化 NVIC 设置中断优先级别,最后编写中断服务函数。

串口设置的一般步骤可以总结为如下几个步骤:

(1) 串口时钟使能,GPIO 时钟使能;

(2) 串口复位

(3)GPIO 端口模式设置

(4) 串口参数初始化

(5) 开启中断并且初始化 NVIC(如果开启中断才需要这个步骤)

(6)使能串口

(7)中断处理函数

与串口基本配置直接相关定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。

4. 主要库函数功能介绍

函数名功能概要解释
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1)串口时钟使能串口挂载在 APB2 下
USART_DeInit(USART_TypeDef* USARTx)串口复位当外设出现异常时,通过复位设置,使其重新工作
USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)串口参数初始化初始化需要设置的参数为:波特率,字长,停止位,奇偶校验位,硬件数据流控制,模式
USART_SendData(USART_TypeDef* USARTx, uint16_t Data)数据发送向串口寄存器 USART_DR 写入一个数据
USART_ReceiveData(USART_TypeDef* USARTx)数据接收操作 USART_DR 寄存器读取串口接收到的数据
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)读取串口状态
USART_GetFlagStatus(USART1, USART_FLAG_RXNE)判断读寄存器是否非空(RXNE)
USART_GetFlagStatus(USART1, USART_FLAG_TC)判断发送是否完成(TC)
USART_Cmd(USART1, ENABLE)串口使能
USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)开启串口响应中断
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)获取相应中断状态中断发生,会设置状态寄存器中的某个标志位,返回值是SET,说明是串口发送完成中断发生

说明:串口的状态可以通过状态寄存器 USART_SR 读取。寄存器如下:

这里写图片描述

RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。

TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式: 1)读 USART_SR,写 USART_DR。 2)直接向该位写 0。

5. 配置全双工的串口 1

TX(PA9):管脚需要配置为推挽复用输出;

RX(PA10):管脚配置为浮空输入或者带上拉输入。

这里写图片描述

6. 例程——USART1为例

6.1 初始化

宏定义:

//GPIO 部分
#define RCC_PCLK_USART1_GPIO		RCC_APB2Periph_GPIOA
#define USART1_TXD_IO				GPIO_Pin_9
#define USART1_RXD_IO				GPIO_Pin_10
#define USART1_TXD_PORT				GPIOA
#define USART1_RXD_PORT				GPIOA
#define RCC_PCLK_USART1				RCC_APB2Periph_USART1
1)IO 初始化

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_gpio_init
//	功能说明: 串口GPIO初始化
//	形    参: 	uart_chl:串口编号
//	返 回 值: 无
//	日    期: 2020-03-25
//  备    注: 串口GPIO初始化
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
static void uart_gpio_init(uint8_t uart_chl)
{
    GPIO_InitTypeDef gpio_config_init;

    if (uart_chl == 1)
    {
        RCC_APB2PeriphClockCmd(RCC_PCLK_USART1_GPIO, ENABLE);	//使能UART1 GPIO时钟,APB2总线

		gpio_config_init.GPIO_Pin 	= USART1_TXD_IO; //PA9
		gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz;
		gpio_config_init.GPIO_Mode 	= GPIO_Mode_AF_PP;	//复用推挽输出
		GPIO_Init(USART1_TXD_PORT, &gpio_config_init);	//初始化GPIOA9

		gpio_config_init.GPIO_Pin 	= USART1_RXD_IO;	//PA10
		gpio_config_init.GPIO_Mode 	= GPIO_Mode_IN_FLOATING;	//浮空输入
		GPIO_Init(USART1_RXD_PORT, &gpio_config_init);	//初始化GPIOA10
    }
}2)NVIC 初始化

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_irq_set
//	功能说明: 串口中断初始化配置
//	形    参: 	uart_chl:串口编号
//	返 回 值: 无
//	日    期: 2020-03-27
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
static void uart_irq_set(uint8_t uart_chl)
{
	NVIC_InitTypeDef uart_nvic_config = {0};

	if (uart_chl == 1)
    {
        uart_nvic_config.NVIC_IRQChannel 					= USART1_IRQn;
		uart_nvic_config.NVIC_IRQChannelPreemptionPriority 	= 3 ;//抢占优先级3
		uart_nvic_config.NVIC_IRQChannelSubPriority 		= 3;		//子优先级3
		uart_nvic_config.NVIC_IRQChannelCmd 				= ENABLE;			//IRQ通道使能
		NVIC_Init(&uart_nvic_config);	//根据指定的参数初始化NVIC寄存器

		USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    }
}3)串口参数设置

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_config
//	功能说明: 串口配置参数初始化
//	形    参: 	uart_chl:串口编号
//              uart_bound:波特率
//	返 回 值: 无
//	日    期: 2020-03-25
//  备    注: 串口编号从1-5(UART1-5),串口中断使能在Nvic设置里面配置
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
static void uart_config(uint8_t uart_chl, uint32_t uart_bound)
{
    USART_InitTypeDef uart_config_init;

    if (uart_chl == 1)
    {
		RCC_APB2PeriphClockCmd(RCC_PCLK_USART1, ENABLE);
		
		uart_config_init.USART_BaudRate              = uart_bound;	//串口波特率
		uart_config_init.USART_WordLength            = USART_WordLength_8b;	//字长为8位数据格式
		uart_config_init.USART_StopBits              = USART_StopBits_1;	//一个停止位
		uart_config_init.USART_Parity                = USART_Parity_No;	//无奇偶校验位
		uart_config_init.USART_HardwareFlowControl   = USART_HardwareFlowControl_None;	//无硬件数据流控制
		uart_config_init.USART_Mode                  = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

		USART_Init(USART1, &uart_config_init); //初始化串口1
		// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断(串口参数初始化之后才能开启中断)
		USART_Cmd(USART1, ENABLE); 		//使能串口1 
    }
}4)对外接口

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_init
//	功能说明: 串口G配置参数初始化
//	形    参: 	uart_chl:串口编号
//              uart_bound:波特率
//	返 回 值: 无
//	日    期: 2020-03-25
//  备    注: 统一调用底层接口(三部分:GPIO + UART + NVIC)
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
void uart_init(uint8_t uart_chl, uint32_t uart_bound)
{
    if (uart_chl == 1)
    {
        uart_gpio_init(uart_chl);
        uart_config(uart_chl, uart_bound);
		uart_irq_set(uart_chl);
    }
}

6.2 收发处理

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_send_byte
//	功能说明: 串口发送一字节数据
//	形    参: 	uart_chl:串口编号
//              send_byte:发送的数据
//	返 回 值: 无
//	日    期: 2020-03-27
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
bool uart_send_byte(uint8_t uart_chl, uint8_t send_byte)
{
	if (uart_chl == 1)
    {
	#if 0
		while((USART1->SR & 0X40) == 0);//循环发送,直到发送完毕   
		USART1->DR = (uint8_t)send_byte; 
	#else
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);	//等待串口发送标志位置位

		USART_SendData(USART1, (uint8_t)send_byte);	//发送数据

	#endif
    }

	return true;
}

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_send_serial_bytes
//	功能说明: 串口发送多字节数据
//	形    参: 	uart_chl:串口编号
//              pbdata:发送的数据地址
//				length:发送数据的长度
//	返 回 值: 无
//	日    期: 2020-03-28
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
bool uart_send_serial_bytes(uint8_t uart_chl, uint8_t *pbdata, uint16_t length)
{
	for (uint16_t i = 0; i < length; i++)
	{
		uart_send_byte(uart_chl, pbdata[i]);
	}
	
	// while (length--)
	// {
	// 	uart_send_byte(uart_chl, *pbdata++);
	// }

	return true;
}

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_recv_byte
//	功能说明: 串口接收一字节数据
//	形    参: 	uart_chl:串口编号
//	返 回 值: recv_byte:接收的数据
//	日    期: 2020-03-28
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
uint8_t uart_recv_byte(uint8_t uart_chl)
{
	uint8_t recv_byte = 0;

	if (uart_chl == 1)
    {
	#if 1
		while((USART1->SR & 0X20) == 0);//循环发送,直到发送完毕   
		recv_byte = USART1->DR; 
	#else
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);	//等待串口接收标志位置位
		recv_byte = USART_ReceiveData(USART1);	//接收数据

	#endif
    }
	
	return recv_byte;
}

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_recv_serial_bytes
//	功能说明: 串口接收多字节数据
//	形    参: 	uart_chl:串口编号
//              pbdata:发送的数据地址
//				length:发送数据的长度
//	返 回 值: 无
//	日    期: 2020-03-28
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
bool uart_recv_serial_bytes(uint8_t uart_chl, uint8_t *pbdata, uint16_t length)
{
	for (uint16_t i = 0; i < length; i++)
	{
		*pbdata++ = uart_recv_byte(1);
	}
	
	return true;
}

6.3 中断处理

此处以接收中断为例

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: USART1_IRQHandler
//	功能说明: 串口中断服务函数
//	形    参: 无
//	返 回 值: 无
//	日    期: 2020-03-28
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)	//检查接收中断标志位
	{	
//		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
		
		uart_test();	//接收的数据处理(收发测试)

		USART_ClearITPendingBit(USART1, USART_IT_RXNE);	//清除串口接收中断标志
	}
}

//--------------------------------------------------------------------------------------------------------------------------
//	函 数 名: uart_test
//	功能说明: 串口收发测试函数
//	形    参: 无
//	返 回 值: 无
//	日    期: 2020-03-28
//  备    注: 无
//	作    者: by 霁风AI
//-------------------------------------------------------------------------------------------------------------------------
void uart_test(void)
{
	uint8_t tmp_buff[6] = {0};

	uart_recv_serial_bytes(1, tmp_buff, sizeof(tmp_buff));

	if (tmp_buff[0] == 0x55)
	{
		uart_send_serial_bytes(1, tmp_buff, sizeof(tmp_buff));
	}
}

中断注意事项:

(1)中断发生后必须清除中断位,否则会出现死循环不断发生这个中断,然后需要对中断类型进行判断再执行代码。

(2)使用 EXTI 的 I/O 中断,在完成 RCC 与 GPIO 硬件设置之后需要做三件事:初始化 EXTI、NVIC 开中断、编写中断执行代码。

测试结果:

串口中断与 MCU 收发测试

在这里插入图片描述

补充: printf 重定向,嵌入式开发调试途径

int fputc(int ch, FILE *f)
{      
//	while((USART1->SR & 0X40) == 0);//循环发送,直到发送完毕   
//    USART1->DR = (uint8_t)ch;      
	
//	 uart_send_byte(1, (uint8_t)ch);

	uart_send_serial_bytes(1, (uint8_t *)&ch, 1);
	
	return ch;
}

参考:
1.STM32学习笔记——USART串口

2.STM32串口USART1的使用方法和程序

3. STM32串口设定流程总结

4.STM32的串口通信

5.原子库函数指导手册

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值