発売日なので買ってきました。
特集は「地デジ受信機のしくみと応用製作」です。
Engineer Awardの最優秀賞受賞作品の紹介があります(最優秀賞の方のブログの記事)。
78K0マイコン基板関連の記事は2つです。
- マルチテスタのファームはアセンブラでしたがC言語版が載っています。
- もっと!付録基板のページは書き込みソフトの作成記事です。
Propellerの作成記事は先月の続きです。
マイコンで作るワンチップICのコーナーはNokiaの6610カラーLCDを使った製作記事です。Spark Funのキャリーボードを使っています。ソースもダウンロードできます。
ソリトンウェーブさんの販売ページ
ストロベリーリナックスさんの販売ページ
Spark Funのではありませんが、aitendoさんでも変換基板を販売しています(販売ページ)。また、液晶単体も販売しています。
すんさんのページのトップでキャリーボードを使った作品の写真が見れます。エレ玩 Konyaの開発記録さんでは、パックマンを作っています。
来月(12月号)の特集が興味をひきます。「作りながら学ぶマイクロコンピュータ ~CPLD上で200MHz動作のPICマイコンをエミュレーションする~」というタイトルです。HDLでPICを作ってCPLD上で動かす話みたいです。トラ技のページに写真があります。
(トラ技の次号予告より)
CPLDはAlteraのMAX IIです(AlteraのMAX IIのページ)。写っているボードは「MAX II マイクロ・キット」みたいです(製造元のTERASICの製品ページ)。国内だとソリトンウェーブさんが販売しています(販売ページ)。一覧では税込み8190円になのに、購入しようとすると10290円になります。差額の2100円は何なんでしょうね。送料は800円と書いてあるので謎です。
来月号のトラ技は楽しみです。
DesignWaveにLSIデザインコンテストの次の課題が載っています。簡単なマイクロプロセッサを設計するという課題のようです。おもしろそうと思ったけど参加資格は学生さんみたいです。コンテストが終わってから作るかな。
特集は「地デジ受信機のしくみと応用製作」です。
Engineer Awardの最優秀賞受賞作品の紹介があります(最優秀賞の方のブログの記事)。
78K0マイコン基板関連の記事は2つです。
- マルチテスタのファームはアセンブラでしたがC言語版が載っています。
- もっと!付録基板のページは書き込みソフトの作成記事です。
Propellerの作成記事は先月の続きです。
マイコンで作るワンチップICのコーナーはNokiaの6610カラーLCDを使った製作記事です。Spark Funのキャリーボードを使っています。ソースもダウンロードできます。
ソリトンウェーブさんの販売ページ
ストロベリーリナックスさんの販売ページ
Spark Funのではありませんが、aitendoさんでも変換基板を販売しています(販売ページ)。また、液晶単体も販売しています。
すんさんのページのトップでキャリーボードを使った作品の写真が見れます。エレ玩 Konyaの開発記録さんでは、パックマンを作っています。
来月(12月号)の特集が興味をひきます。「作りながら学ぶマイクロコンピュータ ~CPLD上で200MHz動作のPICマイコンをエミュレーションする~」というタイトルです。HDLでPICを作ってCPLD上で動かす話みたいです。トラ技のページに写真があります。
(トラ技の次号予告より)
CPLDはAlteraのMAX IIです(AlteraのMAX IIのページ)。写っているボードは「MAX II マイクロ・キット」みたいです(製造元のTERASICの製品ページ)。国内だとソリトンウェーブさんが販売しています(販売ページ)。一覧では税込み8190円になのに、購入しようとすると10290円になります。差額の2100円は何なんでしょうね。送料は800円と書いてあるので謎です。
来月号のトラ技は楽しみです。
DesignWaveにLSIデザインコンテストの次の課題が載っています。簡単なマイクロプロセッサを設計するという課題のようです。おもしろそうと思ったけど参加資格は学生さんみたいです。コンテストが終わってから作るかな。
カラーセンサ・モジュールを使ってみた?の続きです。
いつものごとく、何を測っているのかさっぱりというとんでもない状態なわけですが、気を取り直して蛍光灯を測ってみました。前回のコメントでのりたんさんから蛍光灯の周波数について教えてもらいました。wikiを見てみると50Hzまたは100Hzということみたいです。
R、G、B、Cの4つを250個(計1000個)、ATMega88のメモリに蓄えて、測定後に一気にPCに送るプログラムを書いてみました。本当は測定値は10bitですが、メモリが1kバイトしかないので下の2ビットは捨てました。
結果のグラフです。横軸が時間で縦軸がセンサーの読み取り値です。
Cは飽和していたのでグラフにしませんでした。うえからR, G, BとRGBを重ねてたグラブです。レベルはGが一番高くてBが一番低いです。
測定毎に反転するポートを作りました。14.4Hzくらいだったのでサンプリングレートは倍の28.8Hzくらいになります。蛍光灯が50Hzだとしてもサンプリングレートが足りていません。
グラフは細かい上下があって大きくうなりみたいなのが見えます。まさにサンプリングレートが足りないときのグラフになっています。
ソースの一部です。
測定したら、データを読み出してメモリに蓄えてを繰り返しています。Nは250です。
ここまで書いて、integration timeのことに気づきました。上の結果はintegration timeが2500のときの結果です(gainは10)。試しにintegration timeを1000にするとサンプリングレートが131Hzまであがりました。
integration time = 500(サンプリングレート167Hz) gain = 2で測定しなおしたデータです。
今度はCも表示しています。
左端の30ポイント分を拡大しました。
なんか、それっぽくなってきました。
まとめ
- カラーセンサーモジュールは蛍光灯を測るのには向いていない
- integration timeとgainをちゃんと調節すること
- 対象の周期の2倍以上のサンプリングレートで測定すること(今回はできてません)
元々、LEDの色とか明るさを測りたいと思って買ったのですが、まだそこまでは到達していません。なんというか、測定したり、グラフを書いたりといった実験ごっこの方が楽しくなっていて、元々何をしたかったかがどうでもよくなってきています。いつものことですね。
いつものごとく、何を測っているのかさっぱりというとんでもない状態なわけですが、気を取り直して蛍光灯を測ってみました。前回のコメントでのりたんさんから蛍光灯の周波数について教えてもらいました。wikiを見てみると50Hzまたは100Hzということみたいです。
R、G、B、Cの4つを250個(計1000個)、ATMega88のメモリに蓄えて、測定後に一気にPCに送るプログラムを書いてみました。本当は測定値は10bitですが、メモリが1kバイトしかないので下の2ビットは捨てました。
結果のグラフです。横軸が時間で縦軸がセンサーの読み取り値です。
Cは飽和していたのでグラフにしませんでした。うえからR, G, BとRGBを重ねてたグラブです。レベルはGが一番高くてBが一番低いです。
測定毎に反転するポートを作りました。14.4Hzくらいだったのでサンプリングレートは倍の28.8Hzくらいになります。蛍光灯が50Hzだとしてもサンプリングレートが足りていません。
グラフは細かい上下があって大きくうなりみたいなのが見えます。まさにサンプリングレートが足りないときのグラフになっています。
ソースの一部です。
for(i = 0; i < N; i++){ write(CTRL, 0x01); // 測定開始 while(read(CTRL)) ; // 測定完了待ち // RED読み取り x = read(DATA_RED_HI) << 8 | read(DATA_RED_LO); rr[i] = x >> 2; // GREEN読み取り x = read(DATA_GREEN_HI) << 8 | read(DATA_GREEN_LO); gg[i] = x >> 2; // BLUE読み取り x = read(DATA_BLUE_HI) << 8 | read(DATA_BLUE_LO); bb[i] = x >> 2; // CLEAR読み取り x = read(DATA_CLEAR_HI) << 8 | read(DATA_CLEAR_LO); cc[i] = x >> 2; } for(i = 0; i < N; i++){ sio_hex2(rr[i]); putch(' '); sio_hex2(gg[i]); putch(' '); sio_hex2(bb[i]); putch(' '); sio_hex2(cc[i]); putch(0x0d); putch(0x0a); }
測定したら、データを読み出してメモリに蓄えてを繰り返しています。Nは250です。
ここまで書いて、integration timeのことに気づきました。上の結果はintegration timeが2500のときの結果です(gainは10)。試しにintegration timeを1000にするとサンプリングレートが131Hzまであがりました。
integration time = 500(サンプリングレート167Hz) gain = 2で測定しなおしたデータです。
今度はCも表示しています。
左端の30ポイント分を拡大しました。
なんか、それっぽくなってきました。
まとめ
- カラーセンサーモジュールは蛍光灯を測るのには向いていない
- integration timeとgainをちゃんと調節すること
- 対象の周期の2倍以上のサンプリングレートで測定すること(今回はできてません)
元々、LEDの色とか明るさを測りたいと思って買ったのですが、まだそこまでは到達していません。なんというか、測定したり、グラフを書いたりといった実験ごっこの方が楽しくなっていて、元々何をしたかったかがどうでもよくなってきています。いつものことですね。
好評だったNico-TECH : Takatsuki Meeting(ニコニコ技術部関西勉強会, 8/30開催)に続き、Nico-TECH : Nagoya Meetingが10/12(日)に開催されるそうです(案内のページ)。ニコニコ技術部の技術の粋を生で見れるチャンスです。残念ながら行けそうな雰囲気はなかったりします。しくしく
一つ前の記事を書いているときに、ストロベリーリナックスさんを見てみたら、Arduino Diecimilaの販売が始まっていたことに気づきました。2940円って、かなり安いんじゃないでしょうか?(送料は+240円)。と、思ったら売り切れ中です。 10/13 追記 復活しています
Arduinoを売っているショップさんです(知ってるところ)。
MechRoboShopさん
アールティさん
スイッチサイエンスさん
共立エレショップさん
ストロベリーリナックスさん
他にATMEGA128タッチスクリーンマイコンボードという新製品もあります。「どこかで見たようなあの液晶がついています」なんて書いてあります。Palm Pilotでしたっけ?
TFT液晶が2枚で480円というのもあります。使うのはすごく難しそうです。
Arduinoを売っているショップさんです(知ってるところ)。
MechRoboShopさん
アールティさん
スイッチサイエンスさん
共立エレショップさん
ストロベリーリナックスさん
他にATMEGA128タッチスクリーンマイコンボードという新製品もあります。「どこかで見たようなあの液晶がついています」なんて書いてあります。Palm Pilotでしたっけ?
TFT液晶が2枚で480円というのもあります。使うのはすごく難しそうです。
ストロベリーリナックスさんで販売しているカラーセンサ・モジュール ADJD-S371-QR999を使ってみました。AVRでI2Cが使えるようになったので、何か他のものも使ってみたくなりました。
カラーセンサ・モジュールです。
動作電圧は2.5~3.6Vで測定値はデジタル値としてI2Cを使って読み取れます。ストロベリーリナックスさんでは、このモジュールを使ったキットも販売しています(ADJDカラーセンサ評価キット)。
実験の様子です。
読み取った値です。
上からR, G, B, クリア(?)の値です。0~1023なので000から3ffまでの値をとります。蛍光灯の下の値を表示しています。Nokia5110のバックライトをつけてみました。暗くしても値が読み取れるようにです。
カラーセンサー・モジュールのおおざっぱな使い方です。
(1) 初期化する
(2) CTRLに0x01を書き込む(変換開始)
(3) CTRLが0になるまで待つ(変換終了待ち)
(4) 値を読み取る
初期化はR, G, Bとクリアの4つについてgain(増幅度?)とintegration time(積分時間?)を設定します。クリアはR, G, Bのフィルターのかかってない素の光センサーの読み取り値みたいです。gain(0~15)は大きいほどセンサーの読み取り値が小さくなります。integration time(0~4095)は大きいほどセンサーの値が大きくなります。
ソースです。
とりあえず何か動いているものはできました。
明るい所で測定すると、周囲の光のせいで測定値が飽和してしまいます。フードか何か遮光するものがいります。LEDの光を直接当てると暗闇の中でも飽和してしまいます。測定の方法としては、モジュールの白色LEDをつけて反射光で測るのもあるみたいです。その際は2.5mmくらいに近づけないといけないみたいです。回路をつなげることより、測定環境を整える方がたいへんかもしれません。あくまで絶対的な値を測定するのではなく、相対的な値の測定に使用するものです。
内部でプルアップもされているのでつなぐだけで測定できるのは気楽です。
I2Cさえできれば気軽に使えるのでフィジカルコンピューティングなんかにいいんじゃないでしょうか。
カラーセンサ・モジュールです。
動作電圧は2.5~3.6Vで測定値はデジタル値としてI2Cを使って読み取れます。ストロベリーリナックスさんでは、このモジュールを使ったキットも販売しています(ADJDカラーセンサ評価キット)。
実験の様子です。
読み取った値です。
上からR, G, B, クリア(?)の値です。0~1023なので000から3ffまでの値をとります。蛍光灯の下の値を表示しています。Nokia5110のバックライトをつけてみました。暗くしても値が読み取れるようにです。
カラーセンサー・モジュールのおおざっぱな使い方です。
(1) 初期化する
(2) CTRLに0x01を書き込む(変換開始)
(3) CTRLが0になるまで待つ(変換終了待ち)
(4) 値を読み取る
初期化はR, G, Bとクリアの4つについてgain(増幅度?)とintegration time(積分時間?)を設定します。クリアはR, G, Bのフィルターのかかってない素の光センサーの読み取り値みたいです。gain(0~15)は大きいほどセンサーの読み取り値が小さくなります。integration time(0~4095)は大きいほどセンサーの値が大きくなります。
ソースです。
#include <avr/io.h> #include <util/twi.h> #include <util/delay.h> #include "i2c.h" #include "lcd.h" // S371の定数 #define S371 (0x74 << 1) // S371のスレーブアドレス #define CTRL 0 #define CAP_RED 6 #define CAP_GREEN 7 #define CAP_BLUE 8 #define CAP_CLEAR 9 #define INT_RED_LO 10 #define INT_RED_HI 11 #define INT_GREEN_LO 12 #define INT_GREEN_HI 13 #define INT_BLUE_LO 14 #define INT_BLUE_HI 15 #define INT_CLEAR_LO 16 #define INT_CLEAR_HI 17 #define DATA_RED_LO 64 #define DATA_RED_HI 65 #define DATA_GREEN_LO 66 #define DATA_GREEN_HI 67 #define DATA_BLUE_LO 68 #define DATA_BLUE_HI 69 #define DATA_CLEAR_LO 70 #define DATA_CLEAR_HI 71 // 指定したアドレスにデータを書く void write(byte address, byte data) { i2c_start(S371 | TW_WRITE); // アドレスとデータを送る i2c_write(address); i2c_write(data); i2c_stop(); } // 指定したアドレスのデータを読む byte read(byte address) { byte x; i2c_start(S371 | TW_WRITE); // アドレスを送る i2c_write(address); i2c_stop(); i2c_start(S371 | TW_READ); // データを読む x = i2c_read_nak(); i2c_stop(); return x; } int main() { uint16_t x; lcd_init(); // Noki5110初期化 i2c_init(); // I2C初期化 // S371初期化 write(CAP_RED, 10); // 0~15 write(CAP_GREEN, 10); // 0~15 write(CAP_BLUE, 10); // 0~15 write(CAP_CLEAR, 10); // 0~15 write(INT_RED_LO, 2500 & 255); write(INT_RED_HI, 2500 >> 8); write(INT_GREEN_LO, 2500 & 255); write(INT_GREEN_HI, 2500 >> 8); write(INT_BLUE_LO, 2500 & 255); write(INT_BLUE_HI, 2500 >> 8); write(INT_CLEAR_LO, 2500 & 255); write(INT_CLEAR_HI, 25000 >> 8); while(1){ write(CTRL, 0x01); // 測定開始 while(read(CTRL)) ; // 測定完了待ち // RED読み取り x = read(DATA_RED_HI) << 8 | read(DATA_RED_LO); lcd_locate(0, 0); lcd_hex4(x); // 16進4桁出力 // GREEN読み取り x = read(DATA_GREEN_HI) << 8 | read(DATA_GREEN_LO); lcd_locate(0, 1); lcd_hex4(x); // 16進4桁出力 // BLUE読み取り x = read(DATA_BLUE_HI) << 8 | read(DATA_BLUE_LO); lcd_locate(0, 2); lcd_hex4(x); // 16進4桁出力 // CLEAR読み取り x = read(DATA_CLEAR_HI) << 8 | read(DATA_CLEAR_LO); lcd_locate(0, 3); lcd_hex4(x); // 16進4桁出力 _delay_ms(500); // 0.5秒待つ } }
とりあえず何か動いているものはできました。
明るい所で測定すると、周囲の光のせいで測定値が飽和してしまいます。フードか何か遮光するものがいります。LEDの光を直接当てると暗闇の中でも飽和してしまいます。測定の方法としては、モジュールの白色LEDをつけて反射光で測るのもあるみたいです。その際は2.5mmくらいに近づけないといけないみたいです。回路をつなげることより、測定環境を整える方がたいへんかもしれません。あくまで絶対的な値を測定するのではなく、相対的な値の測定に使用するものです。
内部でプルアップもされているのでつなぐだけで測定できるのは気楽です。
I2Cさえできれば気軽に使えるのでフィジカルコンピューティングなんかにいいんじゃないでしょうか。
暇なときに無料動画のGyaoで映画を見たりするんですが、最近見かけたはんだごてシーンのある映画です。
(1) スペシャリスト (wikiへのリンク)
主演のシルヴェスター・スタローンが爆破工作員の役なんですが、途中で爆弾を作るシーンがあって、はんだづけしてます。Gyaoは11/1まで放映みたいです。
(2) デンジャラス・ビューティー (wikiへのリンク)
主役のサンドラ・ブロック(FBI捜査官役)が追いかける犯人が爆弾魔で、爆弾作成のシーンではんだづけしてます。Gyaoでは先月やっていました。
・・・ って映画に出てくるはんだごては爆弾作成の道具なんですね。しくしく
以前は気にもとめていませんでしたが、電子工作はしはじめると、はんだごてが出てくると反応するようになってしまったみたいです。なんかもっと正義の味方がはんだづけするシーンとかないのかな。
※映画をインターネットで検索するときは注意が必要です。公式サイトはずっとメンテされているわけではなく、放置されたあげく乗っ取られてウィルス配布所になっていることもあります。
(1) スペシャリスト (wikiへのリンク)
主演のシルヴェスター・スタローンが爆破工作員の役なんですが、途中で爆弾を作るシーンがあって、はんだづけしてます。Gyaoは11/1まで放映みたいです。
(2) デンジャラス・ビューティー (wikiへのリンク)
主役のサンドラ・ブロック(FBI捜査官役)が追いかける犯人が爆弾魔で、爆弾作成のシーンではんだづけしてます。Gyaoでは先月やっていました。
・・・ って映画に出てくるはんだごては爆弾作成の道具なんですね。しくしく
以前は気にもとめていませんでしたが、電子工作はしはじめると、はんだごてが出てくると反応するようになってしまったみたいです。なんかもっと正義の味方がはんだづけするシーンとかないのかな。
※映画をインターネットで検索するときは注意が必要です。公式サイトはずっとメンテされているわけではなく、放置されたあげく乗っ取られてウィルス配布所になっていることもあります。
AVRでNokia5110を使ってみる (初ネギ)の続きです。
ネギ振りに使った画像(56×48ドット×6)です。チラチラするドットは修正しました。
髪の毛が黒くつぶれているので、腕がどこにあるのか分かりません。変換元の画像はInterfaceのダウンロードページからダウンロードしたものです。
プログラムを色々変えて、ネギ振りの速度を測ってみました。
測定は1秒間のタイマー割り込みで何回振るかを数えています。左下に秒間振り回数を表示しています。速すぎてネギが見えていませんが、実物は残像が見えます。
(1) 元々のコード 秒間100振り
lcd_data( )を使って1バイトずつ転送しています。呼び出すときは、
PROGMEM byte h1[56*6] = {0x00,...,};
みたいなデータを作って、
lcd_image_p(h1, 28, 0, 56, 6);
のように呼び出します。(28, 0)に大きさ56×6の画像を表示します。pgm_read_byteはコードの格納されているフラッシュからデータを読み出す関数です。この関数を使うときはavr/pgmspace.hをインクルードしておく必要があります。
全画面が84×48なので、はちゅね画像のサイズ56×48は2/3のサイズになります。また一振りが6枚の画像を使ったアニメーションです。
(2) インライン展開+先読み 秒間123振り
lcd_locateとlcd_dataをインライン展開しました。SCEの制御はまとめて最初と最後だけにしています。SPIの送信完了待ちの間に次のデータを読み込むようにしました。loop_until_bit_is_set( )というマクロはsfr_defs.hで定義されています。avr/io.hを読み込むと自動的に読み込まれるので明示的にインクルードする必要はありません。I/Oポートの特定のビットが1になるまで待ちます。sfr_defs.hで定義されているマクロは他にbit_is_set、bit_is_clear、loop_until_bit_is_clearがあります。
(3) ループをアンロールする 秒間153振り
xのforループの内側だけ示します。他は(2)と一緒です。ループを展開して4つずつ送信しています。横方向が56ドットなので8個ずつまでいけます。
(4) ウェイトをnopにしてみる 秒間210振り
xのforループの内側だけ示します。他は(2)と一緒です。loop_until_bit_is_set( )による待ち合わせを止めてみました。nopの個数は試行錯誤した結果6個がちょうどでした。ただし、関数から抜ける前にloop_until_bit_is_set( )を入れる必要がありました。今回は内部発振クロックの8MHzが動作クロックで、4MHz通信を行っています。コンパイラのはくオブジェクトが変わったりするとnopの個数が変わります。環境や条件によって動作しなくなるので、通常はやってはいけないコードです。
loop_until_bit_is_set( )による送信完了判定自体が数クロックかかっているので、実際にSPIEが1になってから、数クロック後に送信が完了したことが分かります。この分の無駄が結構あったみたいです。
最初のコードから2倍以上速くなりました。もっとちゃんとオブジェクトを見たり、出力波形を観測したりすればよかったのですが、試行錯誤が楽しかったので手を抜いています。書き換える必要のある最小限のデータだけ送るようにすれば、もっと速くなるはずです。全画面転送だと、画面サイズが1.5倍、画面枚数が1/6なので計算上は秒間840枚(=210*6/1.5)はいきそうです(実際は座標設定とかも減るのでもっと速い?)。約1.2ミリ秒で一画面の転送が終わることになります。画面全体でも504バイト(=84×48÷8)なので、SRAM上に画面全体のコピーを持っていて必要なときだけ全画面転送するという使い方も可能です。
ネギ振りに使った画像(56×48ドット×6)です。チラチラするドットは修正しました。
髪の毛が黒くつぶれているので、腕がどこにあるのか分かりません。変換元の画像はInterfaceのダウンロードページからダウンロードしたものです。
プログラムを色々変えて、ネギ振りの速度を測ってみました。
測定は1秒間のタイマー割り込みで何回振るかを数えています。左下に秒間振り回数を表示しています。速すぎてネギが見えていませんが、実物は残像が見えます。
(1) 元々のコード 秒間100振り
// (x0, y0)から大きさ(dx, dy)のPROGMEMの絵pを描く void lcd_image_p(byte *p, byte x0, byte y0, byte dx, byte dy) { byte x, y; for(y = 0; y < dy; y++){ lcd_locate(x0, y0 + y); for(x = 0; x < dx; x++){ lcd_data(pgm_read_byte(p++)); } } }
lcd_data( )を使って1バイトずつ転送しています。呼び出すときは、
PROGMEM byte h1[56*6] = {0x00,...,};
みたいなデータを作って、
lcd_image_p(h1, 28, 0, 56, 6);
のように呼び出します。(28, 0)に大きさ56×6の画像を表示します。pgm_read_byteはコードの格納されているフラッシュからデータを読み出す関数です。この関数を使うときはavr/pgmspace.hをインクルードしておく必要があります。
全画面が84×48なので、はちゅね画像のサイズ56×48は2/3のサイズになります。また一振りが6枚の画像を使ったアニメーションです。
(2) インライン展開+先読み 秒間123振り
// (x0, y0)から大きさ(dx, dy)のPROGMEMの絵pを描く void lcd_image_p(byte *p, byte x0, byte y0, byte dx, byte dy) { byte x, y, c; c = pgm_read_byte(p++); // 1バイト先読み PORTB &= ~(_BV(SCE)); // SCE=0 for(y = 0; y < dy; y++){ PORTB &= ~(_BV(DC)); // DC=0 (コマンド) SPDR = SETX | x0; // x座標送信 loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち SPDR = SETY | (y0 + y); // y座標送信 loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち PORTB |= _BV(DC); // DC=1 (データ) for(x = 0; x < dx; x++){ SPDR = c; // データ送信 c = pgm_read_byte(p++); // 次のデータを読み込む loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち } } PORTB |= _BV(SCE); // SCE=1 }
lcd_locateとlcd_dataをインライン展開しました。SCEの制御はまとめて最初と最後だけにしています。SPIの送信完了待ちの間に次のデータを読み込むようにしました。loop_until_bit_is_set( )というマクロはsfr_defs.hで定義されています。avr/io.hを読み込むと自動的に読み込まれるので明示的にインクルードする必要はありません。I/Oポートの特定のビットが1になるまで待ちます。sfr_defs.hで定義されているマクロは他にbit_is_set、bit_is_clear、loop_until_bit_is_clearがあります。
(3) ループをアンロールする 秒間153振り
for(x = 0; x < dx / 4; x++){ SPDR = c; // データ送信(1) c = pgm_read_byte(p++); // 次のデータを読む loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち SPDR = c; // データ送信(2) c = pgm_read_byte(p++); // 次のデータを読む loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち SPDR = c; // データ送信(3) c = pgm_read_byte(p++); // 次のデータを読む loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち SPDR = c; // データ送信(4) c = pgm_read_byte(p++); // 次のデータを読む loop_until_bit_is_set(SPSR, SPIE); // 送信完了待ち }
xのforループの内側だけ示します。他は(2)と一緒です。ループを展開して4つずつ送信しています。横方向が56ドットなので8個ずつまでいけます。
(4) ウェイトをnopにしてみる 秒間210振り
for(x = 0; x < dx; x++){ SPDR = c; // データ送信 c = pgm_read_byte(p++); // 次のデータを読む asm("nop"); // (1) asm("nop"); // (2) asm("nop"); // (3) asm("nop"); // (4) asm("nop"); // (5) asm("nop"); // (6) }
xのforループの内側だけ示します。他は(2)と一緒です。loop_until_bit_is_set( )による待ち合わせを止めてみました。nopの個数は試行錯誤した結果6個がちょうどでした。ただし、関数から抜ける前にloop_until_bit_is_set( )を入れる必要がありました。今回は内部発振クロックの8MHzが動作クロックで、4MHz通信を行っています。コンパイラのはくオブジェクトが変わったりするとnopの個数が変わります。環境や条件によって動作しなくなるので、通常はやってはいけないコードです。
loop_until_bit_is_set( )による送信完了判定自体が数クロックかかっているので、実際にSPIEが1になってから、数クロック後に送信が完了したことが分かります。この分の無駄が結構あったみたいです。
最初のコードから2倍以上速くなりました。もっとちゃんとオブジェクトを見たり、出力波形を観測したりすればよかったのですが、試行錯誤が楽しかったので手を抜いています。書き換える必要のある最小限のデータだけ送るようにすれば、もっと速くなるはずです。全画面転送だと、画面サイズが1.5倍、画面枚数が1/6なので計算上は秒間840枚(=210*6/1.5)はいきそうです(実際は座標設定とかも減るのでもっと速い?)。約1.2ミリ秒で一画面の転送が終わることになります。画面全体でも504バイト(=84×48÷8)なので、SRAM上に画面全体のコピーを持っていて必要なときだけ全画面転送するという使い方も可能です。
最近AVRばかりしていますが、自分の書いたものを探すのが面倒なのでカテゴリーを整理しました。
その他のマイコンからAVRとPSoCを独立させました。電子工作からJoule Thiefを独立させました。これでAVR関連の記事だけ読めるようになります。
手動で変更だったので面倒でした。記事のタイトルを見ながら、必要ならカテゴリーを変更していきます。せめて全部の記事でなく、あるカテゴリーだけ選択できる機能があればいいのにと思いました。一つの記事に一つのカテゴリーしかつけれないのも、なんとかしてほしいです。マイコンを2種類使ったら、どのカテゴリーにいれればいいのか悩みます。
ある程度まとまったものは新たにカテゴリーを作るのがいいですね。日付順だと探すのが面倒です。半分メモがわりみたいなのが多いので、ちゃんとタイトルくらい書け>自分。メモだけじゃ分からん。我ながら、いい加減な性格というか、テキトーです。
その他のマイコンからAVRとPSoCを独立させました。電子工作からJoule Thiefを独立させました。これでAVR関連の記事だけ読めるようになります。
手動で変更だったので面倒でした。記事のタイトルを見ながら、必要ならカテゴリーを変更していきます。せめて全部の記事でなく、あるカテゴリーだけ選択できる機能があればいいのにと思いました。一つの記事に一つのカテゴリーしかつけれないのも、なんとかしてほしいです。マイコンを2種類使ったら、どのカテゴリーにいれればいいのか悩みます。
ある程度まとまったものは新たにカテゴリーを作るのがいいですね。日付順だと探すのが面倒です。半分メモがわりみたいなのが多いので、ちゃんとタイトルくらい書け>自分。メモだけじゃ分からん。我ながら、いい加減な性格というか、テキトーです。
陰気な男でいいですか?さん経由で知ったのですが、PICのMicrochip社がAVRのAtmel社を買収しようとしているそうです(Microchip社のプレスリリース)。MicrochipがAtmelを買収したらAVRの今後はどうなるんでしょうね。
(1) AVRそのものがなくなる
(2) AVRという名前は消えるがchipは生き残って、PIC MEGA88とかになる。
(3) 現行製品はある程度生産されるが、新製品の開発は行われず、徐々に終息する。
タイトルは「ベータマックスはなくなるの?」という新聞広告のパロディです。「答えはもちろんノー」と続きます。シャレなので怒らないでください。
10/4 追記 日経BPのtech-onに「MicrochipとON SemiconductorがAtmelを買収へ」という記事が出ていました。
(1) AVRそのものがなくなる
(2) AVRという名前は消えるがchipは生き残って、PIC MEGA88とかになる。
(3) 現行製品はある程度生産されるが、新製品の開発は行われず、徐々に終息する。
タイトルは「ベータマックスはなくなるの?」という新聞広告のパロディです。「答えはもちろんノー」と続きます。シャレなので怒らないでください。
10/4 追記 日経BPのtech-onに「MicrochipとON SemiconductorがAtmelを買収へ」という記事が出ていました。
以前、こつぜんと姿を消して以来、復活が望まれていたfpga-labさんですが、Kernel-Labさんと名前を変えて復活されました。他では得られない貴重なコンテンツ群の復活が望まれます。
10/5 タイトル修正。fpga-org → FPGA Laboratory
10/5 タイトル修正。fpga-org → FPGA Laboratory
ノキア液晶5110の続きになります。
その後、マイコン工作実験日記さんからトラックバックをいただきました。同じLCDを使っておられます(記事)。
その後見つけた参考になりそうなコードです。
(1) LCD Nokia 3310 (PCD8544) Driver in WinAVR(avr-gcc)。ソースコードもダウンロードできます。
(2) トラ技2008年1月号のp.106「フォント内蔵のグラフィック液晶制御IC」という記事です。こちらはソースのダウンロードはできませんが付録CD-ROMにソースが格納されています。
前回紹介させていただいたトラ技2006年3月号の記事のプログラムですが、初期化の部分にバグがあります。GLCD.cの285行目です。元のコードは
SPI_tx_command(0b00001000 | 4); /* Bias=4 (0-7) */
となっています。biasを設定するコマンドは0x10なのでビット位置が違っています。また4に設定したいときは実際には3を書く必要があります。正しくは以下になります。
SPI_tx_command(0b00010000 | 3); /* Bias=4 (0-7) */
AVRのSPIで遊びたいときって、どうすればいいのか不思議でした。プログラマのISPと同じピンを使うからです。信号がぶつかったりとかしないのかと思っていました。そのあたりの話はすんさんの掲示板で色々教えてもらいました。プログラマはプログラムが終わるとハイインピーダンスになるので、気にせずそのままつないでいいみたいです。リセットしてから実際にSPIを使い始めるまではSPI関連の線が浮いた状態になるのがちょっと気にかかるところです。
AVRでSPIを使うには3つのレジスタを使います。SPCRは制御、SPSRはステータス、SPDRはデータです。今回はマスターで使用します。I2Cと違って面倒なことがありません。初期化したあとは、SPDRに書き込むとデータ送信が始まってSPSRのSPIFビットを見て送信完了を待つだけです。
Nokia5110の端子ですが、用心のために3pinのSCEはpull-up、4pinのRESETはpull-downしています。SCEはISP中に変なコマンドが飛んだりしないように、RESETは電源投入直後に確実にLOWになるようにです。また、Vcc(1pin)とGND(2pin)の間にパスコン(0.1uF)をいれています。
ソースです。
LCDの初期化は以下のようにします。
(1) リセットを100ミリ秒以上Lにする(電源投入直後からずっとLにしてもいい)。
(2) H=1にして拡張コマンドを使えるようにする
(3) BIASの設定。48ラインなので3にします(データシートのn=4の設定)。
(4) Vopの設定。コントラストの設定です。後述
(5) TCの設定。温度補償らしいのですが、よく分かりません。他と同じ1にしています。
(6) H=0にして通常コマンドに切り替える
(7) display modeをnormalまたはreverseにする。
(8) ビデオRAMをクリアする。
(4)のVopの設定でコントラストを変えれるはずなのですが、なぜかうまくいきません。値を変えてもコントラストは変わりません。Vopは変な値にすると定格を超えてしまうので注意深く設定する必要のあるレジスタなのですが、設定がききません。何か変です。
(6)の時点で、縦書きモードと横書きモードを選択できます。データを送った後に座標が縦に進むか横に進むかの切り替えです。
Nokia5110のデータ形式はマイコン工作実験日記さんの解説が分かりやすいです。LCDの上の方がLSBになるところが注意点です。
何を表示しようかと思いましたが、最近のお約束みたいなのでネギ振りに挑戦してみました。絵心がないので、データを変換しただけです。今回、一番時間がかかった所です。ubuntsuに入っていたGIMPというグラフィックツールで縮小とモノクロ化しています。動かしてみると、ちらちらするドットがいたりしますが、修正していません。画像サイズは56x48で1枚あたり336バイトです。6枚あるので2016バイトになります。SRAMには展開できないのでROMから直接読み込むようにしています。
SPIの通信速度は4Mbpsです(Nokia5110の最大)。336バイトを送るのにオーバーヘッドを考えないと0.7ミリ秒くらいです。
データが大きいので描画部分のソースは省きました。
撮影は携帯なのでぶれています。
描画の高速化について少し考えてみました。
(1) 連続してデータを送るときは、SCEの制御を行わない。
(2) 送信待ちの間に次に送信するデータを読み出す。
(3) 縦書きモードと横書きモードを利用して、x, y座標の設定を減らす
絵が動くと楽しいですね。変換した画像はあまりきれいでないので、もう少しなんとかしたいものです。
その後、マイコン工作実験日記さんからトラックバックをいただきました。同じLCDを使っておられます(記事)。
その後見つけた参考になりそうなコードです。
(1) LCD Nokia 3310 (PCD8544) Driver in WinAVR(avr-gcc)。ソースコードもダウンロードできます。
(2) トラ技2008年1月号のp.106「フォント内蔵のグラフィック液晶制御IC」という記事です。こちらはソースのダウンロードはできませんが付録CD-ROMにソースが格納されています。
前回紹介させていただいたトラ技2006年3月号の記事のプログラムですが、初期化の部分にバグがあります。GLCD.cの285行目です。元のコードは
SPI_tx_command(0b00001000 | 4); /* Bias=4 (0-7) */
となっています。biasを設定するコマンドは0x10なのでビット位置が違っています。また4に設定したいときは実際には3を書く必要があります。正しくは以下になります。
SPI_tx_command(0b00010000 | 3); /* Bias=4 (0-7) */
AVRのSPIで遊びたいときって、どうすればいいのか不思議でした。プログラマのISPと同じピンを使うからです。信号がぶつかったりとかしないのかと思っていました。そのあたりの話はすんさんの掲示板で色々教えてもらいました。プログラマはプログラムが終わるとハイインピーダンスになるので、気にせずそのままつないでいいみたいです。リセットしてから実際にSPIを使い始めるまではSPI関連の線が浮いた状態になるのがちょっと気にかかるところです。
AVRでSPIを使うには3つのレジスタを使います。SPCRは制御、SPSRはステータス、SPDRはデータです。今回はマスターで使用します。I2Cと違って面倒なことがありません。初期化したあとは、SPDRに書き込むとデータ送信が始まってSPSRのSPIFビットを見て送信完了を待つだけです。
Nokia5110の端子ですが、用心のために3pinのSCEはpull-up、4pinのRESETはpull-downしています。SCEはISP中に変なコマンドが飛んだりしないように、RESETは電源投入直後に確実にLOWになるようにです。また、Vcc(1pin)とGND(2pin)の間にパスコン(0.1uF)をいれています。
ソースです。
#include <avr/io.h> #include <util/delay.h> // pin接続 // 1 Vcc // 2 GND // 3 SCE PB2(16) pull-up // 4 RESET PB1(15) pull-down // 5 D/C PB0(14) // 6 SDIN PB3/MOSI(17) // 7 SCLK PB5/SCK(19) // 8 LED+ typedef unsigned char byte; // ピン割り当て #define SCK PB5 #define MOSI PB3 #define SCE PB2 #define RESET PB1 #define DC PB0 // LCDコマンド // H=0 or 1 76543210 #define FSET 0b00100000 // 2:PD 1:V 0:H // H=0 76543210 #define DISP 0b00001000 // 2:D 0:E #define SETY 0b01000000 // 2-0:Y (0-5) #define SETX 0b10000000 // 6-0:X (0-83) // H=1 76543210 #define TMP 0b00000100 // 1-0:TC (0-3) #define BIAS 0b00010000 // 2-0:BS (0-7) 3のとき1/48duty #define VOP 0b10000000 // 6-0:Vop (0-127) コントラスト // コマンドを送る void lcd_cmd(byte cmd) { PORTB &= ~(_BV(SCE) | _BV(DC)); // SCE=0 DC=0 SPDR = cmd; // コマンド送信 while(!(SPSR & _BV(SPIE))); // SPIE=1になるまで待つ PORTB |= _BV(SCE) | _BV(DC); // SCE=1 DC=1 } // データを送る void lcd_data(byte d) { PORTB &= ~_BV(SCE); // SCE=0 DC=1 SPDR = d; // データ送信 while(!(SPSR & _BV(SPIE))); // SPIE=1になるまで待つ PORTB |= _BV(SCE); // SCE=1 DC=1 } // LCDクリア void lcd_clear(void) { int i; for(i = 0; i < 84 * 6; i++) lcd_data(0); } // 座標変更 x:0-83 y:0-5 void lcd_locate(byte x, byte y) { lcd_cmd(SETX | (x & 0x7f)); lcd_cmd(SETY | (y & 7)); } // LCD初期化 void lcd_init(void) { PORTB = _BV(SCE); // SCLK=0 SDIN=0 D/C=0 RESET=0 SCE=1 DDRB = _BV(SCK) | _BV(MOSI) | _BV(DC) | _BV(RESET) | _BV(SCE); // SPI初期化 (Nokia5510LCDは4MHzまで) SPCR = _BV(SPE) | _BV(MSTR); // SPI許可、マスター、fosc/4 SPSR = _BV(SPI2X); // 2倍速 // reset _delay_ms(100); // 100ミリ秒待つ PORTB |= _BV(RESET); // LCD初期化 lcd_cmd(FSET | 1); // H=1 lcd_cmd(BIAS | 3); // BIAS n=4 1:48 dup rate lcd_cmd(VOP | 16); // Vop コントラスト(変わらない、なんで?) lcd_cmd(TMP | 1); // TC=1 lcd_cmd(FSET); // H=0 lcd_cmd(DISP | 4); // display mode 4:normal 5:reverse lcd_clear(); lcd_locate(0, 0); } int main() { lcd_init(); // 描画 lcd_locateで移動してlcd_dataでデータを送る // コード省略 while(1) ; // 無限ループ }
LCDの初期化は以下のようにします。
(1) リセットを100ミリ秒以上Lにする(電源投入直後からずっとLにしてもいい)。
(2) H=1にして拡張コマンドを使えるようにする
(3) BIASの設定。48ラインなので3にします(データシートのn=4の設定)。
(4) Vopの設定。コントラストの設定です。後述
(5) TCの設定。温度補償らしいのですが、よく分かりません。他と同じ1にしています。
(6) H=0にして通常コマンドに切り替える
(7) display modeをnormalまたはreverseにする。
(8) ビデオRAMをクリアする。
(4)のVopの設定でコントラストを変えれるはずなのですが、なぜかうまくいきません。値を変えてもコントラストは変わりません。Vopは変な値にすると定格を超えてしまうので注意深く設定する必要のあるレジスタなのですが、設定がききません。何か変です。
(6)の時点で、縦書きモードと横書きモードを選択できます。データを送った後に座標が縦に進むか横に進むかの切り替えです。
Nokia5110のデータ形式はマイコン工作実験日記さんの解説が分かりやすいです。LCDの上の方がLSBになるところが注意点です。
何を表示しようかと思いましたが、最近のお約束みたいなのでネギ振りに挑戦してみました。絵心がないので、データを変換しただけです。今回、一番時間がかかった所です。ubuntsuに入っていたGIMPというグラフィックツールで縮小とモノクロ化しています。動かしてみると、ちらちらするドットがいたりしますが、修正していません。画像サイズは56x48で1枚あたり336バイトです。6枚あるので2016バイトになります。SRAMには展開できないのでROMから直接読み込むようにしています。
SPIの通信速度は4Mbpsです(Nokia5110の最大)。336バイトを送るのにオーバーヘッドを考えないと0.7ミリ秒くらいです。
データが大きいので描画部分のソースは省きました。
撮影は携帯なのでぶれています。
描画の高速化について少し考えてみました。
(1) 連続してデータを送るときは、SCEの制御を行わない。
(2) 送信待ちの間に次に送信するデータを読み出す。
(3) 縦書きモードと横書きモードを利用して、x, y座標の設定を減らす
絵が動くと楽しいですね。変換した画像はあまりきれいでないので、もう少しなんとかしたいものです。
メモがわり
ATMega88にはクロック分周器が内蔵されています。CLKPSレジスタの値で1, 2, 4, 8, 16, 32, 64, 128, 256分周のどれかを選べます。ヒューズのCKDIV8はCLKPSレジスタの初期値を決めるヒューズです。CKDIV8ヒューズは初期値を決めるだけなので、動作中にCLKPSレジスタを書き換えることでクロック分周値を変更することが可能です。
CLKPSレジスタ(4bit)の内容と分周値です。
CLKPS
0000 1分周
0001 2分周
0010 4分周
0011 8分周
0100 16分周
0101 32分周
0110 64分周
0111 128分周
1000 256分周
1001~1111 予約
書き換え手順は、
(1) CLKPRレジスタのCLKPCEビットを1にします。このときCLKPSn (n=3~0)も0にします。
(2) 4クロック以内にCLKPSnを書き換えます。
具体的には以下のようにします。
ENC28J60のデフォルトのCLKOUTを25MHzだと勘違いしていたので、最初はCKDIV8にして8分周した3.125MHzでAVRを動作させて後で2分周の12.5MHzに切り替える方法を調べていました。でもENC28J60のデフォルトのCLKOUTは25MHzを4分周した6.25MHzだったので、あまり意味はなくなりました。動作している最中に外部クロックの周波数を切り替えるのって、誤動作したりしないのでしょうか。
ATMega88にはクロック分周器が内蔵されています。CLKPSレジスタの値で1, 2, 4, 8, 16, 32, 64, 128, 256分周のどれかを選べます。ヒューズのCKDIV8はCLKPSレジスタの初期値を決めるヒューズです。CKDIV8ヒューズは初期値を決めるだけなので、動作中にCLKPSレジスタを書き換えることでクロック分周値を変更することが可能です。
CLKPSレジスタ(4bit)の内容と分周値です。
CLKPS
0000 1分周
0001 2分周
0010 4分周
0011 8分周
0100 16分周
0101 32分周
0110 64分周
0111 128分周
1000 256分周
1001~1111 予約
書き換え手順は、
(1) CLKPRレジスタのCLKPCEビットを1にします。このときCLKPSn (n=3~0)も0にします。
(2) 4クロック以内にCLKPSnを書き換えます。
具体的には以下のようにします。
CLKPR = _BV(CLKPCE); // CLKPCEビットを1にする CLKPR = 0b0011; // 8分周にする
ENC28J60のデフォルトのCLKOUTを25MHzだと勘違いしていたので、最初はCKDIV8にして8分周した3.125MHzでAVRを動作させて後で2分周の12.5MHzに切り替える方法を調べていました。でもENC28J60のデフォルトのCLKOUTは25MHzを4分周した6.25MHzだったので、あまり意味はなくなりました。動作している最中に外部クロックの周波数を切り替えるのって、誤動作したりしないのでしょうか。