少し前にLEDを光センサーとして使う方法を投稿した。
この方法はCPUやLEDのスペックに大きく影響される。色々試して見た結果、AVRでは動作したがESP-WROOM02/32ではGPIOの入力インピーダンスが低すぎるのか全く使い物にならなかった。また、AVRでも数百M以上と思われる超高インピーダンス状態での動作となるためノイズを拾いやすく扱いがかなり面倒でもあり気楽に使うのはなかなか難しいものがありそうだ。
ただダイナミックレンジは広いようで感覚的には最大5万ルクス~最低1ルクス未満もいけそうな感じではある。市販の光センサーなどと比べても特に暗い領域での感度が良さそうなので色々応用は出来そうな気がする。なので、なんとかより気軽に使える方法はないものかと考えてみた。
結論としては完成版の光センサー・モジュールにしてしまえばいいのかな?ということで早速試作してみた。コストも基板も消費電流も全て小さいほうがいいに決まってるのでCPUは使い道に困っていたATtiny10だ。(笑)
【光センサー・モジュールのスペック】
・PCBサイズは、10.4 x 13.6mm。(メチャちっちゃい)
・動作電圧範囲は、3.3V~5.5V
・平均消費電流は、5V動作時の実測値で約0.3mA
・約524.3ms毎にセンサー値を出力(UARTモードかパルス幅モードが選択可能)
・LED点灯機能(データ・ラインをショートすると点灯)
・出力モード選択機能(外部リセット有効(RSTDISBL=1)かPB3オープンでUARTモード、それ以外はパルス幅モード)
※UARTモードの速度は115200bps固定、データ形式は4桁16進文字列+CR+LF
※ボーレート誤差は5V動作時で約3%、3.3V動作時で約4%
※完成モジュールの個体差によるバラツキがどれだけあるかは不明
※データ・ラインは内部プルアップされているが受信エラーが発生するようなら外部回路にプルアップ抵抗(10K以下)が必要
※特性の違うLEDに変更することで異なる波長の光検出が可能
※PCBを除く部品代は約100円程度
【回路図】
【完成モジュール】
いつものようにPCBGOGOに発注したら基板がちっちゃいのに値段が同じなのは可哀そうだと思われたのか発注数10枚に対し14枚ほど届いてしまった。最近オマケが多くなってきた気がするが担当者にもよるのかな?とりあえず謝謝しておこう!<(_ _)>
【UARTケーブルに接続】
【TeraTermでデータ受信】
【消費電流】
最初がデータ送信処理の山で、次のでかいのは計測中の山でプログラム的にはタイマーオーバーフロー割り込み待ちでスリープ中のはず。そして山の頂上がキャプチャー完了タイミング。キャプチャー処理はHW任せでプログラム的には何もしてないからなんでこうなるのかが全くわからない。誰か知ってる人いたら教えて!と思ったが、とりあえず自分で調べてみた。どうやらGPIOの入力電圧がONとOFFの中間の不安定な状態では貫通電流というものが流れるらしい。で、その貫通電流はキャプチャー完了つまりGPIOがOFFに切り替わる瞬間がピークになるということだ。これを防ぐには、外部にコンパレータ等を使うしかないけどピーク電流は最大でも1mAちょいくらい。とりあえず壊れることはなさそうだから気にしないでおこう...
ATtiny10では今までLチカぐらいしかやってみたことはなく今回が初めての本格的?なプログラムの開発だ。開発環境は暫く使っていなかったATmelStudio7.0+AVRISPmkⅡ。
PCBのハンダ・ジャンパーにより、UART出力(オープン)、或いは、パルス幅出力(ショート)のどちらかを約524.3ms間隔でデータ出力する仕様だ。
また、せっかくLEDが付いてるのに光らないのは本末転倒?ということでLEDを点灯させることも可能だ。若干、トリッキーな方法ではあるが、データ・ライン1本しかないのでデータ・ラインをオープン・ドレイン出力としデータ・ラインがショートされるとLEDが点灯する仕様としている。ショートするタイミングによってはUARTモードで約650us程度、パルス幅出力モードで最大約524.3msの点灯オン制御の遅れが発生する。
ちなみに、CPUは常にアイドル・スリープ・モードで停止状態とし割り込みのみで処理することで消費電力削減を図っている。同じことをポーリング処理で行なうと消費電力は10倍ほど増えてしまうぞ!
アリエクスプレスで購入した激安パックのLEDを使って、夜のLED照明中の部屋の中でLEDの色別感度を試したところ、
黄>緑>赤>青=白
の順になった。何故か青と白はほとんど反応なし。LED照明だからなのかな?。参考まで。
で、使って見た感じ、照度はわからないけどRohmのBH1750よりも感度良さげな気が...って、まじっすか?(笑)
【修正】
2021-09-20
ファームウェア(main.cpp)の修正。パルスモードの最小幅が数マイクロ秒であるため、計測した時間に1ミリ秒を加算したパルス幅を出力するように変更。UARTモード時のボーレートを38400に変更。
PS.
2021-07-22
Arduino用のデータ受信ライブラリを作ってみたので追記。
【サンプル(Arduino)】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include "ledsensor.h" LEDSensor<HardwareSerial, Serial> ledsensor; uint32_t start; void setup() { Serial.begin(115200); start = millis(); } void loop() { uint16_t result = ledsensor.handle(); if (millis() - start >= 1000) { start = millis(); Serial.println(result); } } |
【ライブラリ(Arduino)】
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 |
/* ledsensor.h - Optical Detect Sensor Library for ATtiny10 LED Sensor Copyright (c) 2021 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 */ #ifndef __LEDSENSOR_H #define __LEDSENSOR_H #include <stdlib.h> #include <stdint.h> #define LEDSENSOR_LPF 4 // % template<typename UART_T, UART_T& UART, uint8_t LPF = LEDSENSOR_LPF> class LEDSensor { private: uint16_t _result; uint8_t _count; char _buffer[8]; public: LEDSensor(void) : _result(0) , _count(0) { } uint16_t handle(void) { int c; while ((c = UART.read()) >= 0) { if (c == '\n') { char *endptr; _buffer[_count] = 0; _count = 0; uint32_t val = strtoul(_buffer, &endptr, 16); if ((endptr == _buffer + 4) && (*endptr == '\r')) { // low pass filter _result += (int32_t)(val - _result) * LPF / 100; } } else if (_count < sizeof(_buffer)) _buffer[_count++] = c; } return _result; } }; #endif |
【ファームウェア(ATtiny10)】
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
/* main.h - LED Sensor Firmware for ATtiny10 Copyright (c) 2021 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 */ #include <stdint.h> #include <avr/sleep.h> #include <avr/interrupt.h> #include "swuart.h" // // mode config // #define BAUDRATE 38400 // bps #define PULSE1MS (1000 / 8) // pulse count of 1ms // // define I/O Port // #define ANODE PB0 #define CATHODE PB1 #define TX PB2 #define MODE PB3 // // Config Byte // #define CONFIG_BYTE ((uint8_t *)0x3F40) #define RSTDISBL 0 #define WDTON 1 #define CKOUT 2 // // Transmit HEX Character // static void transmit(uint8_t b) { SWUART<IOPORT_PIN('B', TX), BAUDRATE>::transmit(b); } static void transmitHex4(uint8_t b) { transmit(b < 10 ? b + '0' : b + 'A' - 10); } static void transmitHex8(uint8_t b) { transmitHex4((b >> 4) & 0x0F); transmitHex4((b >> 0) & 0x0F); } // // Transmit 16Bit Value // static void transmitData(uint16_t c) { transmitHex8(c >> 8); transmitHex8(c >> 0); transmit(0x0D); transmit(0x0A); } // // Timer Compare-A Interrupt // ISR(TIM0_COMPA_vect) { // Input TX Pin DDRB &= ~_BV(TX); // Enable Pin Change Interrupt PCICR = _BV(PCIE0); } // // Timer Compare-B Interrupt // ISR(TIM0_COMPB_vect) { // Clear Timer Interrupt Request Flag TIFR0 = -1; // Enable Timer Compare Interrupt TIMSK0 = _BV(OCIE0A); } // // Timer Overflow Interrupt // ISR(TIM0_OVF_vect) { // UART Mode ? if ((CONFIG_BYTE[0] & _BV(RSTDISBL)) || (PINB & _BV(MODE))) { // Transmit Capture Data transmitData(ICR0); } // PULSE Mode else { register uint16_t count; // Setup Timer GTCCR = _BV(TSM) | _BV(PSR); TCNT0 = 0; TIFR0 = -1; count = ICR0; OCR0A = count + PULSE1MS; OCR0B = 0x8000; TIMSK0 = (count <= 0xFFFF - PULSE1MS ? _BV(OCIE0A) : _BV(OCIE0B)); // Start Timer GTCCR = 0; // Output(Low) TX Pin DDRB |= _BV(TX); // Disable Pin Change Interrupt PCICR = 0; } } // // Pin Change Interrupt // ISR(PCINT0_vect) { // Output(?) CATHODE Pin DDRB |= _BV(CATHODE); if (PINB & _BV(TX)) { // Setup Timer and Enable Interrupt GTCCR = _BV(TSM) | _BV(PSR); TCNT0 = 0; TIFR0 = -1; TIMSK0 = _BV(TOIE0); ICR0 = -1; // Charging to Internal Parasitic Capacitance of LED PORTB |= _BV(CATHODE); // Output(High) CATHODE Pin PORTB &= ~_BV(ANODE); // Output(Low) ANODE Pin // Start Timer GTCCR = 0; // Input CATHODE Pin DDRB &= ~_BV(CATHODE); } else { // LED Light ON PORTB &= ~_BV(CATHODE); // Output(Low) CATHODE Pin PORTB |= _BV(ANODE); // Output(High) ANODE Pin // Disable Timer Interrupt TIMSK0 = 0; } } // // Main Program // int main(void) { // Disable AC ACSR = _BV(ACD); // Disable ADC PRR = _BV(PRADC); // Start Timer (Normal Mode, 1/8 clock) TCCR0B = _BV(CS01); // Output(Low) CATHODE Pin DDRB |= _BV(CATHODE); // Output(Low) ANODE Pin DDRB |= _BV(ANODE); // Pullup MODE Pin PUEB |= _BV(MODE); // Pullup TX Pin PUEB |= _BV(TX); // Enable Pin Change Interrupt PCICR = _BV(PCIE0); PCMSK = _BV(TX); // Force Pin Change Interrupt Request DDRB |= _BV(TX); DDRB &= ~_BV(TX); // Enable Sleep Mode sleep_enable(); // Enable Interrupts sei(); // Enter Sleep Mode (IDLE) while (1) sleep_cpu(); } |
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 171 172 173 174 175 176 |
/* swuart.h - Software UART Transmitter (open drain mode) for AVR Copyright (c) 2021 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 */ #ifndef __SWUART_H #define __SWUART_H #include "ioport.h" #define SWUART_DELAY_CYCLE 3 // cycle of delay loop #define SWUART_SHIFT_CYCLE 3 // cycle of bit assembly #define SWUART_CLOCK_CYCLE(b) ((float)F_CPU / (b)) #define SWUART_SWCLK_CYCLE(b) (int)(SWUART_CLOCK_CYCLE(b) + 0.5) #define SWUART_DELAY(b, n, m) \ do \ { \ int cycle = 0; \ float error = SWUART_CLOCK_CYCLE(b) - SWUART_SWCLK_CYCLE(b); \ float round = (error < 0 ? -0.5 : 0.5); \ error -= (int)error; \ if (((int)(error * (n) + round) ^ (int)(error * ((n) + 1) + round)) & 1) \ cycle += (error < 0 ? -1 : 1); \ if ((cycle += SWUART_SWCLK_CYCLE(b) - SWUART_SHIFT_CYCLE + (m)) > 0) \ { \ if (cycle / SWUART_DELAY_CYCLE) \ __asm__ volatile \ ( \ "ldi r27, %[_COUNT_]" "\n\t" \ "1: dec r27" "\n\t" \ "brne 1b" "\n\t" \ : \ : [_COUNT_] "M" (cycle / SWUART_DELAY_CYCLE) \ : "r27" \ ); \ if ((cycle % SWUART_DELAY_CYCLE) > 0) \ __asm__ volatile ("nop"); \ if ((cycle % SWUART_DELAY_CYCLE) > 1) \ __asm__ volatile ("nop"); \ } \ } \ while (0) // // Transmit Byte // // assume that PUE(IOPORT_PINPOS(TXPIN))=1, DDR(IOPORT_PINPOS(TXPIN))=0, PORT(IOPORT_PINPOS(TXPIN))=0 // template<uint8_t TXPIN, uint32_t BAUDRATE = 115200> class SWUART { public: static void transmit(uint8_t data) { if (SWUART_SWCLK_CYCLE(BAUDRATE) >= 256 * SWUART_DELAY_CYCLE + SWUART_SHIFT_CYCLE - 1) return; __asm__ volatile ( "mov r24, %[_DATA_]" "\n\t" "mov r25, r24" "\n\t" "lsl r25" "\n\t" "eor r24, r25" "\n\t" "ldi r26, 1 << %[_POS_]" "\n\t" "in r23, __SREG__" "\n\t" "cli" "\n\t" "in r25, %[_DDR_]" "\n\t" "or r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_ ] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) , [_POS_ ] "I" (IOPORT_PINPOS(TXPIN)) , [_DATA_] "r" (data) : "r23", "r25", "r26" ); SWUART_DELAY(BAUDRATE, 0, 0); __asm__ volatile ( "sbrc r24, 0" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 1, 0); __asm__ volatile ( "sbrc r24, 1" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 2, 0); __asm__ volatile ( "sbrc r24, 2" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 3, 0); __asm__ volatile ( "sbrc r24, 3" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 4, 0); __asm__ volatile ( "sbrc r24, 4" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 5, 0); __asm__ volatile ( "sbrc r24, 5" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 6, 0); __asm__ volatile ( "sbrc r24, 6" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 7, 0); __asm__ volatile ( "sbrc r24, 7" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 8, 0); __asm__ volatile ( "or r25, r26" "\n\t" "eor r25, r26" "\n\t" "out %[_DDR_], r25" "\n\t" "out __SREG__, r23" "\n\t" : : [_DDR_] "I" (_SFR_IO_ADDR(IOPORT_DIRREG(TXPIN))) ); SWUART_DELAY(BAUDRATE, 9, 0); } }; #endif |
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
/* ioport.h - I/O Port Library for AVR Series Copyright (c) 2020 Sasapea's Lab. All right reserved. [i/o pin number mapping] PA0 - PA7 ... 0 - 7 PB0 - PB7 ... 8 - 15 PC0 - PC7 ... 16 - 23 PD0 - PD7 ... 24 - 31 PE0 - PE7 ... 32 - 39 PF0 - PF7 ... 40 - 47 PG0 - PG7 ... 48 - 55 PH0 - PH7 ... 56 - 63 PI0 - PI7 ... 64 - 71 PJ0 - PJ7 ... 72 - 79 PK0 - PK7 ... 80 - 87 PL0 - PL7 ... 88 - 95 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 */ #ifndef __IOPORT_H #define __IOPORT_H #ifdef ARDUINO #include "Arduino.h" #else #include <stdint.h> #include <avr/io.h> #define INPUT 0 #define OUTPUT 1 #define INPUT_PULLUP 2 #define LOW 0 #define HIGH 1 #endif #define IOPORT_PID(port) ((port) - 'A') #define IOPORT_PIN(port, pos) ((IOPORT_PID(port) << 3) | (pos & 7)) #define IOPORT_PORTID(pin) ((pin) >> 3) #define IOPORT_PINPOS(pin) ((pin) & 7) #define IOPORT_PINVAL(pin) (1 << IOPORT_PINPOS(pin)) #define IOPORT_UNDEFINED _SFR_MEM8(0xFFFF) #ifdef PINA #define IOPORT_PINA PINA #else #define IOPORT_PINA IOPORT_UNDEFINED #endif #ifdef DDRA #define IOPORT_DDRA DDRA #else #define IOPORT_DDRA IOPORT_UNDEFINED #endif #ifdef PORTA #define IOPORT_PORTA PORTA #else #define IOPORT_PORTA IOPORT_UNDEFINED #endif #ifdef PINB #define IOPORT_PINB PINB #else #define IOPORT_PINB IOPORT_UNDEFINED #endif #ifdef DDRB #define IOPORT_DDRB DDRB #else #define IOPORT_DDRB IOPORT_UNDEFINED #endif #ifdef PORTB #define IOPORT_PORTB PORTB #else #define IOPORT_PORTB IOPORT_UNDEFINED #endif #ifdef PINC #define IOPORT_PINC PINC #else #define IOPORT_PINC IOPORT_UNDEFINED #endif #ifdef DDRC #define IOPORT_DDRC DDRC #else #define IOPORT_DDRC IOPORT_UNDEFINED #endif #ifdef PORTC #define IOPORT_PORTC PORTC #else #define IOPORT_PORTC IOPORT_UNDEFINED #endif #ifdef PIND #define IOPORT_PIND PIND #else #define IOPORT_PIND IOPORT_UNDEFINED #endif #ifdef DDRD #define IOPORT_DDRD DDRD #else #define IOPORT_DDRD IOPORT_UNDEFINED #endif #ifdef PORTD #define IOPORT_PORTD PORTD #else #define IOPORT_PORTD IOPORT_UNDEFINED #endif #ifdef PINE #define IOPORT_PINE PINE #else #define IOPORT_PINE IOPORT_UNDEFINED #endif #ifdef DDRE #define IOPORT_DDRE DDRE #else #define IOPORT_DDRE IOPORT_UNDEFINED #endif #ifdef PORTE #define IOPORT_PORTE PORTE #else #define IOPORT_PORTE IOPORT_UNDEFINED #endif #ifdef PINF #define IOPORT_PINF PINF #else #define IOPORT_PINF IOPORT_UNDEFINED #endif #ifdef DDRF #define IOPORT_DDRF DDRF #else #define IOPORT_DDRF IOPORT_UNDEFINED #endif #ifdef PORTF #define IOPORT_PORTF PORTF #else #define IOPORT_PORTF IOPORT_UNDEFINED #endif #ifdef PING #define IOPORT_PING PING #else #define IOPORT_PING IOPORT_UNDEFINED #endif #ifdef DDRG #define IOPORT_DDRG DDRG #else #define IOPORT_DDRG IOPORT_UNDEFINED #endif #ifdef PORTG #define IOPORT_PORTG PORTG #else #define IOPORT_PORTG IOPORT_UNDEFINED #endif #ifdef PINH #define IOPORT_PINH PINH #else #define IOPORT_PINH IOPORT_UNDEFINED #endif #ifdef DDRH #define IOPORT_DDRH DDRH #else #define IOPORT_DDRH IOPORT_UNDEFINED #endif #ifdef PORTH #define IOPORT_PORTH PORTH #else #define IOPORT_PORTH IOPORT_UNDEFINED #endif #ifdef PINI #define IOPORT_PINI PINI #else #define IOPORT_PINI IOPORT_UNDEFINED #endif #ifdef DDRI #define IOPORT_DDRI DDRI #else #define IOPORT_DDRI IOPORT_UNDEFINED #endif #ifdef PORTI #define IOPORT_PORTI PORTI #else #define IOPORT_PORTI IOPORT_UNDEFINED #endif #ifdef PINJ #define IOPORT_PINJ PINJ #else #define IOPORT_PINJ IOPORT_UNDEFINED #endif #ifdef DDRJ #define IOPORT_DDRJ DDRJ #else #define IOPORT_DDRJ IOPORT_UNDEFINED #endif #ifdef PORTJ #define IOPORT_PORTJ PORTJ #else #define IOPORT_PORTJ IOPORT_UNDEFINED #endif #ifdef PINK #define IOPORT_PINK PINK #else #define IOPORT_PINK IOPORT_UNDEFINED #endif #ifdef DDRK #define IOPORT_DDRK DDRK #else #define IOPORT_DDRK IOPORT_UNDEFINED #endif #ifdef PORTK #define IOPORT_PORTK PORTK #else #define IOPORT_PORTK IOPORT_UNDEFINED #endif #ifdef PINL #define IOPORT_PINL PINL #else #define IOPORT_PINL IOPORT_UNDEFINED #endif #ifdef DDRL #define IOPORT_DDRL DDRL #else #define IOPORT_DDRL IOPORT_UNDEFINED #endif #ifdef PORTL #define IOPORT_PORTL PORTL #else #define IOPORT_PORTL IOPORT_UNDEFINED #endif #define IOPORT_DIRREG(pin) \ ((pin) < (8 * 1) ? IOPORT_DDRA \ : ((pin) < (8 * 2) ? IOPORT_DDRB \ : ((pin) < (8 * 3) ? IOPORT_DDRC \ : ((pin) < (8 * 4) ? IOPORT_DDRD \ : ((pin) < (8 * 5) ? IOPORT_DDRE \ : ((pin) < (8 * 6) ? IOPORT_DDRF \ : ((pin) < (8 * 7) ? IOPORT_DDRG \ : ((pin) < (8 * 8) ? IOPORT_DDRH \ : ((pin) < (8 * 9) ? IOPORT_DDRI \ : ((pin) < (8 * 10) ? IOPORT_DDRJ \ : ((pin) < (8 * 11) ? IOPORT_DDRK \ : ((pin) < (8 * 12) ? IOPORT_DDRL \ : IOPORT_UNDEFINED)))))))))))) #define IOPORT_OUTREG(pin) \ ((pin) < (8 * 1) ? IOPORT_PORTA \ : ((pin) < (8 * 2) ? IOPORT_PORTB \ : ((pin) < (8 * 3) ? IOPORT_PORTC \ : ((pin) < (8 * 4) ? IOPORT_PORTD \ : ((pin) < (8 * 5) ? IOPORT_PORTE \ : ((pin) < (8 * 6) ? IOPORT_PORTF \ : ((pin) < (8 * 7) ? IOPORT_PORTG \ : ((pin) < (8 * 8) ? IOPORT_PORTH \ : ((pin) < (8 * 9) ? IOPORT_PORTI \ : ((pin) < (8 * 10) ? IOPORT_PORTJ \ : ((pin) < (8 * 11) ? IOPORT_PORTK \ : ((pin) < (8 * 12) ? IOPORT_PORTL \ : IOPORT_UNDEFINED)))))))))))) #define IOPORT_TGLREG(pin) IOPORT_INREG(pin) #define IOPORT_INREG(pin) \ ((pin) < (8 * 1) ? IOPORT_PINA \ : ((pin) < (8 * 2) ? IOPORT_PINB \ : ((pin) < (8 * 3) ? IOPORT_PINC \ : ((pin) < (8 * 4) ? IOPORT_PIND \ : ((pin) < (8 * 5) ? IOPORT_PINE \ : ((pin) < (8 * 6) ? IOPORT_PINF \ : ((pin) < (8 * 7) ? IOPORT_PING \ : ((pin) < (8 * 8) ? IOPORT_PINH \ : ((pin) < (8 * 9) ? IOPORT_PINI \ : ((pin) < (8 * 10) ? IOPORT_PINJ \ : ((pin) < (8 * 11) ? IOPORT_PINK \ : ((pin) < (8 * 12) ? IOPORT_PINL \ : IOPORT_UNDEFINED)))))))))))) class IOPORT { public: static void pinMode(uint8_t pin, uint8_t mode) { if (mode == OUTPUT) IOPORT_DIRREG(pin) |= IOPORT_PINVAL(pin); else { IOPORT_DIRREG(pin) &= ~IOPORT_PINVAL(pin); digitalWrite(pin, mode == INPUT_PULLUP); } } static void digitalWrite(uint8_t pin, uint8_t val) { if (val) IOPORT_OUTREG(pin) |= IOPORT_PINVAL(pin); else IOPORT_OUTREG(pin) &= ~IOPORT_PINVAL(pin); } static void toggleOutput(uint8_t pin) { IOPORT_TGLREG(pin) |= IOPORT_PINVAL(pin); } static uint8_t digitalRead(uint8_t pin) { return IOPORT_INREG(pin) & IOPORT_PINVAL(pin) ? HIGH : LOW; } }; #endif |