当前位置: 首页 > news >正文

【嵌入式基础梳理#12】风压计Modbus框架示例

一、风压计简介

        风压(wind pressure)由于通风管道的阻挡,使四周空气受阻,动压下降,静压升高。侧面和背面产生局部涡流,静压下降,动压升高。和远处未受干扰的气流相比,这种静压的升高和降低统称为风压。

        在航空航天领域,“风压” 本质上是气流与飞行器表面相互作用产生的压力(包括静压、动压及压力差)的宏观表现,其分布和变化直接影响飞行器的气动性能、结构设计、飞行控制及任务安全。

        

        风压计是一种用于测量气流对物体表面产生压力(即风压)的仪器,广泛应用于气象、航空航天、建筑、能源、环境监测等多个领域。其核心功能是通过感知流体(通常是空气)的压力变化,将物理量转化为可读取的电信号或机械指示,从而实现对风压的定量测量。

                                      

        某款硬件层采用485,软件层采用Modbus-RTU通信的风压计通信规律如下图所示:

     

 

二、主机端(控制器)的Modbus-RTU驱动框架

        

                驱动框架难度不大,但是有一个核心问题:

                由于Modbus的报文不带长度信息或结束符,我们该如何判断一个报文发送结束呢?

                因为大多Modbus设备都是一问一答的,我们利用这个特点,可以设计一个简单的规则。

                (1)UART中断负责读取485单字节信号并装进队列,每处理一次就置位计时变量

                (2)定时器负责监测计时变量,若信号超过3~7ms(因多种因素决定,需要调)则判定为超时,开始强制解析信号,解析完毕后再次发请求。(直接用systick就行,一个中断1ms)

                利用定时器+串口中断协同的方法,就可以判断何时一个报文传输完毕了。

                

三、代码示例

        (1)main.c 

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "Modbus.h"
#include "FIFO.h"
#include "stdio.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define WINDINIT 0
#define WINDON 1
//两个状态,风压计关闭和开启
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
uint8_t rxbuf;
//串口接收中断寄存器
uint8_t WIND_G_STATE;
//风压计状态寄存器
/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
Modbus_RTU modbus1 = {0};
//初始化modbus结构体
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART2_UART_Init();MX_UART4_Init();/* USER CODE BEGIN 2 */printf("WindSYSTEM-On\r\n");FIFO_init();//初始化环形队列HAL_Delay(100);//等待风压计预热HAL_UART_Receive_IT(&huart2,&rxbuf,1);//打开接收中断WIND_G_STATE = WINDON;//开启风压信号接收Modbus_fun3_Tx(1,33,2);//进行第一轮数据请求/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 */
void HAL_SYSTICK_Callback(){if(WIND_G_STATE==WINDON){//接收打开则进入if(modbus1.ex_time<7){//未超时则计时器自增modbus1.ex_time++;}else{//如果超时则进入处理uint8_t len = RS485_fifo_getusedsize();//获取此时缓冲区长度uint8_t data[len];//按照报文长度开辟一个报文寄存器RS485_uart_rx_fifo_read(data, len);//从缓冲区获取报文int32_t RS485_rxbuf=0;//初始化风压数据寄存器Modbus_fun3_Rx(&RS485_rxbuf,data, 1, len, 2);//进行强制解析并把解析结果给数据寄存器//注意:在严苛应用场合应该根据返回值做调试日志,具体返回规则见后文//			  RS485_rx_fifo_flush();printf("WindPress: %d\r\n",RS485_rxbuf);//print风压,但是建议有上位机的不用这么做,这样会损失时间Modbus_fun3_Tx(1,33,2);//继续下一轮请求modbus1.ex_time=0;//计时器清零}}	}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){//485-》串口接收中断if(huart==&huart2){modbus1.ex_time=0;//每接收一次数据,都要置为计时器
//			printf("INPUT DETECTED: %d\r\n",rxbuf);
//				HAL_UART_Transmit(&huart4,&rxbuf,1,0xff);RS485_uart_rx_fifo_write(&rxbuf, 1);//把读到的一个字节写入fifoHAL_UART_Receive_IT(&huart2,&rxbuf,1);//重新开启串口接收}
}//中断优先级 串口 》 systickint fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart4 , (uint8_t *)&ch, 1, 0xFFFF);return ch;
}/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

        (2)Modbus.c

