STM32F103C8T6搭配E18-D80NK红外传感器,实现低成本物体计数(附完整代码)
2026/6/11 10:53:41 网站建设 项目流程

STM32F103C8T6与E18-D80NK红外传感器构建高精度物体计数系统

在工业自动化、智能仓储和DIY创客领域,物体计数是一个基础但至关重要的功能。传统的光电开关或机械式计数器往往存在成本高、安装复杂或精度不足的问题。本文将介绍如何利用STM32F103C8T6微控制器和E18-D80NK红外传感器构建一个低成本、高可靠性的物体计数系统,并提供完整的代码实现和优化技巧。

1. 系统设计与核心组件选型

1.1 E18-D80NK红外传感器特性解析

E18-D80NK是一款集发射与接收于一体的光电传感器,具有以下突出特点:

  • 抗干扰能力强:采用调制解调技术,有效避免环境光干扰
  • 检测距离可调:通过尾部电位器可在3-80cm范围内调节
  • 输出信号干净:检测到物体时输出低电平(0V),否则保持高电平(5V)
  • 适应不同材质:对白色物体检测距离最远,黑色物体最近

电气连接非常简单:

  • 棕色线:VCC(5V)
  • 蓝色线:GND
  • 黑色线:数字信号输出(OUT)

1.2 STM32F103C8T6的优势

选择STM32F103C8T6作为主控芯片主要基于以下考虑:

  • 成本效益:作为Cortex-M3内核MCU,价格亲民但性能足够
  • 丰富的外设:多达37个GPIO,支持外部中断功能
  • 开发生态完善:有HAL库和LL库支持,开发工具链成熟
  • 低功耗特性:适合电池供电的便携式计数设备

2. 硬件连接与电路设计

2.1 基础电路搭建

系统硬件连接示意图如下:

STM32F103C8T6 E18-D80NK +------------+ +------------+ | 5V -----+---------> VCC | | GND ----+---------> GND | | PB1 <---+--------- OUT | +------------+ +------------+

关键注意事项

  • 传感器工作电压必须为5V,不能直接连接3.3V
  • 输出信号可直接接入STM32的GPIO,无需电平转换
  • 建议在VCC和GND之间添加100nF去耦电容

2.2 抗干扰设计

为提高系统稳定性,建议增加以下电路:

  1. 电源滤波

    • 在传感器电源端并联100μF电解电容和100nF陶瓷电容
    • 使用LDO稳压器而非开关电源为传感器供电
  2. 信号调理

    • 在OUT信号线上串联100Ω电阻
    • 在STM32输入端添加10kΩ上拉电阻

3. 软件实现与代码优化

3.1 外部中断配置

利用STM32的外部中断功能实现精准计数:

// 外部中断初始化 void EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI1_IRQn); }

3.2 中断服务函数实现

通过双边沿触发检测物体通过事件:

volatile uint32_t objectCount = 0; volatile uint8_t lastState = 1; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_1) { uint8_t currentState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // 下降沿检测(物体进入) if(lastState == 1 && currentState == 0) { objectCount++; } lastState = currentState; } }

3.3 防抖动算法优化

红外传感器在实际应用中可能出现信号抖动,导致误计数。以下是改进方案:

  1. 硬件消抖

    • 在传感器输出端添加0.1μF电容到地
    • 使用施密特触发器整形信号
  2. 软件消抖

#define DEBOUNCE_TIME_MS 20 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { static uint32_t lastTime = 0; uint32_t currentTime = HAL_GetTick(); if(GPIO_Pin == GPIO_PIN_1 && (currentTime - lastTime) > DEBOUNCE_TIME_MS) { lastTime = currentTime; // 正常计数逻辑... } }

4. 系统校准与性能提升

4.1 传感器距离校准

E18-D80NK的检测距离可通过尾部电位器调节:

  1. 将标准测试物体放置在所需检测距离
  2. 缓慢旋转电位器直到传感器指示灯刚好触发
  3. 固定电位器位置,测试不同颜色物体的检测一致性

提示:对于黑色物体,建议将检测距离设置为实际需要的1.5倍,以确保可靠性

4.2 计数误差分析与解决

常见计数误差原因及对策:

问题现象可能原因解决方案
漏计数物体移动过快降低传送带速度或使用更快的MCU
多计数信号抖动增加消抖电路和算法
不稳定电源噪声改善电源滤波,使用独立供电

4.3 高级功能扩展

基于基础计数功能,可进一步实现:

  1. 速度计算
float CalculateSpeed(uint32_t count, float beltWidth, uint32_t timeMs) { return (count * beltWidth) / (timeMs / 1000.0f); // 单位:米/秒 }
  1. 方向检测
  • 使用两个传感器布置为"相位差"方式
  • 通过触发顺序判断物体移动方向
  1. 数据记录
  • 添加EEPROM存储历史计数数据
  • 通过串口或蓝牙上传到上位机

5. 完整项目代码实现

5.1 工程结构

Project/ ├── Core/ │ ├── Src/ │ │ ├── main.c │ │ ├── stm32f1xx_it.c │ │ └── system_stm32f1xx.c │ └── Inc/ │ ├── main.h │ └── stm32f1xx_it.h ├── Drivers/ ├── counter/ │ ├── counter.c │ └── counter.h └── STM32F103C8T6_FLASH.ld

5.2 核心计数器模块

counter.h头文件:

#ifndef __COUNTER_H__ #define __COUNTER_H__ #include "stm32f1xx_hal.h" void Counter_Init(void); uint32_t Counter_GetCount(void); void Counter_Reset(void); #endif

counter.c实现文件:

#include "counter.h" volatile uint32_t objectCount = 0; volatile uint8_t lastState = 1; uint32_t lastEventTime = 0; void Counter_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI1_IRQn); } uint32_t Counter_GetCount(void) { return objectCount; } void Counter_Reset(void) { objectCount = 0; } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_1) { uint32_t currentTime = HAL_GetTick(); uint8_t currentState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // 消抖处理(20ms) if((currentTime - lastEventTime) > 20) { // 下降沿检测 if(lastState == 1 && currentState == 0) { objectCount++; } lastState = currentState; lastEventTime = currentTime; } } }

5.3 主程序实现

main.c主文件:

#include "main.h" #include "counter.h" #include <stdio.h> UART_HandleTypeDef huart1; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); Counter_Init(); uint32_t lastDisplayTime = 0; uint32_t lastCount = 0; while (1) { uint32_t currentTime = HAL_GetTick(); // 每秒更新显示计数 if(currentTime - lastDisplayTime >= 1000) { lastDisplayTime = currentTime; uint32_t currentCount = Counter_GetCount(); if(currentCount != lastCount) { char msg[32]; int len = snprintf(msg, sizeof(msg), "Count: %lu\r\n", currentCount); HAL_UART_Transmit(&huart1, (uint8_t*)msg, len, HAL_MAX_DELAY); lastCount = currentCount; } } // 其他任务... HAL_Delay(10); } }

6. 实际应用案例与问题排查

6.1 流水线计件系统实现

在某包装生产线应用中,我们部署了基于该方案的计数系统:

  1. 安装要点

    • 传感器安装在传送带侧面,距离产品约10cm
    • 调整电位器使检测区域刚好覆盖产品通过路径
    • 使用金属支架固定,避免振动影响
  2. 性能指标

    • 计数速度:≤300件/分钟
    • 准确率:99.98%
    • 连续工作时间:24/7

6.2 常见问题排查指南

问题1:传感器不触发

  • 检查电源电压是否为5V
  • 确认输出线连接正确
  • 测试直接短路OUT到GND看计数是否增加

问题2:计数不准确

  • 观察传感器指示灯是否与物体通过同步
  • 检查是否有环境光直射传感器
  • 尝试增加消抖时间常数

问题3:远距离检测不稳定

  • 确保被测物体表面不是高反射材质
  • 尝试在传感器前方加遮光罩
  • 调节电位器找到最佳灵敏度点

6.3 系统优化经验分享

在实际项目中,我们发现以下优化措施特别有效:

  1. 温度补偿:在高温环境下,传感器的检测距离会缩短。可以通过软件动态调整触发阈值来补偿。

  2. 自适应灵敏度:对于不同颜色的物体,可以动态改变传感器的灵敏度设置:

void AdjustSensitivity(uint8_t level) { // 通过PWM控制一个额外的LED照明 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, level); }
  1. 故障自诊断:定期检查传感器状态,提前发现潜在问题:
bool CheckSensorStatus(void) { uint32_t startTime = HAL_GetTick(); while(HAL_GetTick() - startTime < 1000) { if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1) == 0) { return true; // 传感器正常 } HAL_Delay(10); } return false; // 传感器可能故障 }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询