news 2026/9/11 7:17:59

STM32宠物喂食系统:嵌入式实时控制与可靠性设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32宠物喂食系统:嵌入式实时控制与可靠性设计

简介:本资源是一套基于STM32F103的宠物智能喂食系统完整嵌入式项目代码,面向具备C语言与STM32基础的工程师及高校学生,聚焦物联网终端设备开发实践,解决宠物远程监控与定时/触发式自动喂食的实际问题。压缩包共39个文件,含19个C源文件(实现传感器驱动、WiFi通信、电机控制、RTC定时等核心功能)、19个头文件(模块化封装HX711压力采集、DHT11温湿度读取、SPI Flash数据存储、ESP8266联网等逻辑)及1份README说明文档,总大小仅42KB,轻量但结构严谨。已有319人学习下载,代码严格遵循嵌入式编程规范:模块职责清晰(如device/led/network/tim2等目录划分明确)、函数命名语义化、关键路径均附详细中文注释,特别适合用于综合项目实训、毕业设计参考或智能家居类IoT产品原型开发。

1. 为什么用 STM32 做宠物喂食系统,不是“玩具级”而是“可交付级”的嵌入式实践

凌晨三点,猫粮盒空了,手机弹出“饲喂完成”通知——这不是智能音箱的语音提醒,而是你亲手写的 STM32 固件通过 RTC 定时触发步进电机、读取称重传感器校验投料量、再通过串口日志确认执行闭环的真实动作。嵌入式开发 + STM32 + 宠物喂食系统,表面看是毕业设计或创客项目,实则是检验工程师对实时性约束、外设协同、资源边界与物理交互可靠性的综合能力场:它不依赖 Linux 环境或云服务兜底,必须在 64KB Flash、20KB RAM 的 STM32F103C8T6 上跑稳 7×24 小时;电机启停不能抖动导致卡粮,称重采样需滤波抗干扰,Wi-Fi 模块掉线时本地策略仍要生效。这类项目真正筛选的是能跳出 HAL 库封装、直面寄存器位操作、会用示波器抓 GPIO 电平跳变、敢在SysTick_Handler里写关键逻辑的嵌入式开发者。本文不讲“怎么点亮 LED”,只聚焦如何把 STM32 变成一只沉默但可靠的“电子饲主”——代码分享不是贴 main.c,而是给出可复现的模块划分、参数配置依据和现场验证方法。

2. 从芯片选型到外设映射:STM32F103C8T6 的最小可行硬件架构

2.1 为什么是 STM32F103C8T6 而非更高端型号?

在宠物喂食系统中,“性能过剩”是最大陷阱。STM32F4 系列虽有 FPU 和更大 RAM,但其 168MHz 主频带来的功耗激增(典型运行电流 120mA vs F103 的 35mA)直接缩短电池供电周期;而 F0 系列缺乏足够定时器通道,无法同时管理 RTC 定时、步进电机细分脉冲、ADC 采样与串口通信。F103C8T6 成为事实标准:72MHz Cortex-M3 核心提供充足算力,2×SPI(1 路接称重传感器 HX711,1 路接 Wi-Fi 模块 ESP-01S)、3×USART(1 路调试,1 路与 ESP 通信,1 路预留蓝牙)、2×12bit ADC(双路称重冗余采样)、1×高级定时器 TIM1(驱动步进电机细分 PWM)全部可用。更重要的是,其 Keil MDK 兼容性极佳,ST 官方固件库(Standard Peripheral Library)对 GPIO 中断、DMA 传输等底层操作支持成熟,避免 CubeMX 自动生成代码中 HAL_Delay() 导致的阻塞风险——这正是嵌入式开发中“可控性优于便利性”的典型权衡。

提示:不要用 STM32CubeIDE 默认生成的HAL_Delay(1000)实现投料延时。该函数依赖 SysTick 中断,在电机驱动高频 PWM 下易被抢占导致延时不准。应改用TIMx->CNT计数器轮询或独立低速定时器(如 TIM6)中断。

2.2 关键外设物理连接与引脚分配逻辑

