以前にATtiny10で光センサーを作ってみたが、
デジタル入力回路で貫通電流が流れてしまうのが気になっていた。(壊れはしないけど...)
アナログコンパレータで解決できるがATtiny10は基準電圧が内臓されておらず外付け回路が必要となってしまうので基準電圧内臓かつさらなる省電力化を行えそうなものを調べてみると秋月電子でATtiny10の次に安いATtiny202(AVR0)が目に留まった。
ATtiny10と比較するとピン数/メモリ容量/周辺機能/クロックの全てがスペックアップしていながら20円しか違わない。この価格帯では最強のCPUと言えそうだ。ちなみにデータシートを見る限りAVR0/1シリーズであればピン割り当てが違うだけで同様に使えるようだ。
【注文したATtiny202が届くのを待つ間にATtiny1614で試験してみた】
基準電圧の低さとか入力インピーダンスの高さが影響してるのか暗いほうの感度が以前の物よりかなり良い感じだ。
今回の電流測定のためにNordic Power Profiler Kit 2 (PPK2)を初めて使ってみたがデザインも洗練されていて使いやすくて超お勧めだ。
欲を言えばnsオーダーで計測したいときもあるけど普段使いとしては必要十分。nRF52840でこんなの作れるなんて開発者のセンスが眩しすぎるぞ!(-_-;)
【全体的な電流値】
ATtiny1614での測定結果。ATtiny202でも同様な結果となるがピーク電流が460uAと結構低かった。単なる個体差かな?
【スタンバイスリープのための一時的なクロックアップ時の電流変化】
測定結果から851.83uA x 258us / 82uA = 2680us以上待つときはビジーループよりスタンバイスリープのほうが消費電流が少ないという結論になる。
【回路】
シンプルかつ点灯も可能な回路になっているが今のところファームウェアの点灯対応はなし。LEDは普通の安物でOKだが色などにより感度がだいぶ違うようなので色々試してみたほうが良い。
今回の回路は、アナログコンパレータの出力がそのままGPIOへ出力されるようになっている。パルスのON時間(最低1ms)をアプリ側で計測することで明るさが判断でき、明るい時は短いパルス、暗い時は長いパルスとなる。
省電力化のためCPUは内臓の32KHzRCクロックで駆動しスタンバイ・スリープでタイミング待ちするようにしてみたところ平均電流を65uA程度まで落とすことができた。
【修正履歴】
2023-12-28
改良版を作ってみたので興味のある方はどうぞ!
AVR0/1シリーズで省電力な光センサーを作ってみた。(改良版)
ACのVREFを0.55Vから1.1Vに変更。ACのVREFが0.55Vでは不安定っぽくなるようだ。LEDもダイオードなのでなんとなくわかる気も...
【ファームウェア(Microchip Studio)】
ファームウェアはかなりトリッキーな作りになっている。スタンバイ・スリープで動作可能な周辺機能(AC/RTC/GPIO)だけを使っているがACはスタンバイ・スリープ中の割込みに対応していないためAC出力ピンのGPIO割込み機能を利用して復帰させていること、32KHzクロック駆動時にRTCの同期がとれないため一時的に20MHz駆動に切り替えていること、選択したACのAINPn入力ピンのデジタル出力が禁止されないことなどに依存している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
/* main.cpp - Low Power LED Optical Sensor Firmware for Microchip AVR0/1 Series Copyright (c) 2023 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //#define F_CPU 20000000 // 20MHz (OSC20M) //#define F_CPU 1250000 // 1.25MHz (OSC20M) #define F_CPU 32768 // 32786KHz (OSCULP32K) #include <avr/io.h> #include <avr/cpufunc.h> #include <avr/interrupt.h> #include <avr/sleep.h> #include <util/delay.h> #include <string.h> #define AC_OUT_MIN_WIDTH 1000 // us #define AC_OUT_INTERVAL 1000000 // us #if defined(__AVR_ATtiny202__) #define PIN_LED_CATHODE PIN7_bm // PA7 #define PIN_LED_ANODE PIN6_bm // PA6 #define PIN_AC_OUT PIN3_bm // PA3 #define PIN_AC_OUT_CTRL PORTA.PIN3CTRL #elif defined(__AVR_ATtiny1614__) #define PIN_LED_CATHODE PIN7_bm // PA7 // #define PIN_LED_ANODE PIN6_bm // PA6 #define PIN_LED_ANODE PIN4_bm // PA4 (for test board) #define PIN_AC_OUT PIN5_bm // PA5 #define PIN_AC_OUT_CTRL PORTA.PIN5CTRL #endif #define SLEEP(t) \ if ((F_CPU == 32768) && ((t) < 3000)) \ _delay_us(t); \ else \ sleep((t) * 32768.0 / 1000000 + 0.5); #if !defined(AC_LPMODE_bm) #define AC_LPMODE_bm 0 #endif ISR(RTC_CNT_vect) { RTC.INTFLAGS = RTC_CMP_bm; } ISR(PORTA_PORT_vect) { PORTA.INTFLAGS = PIN_AC_OUT; } void sleep(uint16_t count) { #if F_CPU == 32768 _PROTECTED_WRITE(CLKCTRL_MCLKCTRLA, CLKCTRL_CLKSEL_OSC20M_gc); #endif cli(); ////////////////////////////////////////////////////////////////// // If the CPU clock is less than or equal to the RTC clock x 4, // // it cannot be synchronized forever. // ////////////////////////////////////////////////////////////////// while (RTC.STATUS) // continue; // ////////////////////////////////////////////////////////////////// RTC.CMP = RTC.CNT + count; RTC.INTCTRL = RTC_CMP_bm; sei(); sleep_cpu(); RTC.INTCTRL = 0; #if F_CPU == 32768 _PROTECTED_WRITE(CLKCTRL_MCLKCTRLA, CLKCTRL_CLKSEL_OSCULP32K_gc); #endif } void clock_select(void) { #if F_CPU == 32768 _PROTECTED_WRITE(CLKCTRL_MCLKCTRLB, 0); _PROTECTED_WRITE(CLKCTRL_MCLKCTRLA, CLKCTRL_CLKSEL_OSCULP32K_gc); #elif F_CPU == 1250000 _PROTECTED_WRITE(CLKCTRL_MCLKCTRLB, CLKCTRL_PDIV_16X_gc | CLKCTRL_PEN_bm); #elif F_CPU == 20000000 _PROTECTED_WRITE(CLKCTRL_MCLKCTRLB, 0); #endif } void disable_inputs(void) { #if defined(PORTA) memset((void *)&PORTA.PIN0CTRL, PORT_ISC_INPUT_DISABLE_gc, 8); #endif #if defined(PORTB) memset((void *)&PORTB.PIN0CTRL, PORT_ISC_INPUT_DISABLE_gc, 8); #endif #if defined(PORTC) memset((void *)&PORTC.PIN0CTRL, PORT_ISC_INPUT_DISABLE_gc, 8); #endif #if defined(PORTD) memset((void *)&PORTD.PIN0CTRL, PORT_ISC_INPUT_DISABLE_gc, 8); #endif } int main(void) { // Disable all GPIO inputs disable_inputs(); // PORT Configuration PORTA.OUTSET = PIN_LED_CATHODE; PORTA.DIRSET = PIN_LED_ANODE; // VREF Configuration VREF.CTRLA = VREF_DAC0REFSEL_1V1_gc; // AC0 Configuration AC0.MUXCTRLA = AC_MUXPOS_PIN0_gc | AC_MUXNEG_VREF_gc; AC0.CTRLA = AC_RUNSTDBY_bm | AC_OUTEN_bm | AC_INTMODE_NEGEDGE_gc | AC_LPMODE_bm | AC_HYSMODE_10mV_gc | AC_ENABLE_bm; // RTC Configuration while (RTC.STATUS) continue; RTC.CLKSEL = RTC_CLKSEL_INT32K_gc; RTC.CTRLA = RTC_RUNSTDBY_bm | RTC_PRESCALER_DIV1_gc | RTC_RTCEN_bm; // Clock Select clock_select(); // Sleep Mode Configuration set_sleep_mode(SLEEP_MODE_STANDBY); sleep_enable(); // Enable Interrupt sei(); // Main Loop while (1) { // Interval Sleep SLEEP(AC_OUT_INTERVAL); // Charge to LED Parasitic Capacitance PORTA.DIRSET = PIN_LED_CATHODE; // Minimum Width Sleep SLEEP(AC_OUT_MIN_WIDTH); // AC_OUT Pin Interrupt Enable PIN_AC_OUT_CTRL = PORT_ISC_BOTHEDGES_gc; // Discharge from LED Parasitic Capacitance PORTA.DIRCLR = PIN_LED_CATHODE; // Sleep sleep_cpu(); // AC_OUT Pin Interrupt Disable PIN_AC_OUT_CTRL = PORT_ISC_INTDISABLE_gc; } } |