はじめの一歩、LEDの点灯
LEDの点灯の実験を通してマイコンの基本的な動作およびソフトウエアの作り方を勉強します。最初からマイコンを使うの?と思われるでしょうが最初が肝心でするがんばって以下の順番に従って実験を行っていきましょう。
1.タイマを使ったLED点滅の実験。
2.LEDの点灯をスイッチで制御する。
1.必要な部品
今回の工作では、電源は電池2本を使用します。回路はブレッドボード上に構成します。
名称 |
仕様 |
数量 |
マイコン |
ATmega328 |
1 |
LED |
φ3、緑色 |
6 |
抵抗 |
200Ω 1/4W |
6 |
抵抗 |
10kΩ 1/4W |
1 |
抵抗 |
100kΩ 1/4W |
1 |
タクトスイッチ |
|
1 |
ブレッドボード |
|
1 |
ブレットボード用ジャンパ線 |
|
1 |
電池ボックス |
単3電池2本用 |
1 |
電池 |
単3電池またはニッケル水素電池 |
1 |
2.課題
電子さいころを作る際に使用するマイコンの機能について以下の項目について実験し確認します。その後電子さいころを作ります。
1.ハードウエア |
LEDの使い方 |
2.ソフトウエア |
・入出力ポートの使い方
・スイッチの状態(ON、OFFの状態)の読み込み、およびチャタリングについて
・タイマの使い方
・乱数の使い方 |
3.回路
回路図
・回路への電源は今回は電池から供給します。
・単3電池2本(=3V)または単3ニッケル水素電池2本(=2.4V)を使用します。
・マイコンの最小動作電圧は1.8Vなので単3ニッケル電池2本作動できます。
・ISPコネクタはプログラムを書き込むときに使用します。このコネクタにはAVRISP MarkⅡを接続します。AVRISP MarkⅡとマイコンの接続は回路図のとおり接続します。
・LEDは普通の赤色または緑色φ3mmのものを使用します。LEDに直列に接続されている抵抗値は以下の計算式で求めます。また、LEDのアノード(プラス側)はリード線の長い方です。
LEDの順方向電圧は1.8V程度(順方向電流約5mA)です。白色または青色のLEDを使う場合は順方向電圧が3V程度と高くなりますので電池2本では点灯されることができないです。
抵抗値 =(電源電圧 - LEDの順方向電圧) / LEDの順方向電流
・単3電池の場合
抵抗値 = (3.0V - 1.8V) / 5mA = 240Ω
・抵抗240Ωとした場合に単3ニッケル電池を使用したときの順方向電流 は抵抗値 = (2.4V - 1.8V) / 240Ω = 2.5mA
マイコンのドライブの能力(マイコンのポートがどのくらいの電流を流すことができるか)をマニュアルから確認しますと、電源電圧3Vの場合は全てのポートの合計の電流が150mAを超えてはいけないとあります。このことから2.5mAを出力することは十分可能であることがわかります。また全てのLEDを点灯したときも2.5mA
X 6 = 12.5mAですので全てのLEDを点灯することはできます。
・スイッチはタクトスイッチを使用します。
・スイッチを押さないときはマイコンの入力ポートに電池電圧(2.4V)が10kΩの抵抗を介して印加されます。スイッチを押すと0Vが印加されます。スイッチが押されたときはスイッチには300uA流れます。
・リセット端子(1番ピン)には100kΩを介して電池電圧が印加されます。
組立図
ブレットボード上に上図に示す回路をブレットボード上に配線します。最初の電子工作ではLEDは1つだけしか点灯させませんが、電子さいころでは6個のLEDを使用しますので最初から配線しておきます。スイッチを配線します。マイコンにプログラムを書き込むためのコネクタも配線します。これで電子さいころの回路は完成です。
ATmega328概要
フラッシュROM |
32kバイト |
EEPROM |
1kバイト |
RAM |
2kバイト |
周辺機能 |
8ビットカウンタ X 2
16ビットカウンタ X 1
10ビットADコンバータ X 6
USART
SPI
I2C
ウオッチドックタイマ
アナログコンバータ |
IO |
32本(PB X 8、PC X 7、PD X 8) |
パッケージ |
28ピンDIP |
動作電圧 |
1.8V~5.5V |
4.ソフトウェア
4.1LEDを点滅(出力ポート)
最初に出力ポートの使い方を確認します。LEDは6個マイコンに配線されています。そのうちポートD0に接続されているLEDを一定時間毎に点灯、消灯を繰り返し行います。最初にこれを動作させるプログラムを作ります。
フローチャート
フローチャートを作成します。
次にフローチャートに沿ってプログラムを作ります。
以下にフローチャートの説明をします。
ポートの初期化をした後にLEDを点灯します。その後一定時間後LEDを消灯します。再度一定時間後にLEDを点灯します。これを繰り返し行います。この動作をプログラムに置き換えます。
プログラムはC言語を使用して作ります。
プログラム
#include <avr/io.h>
int main(void)
{
uint16_t i;
DDRD = 1 << DDD0; // ポートD0を出力に設定
PORTD |= 0x01; // ポートD0に1を出力(LED点灯)
while(1)
{
for ( i = 0; i < 26500; i++); // 500ms程度時間待ち
PORTD = ~PORTD & 0x01; // ポートD0の出力を反転
}
}
|
・ポートの初期化
マイコンのポートを出力で使用するか入力にするかを決めます。
DDRDのビットのビット構成は以下のようになっていてビットを'1'にすると出力に設定されます。'0'は入力となります。デフォルトは入力'0'になっています。
DDRD |
DDD7 |
DDD6 |
DDD5 |
DDD4 |
DDD3 |
DDD2 |
DDD1 |
DDD0 |
初期値 |
0 |
0 |
0 |
0 |
0 |
|
0 |
0 |
ポートD0を出力にする場合はDDD0を'1'に設定します。これをプログラムで書くと以下のようになります。
DDRD = 1;
プログラムの説明
・ポートへの出力
出力に設定したポートD0を出力します。この'1'を出力する場合はPORTD0に'1'を書きます。
PORTD |
PORTD7 |
PORTD6 |
PORTD5 |
PORTD4 |
PORTD3 |
PORTD2 |
PORTD1 |
PORTD0 |
初期値 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
プログラムで書くと以下のようになります。以下は'1'出力する場合です。
PORTD = 0x01;
・時間待ち
ここではforループで時間待ちをします。待ち時間は500ms位待ちます。
for( i = 0; i < 26500; i++);
緑色の数字をかえることで待ち時間を変えることができます。実際どのくらい待つかを確認するにはAVR Studioでシミュレータを動作させて待ち時間を確認します。
シミュレーション
デバッグ開始画面
メニューの[Debug]->[Start Debugging and Break]で左記画面となります。
forループ開始位置
forループ開始直前までステップオーバーで進めます。このところでプロセッサ画面のストップウオッチの時間を記録しておきます。私の場合は38usでした。
forループ終了位置
forループ実行後画面プロセッサ画面のストップウオッチの時間を読み取ります。
私の場合は503552usでしたので、約500ms待つことがわかります。
なおマイコンのクロックはデフォルトの1MHzです。
このマイコンのユーティリティに含まれています_delay_ms()の使用方法については後ほど説明します。
プログラムの実行
AVR Studioで作ったプログラムをマイコンに書き込みます。書き込み方法はについてはツール->プログラマを参照してください。
プログラムを書き込むと、LEDが約500ms毎に点灯、消灯を繰り返します。
4.2.スイッチの入力
フローチャート
最初にポートの初期化を行い、LEDを消灯させます。その後スイッチの状態を確認し、押されていればLEDを点灯し、押されていなければ消灯します。
プログラム
#include <avr/io.h>
int main(void)
{
uint8_t state_portd;
DDRD = 1 << DDD0; // ポートD0を出力に設定他は入力
PORTD |= 1 << PORTD0; // ポートD0に0を出力(LED消灯)
while(1)
{
state_portd = PIND & (1 << PIND6);
if ( state_portd == 0 )
{
PORTD |= 1 << PORTD0; // LED点灯
}
else
{
PORTD &= ~(1 << PORTD0); // LED消灯
}
}
} |
プログラムの説明
ポートからの入力は以下のようにPINDで8ビット分から必要なビットを取り出します。今回は6ビット目を必要とするのでPIND & (1
<< PIND6)で取り出します。
PIND |
PIND7 |
PIND6 |
PIND5 |
PIND4 |
PIND3 |
PIND2 |
PIND1 |
PIND0 |
初期値 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2.プログラムの実行
スイッチを押している間はLEDは点灯しています。スイッチを離すとLEDは消灯します。
スイッチによるLEDトグル
スイッチを押す都度に点灯と消灯を繰り返すソフトを作ります。スイッチには一般にチャタリングがあると言われています。フローチャートではスイッチのONまたは、OFFを検出した後に約10ms待ってから再度スイッチの状態を確認します。
最初にスイッチのチャタリングを測定します。その後にソフトを作ります。
チャタリング測定
左の回路でチャタリングをオシロスコープで測定します。スイッチは今回はタクトスイッチを使います。また、抵抗Rの値を代えて測定してみます。その結果を以下に示します。
スイッチ波形
左スイッチを押したときのスイッチの波形を示します。(この波形はR = 10kΩ)今回測定に使用したタクトスイッチではチャタリングを観測することはできませんでした。
R[Ω] |
電流[uA] |
チャタリング |
1k |
2200 |
観測されない |
10k |
238 |
観測されない |
100k |
24.4 |
観測されない |
1M |
2.4 |
観測されない |
フローチャート
上述のスイッチのチャタリングの実験において、チャタリングは観察することはできませんでした。一般には10ms程度チャタリング時間があると言われています。従ってこれを配慮して、フローチャートを作ります。
ポートの初期化の後LEDを点灯させます。
一回スイッチを押すとLEDは消灯します。再度スイッチを押すとLEDは点灯します。
ソフトウェアがスイッチを押したときと離したときの状態を検出することでLEDの点灯または消灯を行います。
プログラム
#define F_CPU 1000000UL // 1 MHz
#include <avr/io.h>
#include <util/delay.h> //_delay_ms()を使うためにインクルードします。
int main(void)
{
DDRD = 1 << DDD0; // ポートD0を出力に設定
PORTD |= 1 << PORTD0; // ポートD0に1を出力(LED点灯)
while(1)
{
if ( !(PIND & (1 << PIND6)))
{
_delay_ms(10);
while( !(PIND & (1 << PIND6)));
_delay_ms(10);
if ((PORTD0 & (1 << PORTD0))) // LEDが点灯していれば
{
PORTD &= ~(1 << PORTD0); // LED消灯
}
else
{
PORTD |= 1 << PORTD0; // LED点灯
}
}
}
} |
_delay_msは(10)は10ミリ秒待ちます。この関数を使うにはヘッダファイルdelay.hをインクルードしておきます。
スイッチを離してからLEDが点灯しているときは消灯に、消灯しているときは点灯にしたあとにスイッチを押されるのをまちます。
|
プログラムの実行
スイッチを押す毎に点灯または消灯繰り返します。
4.4.乱数をつくる
乱数を以下の方法で実験します。
1 |
タイマを使って乱数をつくります。 |
2 |
ライブラリ関数に含まれている関数RAND()を使って乱数をつくります。 |
4.4.1.タイマによる乱数
タイマ0をフリーランタイマで動作させて、スイッチを押した都度にタイマ値(TNT0)を読み出して乱数とします。読み出した値を6で割った余り0~5でさいころの目として変数resultに結果を入れるまでを確認します。
フローチャート
タイマ0をフリーランタイマで動作させて、スイッチを押した都度にタイマ値(TNT0を読み出して乱数とします。読み出した値を6で割った余り0~5でさいころの目として変数resultに結果を入れるまでを確認します。
プログラム
#include <avr/io.h>
#include <stdlib.h> // ランダム関数を使用するためにインクルードします。
#include <util/delay.h> // _delay_ms()を使用するためにインクルードします。
int main(void)
{
uint8_t result; // タイマ1の結果を保存する変数を宣言します。
while(1)
{
if ( !( PIND & 0x80)) // スイッチが押されるまでは何もしません。
{
_delay_ms(10); // チャタリング時間待ちます。
result =rand() % 6; // ランダム関数を読み出し6で割った余りをresultに保存します。
while( !(PIND & 0x80)); // スイッチを離すのを待ちます。
_delay_ms(10); // チャタリング時間待ちます。
}
}
} |
4.4.2.ランダム関数による乱数
ランダム関数を使用して、スイッチを押した都度にランダム関数を読み出します。。読み出した値を6で割った余り0~5でさいころの目として変数resultに結果を入れるまでを確認します。
フローチャート
#include <avr/io.h>
#include <stdlib.h> // ランダム関数を使用するためにインクルードします。
#include <util/delay.h> // _delay_ms()を使用するためにインクルードします。
int main(void)
{
uint8_t result; // タイマ1の結果を保存する変数を宣言します。
while(1)
{
if ( !( PIND & 0x80)) // スイッチが押されるまでは何もしません。
{
_delay_ms(10); // チャタリング時間待ちます。
result =rand() % 6; // ランダム関数を読み出し6で割った余りをresultに保存します。
while( !(PIND & 0x80)); // スイッチを離すのを待ちます。
_delay_ms(10); // チャタリング時間待ちます。
}
}
} |