STM32学习笔记15:USART的使用

芯片型号:STM32F103RC

软件开发包:标准外设库

一、USART 基本结构

USART基本结构

如图,最左边是波特率发生器,用于产生约定的通信速率,时钟来源是 PCLK2/1。

经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送控制器和接收控制器用于控制发送移位和接收移位。

对于发送,由发送数据寄存器和发送移位寄存器的配合,将数据一位一位地移出去,通过 GPIO 的复用输出,输出到 TX 引脚,产生串口协议规定的波形,当数据由数据寄存器转到移位寄存器时,会置一个 TXE 的标志位,检查这个标志位,就可以直到是不是可以写下一个数据了。

对于接收,也是一样的,RX 引脚的波形,通过 GPIO 输入,在接收控制器的控制下,一位一位地移入接收移位寄存器,移完一帧数据后,数据就会统一转运到接收数据寄存器,在转移的同时置一个 RXNE 标志位,检查这个标志位,就可以直到是不是收到数据了。同时这个标志位也可以申请中断,这样就可以在收到数据时直接进入中断函数,然后快速地读取和保存数据。

最后,右下角是一个开关控制。

二、USART 初始化结构体详解

typedef struct {
    uint32_t USART_BaudRate;			// 波特率
    uint16_t USART_WordLength;			// 字长
    uint16_t USART_StopBits;			// 停止位
    uint16_t USART_Parity;				// 校验位
    uint16_t USART_Mode;				// USART 模式
    uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
  1. USART_BaudRate:波特率设置。一般设置为 2400、9600、19200、115200。标准库函数会根据
    设定值计算得到 USARTDIV 值,从而设置 USART_BRR 寄存器值。
  2. USART_WordLength:数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。
    如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。
  3. USART_StopBits:停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,它设定 USART_CR2
    寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。
  4. USART_Parity:奇偶校验控制选择,可选 USART_Parity_No(无校验)、USART_Parity_Even(偶
    校验) 以及 USART_Parity_Odd(奇校验),它设定 USART_CR1 寄存器的 PCE 位和 PS 位的值。
  5. USART_Mode:USART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或
    运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
  6. USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效,可选有使
    能 RTS、使能 CTS、同时使能 RTS 和 CTS、不使能硬件流。

三、编程要点

  1. 开启时钟,把需要用到的 GPIO 和 USART 时钟打开;
  2. 初始化 GPIO,把 TX 配置成复用输出,RX 配置成输入;
  3. 配置 USART 参数,即配置USART 初始化结构体;
  4. 如果只需要发送功能,就直接开启 USART,初始化就结束了;如果需要接收功能,可能还需要配置中断。

初始化完成之后,如果要发送数据,就调用函数;如果要接收数据,就调用接收函数;如果要获取发送和接收的状态,就调用获取标志位的函数。

四、举例

4.1 串口发送

这里使用 USART1,设定波特率为 9600,TX 对应 PA9。

  1. 配置 GPIO
void Serial_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 	// 使能GPIOA时钟

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		// 设置GPIO模式为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 				// 设置引脚为PA9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}
  1. 配置 USART
void Serial_USART_Config(void)
{
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 							// 使能USART1时钟

    USART_InitStructure.USART_BaudRate = 9600; 										// 设置波特率为9600
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Tx; 								// 设置为发送模式
    USART_InitStructure.USART_Parity = USART_Parity_No; 							// 无校验位
    USART_InitStructure.USART_StopBits = USART_StopBits_1; 							// 停止位为1
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; 					// 数据位为8位
    USART_Init(USART1, &USART_InitStructure);	 									// 初始化USART1

    USART_Cmd(USART1, ENABLE); 														// 使能USART1
}

配置完 USART1 之后,就可以使用串口实现一些功能了。
(1)发送一个字节

void Serial_SendByte(uint8_t byte)
{
    USART_SendData(USART1, byte); // 发送数据
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送完成
}

(2)发送一个数组

void Serial_SendArray(uint8_t *arr, uint16_t len)
{
    uint16_t i;
    for (i = 0; i < len; i++) // 遍历数组,逐个发送元素
    {
        Serial_SendByte(arr[i]); // 调用Serial_SendByte函数发送数组元素
    }
}

(3)发送字符串

void Serial_SendString(char *str)
{
    uint8_t i;
    for (i = 0; str[i] != '\0'; i++) // 遍历字符串,直到遇到空字符'\0'
    {
        Serial_SendByte(str[i]); // 调用Serial_SendByte函数发送字符串中的字符
    }
}

(4)发送数字字符

uint32_t Serial_Pow(uint32_t x, uint32_t y)
{
    uint32_t result = 1;
    while (y--) // 循环y次,计算x的y次方
    {
        result *= x; // 累乘结果
    }
    return result; // 返回结果
}

void Serial_SendNumber(uint32_t num, uint8_t len)
{
    uint8_t i;
    for (i = 0; i < len; i++) // 遍历数字的每一位
    {
        Serial_SendByte(num / Serial_Pow(10, len - i - 1) % 10 + '0'); // 将数字转换为字符并发送
    }
}

(5)printf 函数重定向

  1. Keil 版
#include <stdio.h>

int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//等待上一次串口数据发送完成     
    USART1->DR = (uint8_t)ch;   //写DR,串口1将发送数据  
	return ch;
}