硬件设计必须服务于软件调度优先级。以下为经量产验证的引脚分配方案(基于 STM32F103C8T6 最小系统板):

功能模块连接器件STM32 引脚配置模式设计依据
步进电机驱动TB6600PA0 (TIM1_CH1)复用推挽输出TIM1 通道 1 输出 PWM,占空比控制转速;PA1 作为方向信号(GPIO_Output)
称重传感器HX711PB0 (ADC1_IN8), PB1 (ADC1_IN9)模拟输入双通道差分采样,规避共模干扰;HX711 的 DOUT 接 PB10(GPIO_Input),PD_SCK 接 PB11(GPIO_Output)
Wi-Fi 通信ESP-01SUSART2_TX/PA2, USART2_RX/PA3复用推挽/浮空输入独立 USART 避免与调试口冲突;波特率固定 115200,禁用流控防止丢包
实时时钟DS3231I2C1_SCL/PB6, I2C1_SDA/PB7开漏输出+上拉电阻DS3231 温度补偿精度 ±2ppm,远超 STM32 内部 RTC 的 ±250ppm,确保每日投喂误差 <10 秒
用户交互按键+LEDPC13 (KEY), PA8 (LED)上拉输入 / 推挽输出PC13 为 WKUP 引脚,支持 STOP 模式唤醒;PA8 高电平点亮,直观指示系统状态
2.2.1 HX711 与 ADC 的协同采样策略

HX711 是 24 位 Sigma-Delta ADC,但 STM32F103 的 12 位 ADC 并非直接替代。实际做法是:仅用 HX711 的 DOUT 信号触发 STM32 的外部中断,由 STM32 控制 PD_SCK 时序读取 24 位数据。这样既发挥 HX711 高精度优势,又避免 ADC 采样噪声影响。关键代码如下:

// HX711 初始化:PD_SCK 置低,DOUT 上拉 GPIO_ResetBits(GPIOB, GPIO_Pin_11); // PD_SCK = 0 // 外部中断配置:DOUT(PB10) 下降沿触发 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line10; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);

逻辑说明:HX711 数据就绪时 DOUT 由高变低,触发中断后在 ISR 中执行 24 次GPIO_SetBits(GPIOB, GPIO_Pin_11); GPIO_ResetBits(GPIOB, GPIO_Pin_11);产生 SCK 脉冲,逐位读取数据。此方式比模拟 I2C 更可靠,且不占用 I2C 总线资源。

2.2.2 TIM1 驱动步进电机的 PWM 参数计算

TB6600 输入为脉冲+方向信号,需生成精确频率的方波。设电机细分数为 16,丝杠导程 2mm/rev,则每 1000 步对应 125μm 位移。要求投料速度 5mm/s,即每秒 2000 步 → PWM 频率 = 2000Hz。STM32F103 APB2 总线频率 72MHz,TIM1 时钟源为 APB2,预分频器 PSC=71,自动重装载值 ARR=3599,满足:

$$ \text{PWM Frequency} = \frac{72\text{MHz}}{(PSC+1) \times (ARR+1)} = \frac{72,000,000}{72 \times 3600} = 2000\text{Hz} $$

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 3599; // ARR TIM_TimeBaseStructure.TIM_Prescaler = 71; // PSC TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 1800; // 占空比 50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_Cmd(TIM1, ENABLE);

参数说明:TIM_Pulse=1800表示高电平持续 1800 个计数周期,占空比 = 1800/(3599+1) ≈ 50%,确保电机力矩稳定;TIM_OCPolarity_High使 PA0 输出高电平时驱动有效。

3. 嵌入式代码的核心分层:从裸机调度到投料逻辑闭环

3.1 主循环调度框架:事件驱动而非轮询

传统 while(1) 轮询会浪费 CPU 且难以响应实时事件。本系统采用“中断触发 + 标志位 + 主循环处理”分层架构:

  • 底层中断层:RTC Alarm 中断(每日投喂)、EXTI_DOUT 中断(称重数据就绪)、USART2_IRQHandler(Wi-Fi 指令接收)
  • 标志位管理层:全局 volatile 标志feed_flag,weight_ready,wifi_cmd_received
  • 主循环业务层:检查标志位,执行对应函数,完成后清零标志
