原因
女票下班回家路上有段路比较乱,还没有路灯,为了安全买了个自行车尾灯,可是这个尾灯每次都要手动打开、关闭,对于我们这种懒鬼,实在是一种煎熬啊,有时不小心忘记了,就没电了,本着懒鬼推动世界发展的精神,开始折腾尾灯。
拆机
1.尾灯全貌
2.开后盖
3.去掉电池
4.取出PCB
分析
说实话这个尾灯做工还是可以的,锂电池充电,PCB厚实,焊接整齐,金属按钮,并且主控居然不是牛屎,而是正正经经SOP8封装,想起刚好以前凑邮费时买了几个STC15w204s是SOP8的,岂不是很巧;又想起来,之前买自行车送了两个气嘴灯里有简单的震动开关,嗯,按钮启动改成震动启动岂不是很不错,既然这么巧,不折腾下岂不是不给面子。。。暗中偷笑
功能
既然什么都有了,就来想想具体要实现怎样的功能,模拟上车-骑行-下车-停车的过程来思考具体需要实现哪些功能。
- 上车:需要实现依靠震动启动,但又不能轻轻一碰就启动(容易被人顺手牵尾灯);
- 骑行:骑行过程中不能停止工作,保持闪烁状态;
- 下车:下车后需要自动停止工作,或者延时停止工作
- 停车:停车后不许处于工作状态,并且不能功耗太大,不然天天充电还不如天天手动打开呢。
总结一下就是:
1.震动延时启动
2.骑车过程中必须保持工作状态
3.停车后自动停止工作
4.未骑行时保持超低功耗
确定可行性
查询了STC15w204s的数据手册,该MCU支持uA级别低功耗,在掉电模式下典型功耗<0.1uA,同时支持外部中断唤醒INT0,INT1,内部集成了高精度R/C时钟。完美!!!!!!!是不可能的,引脚定义居然与原MCU居然不一样,WTF。
使用万用表测试两个MCU引脚对比如下:
原MCU为一个OTP芯片,需要特别的编程器才可以编程,所以只好选则STC的MCU,虽然不是pin2pin,对了原MCU的4脚为按键引脚,下降沿触发,其余IO为LED控制脚,LED为共阳极。
改造
电路改造主要在MCU飞线,更换按键为震动开关。
飞线示意图如下(将stc的vcc连接到1脚,将gnd连接到8脚):
将需要飞线的引脚剪短,以免与原焊盘短路
焊接上漆包线
按照示意图将MCU焊到PCB上,并焊接上震动开关
编程
开发采用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,保证定时器定时的准确性,并且关闭低压复位,以免下载时电压不稳定,引起下载失败。
完成
测试没有问题将锂电池安装回去就大功告成了,目前已经安装在自行车上了,工作良好,一个多星期,依然很亮,不知道具体能工作多久,另外希望别丢了。。。。。
下一步计划
车前灯也是手动的,有机会也改造下,不过会加入光敏电阻判断环境光线强度,再决定是否打开灯光。