ATtiny85のタイマーを使う [Arduino]
生のATtiny85とDigisparkとではタイマーの使い方が違っていました。
C:\Users\(user)\AppData\Local\Arduino15\packages\digistump\hardware\avr\1.6.7\cores\tiny\core_build_options.h より、
Digisparkでは、やんごとなき理由でミリ秒のカウント用にタイマー1を使っているそうです。
タイマー0を使うか、タイマー1の割り込みを殺すかになります。
PWM使用(8bit高速PWM(TOP=0xff), 分周なし)の時のATtiny85用設定の覚え。
(参考) wiring_private.h の cbi(), sbi()
DigisparkでWAVファイル配列化したものを再生する。
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ファイル配列化したものを再生する。
// 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
}