2014年3月22日土曜日

電子部品と、Eagleライブラリの部品の対応

とっても探しにくいEagleの部品群。 また探すのが面倒なのでメモっておきます。

ターミナルブロック


秋月のこれとか  -> con-phoenix-508.lbr  MKDSN1,5/2-5.08

抵抗


普通のカーボン抵抗とか -> resistor.lbr  R-US_0207/7

コンデンサ


パスコンとかによく使う0.1uFのやつとか -> resistor.lbr C-EU050-025X075
電解コンデンサ(ルビコンの4.7uFとか) -> resistor.lbr CPOL-EUE2-5
            (後ろの数字は、穴ピッチ、ケース径。2-5は、穴ピッチ2mm、ケース径5mm)

クリスタル


HC-49/S(秋月のこれとか) -> crystal.lbr CRYSTALHC49S

7セグメントLED


高輝度カソードコモン(秋月のこれ) -> display-hp.lbr  HD-H103

スイッチ


タクトスイッチ(秋月のこれ) -> switch-omron.lbr  10-XX

2014年3月4日火曜日

AVRの16ビットタイマで時間の計測

AVRのマイコンにはたいてい、8ビットタイマーと16ビットタイマーの2つが用意されています。
私の場合、8ビットタイマーはPWMに使って、16ビットタイマーは時間の測定に使うことが多いです。

その、16ビットタイマーで時間の測定をするメモです。

時間の測定ということは、あるタイミングでカウンタをカウントしていくわけですが、16ビットタイマーでは当然16ビット分しかカウントできません。
カウントするタイミングは、最大1024分周、つまり1024クロックごとに1カウントという設定ができますから、
最大で計れる時間は、例えばクロックが16MHZの場合、

65535 * 1024 / 16,000,000 = 4.19424(秒)

4秒程度しか計れません。これではとても役に立たない。

それではどうするかというと、タイマカウンタが65535に達して、次に桁あふれした際に、割り込みを起こすことができます。
その割り込みを起こす度に、4.19424秒を、それまでの経過時間として、どこかに足し込んでいけばいいわけです。


具体的にはこんな感じです。
以下、ATMega328Pの16ビットタイマである、タイマ1を使う前提での説明です。
//CPUのクロック周波数
#define F_CPU 16000000
int32_t msec;//ミリ秒で保存します。

ISR(TIMER1_OVF_vect)  //タイマ1割り込みの定義
{
  msec += (65536L * 1024L * 1000L) / F_CPU ;
}
これは、ミリ秒にするために1000を掛けていますが、これでは大きくなりすぎて、32ビット数値を桁あふれしてしまいます。 そこで、予めCPU周波数を1000で割った数字を用意し、これで割りましょう。
//CPUのクロック周波数を1000で割ったもの
#define F_CPU_DIV1000 16000

int32_t msec;//ミリ秒で保存します。

ISR(TIMER1_OVF_vect)  //タイマ1割り込みの定義
{
  msec += (65536L * 1024L) / F_CPU_DIV1000 ;
}
そして、あとはタイマ関係のレジスタに設定してやれば、その通り動きます。
クラスにしてみました。ATMega328P、Atmel Studio 6.0で確認済み。
Timer.h
#include <stdint.h>

class Timer {
public:
 //タイマを設定し、起動する。
 void StartTimer();

 //タイマを起動してからの経過時間をミリ秒で返す。
 int32_t GetTime();

 //タイマを終了する。
 void EndTimer();
 
 //時間を0にリセットする。
 void ResetTime();
};
Timer.cpp
#include "Timer.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/common.h>

#define F_CPU 16000000L
#define F_CPU_DIV1000 16000L

volatile int32_t current_msec;


void Timer::StartTimer()
{
 current_msec = 0;

 //割り込み禁止
 cli();

 //タイマ/カウンタ1制御レジスタAの設定
 //(タイマ用ピンとして使わない)
 TCCR1A = 0x00; //タイマ関係のピンは標準ポート動作とする
 
 //タイマ/カウンタ1制御レジスタBの設定
 TCCR1B = (1<<CS12)|(1<<CS10);//1024分周でタイマON
 
 //タイマ/カウンタ1割り込みマスク レジスタ設定
 TIMSK1 = (1<<TOIE1);//タイマ1オーバーフロー割り込み許可
 
 //割り込み許可
 sei();
}

int32_t Timer::GetTime()
{
 //16ビットレジスタの読み書きの際には、テンポラリレジスタを使用する。
 //このため、割り込み禁止操作が必要。

 //ステータスレジスタを一時保存する変数
 uint8_t sreg;

 //ステータスレジスタを保存
 sreg = SREG;

 //割り込み禁止
 cli();

 //現在のタイマ値を取得
 uint16_t t = TCNT1;

 //SREGを戻す。これによって割り込み禁止状態が戻る。(SREGのIビットを戻すから)
 SREG = sreg;
 
 int32_t tw = (int32_t)t;
 
 return current_msec + (tw * 1024) / F_CPU_DIV1000;
}

void Timer::ResetTime()
{
 uint8_t sreg;
 sreg = SREG;
 cli();

 //タイマを0にする
 TCNT1 = 0;
 SREG = sreg;

 //経過時間も0にする
 current_msec = 0;
}

void Timer::EndTimer()
{
 TCCR1B = 0;//タイマoff 
}

ISR(TIMER1_OVF_vect)  //タイマ割り込み
{
 current_msec += (65536L * 1024L)/ F_CPU_DIV1000;
}
使い方は、どこかにTimerの実体を定義しておいて、
#include "Timer.h"
Timer timer;

//タイマ起動時に一回だけ実行
timer.StartTimer();

...
...

//時刻を知りたいとき
int32_t time_elapsed = timer.GetTime(); 

...
...

//時間をリセットしたいとき
timer.ResetTime();

...
...

//タイマーが不要になったとき
timer.EndTimer();

こんな感じですかね

あ、そうそう、SREGはavr/common.hに定義されていますので、avr/interrupt.hと合わせてインクルードしておきましょう。
それと、当然ですが、この方法でもミリ秒で符号付き32ビットがいっぱいになるまでしかカウントできませんので、
0x7FFFFFFF / 1000 / 3600 / 24 = 24日程度でいっぱいになります。まあ私の用途では十分です。