SSブログ

ATtiny85のタイマーを使う [Arduino]

生のATtiny85とDigisparkとではタイマーの使い方が違っていました。

C:\Users\(user)\AppData\Local\Arduino15\packages\digistump\hardware\avr\1.6.7\cores\tiny\core_build_options.h より、
/*
  For various reasons, Timer 1 is a better choice for the millis timer on the
  '85 processor.
*/
#define TIMER_TO_USE_FOR_MILLIS                   1

Digisparkでは、やんごとなき理由でミリ秒のカウント用にタイマー1を使っているそうです。

タイマー0を使うか、タイマー1の割り込みを殺すかになります。

PWM使用(8bit高速PWM(TOP=0xff), 分周なし)の時のATtiny85用設定の覚え。
// PLLCSR - PLL制御/状態レジスタ (PLL Control and Status Register)
PLLCSR = B00000110;             // 0x06 (初期値)

// TIMSK - タイマ/カウンタ割り込み許可レジスタ (Timer/Counter Interrupt Mask Register)
TIMSK &= ~_BV(TOIE0);           // Timer0の割り込みを殺す場合
TIMSK &= ~_BV(TOIE1);           // Timer1の割り込みを殺す場合

// TCCR0A - タイマ/カウンタ0制御レジスタA (Timer/Counter0 Control Register A)
//         COM0A1 | COM0A0 | COM0B1 | COM0B0 | - | - | WGM01 | WGM00
TCCR0A = B10100011;             // OC0x比較一致でLow (表11-3参照)
                                // WGM02-0:8ビット高速PWM動作 (TOP値 $FF) (表11-5参照)

// TCCR0B - タイマ/カウンタ0制御レジスタB (Timer/Counter0 Control Register B)
//         FOC0A | FOC0B | - | - | WGM02 | CS02 | CS01 | CS00
TCCR0B = B00000001;             // CS02-1:前置分周なし (表11-6参照)

// TCCR1 - タイマ/カウンタ1制御レジスタ (Timer/Counter0 Control Register)
//         CTC1 | PWM1A | COM1A1 | COM1A0 | CS13 | CS12 | CS11 | CS10
TCCR1  = B01100001;             //  PWM動作A許可, OC1A比較一致でLow, ~OC1Aピン接続断 (表12-1参照), CK×16 (PCK)
                                //  PWM1A=1, COM1A1=1, COM1A0=0, CS13=0, CS12=0, CS11=0, CS10=1

// GTCCR - 一般タイマ/カウンタ制御レジスタ (General Timer/Counter Control Register)
//         TSM | PWM1B | COM1B1 | COM1B0 | FOC1B | FOC1A | PSR1 | PSR0
GTCCR  = B01100000;             //  PWM動作B許可, OC1B比較一致でLow, ~OC1Bピン接続断 (表12-1参照)
                                //  PWM1B=1, COM1B1=1, COM1B0=0
//OCR1C - タイマ/カウンタ1 比較Cレジスタ (Timer/Counter1 Output Compare Register C)
OCR1C  = 255;                   //  OCR1Cはタイマ/カウンタ1の最大値(比較一致での解除値)

// 高速PWM動作時に COM0x1 を 0 で標準ポート動作 (OC0x切断) (WGM02=0のとき)
// 高速PWM動作時に COM1x1 を 0 で標準ポート動作 (OC1x切断)

// TIFR - タイマ/カウンタ割り込み要求フラグ レジスタ (Timer/Counter Interrupt Flag Register)
while( !(TIFR & _BV(TOV0)) );   // タイマー0がオーバーフローするのを待つ
TIFR |= _BV(TOV0);              // Timer/Counter0 Overflow Flag をクリア 
                                //   (論理1を書くことによってもTOV0は解除(0)できる)
while( !(TIFR & _BV(TOV1)) );   // タイマー1がオーバーフローするのを待つ
TIFR |= _BV(TOV1);              // Timer/Counter1 Overflow Flag をクリア
                                //   (論理1を書くことによってもTOV1は解除(0)できる)

// タイマーを殺したときのdelay
void delayCentiseconds16M5Hz( uint16_t ds ) {       // 16.5MHz動作時 0.01sec単位のdelay
  while( ds-- )  _delay_loop_2( 41250 );            // 1MHzなら 2500
}

void delayCentiseconds16MHz( uint16_t ds ) {        // 16.5MHz動作時 0.01sec単位のdelay
  while( ds-- )  _delay_loop_2( 40000 );            // 1MHzなら 2500
}

(参考) wiring_private.h の cbi(), sbi()
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

digispark_wav.jpg
DigisparkでWAVファイル配列化したものを再生する。
// Digispark(Default 16.5MHz)で WAVファイル(8bit, 4kHz, monoral) を配列化したものを再生する。
#include <util/delay_basic.h>   // _delay_loop_2() を使うため
#include <avr/pgmspace.h>       // PROGMEM使用時のお約束
#include "hello4.h"             // 音声データ hello4[]  =「こんにちは」8bit,  4kHz,  monoral

void setup() {
  pinMode( 1, OUTPUT);          //  OC1A(PB1)
  TCCR1 = B01100001;            //  8ビット タイマ/カウンタ1用レジスタ (PWM動作A許可, OC1A比較一致でLow, ~OC1Aピン接続断)
  OCR1C = 255;                  //  タイマ/カウンタ1 比較Cレジスタ
  TIMSK &= ~_BV(TOIE1);         //  タイマ/カウンタ1溢れ割り込み許可(TOIE1) 取り消し
}

void loop() {
  playWav( hello4 );            // hello4[]を再生
  delayCentiseconds16M5Hz(100); // 100センチ秒 = 1秒まつ 
}

void playWav( const uint8_t d[] ) { 
  uint8_t  cyc;                                     // 次の1byte読み込むまでのサイクル数カウンタ(cyc)
  uint16_t i, len = pgm_read_word_near(&d[0x28]);   // データサイズ
  for( i = 0x2c; i < len;) {
    if(!(cyc = ++cyc % 16))  OCR1A =  pgm_read_byte_near(&d[ i++ ]);  // 8kHzの音声なら 除数を16→8へ
    while( !(TIFR & _BV(TOV1)) );                   // タイマのオーバーフロー待ち (256cycle @ 16.5MHz≒15.5usec毎)
    TIFR |= _BV(TOV1);                              // Timer/Counter1 Overflow Flag をクリア
  }
}

void delayCentiseconds16M5Hz( uint16_t ds ) {       // 16.5MHz動作時 0.01sec単位のdelay
  while( ds-- )  _delay_loop_2( 41250 );            // 1MHzなら 2500
}

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。