STM32 CubeMax TCRT5000L光电巡管 原理与实现
1. 光电对管的原理
电子生产中使用的光电对管通常是,该系列分为和,两者的区别在于针脚的长度不同
在每个管道中包含一个光电二极管和一个,红外光电二极管不断向外发射红外线,红外线经外部环境反射后被光敏三极管吸收,光敏三极管导通的程度和吸收的红外线强度成正比。所以只要检测光敏三极管导通的程度就知道反射红外线材料的性质。一般颜色深的材质吸收红外线多,颜色浅的材质反射红外线多,这就是我们检测黑线的基本原理
根据原理图,光敏三极管导通后的输出值是连续的,所以我们可以通过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 <stdio.h> ////光电管中断输入引脚 #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_2)//上升沿 { etubeCkeck[1] = 1; } if(!HAL_GPIO_ReadPin(ETUBE_PORT_2, ETUBE_PIN_2)//下降沿 { etubeCkeck[1] = 0; } } else if(GPIO_Pin == ETUBE_PIN_3)//3号引脚 { if(HAL_GPIO_ReadPin(ETUBE_PORT_3, ETUBE_PIN_3)//上升沿 { etubeCkeck[2] = 1; } if(!HAL_GPIO_ReadPin(ETUBE_PORT_3, ETUBE_PIN_3)///下降沿 { etubeCkeck[2] = 0; } } else if(GPIO_Pin == ETUBE_PIN_4)//4号引脚 { if(HAL_GPIO_ReadPin(ETUBE_PORT_4, ETUBE_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(" ");
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