用意するもの
・Arduino UNO 2個
・抵抗(4.7kΩ) 2個 I2C用
・抵抗 (330Ω) 8個
・抵抗(10kΩ) 12個
・トランジスター2SC1815Y 12個
・ダイナミック接続4桁高輝度青色7セグメントLED表示器 アノードコモン 1個
というわけで、I2CでスレーブArduinoに7セグ4桁LEDをなんやかんやさせることをします。手持ちだったからというだけで、アノードコモンである必要は特に無いです。
配線
Arduino側は全てデジタルピンの番号で、OSL40562-IBはLED表示器の型番です。A~Gのどの箇所を光らせるかによって数字が決まり、12,9,8,6ピンによって表示させる桁を選びます。
| Arduino Pin | OSL40562-IB |
|---|---|
| 1 | 12 (digit1) |
| 2 | 9 (digit2) |
| 3 | 8 (digit3) |
| 4 | 6 (digit4) |
| 5 | 11 (A) |
| 6 | 7 (B) |
| 7 | 4 (C) |
| 8 | 2 (D) |
| 9 | 1 (E) |
| 10 | 10 (F) |
| 11 | 5 (G) |
| 12 | 3 (DP) |
直接Arduinoに繋げると、定格をオーバーしてしまうかもしれないのでトランジスタで全部スイッチングするようにしました。
あと、下の図では4つのアノードピンはArduinoの5Vに繋げていますが、実際Arduinoの5Vを使ったのはI2Cの部分だけで、LEDの方は別の5V電源を使いました。(図を作るときに別に繋げるのがめんどくさかった…)
ごちゃごちゃしてて見づらい!
すみません…
今回は一体何が主役なのかわからなくなってしまいました。
スケッチ
ダイナミック点灯をする際に参考にさせていただいたサイト→ 4桁7セグメントLEDで始めるArduino
例として使われているのはカソードコモンで、今回私が使ったアノードコモンとは逆なのですが、点灯方法は何も変わりません。なのでスケッチもほぼ同じなのですが、最後の割り込みベクタがうまく行かなかったので「MsTimer2」を使って割り込みをしました。
MsTimer2についてはリファレンスを→MsTimer2(ミリ秒単位で指定するタイマ)
ダウンロードはこちらからさせてもらいます。
参考サイトの「4桁の数字を表示する(カウンタ)」(最後から2番目のスケッチ)では、サイト内でも言われているようにdisplay_numbers()を1回しか呼べないため、loop()内でdelay()などを入れて表示する数字を変えていくと毎回一瞬で消えてしまいます。
(display_numbers()内の処理をwhile(1)などで無限ループさせても抜け出せない)
そこでloop()内では、表示する数字を入れておく変数を変えるだけにし、定期的に割り込みでdisplay_numbers()を呼ぶようにしました。
I2Cについては前回同様「Wire.h」を使っていきます。
「スレーブ側」↓
#include <Wire.h>
#include <MsTimer2.h>
const int anode_pin[] = {1,2,3,4}; // digit number
const int cathode_pin[] = {5,6,7,8,9,10,11}; // A,B,C,D,E,F,G
#define DP 12
int numbers_to_display = 0; //LEDに表示する数字を保持
const int number[] =
{
// GFEDCBA
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00100111, // 7
0b01111111, // 8
0b01101111, // 9
0b00000000 // clear segment
};
const int digit[] =
{
0b1000, // 1
0b0100, // 2
0b0010, // 3
0b0001, // 4
};
void setup()
{
Wire.begin(8); // スレーブのアドレスを8に設定
Wire.onReceive(set_numbers);
for(int i=1; i<13; i++)
{
pinMode(i, OUTPUT);
}
//display_numbers関数は少なくとも4[ms]かかるので5[ms]ごと呼ぶ
MsTimer2::set(5,display_numbers);
MsTimer2::start();
}
void loop()
{
delay(100);
}
// anodeのピンをひとつだけHIGHにする
void digit_number (int n)
{
for(int i=0; i<4; i++)
{
digitalWrite(anode_pin[i], digit[n] & (1 << i) ? HIGH:LOW);
}
}
// 0~9の数字を表示する
void display_number (int n)
{
for(int i=0; i<7; i++)
{
digitalWrite(cathode_pin[i], number[n] & (1 << i) ? HIGH:LOW);
}
}
// numbers_to_display へ数字を入れる
// Wire.onReceive()で呼ばれる
void set_numbers(int numBytes)
{
int n = 0;
while(Wire.available())
{
n = Wire.read();
}
numbers_to_display = n;
}
// numbers_to_display の数字を表示する
void display_numbers ()
{
int num = numbers_to_display;
for(int i=0; i<4; i++)
{
digit_number(i); //表示する桁数を選択
display_number(num % 10); //numの1の位の数
num = num/10; //次の桁へ(切り捨て)
display_number(10); // clear segment 4桁目を消すため
delay(1);
}
}今回はスレーブデバイスでマスターからデータが送られてきたときに呼ばれる関数を指定する Wire.onReceive() を使います。あと、余談ですがここによると、Arduinoのスケッチを書く時は関数プロトタイプ宣言をしなくてもいいそうなのです。(ビルドするときに勝手につくってくれるらしい)
「マスター側」↓
#include <Wire.h>
void setup()
{
Wire.begin(); // マスターに設定
}
void loop()
{
for(int i=0; i<256; i++)
{
write_numbers(i);
delay(500);
}
}
void write_numbers(int numbers)
{
Wire.beginTransmission(8);
Wire.write(numbers);
Wire.endTransmission();
}スレーブがマスターにデータを送った時との違いは、Wire.write() するときWire.beginTransmission();
Wire.endTransmission();
で送信するスレーブのアドレスを指定するという点です。
結果
上記の条件で動かしている様子です。 write_numbers() でただ数字だけを入れると1バイトしか送信できないので0~255までしか表示できていません… orz
ですが、一応ちゃんと通信できているようなのでよかったです。SDAとSCLのたった2本の線だけで12本もピンがあるものを制御できてるのはなんだかすごいなあと。
今回は7セグ4桁LEDのダイナミック点灯をライブラリ無しでやってみたかったということもあり、I2Cでマスターからスレーブへ値を送る実験と一緒にしてしまいました。
0~9999までの値を送るのはスケッチを書き換えてまた挑戦したいです。
ATMEGA328Pにブートローダ書き込んでArduinoとして使えるようにしたやつを使って、I2Cで繋ぐだけで表示を制御できるやつも作りたいです。
Arduino同士でI2c通信(1)←前回
参考サイト
2台のarduinoをI2Cバスで接続して通信を行う実験
※2014/07/07 追記
0~9999までの数を送ることができたのでまとめました
→ Arduinoで4桁7セグメントLED
※2015/04/16 追記
I2C通信するシリーズに記事を追加しました
→【Ruby】 Raspberry Pi とArduinoでI2C通信

0 件のコメント:
コメントを投稿