# LoraSensor **Repository Path**: lv-liujun/lora-sensor ## Basic Information - **Project Name**: LoraSensor - **Description**: STM32F405RGT6+Lora通信模块的设备开发(传感器设备) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-04-01 - **Last Updated**: 2025-07-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、目录结构讲解 | 文件目录 | 目录讲解 | | :------------ | ------------------------------------------------------------ | | `BSP` | 板级支持包,包含模块的操作代码(`HAL`库开发移植性好) | | `Core` | 包含项目的核心代码,如 `main.c`、`startup` 、以及项目用到的协议文件等 | | `Drivers` | 包含外设驱动代码,如 `GPIO`、`UART`、`I2C` 等。 | | `MDK-ARM` | 包含 `Keil MDK-ARM` 开发工具相关的配置文件和项目文件(使用`keilkill.bat`释放空间) | | `Middlewares` | 包含中间件代码,如 `FreeRTOS`(本项目使用的操作系统) | | `Tasks` | 存放各个模块的任务代码(使用`cmsis_os2`,对`FreeRTOS`进一步封装的`API`,移植性较好) | | `.mxproject` | `STM32CubeMX` 生成的项目文件,包含硬件配置信息 | | `LoraLed.ioc` | `STM32CubeMX` 的配置文件,用于存储硬件配置和生成初始化代码 | | `README.md` | 项目的说明文件,通常包含项目概述、使用方法、注意事项等 | 本项目采用`stm32cubeMX`+`Keil uVision5`+`vscode`开发 - `stm32cubeMX`:用户配置开发板的引脚信息 - `Keil uVision5`:用于编译,调试,下载代码 - `vscode`:用于编写代码 ## 二、开发遇到的问题 ### 1、无法使用`printf`输出语句 **解决方法**:在串口代码中添加如下代码 ```c #include int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); // 使用 HAL 库发送字符 HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xFFFF); // 使用 HAL 库发送字符 return ch; } ``` 并在`Keil uVision5`集成开发环境中,找到`Options for Target -> Target -> Use MicroLIB(打勾)` ### 2、`DHT11`温湿度模块,刚上电输出读取错误 **解决方法**:任务函数中,初始化模块之后,设置延时等待;并在循环中设置多次尝试机制 ### 3、当`ADC`值为`4095`时,输出的`PPM`值异常 **解决方法**:`MQ2_GetData_PPM`函数中,应除以`4096.0f`而不是`4095.0f` ```c float Vol = MQ2_AO_GetData() * 5.0f / 4096.0f; ``` ## 三、`Lora`编码差异(和开源代码) ### 1、优先级分组问题 下面表格中修改的代码是`STM32cubemx`自动配置好的,我并没有修改,只是对照源码,看能不能正常使用,结果是好的。 | 优先级分组差异 | 修改 | 源码 | | :--------------: | :------------------------------------------: | :------------------------------------------: | | 串口1优先级分组 | `HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);` | `HAL_NVIC_SetPriority(USART1_IRQn, 1, 1);` | | 串口4优先级分组 | `HAL_NVIC_SetPriority(UART4_IRQn, 5, 0);` | `HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);` | | 定时器优先级分组 | `HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 5, 0);` | `HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 2, 2);` | ## 四、模块讲解 ### 4.1 温湿度模块 #### 4.1.1 模块接线 - `DHT11` - `VCC`接`3.3V` - `GND`接`0V` - `Signal`接`PC0` #### 4.1.2 代码讲解 **`BSP`函数:**`DHT11`模块软件处理硬件特定的时序,封装好读取温湿度数据的函数,对外声明。 > 文件路径:`..\BSP\Inc\dht11.h` ```c uint8_t DHT11_Init(void); // 初始化DHT11 uint8_t DHT11_Read_Data(uint8_t *temp, uint8_t *humi); // 读取数据 ``` 由于`DHT11`需要使用us级别的延时,通过设置`TIM2`定时器实现`1us`的计时,部分代码如下 对于预分频器的设置请参考:`STM32F4xx`中文参考手册54页 ```c htim2.Instance = TIM2; htim2.Init.Prescaler = 84 - 1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFF; ``` **具体us延时函数如下:** ```c /* 微秒级延时函数 */ void DelayUs(uint32_t us) { assert_param(us <= 0xFFFF); // 确保us不超过65535 uint32_t start = TIM2->CNT; taskENTER_CRITICAL(); while ((uint32_t)(TIM2->CNT - start) < us) { // 空循环等待 } taskEXIT_CRITICAL(); } ``` ==注意:==需要在`main.c`文件中添加如下代码,开启定时器的计时 ```c /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(&htim2); // 启动定时器? // ... /* USER CODE END 2 */ ``` **任务函数:** > 文件路径:`..\Tasks\Src\humitureTask.c` | 函数 | 作用 | 函数功能 | | ---------------------------------------------- | -------- | :----------------------------------------------------------- | | `void HumitureTaskEnter(void)` | 入口函数 | 创建任务,配置任务属性 | | `static void HumitureTask(void *argument)` | 任务函数 | 初始化`DHT11`模块,并软件延时等待硬件初始化,在循环中使用重试机制防止软件读取的速度过快,造成的读取温湿度失败 | | `void SetDHT11MeasureDelayTime(uint32_t time)` | 接口函数 | 设置`DHT11`模块的测量时间,强制设置最短延时时间 | | `uint8_t GetTemperature(void)` | 接口函数 | 获取传感器温度 | | `uint8_t Gethumidity(void)` | 接口函数 | 获取传感器湿度 | #### 4.1.3 `FreeRTOS`调度 > 该模块的任务文件为:`humitureTask.c`,在`freertos.c`文件中添加如下信息: **添加头文件** ```c /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "humitureTask.h" /* USER CODE END Includes */ ``` **调用任务函数** ```c /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ HumitureTaskEnter(); /* USER CODE END RTOS_THREADS */ ``` **测试接口函数** ```c void StartDefaultTask(void *argument) { SetDHT11MeasureDelayTime(5000); // 修改测量时间 // ... } ``` ### 4.2 烟雾传感器模块 #### 4.2.1 模块接线 - `MQ2` - `VCC`接`5V` - `GND`接`0V` - `A0`接`PC4` #### 4.2.2 代码讲解 **任务函数:** > 文件路径:`..\Tasks\Src\smokeAlarmTask.c` | 函数 | 作用 | 函数功能 | | -------------------------------------------- | -------- | :----------------------------------------------------------- | | `void SmokeAlarmTaskEnter(void)` | 入口函数 | 创建任务,配置任务属性 | | `static void SmokeAlarmTask(void *argument)` | 任务函数 | 设置报警标志位,循环中读取`ADC`值和`PPM`值。默认不输出蜂鸣器关闭的信息。当蜂鸣器报警结束后,输出一次`Buzzer OFF`,优化调试信息。 | | `void SetMQ2MeasureDelayTime(uint32_t time)` | 接口函数 | 设置`MQ2`模块的测量时间,强制设置最短延时时间 | | `uint16_t GetMQ2ADCValue(void)` | 接口函数 | 获取传感器ADC值 | | `float GetMQ2PPMValue(void)` | 接口函数 | 获取传感器PPM值 | | `void SetAlarmADCValue(uint16_t adcVal)` | 接口函数 | 设置蜂鸣器包报警值 | **处理函数:** | 函数 | 函数功能 | | -------------------------------------- | :--------------------------------------------------- | | `static uint16_t MQ2_ADC_Read(void)` | 获取`ADC`的数值,返回`ADC`的原始值(0-4095) | | `static uint16_t MQ2_AO_GetData(void)` | 处理一定数量的`ADC`的数值,返回`ADC`的处理后的平均值 | | `static float MQ2_GetData_PPM(void)` | 计算气体浓度(`PPM`) | #### 4.1.3 `FreeRTOS`调度 > 该模块的任务文件为:`smokeAlarmTask.c`,在`freertos.c`文件中添加如下信息: **添加头文件** ```c /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "smokeAlarmTask.h" /* USER CODE END Includes */ ``` **调用任务函数** ```c /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ SmokeAlarmTaskEnter(); /* USER CODE END RTOS_THREADS */ ``` **测试接口函数** ```c void StartDefaultTask(void *argument) { SetMQ2MeasureDelayTime(2000); // 修改测量时间 // ... } ``` ## 五、如何使用 1. 在仓库中下载完代码后,打开`Keil uVision5`,先编译后下载、并使用`ST-LINK`等下载器,连接对应开发板的`SWD`口。 > 代码上传前先`kill`清理编译和可执行文件,确保整个项目较小,方便下载 2. 下载完成之后先点击开发板的复位按钮,等待`lora`模块连接,后进行模块操作。 > 因为`lora`模块的代码,在启动操作系统调度的前面