STM32 CubeMax TCRT5000L光电对管巡线 原理与实现
时间:2023-04-26 00:07:00
STM32 CubeMax TCRT5000L光电管道巡线的原理和实现
1. 光电对管的原理
电子生产中使用的光电对管通常是TCRT5000系列,该系列分为TCRT5000和TCRT5000L,两者的区别在于针脚的长度不同
在每个管道中包含一个红外光电二极管和一个光敏三极管,红外光电二极管不断向外发射红外线。红外线在外部环境反射后被光敏三极管吸收。光敏三极管的导通程度与吸收的红外强度成正比。因此,只要检测光敏三极管的导通程度,就知道反射红外材料的性质。一般来说,深色材料吸收更多的红外线,浅色材料反射更多的红外线。这是我们检测黑线的基本原理
由原理图可知,光敏三极管导通后输出的值是一个连续的模拟值,所以我们可以通过ADC读取模拟值并将其送入单片机进行处理,以获得非常高的灵敏度。这种做法的缺点是算法复杂。如果管道数量较多,则会对其进行处理ADC提出高要求。因此,为了简单起见,我们通常将这个模拟值发送到一个电压比较器在这种情况下,输出值只有0和1两种情况,单片机的负担会小以应付大多数场合
使用比较器的典型电路长度是目前的主流做法(仅供参考),使用多个光电将管道排成一排检测黑线
图中的三角形是电压比较器的符号,型号是LM393
典型的实物像这样,这是某宝上买的模块
如果你想现成的模块,最好看看上面有没有。电位器(直插贴片都可以),有电位器的一般可以调整比较器阈值,可测黑线的灵敏度可以调整(上面没有电位器,可能要自己换电阻…)
此时的算法编写相对简单,只要使用GPIO读取高低电平
2. CubeMax配置
以上发送的物理模块为例,共有5个光电对管,我们需要5个输入引脚。最好在这里使用输入外部中断而不是GPIO使用中断输入可以避免轮询检测,节省一些GPIO资源
因此,我们需要配置5个GPIO在配置引脚时,外部中断输入引脚,不要配置数字相同的引脚例如,作为中断输入,PA1和PB1.由于数字相同的引脚是共用的中断线,字母后引脚将覆盖字母前引脚(PB会覆盖PA),字母前引脚的中断不起作用(详见数据手册)
由于该模块检测到黑线输出较低,因此需要设置为内部上拉,上升\下降沿触发中断
最后,记得把外面中断优先级稍低,否则可能会出现中断冲突导致卡死,比如说在外部输入中断函数中调用HAL_Delay时,就会与Time base中断冲突导致卡死
其他基本配置不再重复
3. 接线
模块与STM32的接线如下
对管模块 | STM32 |
---|---|
5V | 5V |
GND | GND |
OUT1 | PB0 |
OUT2 | PB1 |
OUT3 | PB10 |
OUT4 | PB11 |
OUT5 | PB12 |
4. 代码编写
eletube.h内容如下
#ifndef _ELETUBE_H_#define _ELETUBE_H_ #include "stm32f1xx.h"#include //#define ETUBE_PIN_1 GPIO_PIN_0#define ETUBE_PIN_2 GPIO_PIN_1#define ETUBE_PIN_3 GPIO_PIN_10#define ETUBE_PIN_4 GPIO_PIN_11#define ETUBE_PIN_5 GPIO_PIN_12 #define ETUBE_PORT_1 GPIOB#define ETUBE_PORT_2 GPIOB#define ETUBE_PORT_3 GPIOB#define ETUBE_PORT_4 GPIOB#define ETUBE_PORT_5 GPIOB void Etube_Check(void);///将探测情况输出 #endif
eletube.c内容如下
#include "eletube.h" uint8_t etubeCkeck[5] = {1,1,1,1,1};//存储每个对管检测到黑线的状态,1没有检测到,0检测到 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//输入中断处理函数{ if(GPIO_Pin == ETUBE_PIN_1)//1号引脚 { if(HAL_GPIO_ReadPin(ETUBE_PORT_1,ETUBE_PIN_1)///上升沿 etubeCkeck[0] = 1; if(!HAL_GPIO_ReadPin(ETUBE_PORT_1,ETUBE_PIN_1))//下降沿 { etubeCkeck[0] = 0; else if(GPIO_Pin == ETUBE_PIN_2)//2号引脚 { if(HAL_GPIO_ReadPin(ETUBE_PORT_2,ETUBE_PIN_(4)///上升沿 etubETUBE_PIN_4))//上升沿 { etubeCkeck[3] = 1; } if(!HAL_GPIO_ReadPin(ETUBE_PORT_4, ETUBE_PIN_4))//下降沿 { etubeCkeck[3] = 0; } } else if(GPIO_Pin == ETUBE_PIN_5)//4号引脚 { if(HAL_GPIO_ReadPin(ETUBE_PORT_5, ETUBE_PIN_5))//上升沿 { etubeCkeck[4] = 1; } if(!HAL_GPIO_ReadPin(ETUBE_PORT_5, ETUBE_PIN_5))//下降沿 { etubeCkeck[4] = 0; } }} void Etube_Check(void)//将探测情况输出{ uint8_t i = 0; for(i = 0;i < 5;i++) { if(etubeCkeck[i] == 0) printf("0 "); else if(etubeCkeck[i] == 1) printf("1 "); else {}; printf("\r\n"); }}
之后在main函数里调用探测情况输出函数即可
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ Etube_Check(); HAL_Delay(500); }
(记得自己将printf重定义)
5. 结果
输出正常,很ok