光纤传感专业的我转行做了嵌入式软件,可是心系光学专业啊。下班回来发现个 stc 的 51 单片机最小系统,是我之前做的,好久没碰了。正好手边有两个光敏电阻,结合着步进电机,琢磨着做一个寻光器
玩玩,让箭头始终指向光源。
总体思路
因为要寻找光源,所以需要一个能感知外界光线的器件,这里选择了光敏电阻
。一般,正对着光源才会接收到最强的光源,所以为了寻找到光源方向,仅仅能够探测到光纤还不够,还需要探测光的强度,这个工作就交给了 STC12C5A60S2 单片机的 ADC
。当然,在使用 ADC 之前,需要设计电路,将光敏电阻的信息转换为电压信息。得到光源方向后,还需要一个能够转向光源的器件,这里选择了步进电机。所以要做的工作如下:
- 设计电路,将对光功率变化敏感的电阻信息转换为电压信息
- 设计单片机程序,驱动 STC12C5A60S2 的 AD 模块采集光敏电压信息
- 设计程序,驱动步进电机转动
- 设计算法,使步进电机时钟指向光源
将光敏电阻信息转换为光敏电压信息
这里直接使用了最简单的电路,如下图,就是一个可调电阻和光敏电阻串联,可调电阻的大小要与光敏电阻的常态接近。
STC12C5A60S2 的 AD 驱动程序
这个程序可参考:STC12C5A60S2 单片机的A/D转换c语言实例
步进电机驱动程序
我使用的是 28BYJ-48
步进电机,驱动程序非常简单,采用了单片机的 P0
口的低 4 位驱动。
\#include "reg51.h"
char motorIndex = 0;
unsigned char code beatCode[8] = {0xe,0xc,0xd,0x9,0xb,0x3,0x7,0x6};
void Delay2ms(unsigned int cnt)
{
unsigned int i = 2000;
while(cnt--)
while(i--);
}
void MotorTurn(unsigned int rounds)
{
while(rounds--){
P0 = beatCode[motorIndex];
motorIndex ++;
motorIndex = motorIndex&0x07;
Delay2ms(1);
}
}
void MotorTurnBack(unsigned int rounds) // 反转
{
while(rounds--){
P0 = beatCode[motorIndex];
if(motorIndex > 0)
motorIndex --;
else
motorIndex = 7;
Delay2ms(1);
}
}
寻光算法
算法也是考虑了很久,最后决定使用两个光敏电阻,两个光敏电阻的电路是相同的,都是上面介绍的电路。只不过,将两个电阻之间用隔光纸板隔开了,如下图
如果光源在左边,则左边的电阻接收光强比右边的大。如果光源在右边,则反之。如此一来,寻光算法就非常简单了,就是判断两边光强,那边强往哪边转。在实验之前,调节两个光敏电阻电路中的可调电阻,使两个光敏电阻在相同光强下,尽量输出接近的电压值。最后,总体程序如下:
void main()
{
WORD res0 = 0, res1 = 0;
InitADC();
while(1){
res0 = get_adc_res(0);
res1 = get_adc_res(1);
if(res0-20 > res1)
MotorTurnBack(100);
else if(res1-20 > res0)
MotorTurn(100);
}
}
可以看出,非常简单。单片机的 ADC 通道 0 和 通道 1 分别探测两个光敏电阻电路输出的电压值,只要两个电压值差值大于阈值,则驱动步进电机步进100个角度。
最终,寻光器的工作效果如下:
[video width="544" height="960" mp4="https://www.xrkzn.cn/wp-content/uploads/2018/08/寻光器.mp4"][/video]