int main(void) { RCC_Configuration(); // 时钟初始化 GPIO_Configuration(); // 引脚初始化 NVIC_Configuration(); // 中断优先级设置 RTC_Configuration(); // DS3231 时间同步 USART2_Configuration(); // Wi-Fi 串口 TIM1_Configuration(); // 步进电机 PWM while(1) { if(feed_flag) { Feed_Routine(); // 执行投料 feed_flag = 0; } if(weight_ready) { Weight_Calibration(); // 称重校准 weight_ready = 0; } if(wifi_cmd_received) { Parse_WiFi_Command(); // 解析指令 wifi_cmd_received = 0; } Low_Power_Mode(); // 无任务时进入 Sleep 模式 } }

逻辑说明:Low_Power_Mode()调用PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI),使 MCU 在 STOP 模式下功耗降至 2.5μA,仅 RTC 和 EXTI 可唤醒,大幅提升电池寿命。

3.2 投料逻辑的三重安全校验

宠物喂食容错率极低,单次投料必须确保“指令正确、执行到位、结果可信”。代码实现三重校验:

3.2.1 指令校验:RTC 时间窗口 + 配方合法性
typedef struct { uint8_t hour; uint8_t minute; uint16_t weight_g; // 目标投料克数 uint8_t repeat_days; // 位图:bit0=周一...bit6=周日 } Feed_Schedule_t; Feed_Schedule_t schedule[3] = { {.hour=7, .minute=0, .weight_g=30, .repeat_days=0x7F}, // 每日早7点 {.hour=12, .minute=0, .weight_g=20, .repeat_days=0x7F}, // 每日午12点 {.hour=18, .minute=0, .weight_g=30, .repeat_days=0x7F} // 每日晚6点 }; uint8_t Check_Schedule_Valid(uint8_t day_of_week) { RTC_TimeTypeDef RTC_TimeStruct; RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct); for(int i=0; i<3; i++) { if((schedule[i].repeat_days & (1<<day_of_week)) && RTC_TimeStruct.RTC_Hours == schedule[i].hour && RTC_TimeStruct.RTC_Minutes == schedule[i].minute) { target_weight = schedule[i].weight_g; return 1; } } return 0; }

参数说明:repeat_days用 1 字节位图表示星期几,避免字符串比较开销;RTC_Format_BIN直接读取 BCD 格式寄存器值,比RTC_Format_BCD转换更高效。

3.2.2 执行校验:步进脉冲计数 + 电机堵转检测

TB6600 驱动器有 ENBL 引脚,但本系统采用更鲁棒的电流检测法:在电机电源路径串联 0.1Ω 采样电阻,用 STM32 ADC 通道实时监测压降。当电流持续 >800mA 超过 500ms,判定堵转并停止脉冲输出。

uint16_t motor_current_raw; ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); motor_current_raw = ADC_GetConversionValue(ADC1); float current_ma = (motor_current_raw * 3.3f / 4095.0f) / 0.1f; // Vref=3.3V, R=0.1Ω if(current_ma > 800.0f) { TIM_Cmd(TIM1, DISABLE); // 立即停机 Error_Log("Motor stall at %d ms", millis()); }
3.2.3 结果校验:投料前后称重差值验证

投料结束需等待 2 秒让粮粒落稳,再触发 HX711 二次采样:

void Feed_Routine(void) { // 1. 启动电机 TIM_Cmd(TIM1, ENABLE); Delay_ms(2000); // 投料时间 // 2. 停机并延时稳定 TIM_Cmd(TIM1, DISABLE); Delay_ms(2000); // 3. 读取投料前重量 uint32_t weight_before = Read_HX711(); // 4. 等待 2 秒后读取投料后重量 Delay_ms(2000); uint32_t weight_after = Read_HX711(); // 5. 校验差值 int32_t delta = weight_after - weight_before; if(abs(delta - target_weight) > 2) { // 允许±2g误差 Error_Log("Feed error: expect %dg, got %dg", target_weight, delta); // 触发重投或告警 } }

注意:Read_HX711()返回值单位为 mg,需除以 1000 转换为 g;Delay_ms()必须基于 TIM6 中断实现,不可用 HAL_Delay。

4. Wi-Fi 指令协议与本地缓存策略:离线可用的嵌入式通信设计

4.1 极简 AT 指令集设计:降低 ESP-01S 资源压力

ESP-01S Flash 仅 1MB,无法运行复杂 MQTT 客户端。本系统采用透传模式 + 自定义二进制协议,避免 AT 指令解析开销:

指令类型二进制格式(16字节)说明
查询状态0x01 0x00 0x00 0x00 ...返回当前时间、剩余粮量、错误码
设置投喂0x02 [hour] [min] [g] [days] 0x00...[days] 为 repeat_days 位图
强制投料0x03 0x00 0x00 0x00 ...立即执行一次投料,忽略时间表

STM32 接收 UART 数据后,仅需判断首字节即可路由:

uint8_t rx_buffer[16]; void USART2_IRQHandler(void) { USART_ClearITPendingBit(USART2, USART_IT_RXNE); uint8_t byte = USART_ReceiveData(USART2); static uint8_t idx = 0; if(idx < 16) { rx_buffer[idx++] = byte; if(idx == 16) { switch(rx_buffer[0]) { case 0x01: Send_Status(); break; case 0x02: Set_Schedule(rx_buffer); break; case 0x03: Force_Feed(); break; } idx = 0; } } }

逻辑说明:固定 16 字节帧长简化解析,避免帧头帧尾校验开销;Set_Schedule()函数将rx_buffer[1]~rx_buffer[4]直接赋值给schedule[0]结构体,无需字符串转换。

4.2 本地配置持久化:Flash 模拟 EEPROM

STM32F103 无独立 EEPROM,需用 Flash 模拟。选择第 128 页(地址 0x0801FC00)存储配置,每次写入前擦除整页:

#define CONFIG_PAGE_ADDR 0x0801FC00 #pragma pack(1) typedef struct { Feed_Schedule_t schedule[3]; uint16_t version; // 配置版本号,用于校验 } Config_Data_t; #pragma pack() void Save_Configuration(void) { FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); FLASH_ErasePage(CONFIG_PAGE_ADDR); uint32_t *p = (uint32_t*)CONFIG_PAGE_ADDR; Config_Data_t config = {...}; for(int i=0; i<sizeof(Config_Data_t)/4; i++) { FLASH_ProgramWord(p+i, ((uint32_t*)&config)[i]); } FLASH_Lock(); }

参数说明:FLASH_ProgramWord写入 32 位字,需确保目标地址按字对齐;version字段初始为 0x1234,读取时校验该值是否有效,避免 Flash 损坏导致配置乱码。

5. 现场调试与可靠性加固:让 STM32 宠物喂食系统真正“无人值守”

5.1 用示波器验证关键时序:电机脉冲与称重采样的时序隔离

Wi-Fi 通信和电机驱动共用同一 MCU,EMI 干扰可能导致 HX711 读数跳变。实测发现:当 TIM1 输出 PWM 时,PB10(DOUT)引脚出现 50ns 毛刺,触发误中断。解决方案是硬件滤波 + 软件消抖双重加固:

  • 硬件:在 PB10 与 HX711 DOUT 间串联 100Ω 电阻,对地并联 100pF 电容,形成 RC 低通滤波(截止频率 ≈ 16MHz)
  • 软件:EXTI 中断服务程序中增加 20μs 延迟再读取 DOUT 电平:
void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line10) != RESET) { Delay_us(20); // 消除毛刺 if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == RESET) { // 确认为有效下降沿 weight_ready = 1; } EXTI_ClearITPendingBit(EXTI_Line10); } }

提示:Delay_us(20)SysTick->VAL轮询实现,禁止调用任何可能被中断打断的函数。

5.2 电池供电下的低功耗实测数据与优化

使用 3.7V 2000mAh 锂电池供电,实测各状态电流:

工作状态电流(典型值)持续时间日均耗电估算
STOP 模式(RTC+EXTI)2.5μA23h50m0.21mAh
Wi-Fi 通信(10s)75mA10s0.21mAh
投料执行(4s)180mA4s0.20mAh
主循环运行(100ms)8mA100ms0.008mAh
日总耗电≈0.63mAh

结论:理论续航 = 2000mAh / 0.63mAh/天 ≈3175 天(8.7 年),实际因电池自放电和温度衰减,保守估计≥2 年。关键优化点在于:Wi-Fi 模块仅在接收指令或上报状态时上电,其余时间由 STM32 的 PA4 引脚控制其 EN 引脚断电。

5.3 故障自恢复机制:看门狗与错误日志的工程化落地

独立看门狗(IWDG)必须启用,但喂狗时机需精心设计。本系统在main()循环末尾喂狗,且仅当所有业务标志处理完毕后才允许:

IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_256); // 32kHz LSI / 256 = 125Hz IWDG_SetReload(624); // 624 / 125Hz = 5s 溢出时间 IWDG_Enable(); // 主循环末尾 if(!feed_flag && !weight_ready && !wifi_cmd_received) { IWDG_ReloadCounter(); // 仅当无未处理任务时喂狗 }

