手っ取り早くマトリックスLEDを使う その2 [Arduino]
前回のスケッチだと、マトリックスLEDに何か表示することが目的となっており他の処理がしづらい状態でした。
表示自体は割り込みを使って、CPUを解放してみました。
交互に顔が変わります。
loop() 関数内は、Lチカみたいな感じかな?
表示自体は割り込みを使って、CPUを解放してみました。
// Arduino Pro Miniで手っ取り早くマトリックスLEDを使うスケッチ (Arduino Pro Mini専用)
// (SZ420788K: 8x8, 0.8inch, カソードコモン)
// timer2使用
const uint8_t MATRIX_ROW[] = { 2, 7, 17, 5, 10, 16, 11, 14 }; // 行 → デジタルピン番号
const uint8_t MATRIX_COL[] = { 9, 8, 4, 15, 3, 13, 12, 6 }; // 列 → デジタルピン番号 (左からMSB -> LSB)
uint8_t *MATRIX_IMAGE; // イメージのポインタ
uint8_t face_smile[] = { // にこにこ
B00111100,
B01000010,
B10100101,
B10000001,
B10100101,
B10011001,
B01000010,
B00111100 };
uint8_t face_angry[] = { // ぷんぷん
B00111100,
B01000010,
B10100101,
B10000001,
B10011001,
B10100101,
B01000010,
B00111100 };
void setup() {
for( int i = 2; i <= 17; i++ ) pinMode( i, OUTPUT );
TCCR2A = B00000000; // Timer2 (8bit) のオーバーフローで割り込む。ちらつきがない分周比は
TCCR2B = B00000010; // dot毎なら010(8分周まで),line毎なら101(128分周まで)くらい。
TIMSK2 = B00000001; // TOIE2 (オーバーフロー割り込み許可)
}
void loop() {
MATRIX_IMAGE = face_smile;
delay(1000);
MATRIX_IMAGE = face_angry;
delay(1000);
}
ISR( TIMER2_OVF_vect ) {
static uint8_t row, col;
digitalWrite( MATRIX_COL[ col ], LOW );
if( ++col == 8 ) {
col = 0;
digitalWrite( MATRIX_ROW[ row ], HIGH );
row = ( row + 1 ) % 8;
digitalWrite( MATRIX_ROW[ row ], LOW );
}
digitalWrite( MATRIX_COL[ col ], ( MATRIX_IMAGE[ row ] >> col ) & 1 );
}
交互に顔が変わります。
loop() 関数内は、Lチカみたいな感じかな?
手っ取り早くマトリックスLEDを使う [Arduino]
マトリックスLEDを使いたくても、16本の配線がネックになります。
手っ取り早くマトリックスLEDをつかう方法を思いついたのでやってみました。
ArduinonPro Mini (およびその互換機) を使う方法です。
単色でピンの列の間が600milのものだとぴったりはまります。
今回は以前に aitendo という通販のお店で購入した、SZ420788K というものを使いました。
aitendo
http://www.aitendo.com/
こんな感じでソケットをつけます。
配線は一切なく、ピンソケットとシリアル&電源のピンをはんだ付けするだけです。
ソケットは普通のピンソケットを使うとゆるゆるなので、丸ピンICソケットを使用しました。
丸ピンICソケット (シングル40P): パーツ一般 秋月電子通商 電子部品 ネット通販
http://akizukidenshi.com/catalog/g/gP-01591/
今回使用したマトリックスLED (SZ420788K) のデータです。
だいたいどのマトリックスLEDも同様のピンアサインのようです。
またピンの順番も通常のICと同様の割り振り方です。
とりあえず表示するということでスケッチをかいてみました。
データシート上は左から列を数えていますが、左を上位ビットにするとそのまま2進表記と一致するので逆としました。
ドットごとの書き換えだと、64回に1回の点灯になるので暗くなりますが、各ドットは明るさ均等のはず。
それに対して、行ごとの書き換えだと、8回に1回の点灯になるので明るくなりますが、おそらく1行全灯した場合にはLOW に流れる電流が多くなるものの引き込める電流に限界があるだろうということで輝度が不均一になることが考えられたり、電流制限抵抗もなにもはさんでいないので点灯時間は短くしたほうがいいということも考えれるのかと思っています。
行ごとで書き換えだけにして表示しつづけた場合ボードが熱くなりました。
アノードコモンのマトリックスLEDを使う場合には、digitalWrite() の出力をすべて逆にすればOK。
手っ取り早くマトリックスLEDをつかう方法を思いついたのでやってみました。
ArduinonPro Mini (およびその互換機) を使う方法です。
単色でピンの列の間が600milのものだとぴったりはまります。
今回は以前に aitendo という通販のお店で購入した、SZ420788K というものを使いました。
aitendo
http://www.aitendo.com/
こんな感じでソケットをつけます。
配線は一切なく、ピンソケットとシリアル&電源のピンをはんだ付けするだけです。
ソケットは普通のピンソケットを使うとゆるゆるなので、丸ピンICソケットを使用しました。
丸ピンICソケット (シングル40P): パーツ一般 秋月電子通商 電子部品 ネット通販
http://akizukidenshi.com/catalog/g/gP-01591/
今回使用したマトリックスLED (SZ420788K) のデータです。
だいたいどのマトリックスLEDも同様のピンアサインのようです。
またピンの順番も通常のICと同様の割り振り方です。
とりあえず表示するということでスケッチをかいてみました。
// Arduino Pro Miniで手っ取り早くマトリックスLEDを使うスケッチ (Arduino Pro Mini専用)
// (SZ420788K: 8x8, 0.8inch, カソードコモン)
const uint8_t MATRIX_ROW[] = { 2, 7, 17, 5, 10, 16, 11, 14 }; // 行 → デジタルピン番号
const uint8_t MATRIX_COL[] = { 9, 8, 4, 15, 3, 13, 12, 6 }; // 列 → デジタルピン番号 (左からMSB -> LSB)
uint8_t smile[] = { // にこにこ
B00111100,
B01000010,
B10100101,
B10000001,
B10100101,
B10011001,
B01000010,
B00111100 };
void setup() {
for( int i = 2; i <= 17; i++ ) pinMode( i, OUTPUT ); // デジタル2~17まで出力ピン設定
}
void loop() {
unsigned int i;
// ドットごと、行ごとでの表示で明るさの比較
for( i = 0; i < 64000; i++ ) {
matrix_update_dot( smile ); // ドットごとの書き換え
}
for( i = 0; i < 1000; i++ ) {
matrix_update_line( smile ); // 行ごとの書き換え
delay(1); // delay(1) を入れることで明るくなる
}
}
void matrix_update_dot( uint8_t *d ) { // 1ドットずつ更新(暗いが明るさ均一)
static uint8_t row, col;
digitalWrite( MATRIX_COL[ col ], LOW );
if( ++col == 8 ) {
col = 0;
digitalWrite( MATRIX_ROW[ row ], HIGH );
row = ( row + 1 ) % 8;
digitalWrite( MATRIX_ROW[ row ], LOW );
}
digitalWrite( MATRIX_COL[ col ], ( *( d + row ) >> col ) & 1 );
}
void matrix_update_line( uint8_t *d ) { // 1行ずつ更新(明るいが明るさ不均一)
static uint8_t row, col;
digitalWrite( MATRIX_ROW[ row ], HIGH );
row = ( row + 1 ) % 8;
for( col = 0; col < 8; col++ ) digitalWrite( MATRIX_COL[ col ], ( *( d + row ) >> col ) & 1 );
digitalWrite( MATRIX_ROW[ row ], LOW );
}
データシート上は左から列を数えていますが、左を上位ビットにするとそのまま2進表記と一致するので逆としました。
ドットごとの書き換えだと、64回に1回の点灯になるので暗くなりますが、各ドットは明るさ均等のはず。
それに対して、行ごとの書き換えだと、8回に1回の点灯になるので明るくなりますが、おそらく1行全灯した場合にはLOW に流れる電流が多くなるものの引き込める電流に限界があるだろうということで輝度が不均一になることが考えられたり、電流制限抵抗もなにもはさんでいないので点灯時間は短くしたほうがいいということも考えれるのかと思っています。
行ごとで書き換えだけにして表示しつづけた場合ボードが熱くなりました。
アノードコモンのマトリックスLEDを使う場合には、digitalWrite() の出力をすべて逆にすればOK。
Uno / Leonardo用のPWMを使用した赤外線リモコン [Arduino]
以前、ATtiny13Aで作ったもののアレンジです。
delayMicroseconds() を使って疑似的にキャリア周波数を作ることもできるのですが、あまりエレガントな感じがしないので、キャリア周波数(38-40kHz)はPWMで作成し、信号の波形の作成はdelayMicroseconds() を使用することとしました。
UnoとLeonardoで動作確認しました。
デジタル10ピンとGNDに赤外線LEDを直結しています。(AVRの駆動力という点と、パルス出力をするという点でたぶん大丈夫?)
UnoとLeonardoとのスケッチ共用とするため、Timer/Counter 1 (16bit)を8bitで使用しています。
デジタル9ピンのAnalogWrite()は競合するので使えません。
delayMicroseconds() を使って疑似的にキャリア周波数を作ることもできるのですが、あまりエレガントな感じがしないので、キャリア周波数(38-40kHz)はPWMで作成し、信号の波形の作成はdelayMicroseconds() を使用することとしました。
UnoとLeonardoで動作確認しました。
デジタル10ピンとGNDに赤外線LEDを直結しています。(AVRの駆動力という点と、パルス出力をするという点でたぶん大丈夫?)
// PWMで38kHzをつくる赤外線リモコン (Arduino Uno, Leonardo共通版)
#include "wiring_private.h" // sbi(), cbi() を使うため
#define F_IR 38000L // キャリア周波数 (NEC, AEHAは38kHz, SONYは40kHz)
void setup() { // OC1B (Arduino的には10番ピン) を 38kHz PWM 出力にする。
pinMode( 10, OUTPUT ); // 出力方向にする
TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (1<<WGM11) | (1<<WGM10);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (0<<CS02) | (1<<CS01) | (0<<CS00);
// 比較出力選択 COM1B1-0 : 10 で 高速PWM時OC1Bが BOTTOMでHIGH、比較一致でLOW, 00で切断
// 波形生成種別 WGM13-0 : 1111 で 高速PWM動作
// クロック選択 CS12-0 : 010 で 8分周 (000 で停止、001で分周なし など)
OCR1AL = F_CPU / F_IR / 8 - 1; // (( 16MHz / 38kHz ) / 8(分周) ) - 1 = 51
OCR1BL = OCR1AL / 3; // duty比を1/3にする OCR1B / 3 = 17
}
void loop() {
sendIrNEC( 0xE41BBF40 ); // REGZA: 0x40 0xBF (TOSHIBA), 0x1B (Ch.Up), 0xE4 (~Ch.Up)
sendIrNEC( 0xE41BBF40 ); // 2回送ってみる。
delay(5000); // 5秒のdelay
}
void sendIrNEC( uint32_t d ) {
// NEC形式の時間単位(T)は、9/16 msec = 562.5usec
sbi(TCCR1A, COM1B1); delayMicroseconds( 9000 ); // リーダ ON (16T)
cbi(TCCR1A, COM1B1); delayMicroseconds( 4500 ); // リーダ OFF (8T)
for(uint16_t i=0; i<32; i++) { // データ 32ビット分処理
sbi(TCCR1A, COM1B1); delayMicroseconds( 562 ); // データ ON (1T)
cbi(TCCR1A, COM1B1); delayMicroseconds( (d>>i) % 2 ? 1687 : 562 ); // データ OFF (0:1T/1:3T)
}
sbi(TCCR1A, COM1B1); delayMicroseconds( 562 ); // トレーラ ON (1T)
cbi(TCCR1A, COM1B1); delayMicroseconds( 39937 ); // トレーラ OFF (71T) (計192T)
}
UnoとLeonardoとのスケッチ共用とするため、Timer/Counter 1 (16bit)を8bitで使用しています。
デジタル9ピンのAnalogWrite()は競合するので使えません。
Leonardo で SDカードの WAVファイルを再生する [Arduino]
Leonardo で SDカードの読み取りができるのなら、そこから WAVファイルの再生にもいけるだろう、、ということでやってみました。
Leonardo で使われている ATmega32u4 には、ATmega328P にあった 8bit Timer/Counter2 がないので以前のスケッチを少し書き換えました。
少し書き換えただけですが、また新たな勉強でした。
16bit の Timer/Counter1 を 8ビットで使うことにしました。
以前のスケッチは、タイマーがオーバーフローするのを割り込みにしていましたが、16ビットになりオーバーフローでの割り込みではだめなので、input capture register1 (ICR1) で Timer/Counter1 のTOP値を8ビットの0xFF (255) として、タイマ/カウンタ1 捕獲割り込みというもので割り込むようにしてみました。
OC1Aを使用しましたが、このピンは Uno (PB1) でも Leonardo (PB5) でも共に Arduino上はデジタル9番なので両機種に対応できているはずです。
せっかくなので、ダ・ヴィンチ32U を使ってみました。
写真の圧電サウンダでは音量が足りませんでしたが、100円ショップスピーカを直結(たぶん良くない)したところそこそこの音量が得られました。
Leonardo で使われている ATmega32u4 には、ATmega328P にあった 8bit Timer/Counter2 がないので以前のスケッチを少し書き換えました。
少し書き換えただけですが、また新たな勉強でした。
16bit の Timer/Counter1 を 8ビットで使うことにしました。
以前のスケッチは、タイマーがオーバーフローするのを割り込みにしていましたが、16ビットになりオーバーフローでの割り込みではだめなので、input capture register1 (ICR1) で Timer/Counter1 のTOP値を8ビットの0xFF (255) として、タイマ/カウンタ1 捕獲割り込みというもので割り込むようにしてみました。
OC1Aを使用しましたが、このピンは Uno (PB1) でも Leonardo (PB5) でも共に Arduino上はデジタル9番なので両機種に対応できているはずです。
// 「SDカードのWAVファイルを再生」
// 16MHzのArduino で 256 clock毎に割り込むので 62.5kHz(8bit Monoral)が理想だが
// ボクの持っているソフトが対応していないので、32kHz(8bit Stereo)で代用
// Leonardo対応バージョン
#include <SD.h>
#define BUF_SIZE 384 // バッファ・サイズ
volatile uint8_t // グローバル変数
buf[2][BUF_SIZE], // バッファ
buf_page, // バッファ・ページ
buf_flg; // バッファ読み込みフラグ
volatile uint16_t buf_index; // バッファ位置
volatile uint16_t read_size[2]; // バッファ読み込みサイズ
void setup() {
pinMode( 4, OUTPUT); // SDライブラリ使用時のお約束 (CSピンの指定)
while( !SD.begin(4) ); // ライブラリとSDカードを初期化
// PWM初期化
pinMode( 9, OUTPUT); // OC1A使用 - Uno(PB1), Leonardo(PB5)ともにD9
TCCR1A = _BV(COM1A1) | _BV(WGM11); // 高速PWM (OC1A 非反転)(TOP値:ICR1), 分周なし
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // (COM1A1-0=10, WGM13-0=1110, CS12-0=001)
ICR1 = 0xFF; // input capture register:TOP値を255(8bit)とする
}
void loop() {
File dataFile;
if ( !(dataFile = SD.open( "hoge.wav" )) ) return; // error opening wavefile
buf_index = 44; buf_page = 0; buf_flg = 1; // パラメータ設定 (44byteはヘッダ)
read_size[buf_page] = dataFile.read( (uint8_t*)buf[buf_page], BUF_SIZE );
TIMSK1 |= _BV(ICIE1); // Input Capture Interrupt Enable (ICIE)で割り込み
while(TIMSK1 & _BV(ICIE1)) {
if(buf_flg) { // データ読み込み指令のフラグが立ったら読み込む
read_size[buf_page ^ 1] = dataFile.read( (uint8_t*)buf[buf_page ^ 1], BUF_SIZE );
buf_flg = 0; // 読み込んだらフラグを下ろす
}
}
OCR1AL = 0;
dataFile.close();
delay(5000);
}
ISR(TIMER1_CAPT_vect) { // タイマ/カウンタ1 捕獲割り込み
OCR1AL = buf[buf_page][buf_index++]; // データをPWMとして出力
if(buf_index == read_size[buf_page]) { // 現在のバッファの最後まで来たら...
if(buf_index != BUF_SIZE) TIMSK1 &= ~_BV(ICIE1); // ファイルの最後なら,ICIE1をクリア
buf_index = 0; buf_page ^= 1; buf_flg = 1; // バッファを切り替え
}
}
せっかくなので、ダ・ヴィンチ32U を使ってみました。
写真の圧電サウンダでは音量が足りませんでしたが、100円ショップスピーカを直結(たぶん良くない)したところそこそこの音量が得られました。