#include "Modbus.h"
#include "gpio.h"
#include "usart.h"
#include "stdio.h"#define RS485_TX_EN HAL_GPIO_WritePin(WIND_EN_GPIO_Port,WIND_EN_Pin,1);
#define RS485_RX_EN HAL_GPIO_WritePin(WIND_EN_GPIO_Port,WIND_EN_Pin,0);#define M_CRC_8 8
#define M_CRC_32 32typedef struct
{uint32_t poly;           uint32_t InitValue;      uint32_t xor;             uint8_t InputReverse;   uint8_t OutputReverse; 
}S_CRC;const S_CRC crc_16_MODBUS       = {0x8005,      0xffff,     0x0000,     1,   1};// An highlighted block
void UART_SendByte(uint8_t byte){HAL_UART_Transmit(&huart2,&byte,1,0xff);
//	printf("bytesent: %d\r\n",byte);
}static uint32_t reverse(uint32_t data, uint8_t bit)
{ uint8_t i;uint32_t temp = 0;uint8_t bitlen = bit;for(i = 0; i < bitlen; i++)	temp |= ((data>>i) & 0x01) << (bitlen-1-i);return temp;
}uint32_t crc_fun(uint8_t *addr, int num, S_CRC type, uint8_t bit)  
{  uint8_t i;  uint8_t data;uint8_t offset = bit - 8;                           uint32_t maxbit = 1 << (bit-1);           uint32_t crc = type.InitValue;					   for (; num > 0; num--)               {  data = *addr++;if(type.InputReverse == 1)data = reverse(data, M_CRC_8);		crc = crc ^ (data << offset);			for (i = 0; i < 8; i++)					{  if (crc & maxbit)				      crc = (crc << 1) ^ type.poly;    else                                crc <<= 1;                  }}if(type.OutputReverse == 1)             crc = reverse(crc, bit);crc = crc^type.xor;	                          if (bit < M_CRC_32)crc &= ~(0xffffffff << bit);return(crc);                               
}void Modbus_fun3_Tx(uint8_t addr, uint16_t reg_add, uint16_t reg_num)
{uint16_t i;         uint16_t crc;      uint8_t j;      //初始化Modbus发送寄存器,避免上次发送有余量for(i=0; i<32; i++){modbus1.Sendbuf[i] = 0;}i = 0; // 对应0x03功能码的请求结构//  设备地址|功能码|16位地址|16位寄存器数目|16位CRC校验码modbus1.Sendbuf[i++] = addr;                  // 通讯地址modbus1.Sendbuf[i++] = 0x03;                  // 功能码modbus1.Sendbuf[i++] = (reg_add >> 8) & 0xFF; // 取地址高8modbus1.Sendbuf[i++] = reg_add & 0xFF;        // 取地址低8modbus1.Sendbuf[i++] = (reg_num >> 8) & 0xFF; // 读取寄存器数目高8modbus1.Sendbuf[i++] = reg_num & 0xFF;        // 读取寄存器数目低8// 进行CRC校验 crc = crc_fun(modbus1.Sendbuf, i, crc_16_MODBUS, 16);modbus1.Sendbuf[i++] = crc & 0xFF;            // CRC?8?modbus1.Sendbuf[i++] = (crc >> 8) & 0xFF;     // CRC?8?// 485发送功能开启RS485_TX_EN;  // ???:DE/RE????// 发送串口数据for(j=0; j<i; j++){UART_SendByte(modbus1.Sendbuf[j]);  // ?????}//提前开启485接收,因为串口接收在485后,其是被动的RS485_RX_EN;}void Modbus_fun3_Rx(int32_t* RS485_rxbuf,uint8_t*data ,uint8_t gadget_adr, uint8_t rx_len, uint8_t reg_num)	{uint16_t i;       uint16_t crc;       uint8_t j;          //对接收数据进行CRC校验crc = crc_fun(data, rx_len - 2, crc_16_MODBUS, 16);if( (data[rx_len-2] != (crc & 0xFF)) || (data[rx_len-1] != (crc >> 8)) ){
//		printf("CRC failed!\r\n");return;}//对接收数据来源校验if( (data[0] != gadget_adr) || (data[1] != 0x03) ){ 
//		printf("FUN failed!\r\n");return; }//对数据位进行强转//每个8bit数据都强转成32然后进行移位,即能求出风压大小和符号for(j=0; j<reg_num * 2; j++){*(RS485_rxbuf) |=((int32_t) data[3 + j] ) << ( 8 * (reg_num * 2 - j - 1) );}
}//发送配置略
void Modbud_fun6_Tx(uint8_t addr, uint16_t reg_add, uint16_t data)  //6??????
{uint16_t i;        uint16_t crc;       uint8_t j;         // for(i=0; i<32; i++){modbus1.Sendbuf[i] = 0;}i = 0;  // ????modbus1.Sendbuf[i++] = addr;                  // ????modbus1.Sendbuf[i++] = 0x06;                  // ???:???????modbus1.Sendbuf[i++] = (reg_add >> 8) & 0xFF; // ????????8?modbus1.Sendbuf[i++] = reg_add & 0xFF;        // ????????8?modbus1.Sendbuf[i++] = (data >> 8) & 0xFF; // ?????8?modbus1.Sendbuf[i++] = data & 0xFF;        // ?????8?// ??CRC???crc = crc_fun(modbus1.Sendbuf, i, crc_16_MODBUS, 16);modbus1.Sendbuf[i++] = crc & 0xFF;            // CRC?8?modbus1.Sendbuf[i++] = (crc >> 8) & 0xFF;     // CRC?8?RS485_TX_EN;for(j=0; j<i; j++){UART_SendByte(modbus1.Sendbuf[j]);  // ?????}RS485_RX_EN;
}

                (3)fifo.c(环形队列)

        内容不做过多解释,详见其他数据结构教程。

