数码之家

 找回密码
 立即注册
搜索
查看: 561|回复: 25

[工仪] 制作激光反射式转速仪与红外遥控器解码

[复制链接]
发表于 2025-3-23 11:10:24 | 显示全部楼层 |阅读模式
这是去年的diy,当时鼓捣无刷电机改造风扇,转速标定没有仪器,这就成了配套工程。
特点:
超低成本,10元。
激光反射式测速,转盘粘贴白纸或者烟盒锡纸,激光照射到纸上就反射回来获得信号,因为射出是点光源,用锡纸的话控制好角度5米距离测转速都可以,转速测量范围6转/分钟-9万转/分钟(没实际验证)。
红外遥控器解码是白送的功能,测转速做好后试验能接受红外信号,索性加上红外解码程序,解码红外遥控器键值。




演示,电磁炉风扇


NEC格式解码


5104格式解码


激光接收管用的是这个





激光发射管是从退役的PPT翻页笔拆下来的激光头,网购约3元,开始加了透镜,距离远了机关点会散焦,后来把透镜去掉了。


自制PCB,stc8g1k08a芯片,5v转3.3v供激光头,IO控制激光头工作,8秒没有反射信号关闭激光头。


外壳用20的pvc水管,没有内置电池,设置一按钮作为功能切换。


拆除透镜后直接一片有机玻璃做发射窗。




0.91寸OLED显示屏,单片机硬件I2C控制。


显示屏左侧的 〉符号实时显示是否收到反射信号。





80厘米距离测试可有效接收反射信号。

发错版块了,麻烦帮转到创意DIY。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 4家元 +135 收起 理由
飞向狙沙 + 30 謝謝分享
家睦 + 60
IlovePLC + 30 原創內容
jf201006 + 15 謝謝分享

查看全部打赏

发表于 2025-3-23 17:29:33 | 显示全部楼层
这个真是厉害
回复 支持 反对

使用道具 举报

发表于 2025-3-23 17:58:57 | 显示全部楼层
楼主做的六啊
   
不开源一下吗
回复 支持 反对

使用道具 举报

发表于 2025-3-23 18:22:16 来自手机浏览器 | 显示全部楼层
好奇的是单片机里面的代码
回复 支持 反对

使用道具 举报

发表于 2025-3-23 18:32:28 来自手机浏览器 | 显示全部楼层
很强,会单片机编程就是玩得花
回复 支持 反对

使用道具 举报

发表于 2025-3-23 20:18:56 | 显示全部楼层
这个好玩哈,可惜没有开源
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-3-23 21:11:16 | 显示全部楼层
开源...
欢迎大家讨论交流。




