STM32 407 RS485通信实现数据收发【我的创作纪念日】

1. 前言

本例中的485驱动,基于标准库编写,不是HAL库,请大家注意。
最近搞嵌入式程序,踩了不少坑,这里统一记录一下。

2. 收获

1.串口通信,数据是一个字节一个字节的发送,对方收到的数据是放在了寄存器中(这个是由硬件控制的),软件在接收数据时,中断处理函数中再把寄存器中的数据取出来,一般是放在一个缓冲区里。因为写入的数据量 和写入时间不确定性 ,会导致软件在读取串口的时候读取的数据不完整 或者一次读取"一条半"这样的问题…
这时候软件上就要解决一个问题,如何保证收到的数据是完整的
一般有下面几种方式:
1. 发送的数据长度是知道的,那么当接收到相应长度的数据时,认为接收完成了。
2. 数据本身有格式,比如数据头+数据+校验信息。那么每当收到下一个数据头时,认为上一个数据收完了。
3. 接收中断处理函数中,添加定时器,当超过一段时间,还没收到数据,认为一段数据收完了。具体设置多长时间,需要根据通信双方的需要,来进行设置。(本例中,使用的此方法)

数据发送到数据接收的切换,中间要加时延。今天测试程序时,发现一个问题:RS485串口发送完数据后,RS485串口接收,中间要加一定时延,才能保证接收的数据是完整的。但有一个问题:为啥要加这个时延?怎么量化计算?

3.程序

3.1 USART GPIO配置,工作模式配置

/** 函数名:_485_Config* 描述:USART GPIO配置,工作模式配置*/
void _485_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;/* config USART clock */RCC_AHB1PeriphClockCmd(_485_USART_RX_GPIO_CLK|_485_USART_TX_GPIO_CLK|_485_RE_GPIO_CLK, ENABLE);RCC_APB2PeriphClockCmd(_485_USART_CLK, ENABLE); // USART1,USART6属于APB2    RCC_APB2PeriphClockCmd, USART2,USART3属于APB1  RCC_APB1PeriphClockCmd//	RCC_APB2PeriphClockCmd(_485_USART_CLK,ENABLE);
//	GPIO_PinRemapConfig(_485_USART_CLK,ENABLE);/* Connect PXx to USARTx_Tx*/GPIO_PinAFConfig(_485_USART_RX_GPIO_PORT,_485_USART_RX_SOURCE, _485_USART_RX_AF);/* Connect PXx to USARTx_Rx*/GPIO_PinAFConfig(_485_USART_TX_GPIO_PORT,_485_USART_TX_SOURCE,_485_USART_TX_AF);/* USART GPIO config *//* Configure USART Tx as alternate function push-pull */GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Pin = _485_USART_TX_PIN  ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(_485_USART_TX_GPIO_PORT, &GPIO_InitStructure);/* Configure USART Rx as alternate function  */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = _485_USART_RX_PIN;GPIO_Init(_485_USART_RX_GPIO_PORT, &GPIO_InitStructure);/* 485收发控制管教 */GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_Pin = _485_RE_PIN  ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(_485_RE_GPIO_PORT, &GPIO_InitStructure);/* USART mode config */USART_InitStructure.USART_BaudRate = _485_USART_BAUDRATE;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No ;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(_485_USART, &USART_InitStructure); USART_Cmd(_485_USART, ENABLE);NVIC_Configuration();/* 使能串口接收中断 */USART_ITConfig(_485_USART, USART_IT_RXNE, ENABLE);GPIO_ResetBits(_485_RE_GPIO_PORT,_485_RE_PIN); //默认进入接收模式
}

3.2 GPIO宏定义

// 串口1 定义:PA9 Tx,PA10 Rx,PA8 DE
#define _485_USART                             USART1
#define _485_USART_CLK                         RCC_APB2Periph_USART1
#define _485_USART_BAUDRATE                    115200#define _485_USART_RX_GPIO_PORT                GPIOA
#define _485_USART_RX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define _485_USART_RX_PIN                      GPIO_Pin_10
#define _485_USART_RX_AF                       GPIO_AF_USART1
#define _485_USART_RX_SOURCE                   GPIO_PinSource10#define _485_USART_TX_GPIO_PORT                GPIOA
#define _485_USART_TX_GPIO_CLK                 RCC_AHB1Periph_GPIOA
#define _485_USART_TX_PIN                      GPIO_Pin_9
#define _485_USART_TX_AF                       GPIO_AF_USART1
#define _485_USART_TX_SOURCE                   GPIO_PinSource9#define _485_RE_GPIO_PORT												GPIOA
#define _485_RE_GPIO_CLK												RCC_AHB1Periph_GPIOA
#define _485_RE_PIN															GPIO_Pin_8#define _485_INT_IRQ                 						USART1_IRQn
#define _485_IRQHandler                         USART1_IRQHandler

