2014/03/30

タイマ割込みでLED点滅(AVR)

 前回の記事はLED点滅タイミングに_delay_ms()関数を使いましたが、今回はAVR内蔵タイマを使ってみたいと思います。ついでに外部クロックも使ってみたいと思います。

ゴール


     
  • LEDの点滅周期は1秒(点灯0.1秒、消灯0.9秒)
  • 点灯、および消灯のタイミングをタイマ割込みで行う  
  • 外部クロック(20MHz X'tal)を使う

タイマ割込みについて


 ATmega168は8bitタイマと16bitタイマを持っていますが、秒単位の長い時間を測るには8bitでは足りないので16bitタイマを使うことにします。タイマをピンに出力し直接LEDを制御しても良いのですが、ここではタイマ割込みを使います。
 タイマ/カウンタのブロック図が参考文献1のFigure16-1(16-bit Timer/Counter block Diagram)にあります。眺めてみると、タイマ値TCNTnと比較レジスタOCRnAが一致した時に割込みを発生させることができるようです。同様にOCRnBとの一致でも割込みを発生できるようです。タイマのクリアは"Control Logic"で行うようですが、この"Control Logic"に"TOP"という入力があり、OCRnAと一致した時に発生できるようです。
 TOPになるとタイマ値をクリアできれば指定した時間周期での割込みが発生できそうです。このような使い方はタイマをMode=4(CTC/OCR1A)にするとできるようです。このモードはOCR1Aレジスタに設定した値になるとカウンタが0に戻ります。(詳細は参考文献1の16.9.2章を参照のこと)
 これで2種類のタイマ割込みが用意できました。OCR1Aを1秒にOCR1Bを0.1秒に設定すればそれぞれの割込みでLED点灯、LED消灯ができます。
 ではOCR1A/OCR1Bにどんな値を設定すれば良いでしょうか? それにはタイマの1カウントがどのくらいの時間かを知る必要があります。タイマの1カウントは1クロック分なのでタイマに入力するクロックを調べる必要があります。

タイマに入力するクロックについて


 前述のタイマ/カウンタのブロック図を引き続き眺めてみます。"Control Logic"の入力に"clkTn"があります。ここがタイマに入力するクロックのようです。外部ピン入力とプリスケーラ入力から選択できるようです。ここではプリスケーラ入力を選びます。プリスケーラは参考文献1のTable 16-5(Clock Select Bit Description)によるとclkI/Oを1/1024まで分周できるようです。clkI/Oは参考文献1のFigure9-1(Clock Distribution)によると"AVR Clock Control Unit"の出力です。
 "AVR Clock Control Unit"の入力であるSystem ClockはFuseビットで設定します。Fuseビットの設定はAVRISPmkIIで書き込みをする時に設定します。Fuse設定は以下の3点を変更しました。
  • BODLVEL=4V3
  • SUT_CKSEL=EXTFSXTAL_1KCK_14CK_0MS
  • CKDIV8にチェックを入れる
 ここだけはAtmel Studioを使って書き込みをしています。この設定により外部クロック(20MHz X'tal)を使い、内部で1/8(つまり2.5MHz)にしてからタイマにclkI/Oが供給されます。
 プリスケーラの設定(参考文献1のTable16-5)を幾つにするかですが1/64にしました。これでタイマのクロックは39.0625KHz(=2.5MHz/64)になります。1秒タイマにするにはOCR1A=0x9897(=39,063)にします。

LEDについて


 LEDは前回と同じくPORT Bのbit5に接続されているLEDを使います。

プログラムについて


 例によってdebian環境(avr-gcc+avr-libc)で作成しています。

リスト1 main.c

/*
 * LED点滅プログラム(タイマ割込み使用)
 *
 * For AE-ATmega board
 *
 * Ver. 1.0 2014/03/30
 */
#include <avr/io.h>
#include <avr/interrupt.h>

void init_timer1(void) {

  /*
   * タイマ設定
   * 外部クロック   : 20MHz (X'tal)
   * CKDIV8=1       : 20MHz / 8 = 2.5MHz
   * プリスケーラ   : 2.5MHz / 64 = 39,062.5 Hz
   * 比較レジスタ1A : 39,063 ( 39,063/ 39,062.5 = 1.000013 Hz)
   * 比較レジスタ1B :  3,906
   * 波形発生モード : CTC(OCR1A)
   */

  /*
   * 比較レジスタ1Aで1秒毎に割込みを発生させる。→LED ON
   * 比較レジスタ1Bは1A割込み発生の0.1秒後に割込みが発生。→LED OFF
   */

  TCCR1A = 0x00;
  TCCR1B = _BV(WGM12) | _BV(CS11) | _BV(CS10);
  TCCR1C = 0x00;
  OCR1AH = 0x98;        // 16bitレジスタなので上位バイトを先にライトする
  OCR1AL = 0x97;        // OCR1A=39,063=0x9897
  OCR1BH = 0x0F;        // OCR1B=3,906=-0x0F42
  OCR1BL = 0x42;
  TIMSK1 = ( _BV(OCIE1B) | _BV(OCIE1A) );
  sei();                // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {
  PORTB |= _BV(PB5);    // LED ON
}

ISR(TIMER1_COMPB_vect) {
  PORTB &= !(_BV(PB5)); // LED OFF
}

int main(void) {

  DDRB  = _BV(DDB5);    // PORTB bit5を出力に設定

  init_timer1();        // タイマ初期化
  while(1);             // 無限ループ(割込み待ち)
}

参考文献


  1. Atmel ATmega48P/V 88P/V 168P/V datasheet
  2. avr libcのinterrupt.h解説

2014/03/22

AVR開発環境インストールからLED点滅まで(AVRマイコンボード)

 AVRの開発環境として通常は"Atmel Studio"を使うと思いますが、ここではdebianで使える物を選択してみました。Linux用のCコンパイラを探してみたところ、avr-gcc + avr-libcが使えるようです。debinの場合、以下のコマンドでインストールできます。
# apt-get install gcc-avr
# apt-get install binutils-avr
# apt-get install avr-libc
# apt-get install avrdude

LED点滅プログラムについて


 ATmega168/328マイコンボード回路図を見るとマイコンのPB5端子の先にLEDが接続されています。PB5にHを出力すればLEDが点灯します。このLEDを点滅するプログラムを書いてみます。

リスト1 blink_LED.c

/*
 * LED 点滅プログラム
 *
 *  K-04590 (ATMEGA168/328用マイコンボード)
 *
 */
#include <avr/io.h>
#include <util/delay.h>

#define PB5_LED   5             // H: LED ON | L: LED OFF

int main(void) {

  DDRB   = _BV(PB5_LED);        // Port B bit 5の方向を出力に設定
  PORTB |= _BV(PB5_LED);        // Port B bit 5にHを出力(LED点灯)

  while (1) {
    _delay_ms(500);             // 0.5秒待つ
    PORTB ^= _BV(PB5_LED);      // Port B bit5を0/1反転
  }
}
 0.5秒毎にLED点灯、LED消灯を繰り返すプログラムです。_delay_ms()や_BV()はavr-libcで定義されています。詳細は"AVR Libc Home Page"に説明があります。

F_CPUについて


 _delay_ms()を使うには事前にマクロ"F_CPU"を定義しておく必要があります。F_CPUにはCPUのクロック周波数を設定します。ソースコードの中に書いても良いのですが、ここではMakefileの中に定義しました。マイコンのクロック周波数は1MHzです。これは工場出荷時はクロックソースとしてマイコン内蔵オシレータ(8MHz)が選択されており、さらにCKDIV8オプションが設定されているため、1MHz(=8MHz÷8)になっているためです。したがってF_CPU=1000000を設定します。

リスト2 Makefile

PROG=blink_LED
OBJS=${PROG}.o
MCU=atmega168p
F_CPU=1000000UL # internal 8MHz clock & CKDIV8 fuse bit enabled
#--------

ELF=$(PROG)
IHEX=$(PROG).ihex

CC=avr-gcc
CFLAGS=-g2 -O1 -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall
LDFLAGS=-g2 -mmcu=$(MCU)
OBJCOPY=avr-objcopy

all: $(IHEX)

$(IHEX): $(ELF)

$(ELF): $(OBJS)

%.ihex: %
        $(OBJCOPY) -j .text -j .data -O ihex $< $@

clean:
        -$(RM) $(ELF) $(IHEX) $(OBJS)

flash:
        sudo avrdude -c avrispmkII -P usb -p atmega168p -U flash:w:$(IHEX)

.PHONY: all clean
 Makefileを書くのは正直苦手で適当に書いています。(すみません)

リスト3 コンパイル&マイコン書き込み

$ make
avr-gcc -g2 -O1 -mmcu=atmega168p -DF_CPU=1000000UL  -Wall   -c -o blink_LED.o blink_LED.c
avr-gcc -g2 -mmcu=atmega168p  blink_LED.o   -o blink_LED
avr-objcopy -j .text -j .data -O ihex blink_LED blink_LED.ihex

$ make flash
sudo avrdude -c avrispmkII -P usb -p atmega168p -U flash:w:blink_LED.ihex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e940b
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "blink_LED.ihex"
avrdude: input file blink_LED.ihex auto detected as Intel Hex
avrdude: writing flash (166 bytes):

Writing | ################################################## | 100% 0.05s

avrdude: 166 bytes of flash written
avrdude: verifying flash memory against blink_LED.ihex:
avrdude: load data flash data from input file blink_LED.ihex:
avrdude: input file blink_LED.ihex auto detected as Intel Hex
avrdude: input file blink_LED.ihex contains 166 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.05s

avrdude: verifying ...
avrdude: 166 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

$
 debianマシンにAVRISPmkII経由でAVRマイコンボードを接続し、"make flash"と入力すればマイコンにプログラムが書き込まれます。

2014/03/11

AVRマイコンボードを購入しました

 秋月電子で「ATmega168/328マイコンボードキット」と「Atmel AVRISPmkII」を購入しました。合計で5,300円です。
 このマイコンボードはブートローダを書き込むとArduino互換ボードとして使えるようですが、まずはそのままで遊ぼうと思っています。

搭載されるマイコンのスペック
メーカAtmel
品番ATmega168P-20PU
Flash16kbytes
EEPROM512bytes
RAM1kbytes
Speed0-20MHz @ 4.5V~5.5V (※1)
0-10MHz @ 2.7V~5.5V
Power2.7V~5.5V (※2)
動作温度-40℃~85℃
※1:マイコンボードでは外部X'talとして16MHz品と20MHz品の2種類が入っており、
   好きなほうを実装できます。私は20MHz品を実装しました。
※2:マイコンボードでは5Vに接続されています。

★気づき点

マニュアルの回路図にミス?がある
 コネクタX4の3pin(M8RXD)、1pin(M8TXD)の飛び先がない。→実際はATMEGA168の2pin、3pinに接続されているようです。(参考:eJackino回路図
・USBシリアル(AE-UM232R)基板上のジャンパJ2は外しておいたほうが良いかも?

 マイコン基板の電源(5V)はUSB経由とACアダプタ経由を選択できるが、J2を接続し、PWR-SELをEXTに設定すると、USB経由の5VとACアダプタ経由の5Vが直結されあまりよくない。


【備忘録】時系列データの編集方法(R言語, tidyverse)

TimeSeries.knit 1 サンプルデータ作成 2 日付単位に集計する 2.1 月毎集計 2.2 四半期毎集計 ...