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

devtools/2025/3/14 6:18:33/

文章目录

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


前言

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


1. 头文件和全局变量

#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初始化函数

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发送字节函数

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发送字符串函数

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

5. 串口1发送数字函数

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函数

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. 串口数据解析函数

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中断服务程序

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



http://www.ppmy.cn/devtools/166948.html

相关文章

【算法】数据结构

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 持续更新中...数组、链表点击消除环形链表环形链表 II 栈、队列树图 持续更新中… 数组、链表 点击消除 AB5 点击消除 这个题很…

OSPF的LSA详解(报文分析+具体例子)

OSPF LSA 要点掌握: 名称 携带的内容 作用 传播的范围 由谁产生 LSA报头格式 所有的LSA都有相同的报文头 LS age&#xff1a;LSA产生后所经过的时间&#xff0c;以秒为单位。LSA在本路由器的LSDB中会随时间老化&#xff08;每秒钟加1&#xff09;&#xff0c;但在网络的…

中级软件设计师2004-2024软考真题合集下载

中级软件设计师2004-2024软考真题合集下载 &#x1f31f; 资源亮点&#x1f3af; 适用人群&#x1f4a1; 资源使用指南&#x1f4cc; 资源获取方式 &#x1f31f; 资源亮点 「中级软件设计师历年真题及答案解析&#xff08;2004-2024&#xff09;」 是全网最全、最新的备考资料…

【每日学点HarmonyOS Next知识】tab拦截、组件方法做参数、自定义组件链式调用、多次观察者监听、横竖屏切换

1、HarmonyOS Tab组件里的tabBar点击如何拦截&#xff0c;根据情况判断是否允许切换tab&#xff1f; Tab组件里的tabBar点击如何拦截&#xff0c;根据情况判断是否允许切换tab 暂时没有tabBar点击拦截功能实现&#xff0c;可以使用TabsController自定义页签以及并在其中添加事…

LangGraph 构建的工作流调用数据库的时候怎么添加重试机制

在工作流许多使用场景中&#xff0c;我们可能希望我们的节点具有自定义的重试策略&#xff0c;例如在调用API、查询数据库或调用LLM等情况下。这里我给大家介绍一种方式叫 RetryPolicy。 from langgraph.pregel import RetryPolicy RetryPolicy()RetryPolicy(initial_interval…

聊一聊测试过程中接口不通的原因排查

目录 一、 确认网络连通性 二、DNS 解析问题 三、客户端请求配置 四、 服务端状态检查 五、 查看日志 六、 接口逻辑与参数 七、 使用工具辅助排查 八、 环境与依赖问题 九、高级场景排查 十、其他方式 在我们进行接口测试时&#xff0c;大概率会遇到接口调不通的情况…

证券行业SCA开源风险治理实践

近日&#xff0c;悬镜安全成功中标国内领先的证券公司海通证券软件成分分析工具采购项目&#xff0c;中标产品为源鉴SCA开源威胁管控平台。 海通证券作为国内领先的证券公司之一&#xff0c;当下已基本建成涵盖证券期货经纪、投行、自营、资产管理、私募股权投资、另类投资、融…

Deepseek可以通过多种方式帮助CAD加速工作

自动化操作&#xff1a;通过Deepseek的AI能力&#xff0c;可以编写脚本来自动化重复性任务。例如&#xff0c;使用Python脚本调用Deepseek API&#xff0c;在CAD中实现自动化操作。 插件开发&#xff1a;结合Deepseek进行二次开发&#xff0c;可以创建自定义的CAD插件。例如&a…