每一个外设,都有对应的地址。通过对这个地址操作,操作这些外设。
在这里插入图片描述

3.3 接收中断处理函数

定义了一个缓冲区来专门存放接收到的数据。

//串口中断处理函数---接收
#define UART_BUFF_SIZE      1024
volatile    uint16_t uart_p = 0;
uint8_t     uart_buff[UART_BUFF_SIZE];
extern uint8_t g_recvMotorData;void bsp_485_IRQHandler(void)
{//	printf("come here1");//g_recvMotorData = 1;uint8_t clear;	//Delay(115200/8); if(uart_p<UART_BUFF_SIZE){while(USART_GetITStatus(_485_USART, USART_IT_RXNE) != RESET){uart_buff[uart_p] = USART_ReceiveData(_485_USART);uart_p++;TIM_Cmd(BASIC_TIM, ENABLE);	USART_ClearITPendingBit(_485_USART, USART_IT_RXNE);}}else{ //接收的数据长度超过UART_BUFF_SIZE 时,这次数据全部丢弃USART_ClearITPendingBit(_485_USART, USART_IT_RXNE);//clean_rebuff();       }//printf("uart_p = %u",uart_p);if (uart_p == 1) // 用来判断是否收到一帧数据 https://blog.csdn.net/ASKLW/article/details/79246786  uart_p == 1  USART_GetITStatus(_485_USART, USART_IT_IDLE) != RESET{
//  clear = _485_USART->SR; //先读SR,再读DR才能完成idle中断的清零,否则一直进入中断
//  clear = _485_USART->DR;	uint16_t len = 0;
//			char str[1024] ={0};
//		  sprintf(str, "%d", ii);  
//			Usart_SendByte(USART6, len);//Usart_SendString(USART6, get_rebuff(&len));	
//			printf("come here");g_recvMotorData = 1;}
}

这个中断处理函数,是与启动文件中的中断号对应的。当硬件感知到来了一个中断时,调用对应的中断处理函数。
在这里插入图片描述

3.4 发送和接收函数

***************** 发送一个字符  **********************/
// 使用单字节数据发送前要使能发送引脚,发送后要使能接收引脚
void _485_SendByte(  uint8_t ch )
{/* 发送一个字节数据到USART1 */USART_SendData(_485_USART,ch);/* 等待发送完成 */while (USART_GetFlagStatus(_485_USART, USART_FLAG_TXE) == RESET);	}
/*****************  发送指定长度的字符串 **********************/
void _485_SendStr_length( uint8_t *str,uint32_t strlen )
{unsigned int k=0;_485_TX_EN();	//	使能发送数据	do {_485_SendByte( *(str + k) );k++;} while(k < strlen);/*加短暂延时,保证485发送数据完毕*/Delay(0xFFF);_485_RX_EN()	;//	使能接收数据
}/*****************  发送字符串 **********************/
void _485_SendString(  uint8_t *str) //字符发的什么样,收的就是什么样子。
{unsigned int k=0;_485_TX_EN()	;//	使能发送数据do {_485_SendByte(  *(str + k) );k++;} while(*(str + k)!='\0');/*加短暂延时,保证485发送数据完毕*/Delay_us(100);// 延时1ms_485_RX_EN()	;//	使能接收数据
}void _485_SendArray(uint8_t *arr, uint8_t len)
{_485_TX_EN();// 切换到发送模式if (len < 1){return;}for (uint8_t i = 0; i < len; i++){_485_SendByte(arr[i]);}_485_RX_EN();// 切换到接收模式
}/*****************  发送一个16位数,此计算结果低位在前高位在后 **********************/
void _485_SendHalfWord(uint16_t ch)
{uint8_t temp_h, temp_l;/* 取出高八位 */temp_h = (ch&0XFF00)>>8;/* 取出低八位 */temp_l = ch&0XFF;/* 发送低八位 */_485_SendByte(temp_l);while (USART_GetFlagStatus(_485_USART, USART_FLAG_TXE) == RESET);	/* 发送高八位 */_485_SendByte(temp_h);while (USART_GetFlagStatus(_485_USART, USART_FLAG_TXE) == RESET);
}

