akira.kei が 2026年11月08日22時58分29秒 に編集
コメント無し
本文の変更
[<前の記事](https://elchika.com/article/b37dd1b0-5604-459a-960d-51e1c1896613/) : 次の記事>
[<前の記事](https://elchika.com/article/b37dd1b0-5604-459a-960d-51e1c1896613/) : [次の記事>](https://elchika.com/article/db79dfde-1976-4ede-9a20-cb8aec79151d/)
--- ## 大型7セグメントLED @[youtube](https://youtu.be/rHOd0E0gljQ) 秋月で買って死蔵していた[大型の7セグメントLED](https://akizukidenshi.com/catalog/g/g108258/)をなんとなく使う気になったので、頑張ってみた。死蔵していたのは12V駆動であることを把握してなかったためだ。また当然ながら遠くからでも見えるような明るさにするには多くの電流を流す必要があり、マイコンの端子だけでは太刀打ちできないからだ。 ## 複数桁表示のために 昔はもうちょっと安かったのかセールだったのか覚えていないが、4桁表示ができるように4つ買ってある。ダイナミック点灯させようと思うと10本の配線を互いに接続しあうことになってちょっと面倒臭いので、スタティック点灯させることにした。1桁分で1つのマイコンを割り当て、4つのマイコンをI2C接続でバス接続することにした。つまり、1つの7セグメントLEDの裏にPICマイコンを接続し、上位のArduinoやRaspberry Pi Pico WなどからGND /VCC/SDA/SCLと12V電源を接続するだけにするわけだ。これなら1桁あたり5〜6本だけ繋げば良いのでだいぶ楽になる。 HWはこんな感じで写真の右がPIC16F1823、右が[TBD62084APG](https://akizukidenshi.com/catalog/g/g112536/)だ。最大定格が20mAなので、10mA程度を流すよう電流制限抵抗を470Ωくらいにしてある。  ## PIC側の処理 PIC 16F1823側の処理は、 1. I2Cで1バイトを受け取って保管し、フラグを立てる 1. メインループでフラグが立ってるか確認したら、保管したバイトを表示ルーチンに 1. 表示ルーチンでは各ビットに対応してON/OFFを出力 1. もし回答要求が来るなら保管している値を返す というこれだけだ。 例によってMCC Classicで各部設定を行ったが、また地雷が埋まっていた。MCCでスレーブアドレスを設定してもちゃんとソースに反映されない、というバグ?があった。結局は```i2c_slave.c```の中身を以下のように指定の値を直接編集した。 ``` #define I2C_SLAVE_ADDRESS 21 ``` 1桁目は21として2桁から4桁目を22から24として、アドレス以外は全く同じファームウェアを書き込む。```main```関数は以下の様になる。 ```cpp: void main(void) { SYSTEM_Initialize(); I2C_Open(); I2C_SlaveSetReadIntHandler(My_I2C_Receive_Callback); I2C_SlaveSetWriteIntHandler(My_I2C_Transmit_Callback); INTERRUPT_GlobalInterruptEnable(); INTERRUPT_PeripheralInterruptEnable(); while (1) { if(flag) { flag=0; display(segments); } } } ``` 表示関数```disp```は脳筋だ。ビット位置に対応してGPIOを上げたり下げたりする。MSBはセグメントPで順にAからGまで合計8ビットのGPIO制御を行う。7セグメントLEDはアノードコモンであり、アノードは常に12V電源に接続し、各ビットを```High```にすればシンクドライバーの出力がGNDに落ちて対応するセグメントが点灯する。 シンクドライバを使わないでマイコン端子をそのまま使う(非推奨)なら```SetHigh()```と```SetLow()```を入れ替える必要があるが、この場合は電流値を1mA程度にする必要がある。 ```cpp: void display(uint8_t seg) { if(seg&0b01000000) A1_SetHigh(); else A1_SetLow(); if(seg&0b00100000) B2_SetHigh(); else B2_SetLow(); if(seg&0b00010000) C3_SetHigh(); else C3_SetLow(); if(seg&0b00001000) D4_SetHigh(); else D4_SetLow(); if(seg&0b00000100) E5_SetHigh(); else E5_SetLow(); if(seg&0b00000010) F6_SetHigh(); else F6_SetLow(); if(seg&0b00000001) G7_SetHigh(); else G7_SetLow(); if(seg&0b10000000) P8_SetHigh(); else P8_SetLow(); } ``` また、I2C通信の割り込みルーチンは以下の様に非常にシンプルになっている。 ```cpp: volatile uint8_t segments = 0; volatile uint8_t flag=0; void My_I2C_Receive_Callback(void) { segments = I2C_Read(); LED_Toggle(); flag=1; } void My_I2C_Transmit_Callback(void) { I2C_Write(segments); } ``` ## 上位側の表示制御 Arduinoを使った場合のスケッチは、例えば以下の様になる。ループ内ではエラー処理は省略してある。 ```Arduino: #include <Wire.h> const int SLAVE_ADDRESS = 21; byte cnt=0; byte segments[]={ 0b1111110, 0b0110000, 0b1101101, 0b1111001, 0b0110011, 0b1011011, 0b1011111, 0b1110010, 0b1111111, 0b1111011, }; void setup() { Wire.begin(); Serial.begin(115200); while (!Serial); Serial.println("I2C Direct Master Started"); } void loop() { Wire.beginTransmission(SLAVE_ADDRESS); Wire.write(segments[cnt]); byte error = Wire.endTransmission(); Serial.print("Write Success: sent "); Serial.println(segments[cnt]); delay(100); byte bytesReceived = Wire.requestFrom(SLAVE_ADDRESS, 1); byte val = Wire.read(); Serial.print("Read Success, received: "); Serial.println(val); cnt++; if(cnt>9) cnt=0; Serial.println(); delay(1000); } ``` ---
[<前の記事](https://elchika.com/article/b37dd1b0-5604-459a-960d-51e1c1896613/) : 次の記事>
[<前の記事](https://elchika.com/article/b37dd1b0-5604-459a-960d-51e1c1896613/) : [次の記事>](https://elchika.com/article/db79dfde-1976-4ede-9a20-cb8aec79151d/)