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


相关推荐
△曉風殘月〆3 小时前
C#串口通信
嵌入式硬件·c#·串口
我不是帅戈6 小时前
STM32+安信可Ai-WB2-12F连接阿里云物联网平台
stm32·物联网·阿里云·云计算·安信可
教练、我想打篮球7 小时前
04 基于 STM32 的时钟展示程序
stm32·单片机·嵌入式硬件
大鱼YY7 小时前
STM32系统定时器以及微秒延时函数分析
stm32·滴答定时器
芯岭技术7 小时前
普冉MS32C001单片机,国产32位单片机,芯片特性和功能介绍
单片机·嵌入式硬件
吃货界的硬件攻城狮9 小时前
【STM32 学习笔记】ADC数模转换器
笔记·stm32·单片机·学习
小昭dedug12 小时前
功能安全的关键——MCU锁步核技术全解析(含真实应用方案)
单片机·嵌入式硬件
负里5513 小时前
STM32-模电
嵌入式硬件
BW.SU14 小时前
单片机 + 图像处理芯片 + TFT彩屏 指示灯控件
单片机·嵌入式硬件·人机交互·控件·触摸屏设计·指示灯·液晶屏
newtonltr15 小时前
CANopen TPDO 配置详解:对象 1800h、1A00h 与实践指南
stm32·canopen