3.4 主函数

_485_SendArray(cmd_6064, 8); Delay_us(100);// 加1ms时延;‌RS485串口发送完数据后,为了保证接收的数据完整性,中间需要加一定的时延。if (g_timer) {g_timer = 0;_485_RX_EN();uint16_t len =0;char * pbuf = get_rebuff(&len);for (int i = 0; i < len; i++){printf("%c", pbuf[i]);}clean_rebuff();// 用完之后,清空缓冲区}

里面定时器相关内容,略写了。

参考

  1. modbus串口编程接收到的数据不完整问题
  2. 本文章将会展示至 里程碑专区 ,您也可以在 专区 内查看其他创作者的纪念日文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56515.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【消息队列】Kafka从入门到面试学习总结

国科大学习生活&#xff08;期末复习资料、课程大作业解析、大厂实习经验心得等&#xff09;: 文章专栏&#xff08;点击跳转&#xff09; 大数据开发学习文档&#xff08;分布式文件系统的实现&#xff0c;大数据生态圈学习文档等&#xff09;: 文章专栏&#xff08;点击跳转&…

小米电机与STM32——CAN通信

背景介绍&#xff1a;为了利用小米电机&#xff0c;搭建机械臂的关节&#xff0c;需要学习小米电机的使用方法。计划采用STM32驱动小米电机&#xff0c;实现指定运动&#xff0c;为此需要了解他们之间的通信方式&#xff0c;指令写入方法等。花了很多时间学习&#xff0c;但网络…

【Next.js 项目实战系列】05-删除 Issue

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】04-修改 Issue 删除 Issue 添加删除 Button​ 本节代码链接 这里我们主要关注布局…

类和对象的认识

类&#xff1a;类是用来描述一个对象的&#xff0c;在java中万物皆对象&#xff0c;通过对类的抽象&#xff0c;类有哪些属性和行为&#xff0c;将这些抽象出来就是类。比如&#xff1a;狗&#xff0c;有名字&#xff0c;年龄&#xff0c;要吃饭的行为等等&#xff0c;将这些动…

仓储管理系统原型图移动端(WMS),出入库管理、库存盘点、库存调拨等(Axure原型、Axure实战项目)

仓储管理系统原型图移动端 Warehouse Management System Prototype 仓储管理系统原型图移动端是一个以图形化方式展示系统移动端界面和功能的原型设计图。原型图展示和说明系统移动端的功能和界面布局&#xff0c;为相关利益方提供一个直观的视觉化展示&#xff0c;帮助他们更…

RAG(检索增强生成)面经(1)

1、RAG有哪几个步骤&#xff1f; 1.1、文本分块 第一个步骤是文本分块&#xff08;chunking&#xff09;&#xff0c;这是一个重要的步骤&#xff0c;尤其在构建与处理文档的大型文本的时候。分块作为一种预处理技术&#xff0c;将长文档拆分成较小的文本块&#xff0c;这些文…

Android中的内容提供者

目录 1.创建内容提供者 1--手动创建一个Android应用程序 2--创建自定义的内容提供者 2.访问其他应用程序 1. 解析URI 2. 查询数据 3. 遍历查询结果 3)案例:读取手机通信录 1.声明权限 2.activity_main.xml文件内容 3.my_phone_list.xml文件内容 4.定义PhoneInfo实体 5.定义MyPh…

线程异步和通信(promise和future)

线程异步和通信&#xff08;promise和future&#xff09; #include <thread> #include <iostream> #include <future> #include <string> using namespace std;void TestFuture(promise<string> p)//线程函数 {cout << "begin TestFu…

JAVA就业笔记7——第二阶段(4)

课程须知 A类知识&#xff1a;工作和面试常用&#xff0c;代码必须要手敲&#xff0c;需要掌握。 B类知识&#xff1a;面试会问道&#xff0c;工作不常用&#xff0c;代码不需要手敲&#xff0c;理解能正确表达即可。 C类知识&#xff1a;工作和面试不常用&#xff0c;代码不…