错误日志存储于 Flash 第 127 页(0x0801F800),采用环形缓冲区记录最近 10 条错误:

字段长度说明
Timestamp4BRTC 秒计数
Error_Code2B自定义错误码(0x01=堵转)
Context8B相关变量快照(如 current_ma)
Reserved2B对齐填充

每次写入前擦除整页,确保日志不因 Flash 寿命失效。现场排查时,用 ST-Link 读取该页数据即可还原故障现场。

最后,真正的嵌入式开发不是堆砌功能,而是让每个时钟周期、每字节 RAM、每微安电流都服务于物理世界的确定性——当你看到猫碗里准时出现的猫粮,那不是代码的胜利,是工程师对“可控”二字的具象兑现。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 7:15:56

用10秒视频克隆你的AI数字人:口播视频生成全在本地跑

用10秒视频克隆你的AI数字人&#xff1a;口播视频生成全在本地跑 【免费下载链接】Duix-Avatar &#x1f680; Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Trendin…

作者头像 李华
网站建设 2026/9/11 7:15:50

Maestro AI 测试指南:5 分钟跑通第一条自然语言断言

Maestro AI 测试指南&#xff1a;5 分钟跑通第一条自然语言断言 【免费下载链接】Maestro Painless E2E Automation for Mobile and Web 项目地址: https://gitcode.com/GitHub_Trending/ma/Maestro 界面一改版&#xff0c;移动 UI 自动化测试脚本就成片报红。你不用逐个…

作者头像 李华
网站建设 2026/9/11 7:14:38

无锡南途科技:GEO优化如何重构企业线上化交付标准

传统SEO时代与AI搜索时代的运营逻辑差异&#xff0c;正在从“关键词排名”转向“实体信任构建”。前者依赖页面标题与链接权重的匹配效率&#xff0c;后者则要求企业内容被大模型识别为可引用的权威信源。无锡南途科技有限公司&#xff08;品牌英文名NATUX&#xff09;在服务工…

作者头像 李华
网站建设 2026/9/11 7:14:27

IEEE33节点系统中泄流效应对无功优化的影响与Matlab实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 7:13:33

ZLUDA 在 AMD 显卡上跑起 CUDA 应用:三步配置与验证清单

ZLUDA 在 AMD 显卡上跑起 CUDA 应用&#xff1a;三步配置与验证清单 【免费下载链接】ZLUDA CUDA on non-NVIDIA GPUs 项目地址: https://gitcode.com/GitHub_Trending/zl/ZLUDA 你的 AMD 显卡跑不了 PyTorch 的 CUDA 版&#xff0c;原因是装不了 NVIDIA 驱动。ZLUDA 是…

作者头像 李华