这里也可以直接使用 Serial_SendByte函数:

int fputc(int ch, FILE *f)
{
	Serial_SendByte((uint8_t)ch);
	return ch;
}
  1. GCC 版
#include <stdio.h>

int _write (int fd, char *pBuffer, int size)  
{  
    for (int i = 0; i < size; i++)  
    {  
        while((USART1->SR&0X40)==0);          //等待上一次串口数据发送完成  
        USART1->DR = (uint8_t) pBuffer[i];    //写DR,串口1将发送数据
    }  
    return size;  
}

同样的:

int _write(int fd, char *pBuffer, int size)
{
    for (int i = 0; i < size; i++) // 遍历缓冲区
    {
        Serial_SendByte((uint8_t)pBuffer[i]); // 调用Serial_SendByte函数发送缓冲区中的字节
    }
    return size; // 返回发送的字节数
}
4.2 串口发送和接收

这里使用 USART1,设定波特率为 9600,TX 对应 PA9,RX 对应 PA10。在串口发送的基础上,修改如下:

(1)不使用中断

Serial_GPIO_Config 函数中添加如下代码:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 设置GPIO模式为上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // 设置引脚为PA10
GPIO_Init(GPIOA, &GPIO_InitStructure);

Serial_USART_Config 函数中将

USART_InitStructure.USART_Mode = USART_Mode_Tx; 	// 设置为发送模式

修改为

USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 设置为发送和接收模式

为了方便读取数据,编写函数

uint8_t Serial_ReceiveByte(void)
{
    // 等待接收缓冲区非空
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    // 从接收缓冲区读取一个字节并返回
    return USART_ReceiveData(USART1);
}

(2)使用中断

在不使用中断的基础上,在 Serial_USART_Config 函数中 USART_Cmd(USART1, ENABLE); 前添加代码

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能USART1的接收中断

并且还需要配置 NVIC:

void Serial_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 			// 设置中断通道为USART1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 			// 使能中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 	// 设置抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 			// 设置响应优先级为1
    NVIC_Init(&NVIC_InitStructure);
}

既然使用了中断,那么就不需要使用 Serial_ReceiveByte 函数读取数据了,直接使用中断服务函数:

// 定义串口接收数据变量和标志位
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

// 获取接收标志位的函数
uint8_t Serial_GetRxFlag(void)
{
    if (Serial_RxFlag == 1) // 如果接收标志位为1
    {
        Serial_RxFlag = 0; 	// 将接收标志位清零
        return 1; 			// 返回1表示有新数据接收
    }
    return 0; 				// 返回0表示没有新数据接收
}

// 获取接收数据的函数
uint8_t Serial_GetRxData(void)
{
    return Serial_RxData; // 返回接收到的数据
}

// USART1中断服务函数
void USART1_IRQHandler(void)
{
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) 	// 如果接收寄存器非空中断标志位为1
    {
        Serial_RxData = USART_ReceiveData(USART1); 			// 读取接收到的数据
        Serial_RxFlag = 1; 									// 设置接收标志位为1

        USART_ClearITPendingBit(USART1, USART_IT_RXNE); 	// 清除接收寄存器非空中断标志位
    }
}

参考视频源于B站up主: 野火科技、江协科技
参考文档:《STM32库开发实战指南——基于野火MINI开发板》

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
STM32CubeIDE中配置USART串口需要进行以下步骤: 1. 打开STM32CubeIDE,并创建一个新的工程。 2. 在工程导航器中,右键单击"Core"文件夹,选择"Add Component",然后选择"USART"组件。这将在工程中添加USART相关的文件和代码。 3. 在"Pinout & Configuration"选项卡中,选择你要使用USART引脚。 4. 在"Configuration"选项卡中,配置USART的参数,包括波特率、数据位数、停止位等等。可以根据具体需求进行配置。 5. 在代码中,使用HAL库函数来初始化和配置USART,例如使用`HAL_UART_Init()`函数来初始化USART使用`HAL_UART_MspInit()`函数来配置USART引脚等等。 6. 在代码中,根据需求来实现USART的发送和接收功能。可以使用轮询方式或中断方式来发送和接收数据。如果使用中断方式,需要编写相应的中断回调函数,例如使用`HAL_UART_RxCpltCallback()`函数来处理接收完成中断。 在以上步骤中,引用提供了一些关键词,如"STM32F407VE"、"FreeRTOS"、"DMA"等,可以参考这些关键词在STM32CubeIDE中进行配置。同时,引用和引用提供了一些代码示例,可以参考这些示例来实现USART的配置和功能。 请注意,具体的配置和代码实现可能会因具体的硬件平台和需求而有所不同。建议查阅官方的文档和资料来获取更详细和准确的配置步骤。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STM32F407VE+FreeRTOS+STM32CubeIDE+串口DMA+Freemodbus移植代码](https://download.csdn.net/download/qq_31272725/83163651)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [STM32CubeIDE学习笔记——USART(三种收发方式,printf重定向,工程配置)](https://blog.csdn.net/Reasally/article/details/126751127)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值