绘制YOLOv11模型在训练过程中,精准率,召回率,mAP_0.5,mAP_0.5:0.95,以及各种损失的变化曲线

一、本文介绍 本文用于绘制模型在训练过程中,精准率,召回率,mAP_0.5,mAP_0.5:0.95,以及各种损失的变化曲线。用以比较不同算法的收敛速度,最终精度等,并且能够在论文中直观的展示改进效果。支持多文件的数据比较。 专栏目录:YOLOv11改进目录一览 | 涉及卷积层、轻量化…

spring task的使用场景

spring task 简介 spring task 是spring自带的任务调度框架按照约定的时间执行某个方法的工具&#xff0c;类似于闹钟 应用场景 cron表达式 周和日两者必定有一个是问号 简单案例 使用步骤 demo Component注解表示这是一个Spring的组件&#xff0c;会被Spring容器扫描到&#…

全面超越Spark,Clickhouse,比 Spark 快 900%,基于云器Lakehouse构建新一代一体化数据平台

人工智能的迅速发展正在改变着我们的世界&#xff0c;对于大数据企业来说更是如此。 在大语言模型的引领下&#xff0c;数据平台领军企业 Databricks 和 Snowflake 的未来正在被重新书写。这两家企业在不久前的发布会上强调了大语言模型和 AI 能力的重要性&#xff0c;试图通过…

[单master节点k8s部署]41.部署springcloud项目

在之前的文章中我们配置了mysql和harbor&#xff0c;现在我们可以将一个springcloud部署在k8s集群中了。 项目概述 这个springcloud项目将采用maven进行打包部署。首先安装maven&#xff1a; yum install java-1.8.0-openjdk maven-3.0.5* -y 然后将该项目上传到k8s集群的m…

ANSYS 2024 R2设置中文

ANSYS 2024 R2设置中文 打开ANSYS Workbench R2软件依次点击Tools、Options 在弹出的Options选项卡中选择Regional and Language Options项&#xff0c;选择Language为Chinese然后点击OK 重启软件即可切换为中文界面

珠海自闭症寄宿学校:打造温馨家庭般的学习氛围

原文链接&#xff1a;http://www.zibizhengwang.com/page35.html 在探索自闭症儿童教育的广阔领域里&#xff0c;寄宿制学校以其独特的优势&#xff0c;为自闭症儿童提供了一个集教育、康复与生活于一体的综合性环境。而在珠海乃至全国&#xff0c;众多自闭症寄宿学校正不断努…

为什么inet_ntoa会返回错误的IP地址?

目录 1、调用inet_addr和inet_ntoa实现整型IP与点式字符串之间的转换 1.1、调用inet_addr将点式字符串IP转换成整型IP 1.2、调用inet_ntoa将整型IP转换成点式字符串IP 2、调用inet_ntoa返回错误点式字符串IP的原因分析 3、解决多线程调用inet_ntoa返回错误点式字符串IP的办…

请求第三方接口有反斜杠和双引号怎么处理,且做格式校验?

如&#xff1a;接口文档要求 直接使用转义失败&#xff0c;在postman中填值请求正常。 String para "[" "\\" "\"" "预计今天白天我市多云间晴&#xff1b;" "\\" "\"]"; System.err.println(pa…

Applied Spatial Statistics(九)GWR示例

Applied Spatial Statistics&#xff08;九&#xff09;GWR 示例 这是一个基本的示例笔记本&#xff0c;演示了如何使用开源“mgwr”包在 Python 中校准 GWR&#xff08;Fotheringham 等人&#xff0c;2002&#xff09;模型。mgwr 包由 Oshan 等人&#xff08;2019 年&#xff…

塞班和诺基亚(中古手机图,你见过哪个?)

诺基亚的塞班系统&#xff0c;是比较早和强大的移动操作系统了。当时还有Palm&#xff0c;微软的平台&#xff0c;但市占率都很低。 安卓从被谷歌收购那天&#xff0c;每个特性都预示着&#xff0c;未来一定会超越塞班。而塞班后来取消了生态&#xff0c;自己来使用&#xff0c…

element-ui点击文字查看图片预览功能

今天做一个点击文字查看图片的功能&#xff0c;大体页面长这样子&#xff0c;点击查看显示对应的图片 引入el-image-viewer&#xff0c;点击的文字时候设置图片预览组件显示并传入图片的地址 关键代码 <el-link v-if"scope.row.fileList.length > 0" type&…