自行车尾灯改造

欧大琛 2019-01-29 PM 4912℃ 0条

原因

女票下班回家路上有段路比较乱,还没有路灯,为了安全买了个自行车尾灯,可是这个尾灯每次都要手动打开、关闭,对于我们这种懒鬼,实在是一种煎熬啊,有时不小心忘记了,就没电了,本着懒鬼推动世界发展的精神,开始折腾尾灯。


拆机

1.尾灯全貌
尾灯全貌

2.开后盖
开后盖

3.去掉电池
去掉电池

4.取出PCB
PCB背面

PCB正面

分析

说实话这个尾灯做工还是可以的,锂电池充电,PCB厚实,焊接整齐,金属按钮,并且主控居然不是牛屎,而是正正经经SOP8封装,想起刚好以前凑邮费时买了几个STC15w204s是SOP8的,岂不是很巧;又想起来,之前买自行车送了两个气嘴灯里有简单的震动开关,嗯,按钮启动改成震动启动岂不是很不错,既然这么巧,不折腾下岂不是不给面子。。。暗中偷笑

功能

既然什么都有了,就来想想具体要实现怎样的功能,模拟上车-骑行-下车-停车的过程来思考具体需要实现哪些功能。

  • 上车:需要实现依靠震动启动,但又不能轻轻一碰就启动(容易被人顺手牵尾灯);
  • 骑行:骑行过程中不能停止工作,保持闪烁状态;
  • 下车:下车后需要自动停止工作,或者延时停止工作
  • 停车:停车后不许处于工作状态,并且不能功耗太大,不然天天充电还不如天天手动打开呢。

总结一下就是:
1.震动延时启动
2.骑车过程中必须保持工作状态
3.停车后自动停止工作
4.未骑行时保持超低功耗

确定可行性

查询了STC15w204s的数据手册,该MCU支持uA级别低功耗,在掉电模式下典型功耗<0.1uA,同时支持外部中断唤醒INT0,INT1,内部集成了高精度R/C时钟。完美!!!!!!!是不可能的,引脚定义居然与原MCU居然不一样,WTF。
使用万用表测试两个MCU引脚对比如下:
mcu对比

原MCU为一个OTP芯片,需要特别的编程器才可以编程,所以只好选则STC的MCU,虽然不是pin2pin,对了原MCU的4脚为按键引脚,下降沿触发,其余IO为LED控制脚,LED为共阳极。

改造

电路改造主要在MCU飞线,更换按键为震动开关。
飞线示意图如下(将stc的vcc连接到1脚,将gnd连接到8脚):
飞线示意图

将需要飞线的引脚剪短,以免与原焊盘短路
减脚

焊接上漆包线
漆包线

按照示意图将MCU焊到PCB上,并焊接上震动开关
特写1

特写2

特写3

编程

开发采用Keil C51 IDE进行,由于原版开发环境不包含15w204s,因此先使用STC-ISP将STC的单片机型号添加到Keil中去。
添加型号

同时复制STC-ISP提供的头文件
头文件

具体程序如下:

/*!
 *****************************************************************************
 *
 *         Copyright © 2017-2018 Gustav. All Rights Reserved.
 *
 * \file      main.c
 * \author    Gustav
 * \version   1.0
 * \date      2019年1月30日
 * \brief     自动感应尾灯
 *
 *----------------------------------------------------------------------------
 * \attention
 *
 *
 *****************************************************************************
 */
#include "reg.h"

#define T0MS 50000  //!< 定时器中断溢出值 50ms By: Gustav 2019年1月30日
#define TIMER0_INTERVAL (20 * 15) //!< 自动休眠超时时长 15s By: Gustav 2019年1月30日
#define WAKE_BEFORE_NUM 50 //!< TIMER0_INTERVAL内外部中断触发次数上限,用于延时启动,根据震动开关灵敏度调节 By: Gustav 2019年1月30日

/*!
 * \fn     delay_ms
 * \brief  毫秒软件延时
 *
 * \param  [in] unsigned int x   延时毫秒数
 *
 * \retval void
 */
void delay_ms(unsigned int x)
unsigned char i, j;

do
{
    i = 11;
    j = 190;

    do {
        while (--j);
    } while (--i);
} while (--x);
}