#include "FIFO.h"
#include "usart.h"static struct
{uint8_t buf[RS485_UART_RX_FIFO_BUF_SIZE];  /* ?? */uint16_t size;                                  /* ???? */uint16_t reader;                                /* ??? */uint16_t writer;                                /* ??? */
} g_uart_rx_fifo;                                   /* UART??FIFO *//*** @brief       ATK-MS901M UART??FIFO????* @param       dat: ?????*              len: ????????* @retval      0: ??????*              1: FIFO??????*/
uint8_t RS485_uart_rx_fifo_write(uint8_t *dat, uint16_t len)
{uint16_t i;for (i=0; i<len; i++){g_uart_rx_fifo.buf[g_uart_rx_fifo.writer] = dat[i];g_uart_rx_fifo.writer = (g_uart_rx_fifo.writer + 1) % g_uart_rx_fifo.size;}return 0;
}/*** @brief       ATK-MS901M UART??FIFO????* @param       dat: ????????*              len: ????????* @retval      0: FIFO????*              ???: ?????????*/
uint16_t RS485_uart_rx_fifo_read(uint8_t *dat, uint16_t len)
{uint16_t fifo_usage;uint16_t i;/* ??FIFO????? */if (g_uart_rx_fifo.writer >= g_uart_rx_fifo.reader){fifo_usage = g_uart_rx_fifo.writer - g_uart_rx_fifo.reader;}else{fifo_usage = g_uart_rx_fifo.size - g_uart_rx_fifo.reader + g_uart_rx_fifo.writer;}/* FIFO????? */if (len > fifo_usage){len = fifo_usage;}/* ?FIFO????* ???FIFO????*/for (i=0; i<len; i++){dat[i] = g_uart_rx_fifo.buf[g_uart_rx_fifo.reader];g_uart_rx_fifo.reader = (g_uart_rx_fifo.reader + 1) % g_uart_rx_fifo.size;}return len;
}/*** @brief       ATK-MS901M UART??FIFO??* @param       ?* @retval      ?*/
void RS485_rx_fifo_flush(void)
{g_uart_rx_fifo.writer = g_uart_rx_fifo.reader;
}uint16_t RS485_fifo_getusedsize(void){if(g_uart_rx_fifo.reader<=g_uart_rx_fifo.writer){return (g_uart_rx_fifo.writer - g_uart_rx_fifo.reader);}else {return (g_uart_rx_fifo.size + g_uart_rx_fifo.writer - g_uart_rx_fifo.reader);}
}void FIFO_init(){g_uart_rx_fifo.size = RS485_UART_RX_FIFO_BUF_SIZE;       g_uart_rx_fifo.reader = 0;                                   g_uart_rx_fifo.writer = 0;      
}

http://www.dtcms.com/a/338424.html

相关文章:

  • RAG:让AI成为你的知识专家
  • Maven Assembly Plugin 插件使用说明
  • Linux下使用ssh-agent实现集群节点间无免密安装部署
  • 深度学习——R-CNN及其变体
  • 【轨物交流】轨物科技与华为鲲鹏生态深度合作 光伏清洁机器人解决方案获技术认证!
  • Session共享与Sticky模式:优化Web应用性能
  • [激光原理与应用-296]:理论 - 非线性光学 - 线性光学与非线性光学对比
  • SpringBoot校园商铺运营平台
  • 跨平台RTSP播放器深度对比:开源方案与商业SDK的取舍之道
  • MiniMax Agent 上线 Market Place ,AI一键复制克隆网站
  • 视觉语言导航(5)——VLN的具体工作原理——Seq2Seq CMA模型 KL散度 TRANSFORMER 3.1前半段
  • PMP-项目管理-十大知识领域:资源管理-管理团队、设备、材料等资源
  • Win10下配置WSL2后nvidia-smi不正常显示问题
  • 第一阶段C#基础-15:面向对象梳理
  • python-----机器学习中常用的数据预处理
  • 【前端面试题】JavaScript 核心知识点解析(第二十二题到第六十一题)
  • 【数据分析】R语言在生态学数据分析中的应用:从数据处理到可视化
  • 美图披露半年报:AI应用取得突破,净利润同比大增71.3%
  • C++11列表初始化 {}
  • GitHub Actions 从核心思想到最佳实践
  • 宋红康 JVM 笔记 Day04|双亲委派机制、沙箱安全机制、类的自动使用与被动使用
  • 电子电气架构 --- 软件会给汽车带来哪些变化?
  • 鸿蒙生态7月技术月报 | HarmonyOS 5.1 开发特性详解
  • 蓝池参与雅江水电工程融资,助力国家基础设施建设与经济发展
  • 08.常见文本处理工具
  • 03.文件管理和操作命令
  • 解读60页全面认识大数据基础知识培训【附全文阅读】
  • 8.18 打卡 DAY 45 Tensorboard使用介绍
  • Mysql——前模糊索引失效原因及解决方式
  • 深度强化学习之前:强化学习如何记录策略与价值?