基于ATtiny13的无级调光头灯程序 市面上的头灯一般有强光、弱光、爆闪三个挡位,一个按钮来控制开/关和换挡,顺序是:开(强光)-〉弱光->闪烁->关,每次开/关灯需要按三次,非常不方便。 两年前改造的五挡调光头灯一直用的很好,操作简便又人性化。近来比较空闲,突发DIY一个有无级调光功能的头灯想法。于是在网上搜索合适DIY的头灯,搜索原则是散热性能好、能变焦、尽量是装两节18650电池的灯。本人喜欢变焦灯,因为照射范围可调又均匀。第一次购买的灯完全失败,散热极差,为了节省成本居然灯头的重要部件用的是硬塑料,直接丢掉。第二次买的还好,惊喜的是他的控制芯片用的是CX2812,它的引脚与ATTiny13单片机类似,替换控制芯片,稍微改动电路板的布线就可以利用原配的控制板,避免了从头开始做控制板,省去了最繁琐的印刷电路板的制作了。 通过ATtiny13a单片机的编程实现了一下功能: 1、按一次开灯,再按一次就关灯,解决了按三次关灯的不方便的问题。 2、开关灯保留了逐步亮和逐步暗的功能,此功能非常人性化,本人很喜欢。具体实现如下:开灯的时候PWM信号的占空比从0开始逐步加到设定的亮度,关灯时候占空比逐步减到0。3、无级调光:在灯亮状态下按住按钮,LED灯亮度增加或减少,亮度达到100%或10%时闪烁三次来提醒亮度已经达到了全亮或最暗,松开按钮就停止调光,一直按住就循环调光。 程序中利用了ATtiny13a的PWM、外部中断(PCINT0)、睡眠、看门狗和EEPROM的读写功能,基本上每一行都加了注释,对于初次接触ATtiny13的人来说应该有帮助。 图片1:头灯
头灯
图片2:头灯控制板(此控制板网上也有卖的)
头灯控制板
图片3:程序调试电路图
程序调试电路图
图片4:用面包板搭建程序调试电路
用面包板搭建用面包板搭建程序调试电路程序调试电路
图片5:基于ATTiny13A的控制板电路图
基于ATTiny13A的控制板电路图
图片6:修改后的ATTiny13A控制板
修改后的ATTiny13A控制板
图片7:18650电池下的待机电流为0.26uA。
18650电池下的待机电流为0.26uA
程序编辑和编译工具用了CodeVisionAVR 2.04.4a,烧写工具用了progisp1.72。
需要工程文件的请本人百度消息联系我(百度ID: zs_jin)。 程序源码: /********************************************************************** * 名称 :LED控制程序 * 描述 :无级调光功能,单击开/关,长按无级调光 * MCU :ATTiny13A * 晶振 :RC 9.6MHz * 编译环境 :CodeVisionAVR2.04.4a * 版本 :4.0 KEY用外部中断方式、增加睡眠功能、看门狗,8分频(4.6875KHz) * 外部中断服务程序=>PCINT0, 无级调光 ***********************************************************************/ #include <tiny13a.h> #include <delay.h> #define uchar unsigned char #define uint unsigned int #define KEY PINB.2 //tiny13第7脚,PCINT0中断 #define LEDDRV PORTB.0 //tiny13第5脚 #define KEY_DOWN 0 //按下按键 #define LED_ON 0 //LED 亮 #define LED_OFF 1 //LED 灭 #define PWM_ON 0xc3 //PWM 启动 #define PWM_OFF 0x03 //PWM 关闭 #define MIN_VAL 0x1a //最小亮度 #define MAX_VAL 0xff //最大亮度 #define LONG_KEY 3 //长按键的时间 #define ADDRESS 0x01 //保存挡位的地址 uchar pwmVal = 0; //亮度值 uint keytime = 0; //按键时间 bit status = 0; //0为LED ON,1为LED OFF bit fangxiang = 0; //亮度增减方向 /********************************************************************** * 描述:eeprom写一个字节数据 * 参数:address--地址 data--数据 ***********************************************************************/ void eeprom_write(uchar address,uchar data) { //等待上一次写操作结束 while(EECR&(1<<EEPE)); //设置编程模式 EECR=(0<<EEPM1)|(0>>EEPM0); //设置地址和数据寄存器 EEAR = address; EEDR = data; //置位EEMPE EECR |= (1<<EEMPE); //置位EEWE,启动写操作 EECR |= (1<<EEPE); } /*********************************************************************** * 描述:eeprom读一个字节数据 * 参数:address--地址 ************************************************************************/ uchar eeprom_read(uchar address) { //等待上一次写操作结束 while(EECR&(1<<EEPE)); //设置地址寄存器 EEAR=address; //设置EERE以启动读操作 EECR |= (1<<EERE); //自数据寄存器返回数据 return(EEDR); } /*********************************************************************** * 描述:开启看门狗 * 参数:无 ************************************************************************/ void WDT_on(void) { // 看门狗设置 //#asm("cli") // 关闭全局中断 #asm("WDR") // 看门狗复位指令WDR用来复位看门狗定时器 WDTCR=0x18; // 启动时序 WDTCR |= (1<<WDCE) | (1<<WDE); //WDTCR=0x28; // 看门狗定时4s WDTCR = (1<<WDP3) | (1<<WDE); WDTCR = (1<<WDP3) | (1<<WDE); // 看门狗定时4s //#asm("sei") // 开启全局中断 } /*********************************************************************** * 描述:关闭看门狗 * 参数:无 ************************************************************************/ void WDT_off(void) { // 看门狗设置 //#asm("cli") // 关闭全局中断 #asm("WDR") // 看门狗复位指令WDR用来复位看门狗定时器 MCUSR &= ~(1<<WDRF); // 清除MCUSR 寄存器中WDRF // 在WDCE 与WDE 中写逻辑1,保持旧预分频器设置防止无意暂停 WDTCR |= (1<<WDCE) | (1<<WDE); WDTCR = 0x00; // 关闭WDT //#asm("sei") // 开启全局中断 } /*********************************************************************** * 描述:LED Flicker 闪烁 * 参数:无 ************************************************************************/ void led_flicker(uchar data) { uchar i; for(i=0; i<data; i++){ OCR0A = 0x00; delay_ms(20); OCR0A = pwmVal; delay_ms(20); } } /*********************************************************************** * 描述:LED ON 开灯 * 参数:无 ***********************************************************************/ void led_on(void) { uchar i; TCCR0A = PWM_ON; //开启PWM for (i=0; i<pwmVal; i++) { OCR0A = i; //更改占空比 delay_us(500); } //全亮时候 PWM Off if (pwmVal >= MAX_VAL) { TCCR0A = PWM_OFF; LEDDRV = LED_ON; } WDT_on(); //启动看门狗 } /*********************************************************************** * 描述:LED OFF 关灯 * 参数:无 ************************************************************************/ void led_off(void) { uchar i; TCCR0A = PWM_ON; //开启PWM for (i=pwmVal; i>0; i--) { OCR0A = i; //更改占空比 delay_us(500); } TCCR0A = PWM_OFF; //关闭PWM LEDDRV = LED_OFF; } /*********************************************************************** * 描述:LED 亮度增加 * 参数:无 ***********************************************************************/ void led_zeng(void) { TCCR0A = PWM_ON; //开启PWM for (; pwmVal<MAX_VAL && KEY==KEY_DOWN; pwmVal++) { OCR0A = pwmVal; //更改占空比 delay_ms(2); } if(pwmVal>=MAX_VAL) led_flicker(3); } /*********************************************************************** * 描述:LED 亮度减少 * 参数:无 ************************************************************************/ void led_jian(void) { TCCR0A = PWM_ON; //开启PWM for (; pwmVal>MIN_VAL && KEY==KEY_DOWN; pwmVal--) { OCR0A = pwmVal; //更改占空比 delay_ms(2); } if(pwmVal <= MIN_VAL) led_flicker(3); } /********************************************************************** * 描述 :定时器中断服务函数 * 输入 :无 ***********************************************************************/ /*interrupt[TIM0_OVF] voidtimer0_ovf_isr(void) { }*/ /********************************************************************** * 名称 : 外部中断INT0服务程序 * 功能 : * 输入 :无 * 输出 :无 ***********************************************************************/ /*interrupt [EXT_INT0] void int0_isr(void) { }*/ /********************************************************************** * 名称 : 中断PC_INT0服务程序 * 功能 : * 输入 :无 * 输出 :无 ***********************************************************************/ interrupt [PC_INT0] void pc_int0_isr(void) { if (KEY==KEY_DOWN) //判断是否按下键,KEY接地 { delay_ms(5); //防抖延时 for (keytime=0; (KEY==KEY_DOWN) && keytime<LONG_KEY;keytime++) { keytime++; delay_ms(50); // 50ms } //长按逻辑处理 while (keytime > LONG_KEY && KEY==KEY_DOWN && status== 1) //长按逻辑处理 { //#asm("WDR") //喂狗 if (pwmVal >= MAX_VAL) fangxiang = 1; if (pwmVal <= MIN_VAL) fangxiang = 0; if (fangxiang == 0) led_zeng(); else led_jian(); fangxiang = ~fangxiang; delay_us(10); eeprom_write(ADDRESS,pwmVal); //写eeprom里的数据 delay_us(100); //全亮时候 PWM Off if (pwmVal >= MAX_VAL) { TCCR0A = PWM_OFF; LEDDRV = LED_ON; } } //短按逻辑处理 if (keytime > 0 &&keytime <= LONG_KEY) //短按逻辑处理 { if (status == 0) // LED NO led_on(); //开灯 else if (status == 1) // LED OFF led_off(); //关灯 status = ~status; } } } /********************************************************************** * 名称 : Main() * 功能 : 主函数 * 输入 :无 * 输出 :无 ***********************************************************************/ void main(void) { // Crystal Oscillator division factor: 8 #pragma optsize- CLKPR=0x80; CLKPR=0x03; #ifdef _OPTIMIZE_SIZE_ #pragma optsize+ #endif // I/O端口的初始化 DDRB = 0x01; // PB.1输入,PB.0输出 PORTB = 0x05; // PB.1上拉电阻,PB.0高电平 //PWM 初始化(TCCR0A=0xC3; 占空比相反时TCCR0A = 0x83 0<<COM0A0) //TCCR0A |=(1<<COM0A1)|(0<<COM0A0)|(1<<WGM01)|(1<<WGM00); TCCR0A = PWM_ON; //开启PWM TCCR0B |=(0<<WGM02)|(0<<CS02)|(1<<CS01)|(0<<CS00); //8分频(4.6875KHz) OCR0A = 0x00; TCNT0 = 0x00; // T/C0计数寄存器初值 TCCR0A = PWM_OFF; // 关闭PWM // 外部中断初始化设置 //MCUCR |= (1<<ISC01) | (0<<ISC00); // INT0 为下降沿时产生中断请求 //GIMSK |= (1<<INT0); // 允许 INT0 产生中断 GIMSK |= (1<<PCIE); // 允许 PCINT0 产生中断 PCMSK |= (1<<PCINT2); //引脚变化使能 -PB.2(第7引脚) // 读取eeprom中的亮度档位数据 pwmVal = eeprom_read(ADDRESS); //读取eeprom里的数据 delay_us(100); //延时10us if (pwmVal<MIN_VAL || pwmVal >=MAX_VAL) //eeprom无数据的话设为最高档位 pwmVal = MAX_VAL; if (pwmVal >= MAX_VAL) fangxiang = 1; //亮度减 else fangxiang = 0; //亮度增 // 睡眠模式初始化 MCUCR|=(1<<SM1)|(0<<SM0); // 掉电模式 MCUCR|=(1<<SE); // 休眠使能 #asm("sei") // 打开全局中断 #asm ("sleep") //睡眠 #asm("NOP"); while(1) { #asm("WDR") //喂狗 if (status == 0) //关机的话睡眠 { TCCR0A = PWM_OFF; //关闭PWM WDT_off(); // 关闭WDT(关闭看门狗) #asm ("sleep") #asm("NOP"); } delay_ms(10); }//while }
|