/*!
 * \fn     clk_div
 * \brief  时钟分频
 *
 * \param  [in] unsigned char div   0:不分频
 *                                  1:/2
 *                                  2:/4
 *                                  3:/8
 *                                  4:/16
 *                                  5:/32
 *                                  6:/64
 *                                  7:/128
 *
 * \retval void
 */
void clk_div(unsigned char div)
{
    if (div > 0x07) while (1); //div非法,程序挂起

    CLK_DIV = (CLK_DIV & 0xF8) | div;
}

/*!
 * \fn     int1_init
 * \brief  外部中断1初始化
 *
 *
 * \retval void
 */
void int1_init()
{
    IT1 = 1;
    EX1 = 1;
}

/*!
 * \fn     enter_powerdown
 * \brief  进入低功耗模式
 *
 *
 * \retval void
 */
void enter_powerdown()
{
    PCON = (PCON & 0xFD) | 0x02;
}

/*!
 * \fn     turn_on_off_led
 * \brief  LED控制
 *
 * \param  [in] unsigned char on   #
 *
 * \retval void
 */
void turn_on_off_led(unsigned char on)
{
    if (on == 0) {
        P30 = 1;
        P31 = 1;
        P32 = 1;
        P54 = 1;
        P55 = 1;
    } else {
        P30 = 0;
        P31 = 0;
        P32 = 0;
        P54 = 0;
        P55 = 0;
    }
}

/*!
 * \fn     timer0_init
 * \brief  定时器0初始化
 *
 *
 * \retval void
 */
void timer0_init()
{
    //AUXR |= 0x80;  //!< 1T By: Gustav 2019年1月30日
    AUXR &= 0x7f;  //!< 12T By: Gustav 2019年1月30日

    TMOD = 0x00;  //!< 定时工作模式0 By: Gustav 2019年1月30日
    TL0 = 65536 - T0MS;
    TH0 = (65536 - T0MS) >> 8;
    TR0 = 1;
    ET0 = 1;
}

static unsigned char wake_count = 0; //!< 延时唤醒外部中断触发次数 By: Gustav 2019年1月30日
static unsigned int time0_count = 0; //!< 超时计数 By: Gustav 2019年1月30日

/*!
 * \fn     tm0_isr
 * \brief  定时器0中断处理函数
 *
 *
 * \retval void
 */
void tm0_isr()interrupt 1 using 1
{
    ++time0_count;
}

/*!
 * \fn     exint1
 * \brief  外部中断1处理函数
 *
 *
 * \retval void
 */
void exint1()interrupt 2
{
    if (wake_count <= WAKE_BEFORE_NUM) { //!< 从休眠中醒来,且连续外部中断次数小于设定的上限,用于延时启动 By: Gustav 2019年1月30日
        wake_count++;
    } else { //!< 进入工作状态后,每次外部中断发生则刷新time0_count,以实现骑行过程中一直处于工作状态 By: Gustav 2019年1月30日
        EA = 0;
        time0_count = 0;
        EA = 1;
    }
}

/*!
 * \fn     main
 * \brief  主函数
 *
 *
 * \retval void
 */
void main()
{
    clk_div(0);
    int1_init();
    timer0_init();
    EA = 1;  //!< 开总中断 By: Gustav 2019年1月30日

    while (1) {
        if (wake_count > WAKE_BEFORE_NUM - 1) {
            turn_on_off_led(0);
            delay_ms(300);
            turn_on_off_led(1);
            delay_ms(300);
        }

        if (time0_count >= TIMER0_INTERVAL) { //!< 超过15s未发生震动,表示已经停车,进入休眠状态 By: Gustav 2019年1月30日
            time0_count = 0;
            wake_count = 0;
            turn_on_off_led(0);
            enter_powerdown();
        }
    }
}

下载程序

下载前,需要将IRC频率设置为12M,保证定时器定时的准确性,并且关闭低压复位,以免下载时电压不稳定,引起下载失败。
下载设置

完成

测试没有问题将锂电池安装回去就大功告成了,目前已经安装在自行车上了,工作良好,一个多星期,依然很亮,不知道具体能工作多久,另外希望别丢了。。。。。

下一步计划

车前灯也是手动的,有机会也改造下,不过会加入光敏电阻判断环境光线强度,再决定是否打开灯光。

附件

Keil工程

标签: stc

「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」

欧大琛

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码完成赞赏

非特殊说明,本博所有文章均为博主原创。

评论啦~