STM32F030 开关量传感器程序设计(GPIO+Modbus Poll 控制)
目录
- 一、前言
- 二、开关量传感器基础:硬件与通信架构
- 三、硬件引脚与寄存器映射规划
- 四、点表设计:通信规则标准化
- 五、F030 程序实现:寄存器与 GPIO 联动
- 六、功能验证:Modbus Poll 控制硬件
- 七、总结
- 八、结尾
一、前言
开关量传感器是工业场景中最基础的一类传感器,核心对应 LED、按键、继电器等简单 GPIO 外设的状态采集与控制。本次基于 STM32F030 开发板设计开关量传感器程序,沿用已验证的通信架构(PC→STM32H5→STM32F030),通过 PC 端 Modbus Poll 软件实现对 F030 开关量硬件的远程采集与控制,完成从寄存器规划到代码落地的全流程设计。
二、开关量传感器基础:硬件与通信架构
开关量传感器的硬件连接保持与此前一致:PC 通过 USB 连接 STM32H5 开发板,H5 通过 RS485 连接 F030 开发板,H5 仍承担 RS485-USB 的转接作用,保障 PC 端 Modbus Poll 软件与 F030 传感器的通信链路。
资料来源韦东山
开关量传感器硬件示意图如下:
![[屏幕截图 2026-02-19 095911.png]]
三、硬件引脚与寄存器映射规划
F030 开发板上开关量模块的引脚、工作电平与寄存器类别对应关系如下表,CubeMX 中需根据寄存器类别(DI 为输入、DO 为输出)配置对应 GPIO 的工作模式:
| 模块 | 功能 | 引脚 | 工作电平 | 寄存器类别 |
|---|---|---|---|---|
| SWITCH 开关量模块 | KEY1 | PA3 | 低 | DI |
| KEY2 | PA4 | 低 | DI | |
| KEY3 | PA5 | 低 | DI | |
| K1_CTRL | PB5 | 高 | DO | |
| K2_CTRL | PB4 | 高 | DO | |
| LED1 | PB11 | 低 | DO | |
| LED2 | PB12 | 低 | DO | |
| LED3 | PB13 | 低 | DO |
四、点表设计:通信规则标准化
为明确 PC 与 F030 开关量传感器的通信规则,设计如下点表,清晰定义设备地址、寄存器地址、类别及功能:
| 设备地址 | 寄存器地址 | 寄存器类别 | 用途 | 描述 |
|---|---|---|---|---|
| 01H | 0000H | DI | 读取按键 KEY1 | 1 - 被按下 |
| 01H | 0001H | DI | 读取按键 KEY2 | 1 - 被按下 |
| 01H | 0002H | DI | 读取按键 KEY3 | 1 - 被按下 |
| 01H | 0000H | DO | 控制蜂鸣器 1 | 1 - 响 |
| 01H | 0001H | DO | 控制蜂鸣器 2 | 1 - 响 |
| 01H | 0002H | DO | 控制 LED1 | 1 - 亮 |
| 01H | 0003H | DO | 控制 LED2 | 1 - 亮 |
| 01H | 0004H | DO | 控制 LED3 | 1 - 亮 |
五、F030 程序实现:寄存器与 GPIO 联动
1. 宏定义设计
通过宏定义区分不同设备逻辑,同时根据硬件实际数量定义寄存器相关宏,实现代码的可复用性与适配性:
/* USER CODE BEGIN PD */
// 启用开关量传感器功能(宏定义便于切换不同传感器逻辑)
#define USE_SWITCH_SENSOR 1
#ifdef USE_SWITCH_SENSOR
// 从机地址(对应点表中的01H)
#define SLAVE_ADDR 1
// DO寄存器(线圈状态)数量:5个(2个蜂鸣器+3个LED)
#define NB_BITS 5
// DI寄存器(离散输入状态)数量:3个(3个按键)
#define NB_INPUT_BITS 3
// 保持寄存器数量:0(本传感器未使用)
#define NB_REGISTERS 0
// 输入寄存器数量:0(本传感器未使用)
#define NB_INPUT_REGISTERS 0
#endif
2. 核心任务实现
在默认 FreeRTOS 任务中实现 Modbus 从机逻辑,核心流程为:读取 GPIO 状态更新 DI 寄存器,根据 DO 寄存器值控制 GPIO 输出,代码及关键注释如下(未修改原有代码逻辑):
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
// 存储GPIO读取状态
GPIO_PinState val;
// Modbus请求报文缓冲区
uint8_t *query;
// Modbus RTU上下文
modbus_t *ctx;
// 函数返回值,判断操作是否成功
int rc;
// Modbus寄存器映射结构体
modbus_mapping_t *mb_mapping;
// 创建串口1的Modbus RTU上下文:波特率115200、无校验、8数据位、1停止位
ctx = modbus_new_st_rtu("uart1", 115200, 'N', 8, 1);
// 设置从机地址(匹配点表中的01H)
modbus_set_slave(ctx, SLAVE_ADDR);
// 分配请求报文缓冲区(FreeRTOS内存接口)
query = pvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH);
// 初始化寄存器映射:基于宏定义的寄存器数量配置
mb_mapping = modbus_mapping_new_start_address(0,
NB_BITS, // DO寄存器数量
0,
NB_INPUT_BITS, // DI寄存器数量
0,
NB_REGISTERS, // 保持寄存器数量
0,
NB_INPUT_REGISTERS); // 输入寄存器数量
// 建立Modbus RTU连接
rc = modbus_connect(ctx);
if (rc == -1) {
//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
modbus_free(ctx);
vTaskDelete(NULL);; // 连接失败则删除任务
}
// 无限循环接收并响应PC端Modbus请求
for (;;) {
do {
// 接收PC端请求报文,过滤无效请求(返回0的查询)
rc = modbus_receive(ctx, query);
/* Filtered queries return 0 */
} while (rc == 0);
/* The connection is not closed on errors which require on reply such as
bad CRC in RTU. */
// 非CRC错误则继续循环,不退出
if (rc == -1 && errno != EMBBADCRC) {
/* Quit */
continue;
}
/* update values of registers
* a. read gpio
* b. update reg
*/
#ifdef USE_SWITCH_SENSOR
// 读取KEY1(PA3)状态,更新DI寄存器0号
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3);
if (val == GPIO_PIN_RESET) // 按键按下(低电平)
{
mb_mapping->tab_input_bits[0] = 1;
}
else // 按键未按下(高电平)
{
mb_mapping->tab_input_bits[0] = 0;
}
// 读取KEY2(PA4)状态,更新DI寄存器1号
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4);
if (val == GPIO_PIN_RESET)
{
mb_mapping->tab_input_bits[1] = 1;
}
else
{
mb_mapping->tab_input_bits[1] = 0;
}
// 读取KEY3(PA5)状态,更新DI寄存器2号
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
if (val == GPIO_PIN_RESET)
{
mb_mapping->tab_input_bits[2] = 1;
}
else
{
mb_mapping->tab_input_bits[2] = 0;
}
#endif
// 解析请求并回复PC端(包含更新后的DI寄存器数据)
rc = modbus_reply(ctx, query, rc, mb_mapping);
if (rc == -1) {
//break;
}
#ifdef USE_SWITCH_SENSOR
// 根据DO寄存器0号值控制蜂鸣器1(PB5):1-响(高电平)、0-停(低电平)
if (mb_mapping->tab_bits[0])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //switch1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); //switch1
// 根据DO寄存器1号值控制蜂鸣器2(PB4):1-响(高电平)、0-停(低电平)
if (mb_mapping->tab_bits[1])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET); //switch2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET); //switch2
// 根据DO寄存器2号值控制LED1(PB11):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[2])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); //LED1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); //LED1
// 根据DO寄存器3号值控制LED2(PB12):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[3])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); //LED2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); //LED2
// 根据DO寄存器4号值控制LED3(PB13):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[4])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); //LED3
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); //LED3
#endif
}
// 释放资源(实际循环不会执行到此处)
modbus_mapping_free(mb_mapping);
vPortFree(query);
modbus_close(ctx);
modbus_free(ctx);
vTaskDelete(NULL);
/* USER CODE END StartDefaultTask */
}
六、功能验证:Modbus Poll 控制硬件
STM32H5 端代码无需修改,仍承担 RS485-USB 的转接功能;将 F030 端编译后的程序烧录至开发板,通过 PC 端 Modbus Poll 软件按照点表规则改写 / 读取寄存器值:
- 读取 DI 寄存器(0000H-0002H):可获取 KEY1-KEY3 的按下状态;
- 改写 DO 寄存器(0000H-0004H):可控制蜂鸣器 1/2 的启停、LED1-LED3 的亮灭。
七、总结
- 开关量传感器程序核心是 GPIO 与 Modbus 寄存器的联动:DI 寄存器同步 GPIO 输入(按键),DO 寄存器控制 GPIO 输出(LED / 继电器);
- 宏定义设计实现了代码的适配性,可快速切换不同传感器逻辑;
- 点表是 PC 与传感器通信的核心规则,需严格匹配寄存器地址与硬件功能。
八、结尾
本次完成了 STM32F030 开关量传感器的全流程程序设计,实现了 PC 端 Modbus Poll 软件对 GPIO 外设的远程采集与控制,这套设计思路可直接复用至各类工业开关量模块的开发。掌握开关量传感器的设计逻辑,是进阶开发温湿度、环境检测等复杂传感器的基础。感谢各位的阅读,持续关注本系列笔记,一起探索更多工业级 Modbus 传感器的开发技巧!
更多推荐
所有评论(0)