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


相关推荐
仰泳之鹅6 分钟前
【天气时钟】第一课:工程模板的搭建
单片机·嵌入式硬件
Moonquakes54013 分钟前
嵌入式开发基础学习笔记(LED实验C语言实现、蜂鸣器实验、SDK裸机驱动、链接脚本、BSP工程管理)
stm32·单片机·嵌入式硬件
思茂信息18 分钟前
CST仿真实例:手机Type-C接口ESD仿真
c语言·开发语言·单片机·嵌入式硬件·智能手机·cst·电磁仿真
梁洪飞20 分钟前
armv7 cache机制
linux·arm开发·嵌入式硬件·arm·memcache
我是海飞27 分钟前
杰理 AC792N 使用 WebSocket 连接百度语音大模型,实现 AI 对话
c语言·单片机·嵌入式·ai对话·杰理·websockey
别掩29 分钟前
光耦选型指南
单片机·嵌入式硬件
2023自学中29 分钟前
嵌入式系统中的非易失性存储设备
linux·嵌入式硬件
Zeku9 小时前
Linux内核中SPI 子系统的整体架构
stm32·freertos·linux驱动开发·linux应用开发
czhaii12 小时前
MP3音乐播放器【FatFs+SD/TF卡+I2S-DAC】@STC32G144K246,实时解码MP3
单片机·硬件工程
炸膛坦客13 小时前
FreeRTOS 学习:(二十五)任务时间统计相关 API 函数
stm32·操作系统·freertos