程序代码篇---STM32串口通信


文章目录

  • 前言
  • [1. 头文件和全局变量](#1. 头文件和全局变量)
  • [2. 串口1初始化函数](#2. 串口1初始化函数)
  • [3. 串口1发送字节函数](#3. 串口1发送字节函数)
  • [4. 串口1发送字符串函数](#4. 串口1发送字符串函数)
  • [5. 串口1发送数字函数](#5. 串口1发送数字函数)
  • [6. 重定义fputc函数](#6. 重定义fputc函数)
  • [7. 串口数据解析函数](#7. 串口数据解析函数)
  • [8. 串口2中断服务程序](#8. 串口2中断服务程序)
  • 总结

前言

本次将介绍一个基于STM32微控制器串口通信 实现,包含了串口的初始化、数据发送、数据接收和解析等功能。下面我将逐句详细解释这段代码。


1. 头文件和全局变量

c 复制代码
#include "y_usart/y_usart.h"

char uart_receive_buf[UART_BUF_SIZE];
uint16_t uart_get_ok;
char uart_mode;

#include "y_usart/y_usart.h":包含了串口相关的头文件 ,定义了串口的配置和函数声明
char uart_receive_buf[UART_BUF_SIZE]:定义一个字符数组 ,用于存储从串口接收到的数据
uint16_t uart_get_ok:一个标志位 ,用于指示是否成功接收到完整的数据帧
char uart_mode:用于指示当前串口的接收模式

2. 串口1初始化函数

c 复制代码
void uart1_init(uint32_t BaudRate)
{
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 使能端口时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    USART_DeInit(USART1);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;        /* PA.9 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /* 复用推挽输出 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; /* 浮空输入 */
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = BaudRate;                                    /* 串口波特率 */
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                        /* 字长为8位数据格式 */
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            /* 字长为8位数据格式 */
    USART_InitStructure.USART_Parity = USART_Parity_No;                                /* 无奇偶校验位 */
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                    /* 收发模式 */
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* 无硬件数据流控制 */
    USART_Init(USART1, &USART_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 抢占优先级 */
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          /* 子优先级 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              /* IRQ通道使能 */
    NVIC_Init(&NVIC_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); /* 开启串口接受中断 */
    USART_ITConfig(USART1, USART_IT_TXE, DISABLE); /* 禁止串口发送中断 */

    USART_Cmd(USART1, ENABLE); /* 使能串口1  */
}

USART_InitTypeDef USART_InitStructure:定义了一个USART初始化结构体 ,用于配置串口参数
GPIO_InitTypeDef GPIO_InitStructure:定义了一个GPIO初始化结构体 ,用于配置GPIO引脚
NVIC_InitTypeDef NVIC_InitStructure:定义了一个NVIC初始化结构体 ,用于配置中断优先级
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE):使能GPIOA和USART1的时钟。
USART_DeInit(USART1):复位USART1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9:配置PA9引脚为复用推挽输出模式,用于串口1的TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10:配置PA10引脚为浮空输入模式,用于串口1的RX
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_Mode = USART_Mode_Rx | USART_Mode_Tx:设置串口为收发模式。
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None:设置无硬件流控制。

USART_Init(USART1, &USART_InitStructure):初始化USART1。
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn:设置USART1的中断通道。
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1:设置抢占优先级为1。
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0:设置子优先级为0。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE:使能USART1中断。
NVIC_Init(&NVIC_InitStructure):初始化NVIC。
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE):使能USART1接收中断。
USART_ITConfig(USART1, USART_IT_TXE, DISABLE):禁用USART1发送中断。
USART_Cmd(USART1, ENABLE):使能USART1。

3. 串口1发送字节函数

c 复制代码
void uart1_send_byte(char dat)
{
    USART_SendData(USART1, dat);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
        ;
}
USART_SendData(USART1, dat);:发送一个字节数据。

while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);:等待发送完成。

4. 串口1发送字符串函数

c 复制代码
void uart1_send_str(char *s)
{
    while (*s)
    {
        uart1_send_byte(*s++);
    }
}
while (*s):循环发送字符串中的每个字符,直到字符串结束。

5. 串口1发送数字函数

c 复制代码
void uart1_send_int(int tmp)
{
    static char str[20];
    sprintf((char *)str, "%d", tmp);
    uart1_send_str(str);
}
sprintf((char *)str, "%d", tmp);:将整数转换为字符串。

uart1_send_str(str);:发送转换后的字符串。

6. 重定义fputc函数

c 复制代码
int fputc(int ch, FILE *f)
{
    while ((UART5->SR & 0X40) == 0)
        ; // 循环发送,直到发送完毕
    UART5->DR = (u8)ch;
    return ch;
}

while ((UART5->SR & 0X40) == 0);:等待UART5发送寄存器为空。

UART5->DR = (u8)ch;:发送字符。

return ch;:返回发送的字符。

7. 串口数据解析函数

c 复制代码
void uart_data_parse(char rx_data,uint8_t uart_num)
{
	static u16 buf_index = 0;
	
	if (uart_get_ok)
		return;
		
	if (uart_mode == 0)
	{
		if (rx_data == '$')
		{
			// 命令模式 $XXX!
			uart_mode = 1;
		}
		else if (rx_data == '#')
		{
			// 单舵机模式	#000P1500T1000! 类似这种命令
			uart_mode = 2;
		}
		else if (rx_data == '{')
		{
			// 多舵机模式	{#000P1500T1000!#001P1500T1000!} 多个单舵机命令用大括号括起来
			uart_mode = 3;
		}
		else if (rx_data == '<')
		{
			// 保存动作组模式	<G0000#000P1500T1000!#001P1500T1000!B000!> 用尖括号括起来 带有组序号
			uart_mode = 4;
		}
		buf_index = 0;
	}

	uart_receive_buf[buf_index++] = rx_data;

	if ((uart_mode == 1) && (rx_data == '!'))
	{
		uart_receive_buf[buf_index] = '\0';
		uart_get_ok = 1;
	}
	else if ((uart_mode == 2) && (rx_data == '!'))
	{
		uart_receive_buf[buf_index] = '\0';
		uart_get_ok = 1;
	}
	else if ((uart_mode == 3) && (rx_data == '}'))
	{
		uart_receive_buf[buf_index] = '\0';
		uart_get_ok = 1;
	}
	else if ((uart_mode == 4) && (rx_data == '>'))
	{
		uart_receive_buf[buf_index] = '\0';
		uart_get_ok = 1;
	}
	
	if(uart_get_ok==1)
	{
		uart_receive_num = uart_num;
	}

	if (buf_index >= UART_BUF_SIZE)
		buf_index = 0;
}

static u16 buf_index = 0:定义一个静态变量,用于记录接收缓冲区的索引。

if (uart_get_ok) return:如果已经接收到完整的数据帧,则直接返回。

if (uart_mode == 0):根据接收到的字符判断当前接收模式。

uart_receive_buf[buf_index++] = rx_data:将接收到的字符存入缓冲区。

if ((uart_mode == 1) && (rx_data == '!')):根据不同的模式判断是否接收到完整的数据帧。

uart_receive_buf[buf_index] = '\0':在缓冲区末尾添加字符串结束符。

uart_get_ok = 1:设置接收完成标志。

if (buf_index >= UART_BUF_SIZE) buf_index = 0:防止缓冲区溢出。

8. 串口2中断服务程序

c 复制代码
void USART2_IRQHandler(void)
{
    //先判断标志位
    if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
    {
        //接收数据
        uint8_t sbuf_bak = USART_ReceiveData(USART2);
        
        uart_data_parse(sbuf_bak,2);//数据解析
        //手动清除标志位
        USART_ClearITPendingBit(USART2, USART_IT_RXNE);  
    }
}

uint8_t sbuf_bak = USART_ReceiveData(USART1):读取接收到的数据

uart_data_parse(sbuf_bak,1):解析接收到的数据

USART_ClearITPendingBit(USART1, USART_IT_RXNE):清除接收中断标志位

总结

这段代码实现了STM32微控制器的多个串口初始化、数据发送、数据接收和解析功能。通过中断机制,程序能够在接收到数据时及时处理,并根据不同的数据格式进行解析。代码结构清晰,功能模块化,便于维护和扩展。


相关推荐
电子小子洋酱25 分钟前
ESP32移植Openharmony外设篇(10)inmp441麦克风
单片机·物联网·华为·harmonyos·鸿蒙
智木芯语30 分钟前
【21】单片机编程核心技巧:if语句逻辑与真假判断
单片机·嵌入式·#stm32·#stc8
美好的事情总会发生35 分钟前
LVDS(Low Voltage Differential Signaling)电平详解
嵌入式硬件·硬件工程·智能硬件
煜3642 小时前
stm32 蓝桥杯 物联网 独立键盘的使用
stm32·物联网·蓝桥杯
honey ball3 小时前
时钟频率和GPIO的设定【DSP】
单片机·嵌入式硬件
爱学习的张哥4 小时前
IP层之分片包的整合处理---BUG修复
单片机·tcp/ip·bug
ElePower95274 小时前
浅谈时钟启动和Systemlnit函数
单片机·嵌入式硬件
阿楠小波4 小时前
蓝桥杯嵌入式组第十二届省赛题目解析+STM32G431RBT6实现源码
c语言·stm32·单片机·学习·蓝桥杯
小麦嵌入式5 小时前
Linux驱动开发实战(四):设备树点RGB灯
linux·驱动开发·stm32·单片机·嵌入式硬件·物联网·ubuntu
风起逸尘5 小时前
硬件驱动——51单片机、LED、动态数码管
单片机·嵌入式硬件·51单片机