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. 本文章将会展示至 里程碑专区 ,您也可以在 专区 内查看其他创作者的纪念日文章

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

github学生认证(Github Copilot)

今天想配置一下Github Copilot&#xff0c;认证学生可以免费使用一年&#xff0c;认证过程中因为各种原因折腾了好久&#xff0c;记录一下解决方法供大家参考。 p.s.本文章只针对Github学生认证部分遇到的问题及解决方法&#xff0c;不包括配置copilot的全部流程~ 1、准备工作…

无图化加速!MemFusionMap提出时序重叠热图策略,在线建图mAP暴涨5.4%!

导读&#xff1a; HDMap对于自动驾驶系统至关重要&#xff0c;因为它可以为规划提供了精细的道路信息。尽管现有的单帧输入方法在在线矢量化高精地图构建方面取得了不错的成绩&#xff0c;但在处理复杂场景和遮挡时仍然存在挑战。为了解决这些问题&#xff0c;作者提出了 MemFu…

AWR1642+DCA1000采集ADC数据并解析

文章同步发布在CSDN和公众号(雷达原理与系统),后续文章中出现的资料,参考文档等都会放在GitHub仓库,欢迎fork和star。 0. 序言 为什么要先将采集ADC数据呢?因为ADC数据是信号处理的输入,是后续理解信号处理手段的基础。当然这里也可以采用仿真信号,但我的想法是单独出…

SQL第13课——创建高级联结

本课讲另外一些联结&#xff08;含义和使用方法&#xff09;&#xff0c;如何使用表别名&#xff0c;如何对被联结的表使用聚集函数。 13.1 使用表别名 第7课中使用别名引用被检索的表列&#xff0c;给列起别名的语法如下&#xff1a; SQL除了可以对列名和计算字段使用别名&a…

聚类分析 | IPOA优化FCM模糊C均值聚类优化算法

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (多图聚类)IPOA优化FCM模糊C均值聚类优化算法&#xff0c;matlab代码&#xff0c;超多图 基于改进的鹈鹕优化算法&#xff08;IPOA&#xff09;优化FCM模糊C均值聚类优化&#xff0c;matlab代码&#xff0c;直接运行…

HTB:Preignition[WriteUP]

连接至HTB服务器并启动靶机 靶机IP&#xff1a;10.129.157.49 分配IP&#xff1a;10.10.16.12 1.Directory Brute-forcing is a technique used to check a lot of paths on a web server to find hidden pages. Which is another name for this? (i) Local File Inclusion, (…

窗口售票系统1.0版本

本窗口售票系统实现了三个售票窗口的随机售票&#xff0c;实现随机到某一个窗口买票&#xff0c;总票余量都会减少&#xff0c;即三个窗口共享同一个票余量。若票余量小于一次性购票量&#xff0c;则提示报错&#xff1b;若车票售罄&#xff0c;则代码结束运行。 代码实现&…

用户和组管理

用户管理 用户管理包括创建用户、修改用户属性、删除用户等操作。 创建用户 使用 useradd 命令可以创建新用户。 格式&#xff1a;useradd [选项] username 步骤1&#xff1a;创建新用户 useradd tom 步骤 2: 设置用户密码 新用户创建后&#xff0c;需要设置一个密码才能…

需求8——通过一个小需求来体会AI如何帮助改bug

这篇文章&#xff0c;我们通过一个简单的例子来说明&#xff0c;平时在写需求的时候&#xff0c;我们可以在什么时候用AI来帮助我们写代码。 首先来看一下这个需求&#xff1a;系统中某个用户使用的时候出现了bug&#xff0c;通过手机建立临时任务报错&#xff0c;没有办法新增…

ElasticSearch备考 -- Update by query Reindex

一、题目 有个索引task&#xff0c;里面的文档长这样 现在需要添加一个字段all&#xff0c;这个字段的值是以下 a、b、c、d字段的值连在一起 二、思考 需要把四个字段拼接到一起&#xff0c;组成一个新的字段&#xff0c;这个就需要脚本&#xff0c; 这里有两种方案&#xff…