/*激光转速测量*/
/*设定内部晶振24M 时钟周期0.041666us 1T机器周期0.041666us
使用stc8g1k08a,pcb文件名leise6.lay6,红点光笔拆激光管,工作电压3v,激光调制接收管180k-200kHz(入光1,遮光0) */
#include "intrins.h"
#include "stc8g.h"
#include "codetab.h"
#include "iic.h"
#include "oled12832.h"
#include "NECir.h"
//#include "BA5104ir.h"
//sbit p54=P5^4;    //iic-scl
//sbit p55=P5^5;    //iic-sda
sbit ir=P3^2; //int0
sbit p33=P3^3;
sbit key=P3^0;
unsigned char mun;
unsigned int tes,ird0,ird1;
unsigned char irdat,kx,ok5104;
//在单片机中,CCP是英文单词Capture(捕获),Compare(比较),PWM(脉宽调制)的缩写。
//这个模块可配置为工作在输入捕捉、定时器比较或 PWM 输出方式下。
//可工作在捕捉输入、比较输出和脉宽调制输出三种状态,三种状态各自独立工作。
//PCA 计数器
//激光发射调制180-200kHz
void stcIO_Init(void)
{
    P3M0=0x00;  //设置P3.0-P3.7为双向口模式
    P3M1=0x00;
    P5M0=0x00;  //设置P5.0-P5.7为双向口模式
    P5M1=0x00;
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    // P3M0 = 0x00;
    // P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    // P5M0 = 0x00;
    // P5M1 = 0x00;
    P3=0xff;  //芯片内部没有打线到外部管脚的IO是悬空的,设置成准双向模式,并固定输出高电平
    P5=0xff;
}
//调制190k  2.63us高/2.63us低
//*************定时器0初始化*********************//
void laser_Timer0_Init(void)        //2.625微秒@24.000MHz  190kHz  laser
{
    AUXR |= 0x80;           //定时器时钟1T模式
    TMOD &= 0xF0;           //设置定时器模式 16位自动重装
    TL0 = 0xc1;             //设置定时初始值
    TH0 = 0xFF;             //设置定时初始值
    TF0 = 0;                //清除TF0标志
    TR0 = 1;                //定时器0开始计时
    ET0 = 1;                //使能定时器中断
}
void ir_Timer0_Init(void)       //32毫秒@24.000MHz  定时器0/12T模式/16位不自动重载/最大32毫秒溢出
{
    AUXR &= 0x7F;           //定时器时钟12T模式
    TMOD &= 0xF0;           //设置定时器模式
    TMOD |= 0x01;           //设置定时器模式 16位不自动重载
    TL0 = 0x00;             //设置定时初始值
    TH0 = 0x00;             //设置定时初始值
    TF0 = 0;                //清除TF0标志
    TR0 = 1;                //定时器0开始计时
    ET0 = 0;                //关闭定时器0中断
}
/************************************************
BA5104的编码格式:
每一帧遥控码的长度为12位,高位在前,包括3位起始码位(110)、2位用户码位、7位指令码位。
每一帧遥控码的时间间隔为4T(6.7516ms),其中T=1.6879ms为每一位遥控码的周期。
遥控码“0”用1/4T的高电平(0.395ms)、3/4T(1.284ms)的低电平表示,遥控码“1”用3/4T的高电平、1/4T的低电平表示。
    关-0x01  摇头-0x10  定时-0x08  风类-0x04  开/风速-0x02  彩灯-0x43
    110 11 1000011=0x0dc3=67 彩灯
*************************************************/
void do_ir5104()
{
//uchar k;
    if(ird0<<4==ird1<<4)
    {
        show[7]=(ird0>>9&0x07)>>4;  //0x00
        show[6]=(ird0>>9&0x07)&0x0f;  //0x06
        show[3]=(ird0>>7&0x03)>>4;
        show[2]=(ird0>>7&0x03)&0x0f;
        show[1]=(ird0&0x7f)>>4;
        show[0]=(ird0&0x7f)&0x0f;
        //_nop_();
        //_nop_();
        OLED_WrCmd(0x81);//--set contrast control register  设置对比度 0x00-0xff  值增加对比度增加
        OLED_WrCmd(0xff);//0xff);
        t1k=0;
    }
    else
    {
        ird1=ird0;
    }
    irdat=kx=ok5104=0;
    TR0=0;
    ET0=0;
}
void TM0_Isr() interrupt 1
{
    switch (mun)
    {
    case 0:
        p33 = !p33;
        break;
    case 2:
        TR0=0;
        ird0=(ird0<<1)|ir;
        irdat++;
        if(irdat>11)
        {
            ok5104=1;
        }
        break;
    default:
        break;
    }
}
/**********************以下转速测量************************/
//1w转  每转脉冲间隔6000us
//200转  每转脉冲间隔300000us=300ms
//24M晶振 定时器12T模式 机器周期0.5us 最小可测每分钟10转 最大可测每分钟100万转 显示只有5位数最高10万转
void Timer1_Init(void)      //25毫秒@24.000MHz  16位不自动重装  机器周期0.5us
{
    AUXR &= 0xBF;           //定时器时钟12T模式
    TMOD &= 0x0F;           //设置定时器模式
    TMOD |= 0x10;           //设置定时器模式
    TL1 = 0xB0;             //设置定时初始值
    TH1 = 0x3C;             //设置定时初始值
    TF1 = 0;                //清除TF1标志
    TR1 = 1;                //定时器1开始计时
    ET1 = 1;                //使能定时器中断
}
unsigned int t1k;
unsigned long speedT;
void TM1_Isr() interrupt 3     //定时器1中断
{


    TL1 = 0xB0;             //设置定时初始值
    TH1 = 0x3C;             //设置定时初始值
    //TR1 = 1;              //定时器1开始计时
    t1k++;
}
void int0_Init(void)
{
    IT0=0;      //IT0 = 1; //使能INT0下降沿中断
    EX0 = 1;    //使能INT0中断
    //EA = 1;
}
void INT0_Isr() interrupt 0  //ir接收中断0
{
    if(ir)
    {
        switch (mun)
        {
        case 0:  //转速
            speedT=((TH1-0x3c)*256+(TL1-0xb0))/2+(t1k*25000UL); //us  "UL"指定"25000"为unsigned long,否证keil会导致溢出
            TL1 = 0xB0;             //设置定时初始值
            TH1 = 0x3C;             //设置定时初始值
            //TR1 = 1;              //定时器1开始计时
            t1k=0;
            //ET1 = 1;                //使能定时器中断
            break;
        case 1:  //NEC解码
            Tc=TH0*256+TL0;   //提取中断时间间隔时长
            TH0=0;
            TL0=0;         //定时中断重新置零
            if((Tc>Imin)&&(Tc<Imax))
            {
                m=0;
                f=1;
                return;
            }       //找到启始码
            if(f==1)
            {
                if(Tc>Inum1&&Tc<Inum3)
                {
                    Im[m/8]=Im[m/8]>>1;
                    m++; //取码//Im[m/8]=Im[m/8]>>1|0x80; m++;
                }
                if(Tc>Inum2&&Tc<Inum1)
                {
                    Im[m/8]=Im[m/8]>>1|0x80;
                    m++;//Im[m/8]=Im[m/8]>>1; m++; //取码
                }
                if(m==32)
                {
                    m=0;
                    f=0;
                    if(Im[2]==~Im[3])
                    {
                        IrOK=1;
                    }
                    else
                    {
                        IrOK=0;    //取码完成后判断读码是否正确
                    }
                }
                //准备读下一码
            }
            break;
        case 2:  //BA5104解码
            if(!kx)
            {
                tes=TH0*256+TL0;   //提取中断时间间隔时长
                TH0=0;
                TL0=0;
                ET0=0;
                TR0=1;        //定时中断重新置零
                if((tes>16492)&&(tes<17794))//11.0592_if((tes>7600)&&(tes<8200)) //8246us-8897us
                {
                    //24M _if((tes>16492)&&(tes<17794)) //8246us-8897us
                    kx=1;
                }
            }
            if(!ok5104&&kx)  //11.0592_ TH0=0xfc;TL0=0xf1;
            {
                TL0=0x68;  //溢出时间844us,取遥控码“0”或“1”的一个T的一半(0.84395ms),根据此时的高低电平判断是0还是1//
                TH0=0xf9;
                TR0=1;
                ET0=1;
            }
            break;
        default:
            break;
        }
    }
}
unsigned dis[5]= {0x0a,0x0a,0x0a,0x0a,0x0a};
unsigned dis_tmp[5]= {0,0,0,0,0};
unsigned irdis[8]= {1,1,1,1,1,1,1,1};
unsigned long rpm;
void do_rpm (void)
{
    rpm=60000000/speedT;//t1k;//60000/speedT;
    if(!speedT)
    {
        rpm=0;
    }
    //rpm=mun;
    dis_tmp[4]=rpm%100000/10000;
    dis_tmp[3]=rpm%10000/1000;
    dis_tmp[2]=rpm%1000/100;
    dis_tmp[1]=rpm%100/10;
    dis_tmp[0]=rpm%10;
}
void scan_key(void)
{
    static unsigned int k;
    if(t1k==600)
    {
        if(mun==0)
        {
            TR0=0;
            p33=0;
        }
    }
    if(t1k==800)
    {
        OLED_WrCmd(0x81);   //--set contrast control register   设置对比度 0x00-0xff  值增加对比度增加
        OLED_WrCmd(0x00);//0xff);
    }
    if(!key)
    {
        OLED_WrCmd(0x81);   //--set contrast control register   设置对比度 0x00-0xff  值增加对比度增加
        OLED_WrCmd(0xff);//0xff);
        TR0=1;
        t1k=0;
        k++;
        if(k==1500)  //开关长按约3秒,改变状态
        {
            mun++;
            if(mun>2)
            {
                mun=0;
            }
            OLED_CLS();
            dis[0]=dis[1]=dis[2]=dis[3]=dis[4]=0x0b;
            irdis[7]=irdis[6]=irdis[5]=irdis[4]=irdis[3]=irdis[2]=irdis[1]=irdis[0]=0x10;
            switch (mun)
            {
            case 0:  //测转速
                laser_Timer0_Init();
                break;
            case 1:  //红外NRC格式解码
                ir_Timer0_Init();
                p33=0;
                OLED_P8x16Str(30,0,"NEC  KEY:");
                break;
            case 2:  //红外BA5104格式解码
                ir_Timer0_Init();
                p33=0;
                OLED_P8x16Str(30,0,"5104 KEY:");
                break;
            default:
                break;
            }
        }
    }
    else
    {
        k=0;
    }
    if((mun==1)&&IrOK)
    {
        irshow();
    }
}
//**********************将P0.5,P0.7,P3.6,和P3.7片内上拉电阻使能************************//
// P_SW2 |= 0X80; //将EAXFR位置1,以访问在XDATA区域的扩展SFR
// P0PU |= 0XA0; //设置P0.5,P0.7口有上拉电阻
// P3PU |= 0XC0; //设置P3.6,P3.7口有上拉电阻
// P_SW2 &= 0X7E; //将EAXFR位置0,恢复访问XRAM
//****************************************************************************************//
void main(void)
{
    stcIO_Init(); //STC IO 模式设置
    iicInit();
    laser_Timer0_Init();
//stc8g_pwm_init    ();
    Timer1_Init();
    OLED_Init(); //OLED初始化
    int0_Init(); //int0初始化
    m=0;
    f=0;
    TR1=1;
    EA=1;
    while(1)
    {
        //p33=0;
        //OLED_P6x8Str(0,1,"www.taobao.com");
        //OLED_P8x16Str(0,0,"Hello");//第一行 -- 8x16的显示单元显示ASCII码
        //OLED_z8x16    (14,2,6);
        //OLED_P16x16Ch(24,0,1);    /**功能描述:显示16*16点阵  显示的坐标(x,y),y为页范围0~7** void OLED_P16x16Ch(unsigned char x, y, N)**/
        //OLED_z17x32 (6,0,k);
        scan_key();
        if(!mun)
        {
            do_rpm();
        }
        if((mun==2)&&ok5104)
        {
            do_ir5104();
        }
        switch (mun)
        {
        case 0:
            if(dis[4]!=dis_tmp[4])
            {
                dis[4]=dis_tmp[4];
                OLED_z17x32 (18,0,dis[4]);
            }
            if(dis[3]!=dis_tmp[3])
            {
                dis[3]=dis_tmp[3];
                OLED_z17x32 (40,0,dis[3]);
            }
            if(dis[2]!=dis_tmp[2])
            {
                dis[2]=dis_tmp[2];
                OLED_z17x32 (62,0,dis[2]);
            }
            if(dis[1]!=dis_tmp[1])
            {
                dis[1]=dis_tmp[1];
                OLED_z17x32 (84,0,dis[1]);
            }
            if(dis[0]!=dis_tmp[0])
            {
                dis[0]=dis_tmp[0];
                OLED_z17x32 (106,0,dis[0]);
            }
            break;
        case 1:
            if(irdis[7]!=show[7])
            {
                irdis[7]=show[7];
                OLED_F8x16(30,2,irdis[7]);
            }
            if(irdis[6]!=show[6])
            {
                irdis[6]=show[6];
                OLED_F8x16(38,2,irdis[6]);
            }
            if(irdis[5]!=show[5])
            {
                irdis[5]=show[5];
                OLED_F8x16(54,2,irdis[5]);
            }
            if(irdis[4]!=show[4])
            {
                irdis[4]=show[4];
                OLED_F8x16(62,2,irdis[4]);
            }
            if(irdis[3]!=show[3])
            {
                irdis[3]=show[3];
                OLED_F8x16(78,2,irdis[3]);
                OLED_F8x16(102,0,irdis[3]);
            }
            if(irdis[2]!=show[2])
            {
                irdis[2]=show[2];
                OLED_F8x16(86,2,irdis[2]);
                OLED_F8x16(110,0,irdis[2]);
            }
            if(irdis[1]!=show[1])
            {
                irdis[1]=show[1];
                OLED_F8x16(102,2,irdis[1]);
            }
            if(irdis[0]!=show[0])
            {
                irdis[0]=show[0];
                OLED_F8x16(110,2,irdis[0]);
            }
            break;
        case 2:
            if(irdis[7]!=show[7])
            {
                irdis[7]=show[7];
                OLED_F8x16(30,2,irdis[7]);
            }
            if(irdis[6]!=show[6])
            {
                irdis[6]=show[6];
                OLED_F8x16(38,2,irdis[6]);
            }
            //if(irdis[5]!=show[5]){irdis[5]=show[5];OLED_F8x16(54,2,irdis[5]);}
            //if(irdis[4]!=show[4]){irdis[4]=show[4];OLED_F8x16(62,2,irdis[4]);}
            if(irdis[3]!=show[3])
            {
                irdis[3]=show[3];
                OLED_F8x16(66,2,irdis[3]);
            }
            if(irdis[2]!=show[2])
            {
                irdis[2]=show[2];
                OLED_F8x16(74,2,irdis[2]);
            }
            if(irdis[1]!=show[1])
            {
                irdis[1]=show[1];
                OLED_F8x16(102,2,irdis[1]);
                OLED_F8x16(102,0,irdis[1]);
            }
            if(irdis[0]!=show[0])
            {
                irdis[0]=show[0];
                OLED_F8x16(110,2,irdis[0]);
                OLED_F8x16(110,0,irdis[0]);
            }
            break;
        default:
            break;
        }
        if(ir)
        {
            OLED_F8x16(2,0,36);
        }
        else
        {
            OLED_F8x16(2,0,37);
        }
        //_nop_();
        //_nop_();
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

打赏

参与人数 1家元 +30 收起 理由
IlovePLC + 30 熱心助人

查看全部打赏

回复 支持 反对

使用道具 举报

发表于 2025-3-23 22:14:39 | 显示全部楼层
万用表带频率测试的是不是可以不用解码,直接接激光管测试?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-3-23 22:17:19 | 显示全部楼层
制作过程没画原理图,方便大家参考,现在我的pcb图上加上硬件连接示意图。
如果你使用的激光头是两线的,就不用接那个PD点,同样如果激光头是5v供电的,就接到+5端。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-3-23 22:21:52 | 显示全部楼层
lookball 发表于 2025-3-23 22:14
万用表带频率测试的是不是可以不用解码,直接接激光管测试?

测转速不用解码,测键值才要。你用万用表测转速那还是要搭建接收反射信号的东西。
回复 支持 反对

使用道具 举报

发表于 2025-3-23 22:38:50 | 显示全部楼层
高手的作品!
回复 支持 反对

使用道具 举报

发表于 2025-3-24 00:32:55 | 显示全部楼层
理论上可以测到多少转/分钟?实际估计是多少?
回复 支持 反对

使用道具 举报

发表于 2025-3-24 08:16:22 | 显示全部楼层
这种标称激光接收管的,实际上就是光敏管。
回复 支持 反对

使用道具 举报

发表于 2025-3-24 08:30:18 | 显示全部楼层
本帖最后由 纯今 于 2025-3-24 08:35 编辑




楼主这单片机实验板,都是插拔,透露了楼主玩单片机是老资格了

还同时利用红外接收管来解码红外遥控器

解码红外遥控器,我都是利用现有的库

硬件我是面包板与Breakout Board结合

而楼主这么多条代码,楼主这是从源头解码

我们是有代差的,致敬楼主,因为您资格老

我会参考你的代码,寻找现成的库去解决测速
回复 支持 反对

使用道具 举报

发表于 2025-3-24 08:39:09 | 显示全部楼层
做的很好,很实用。
回复 支持 反对

使用道具 举报

发表于 2025-3-24 08:52:35 | 显示全部楼层
典型CPU风扇转速范围:
普通风扇:800~2,000 RPM
高性能风扇:2,000~6,000 RPM
服务器/涡轮风扇:最高约 ​12,000 RPM​(如戴尔服务器风扇)
楼主是否通过CPU风扇自身的霍尔测速对比过准确性
回复 支持 反对

使用道具 举报

发表于 2025-3-24 08:56:57 | 显示全部楼层
谢谢分享~学习了
回复 支持 反对

使用道具 举报

发表于 2025-3-24 09:01:21 | 显示全部楼层
谢谢分享,详细diy帖,有干货
回复 支持 反对

使用道具 举报

发表于 2025-3-24 09:03:06 | 显示全部楼层
本帖最后由 纯今 于 2025-3-24 09:12 编辑




楼主采用定时器中断生成 ​190kHz方波​(占空比50%),调制激光发射频率
我手头有这种2.5元/个的激光头,也可以拿来调制吗,我是控制端高电平发射激光
有时间我也做一个,届时开源代码,评估了一下,代码比您少多了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-5-3 00:22 , Processed in 0.234001 second(s), 13 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表