ES(Elasticsearch)SSL集群部署

8.x后ES不在需要自行准备JDK环境&#xff0c;部署的服务包含ES、Kibana、Logstash&#xff0c;使用二进制方式部署&#xff0c;为了提高安全性&#xff0c;加密logstash、kibana及其他客户端到ES间的通信。 1、准备工作 1.1、 es无法使用root用户启动 useradd -m -s /bin/bas…

论文阅读:Split-Aperture 2-in-1 Computational Cameras (二)

Split-Aperture 2-in-1 Computational Cameras (一) Coded Optics for High Dynamic Range Imaging 接下来&#xff0c;文章介绍了二合一相机在几种场景下的应用&#xff0c;首先是高动态范围成像&#xff0c;现有的快照高动态范围&#xff08;HDR&#xff09;成像工作已经证…

Kubernetes-Kind篇-01-kind搭建测试集群

1、Kind 介绍 官方文档地址&#xff1a;https://kind.sigs.k8s.io/ github仓库地址&#xff1a;https://github.com/kubernetes-sigs/kind 国内镜像仓库地址&#xff1a;https://gitcode.com/gh_mirrors/ki/kind/overview kind 是一种使用 Docker 容器 nodes 运行本地 Kubern…

HI6338 (DIP-8内置75W方案)

Hi6338 combines a dedicated current mode PWM controller with integrated high voltage power MOSFET.Vcc low startup current and low operating current contribute to a reliable power on startup design with Hi6338. the IC operates in Extended ‘burst mode’ to …

Nginx请求头丢失,引发出来的问题

1.问题 新增的几个 header 参数是这样的&#xff1a; api_key_idapi_key_value 我配置有2层nginx转发&#xff0c;从机器A到机器B再到目标服务&#xff0c;遇到一个接口请求需要在header中传递api_key_id和api_key_value这2个参数&#xff0c;但是在EC2机器上直接curl目标服…

更美观的HTTP性能监测工具:httpstat

reorx/httpstat是一个旨在提供更美观和详细HTTP请求统计信息的cURL命令行工具&#xff0c;它能够帮助开发者和运维人员深入理解HTTP请求的性能和状态。 1. 基本概述 项目地址&#xff1a;https://github.com/reorx/httpstat语言&#xff1a;该工具主要是以Python编写&#xff…

机器人末端的负载辨识

关节处的摩擦力变小了&#xff0c;导致系统的参数辨识精度会变高&#xff0c;因为动力学方程中的摩擦力项占的比例会变小。 为什么要有一个负载的参数辨识&#xff0c;因为对于整个系统来说&#xff0c;除了负载哈&#xff0c;其他关节都是不变的&#xff0c;出厂时都设置好了&…

金蝶云星空个别字段无法录入异常处理

用户反馈&#xff0c;在录入单据时&#xff0c;第一条数据能录入数量&#xff0c;新增第二条时就无法录入。 用户反馈截图如下&#xff1a; 我登录自己的账号查看&#xff0c;并未发现相同的问题&#xff0c;同时用户也说已经退出重新登录过&#xff0c;问题依旧。 到现场看用…

使用RESTful API构建Web应用程序

开始正式介绍 RESTful API 之前&#xff0c;我们需要首先搞清&#xff1a;API 到底是什么&#xff1f; API&#xff08;Application Programming Interface&#xff09; 翻译过来是应用程序编程接口的意思。 我们在进行后端开发的时候&#xff0c;主要的工作就是为前端或者其…

LUCEDA IPKISS Tutorial 77:在版图一定范围内填充dummy

案例分享&#xff1a;在给定的Shape内填充dummy 所有代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 from shapely.geometry import Polygon, MultiPolygon import numpy as np import matplotlib.pyplot as pltclass CellFilledWithCon…