ATtiny3224で裸の32bit周波数カウンタ
「tinyAVR2系」のATtiny3224(14pin)。
これに内蔵された2つの16bitタイマー、
TCB0とTCB1が連結できるということで、
32bitの周波数カウンタを試してみました。
こんな回路。
6pinと7pinに時計用水晶を接続。
ここから1Hzのキャプチャタイミングを得ます。
32bitにしたタイマでパルス入力をカウント。
1Hzサイクルでそのカウント値をキャプチャ。
周波数計測モードにすると、そのタイミングで
カウンタをゼロクリアしてくれます。
結果はPA1から9600BPSでシリアル出力。
こんな流れですが、チップのレジスタを操作するので
スケッチはけっこうややこしくなります。
// ATtiny3224 (14pin) 裸の周波数カウンタ
// 32.768kHz水晶を使用
// TCB0とTCB1を連結して32bitカウント
// PA1 シリアル出力データ 9600BPS
// PA2 キャプチャタイミング RTCによる1Hz
// PA4 測定クロック入力
#define CYC_HZ 1 // キャプチャ繰り返し周波数(Hz)
// 毎秒あたりのキャプチャ回数 (2^n)
// RTC.PER設定値
/***** RTC,PIT busy待ち *****/
// 両方とも0になるのを待つ
void rtcbusy(void) {
while((RTC.STATUS & 0x0F) || // RTC busy ?
(RTC.PITSTATUS & 1)); // PIT busy ?
}
/***** シリアル出力 *****/
void tx(char c)
{
while(!(USART0.STATUS & (1 << 5))); // DREIF 送信できるまで待つ
USART0.TXDATAL = c; // 1文字出力
}
/***** シリアル文字列出力 *****/
void txstr(const char *s)
{
while(*s != '\0'){ // Nullまで
tx(*s); // 1文字出力
s++; // 次文字
}
}
/***** 書式付シリアル出力 *****/
// long値は10桁
void txprintf(const char *s, ...)
{
va_list vp;
char bff[40]; // バッファを確保
va_start(vp,s);
vsnprintf(bff, sizeof bff, s, vp);
txstr(bff);
va_end(vp);
}
/***** SETUP *****/
void setup() {
cli(); // 割込禁止
PORTA.DIR = 0b11101110;
// |||||||+---- PA0 10pin UPDI
// +++++++----- PA4 in, 他 out
PORTB.DIR = 0b00000011;
// |||+---- PB0 9pin out
// ||+----- PB1 8pin out
// |+------ PB2 7pin TOSC2 32.768kHz
// +------- PB3 6pin TOSC1 XTAL
PORTA.PIN4CTRL = PORT_PULLUPEN_bm; // PA4 pull up
// 測定クロック入力
// 32.768kHz XTAL
// _PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, 0x00); // XTALオフ
// while(CLKCTRL.MCLKSTATUS & (1 << 6)); // XOSC32KSの0を待つ
// リセット起動なら↑の手順は不要 いきなりオン↓でOK
_PROTECTED_WRITE(CLKCTRL.XOSC32KCTRLA, 0b00000001); // 発振イネーブル
// || ||+-- ENABLE
// || |+--- RUNSTBY
// || +---- SEL
// ++------ CSUT 1kサイクル
// CSUT 01で16kサイクル、10で32k、11で64kサイクルの起動時間待ち
rtcbusy(); // RTC,PIT ビジー待ち
RTC.CLKSEL = 0b00000010;
// ++---- XOSC32K 32.768kHzHz水晶
RTC.CTRLA = 0b00110001;
// ||||| +---- RTCEN RTC周辺機能許可
// |++++------- CLK_RTC 1/64 →512Hz
// +----------- RUNSTBY
RTC.PER = (512 / CYC_HZ) - 1; // 512/512 →1Hz
RTC.PITCTRLA = 0b01010001;
// |||| +-- PITEN PIT有効
// ++++----- PERIOD 32.768kHz/2048 →16Hz
// イベント制御: TCBクロック入力
EVSYS.CHANNEL0 = 0x44; // ch0入力:PA4
EVSYS.USERTCB0COUNT = 1; // ch0出力:TCB0 クロック
// イベント制御: TCBキャプチャ
// RTCのオーバーフローでキャプチャ
// PA2(EVOUTA)にタイミングを出力
EVSYS.CHANNEL1 = 0x06; // ch1入力:RTC_OVF
EVSYS.USERTCB0CAPT = 2; // ch1出力:TCB0 CAPT入力
EVSYS.USERTCB1CAPT = 2; // ch1出力:TCB1 CAPT入力
EVSYS.USEREVSYSEVOUTA = 2; // ch1出力:EVOUTA(PA2) 出力
// 32bitカウントのためにTCB0のオーバーフローをTCB1のクロックに
EVSYS.CHANNEL2 = 0xA1; // ch2入力:TCB0オーバーフロー出力
EVSYS.USERTCB1COUNT = 3; // ch2出力:TCB1クロック入力
// タイマー TCA0,TCB0,TCB1割り込み禁止
TCA0.SINGLE.INTCTRL = 0;
TCB0.INTCTRL = 0;
TCB1.INTCTRL = 0;
// タイマーTCB0 (下位側)
// RTCのオーバーフローでキャプチャ
// カウントクロックはPA4 EVSYS_USERTCB1COUNTで
TCB0.CTRLA = 0b00001111;
// ||||||+-- ENABLE
// |||+++--- CLKSEL EVENTでクロック
// ||+------ SYNCUPD
// |+------- CASCADE
// +-------- RUNSTBY
TCB0.CTRLB = 0b00000011;
// ||| +++-- CNTMODE 周波数測定
// ||+------ CCMPEN
// |+------- CCMPINIT
// +-------- ASYNC
TCB0.EVCTRL = 0b00000001;
// | | +-- CAPTEI キャプチャ有効
// | +------ EDGE
// +-------- FILTER
// タイマーTCB1 (上位側)
// 32bit動作 EVSYSを通じてTCB0のOVFでカウント
TCB1.CTRLA = 0b00101111;
// ||||||+-- ENABLE
// |||+++--- CLKSEL EVENTでクロック
// ||+------ SYNCUPD
// |+------- CASCADE 連結して32bitカウント
// +-------- RUNSTBY
TCB1.CTRLB = 0b00000011;
// ||| +++-- CNTMODE 周波数測定
// ||+------ CCMPEN
// |+------- CCMPINIT
// +-------- ASYNC
TCB1.EVCTRL = 0b00000001;
// | | +-- CAPTEI キャプチャ有効
// | +------ EDGE
// +-------- FILTER
// シリアル
// 9600BPSでシリアル出力 受信はなし
USART0.CTRLB = 0b01000000;
// || ||||+-- MPCE
// || ||++--- RXMODE
// || |+----- ODME
// || +------ SFDEN
// |+-------- TXEN 送信有効
// +--------- RXEN 受信はしない
USART0.CTRLC = 0b00000011;
// |||||+++-- CHSIZE 8BIT
// ||||+----- SBMODE 1stop
// ||++------ PMODE Parity無し
// ++-------- CMODE 非同期USART
USART0.BAUD = (4L * F_CPU) / 9600L; // 20MHzなら8333
PORTMUX.USARTROUTEA = 0b00000001; // 代替ポート
// ||++-- USART0 TXをPA1に
// ++---- USART1
sei(); // 割込有効
}
// 表示用データ
byte f_disp; // 周波数表示指令
byte cnt_pit; // PITタイミング
// 16Hz upカウント
/***** LOOP *****/
void loop() {
word d0, d1; // TCB0,TCB1 16bitキャプチャ値
uint32_t d; // 32bitデータ
txstr("RTC Test\r\n");
txprintf("%luHz\r\n", F_CPU); // 動作周波数
while(1){
if(TCB0.INTFLAGS & 1){ // キャプチャ? (1Hzサイクル)
TCB0.INTFLAGS = 1;
d0 = TCB0.CCMP; // 1Hzごとのキャプチャデータ
d1 = TCB1.CCMP; // 上位16bit
d = ((uint32_t)d1 << 16) | (uint32_t)d0; // 32bitに
f_disp = 1; // 表示指令
cnt_pit = 0; // PIT 16Hzカウンタクリア
}
if(RTC.PITINTFLAGS & 1){ // PIT 16Hz
RTC.PITINTFLAGS = 1;
cnt_pit++; // PITカウンタ +1
if(cnt_pit >= 18){ // 18/16サイクル
txstr("No clock\r\n"); // クロックなし表示
cnt_pit = 0; // PIT 16Hzカウンタクリア
}
}
if(f_disp){ // 表示タイミング
f_disp = 0;
txprintf("%10luHz\r\n", d); // Hzで
}
}
}
で、分かったこと。
・ATtiny1系のように、
「クロックがないとキャプチャしてくれない」
は、大丈夫。クロックがなくてもキャプチャして
くれるので、何もせずに「0Hz」が出てきます。
・ゼロクリアのタイミングも大丈夫。
カウントが1つ減るということもありませんで
した。
・スケッチでは、イベント関連レジスタの構成が
tiny1系と変わっていました。ややこしい。
ヘッダーファイル:iotn3224.hを捜索
※tinyAVR1系のATtiny1614での実験
・ATtiny1614で(裸の)周波数カウンタ
・ATtiny1614で周波数カウンタ:キャプチャタイミング
この↑記事のようにtinyAVR1系で周波数カウンタを
作ろうとすると、けっこうめんどう。
そして、キャプチャタイミングがとクリアのタイミングが
カウントクロックで刻まれるのがなんともあきません。
ということで、周波数カウンタを作るならtinyAVR2系を
どうぞ。
16bitカウンタなら1Hzのゲートで65535Hzがmaxで、
それを越えるカウント値はオーバーフローを見ておか
ないといけません。
それが連結して32bitになると、まぁラクチンに。
・ブレッドボードでの試運転中の様子
※自分のクロックを計ってみる
TCB0(下位側)のクロック入力設定をこのように
変えれば、内蔵クロックを計ることができます。
//TCB0.CTRLA = 0b00001111; // 外部クロックを計る
// ||||||+-- ENABLE
// |||+++--- CLKSEL EVENTでクロック
// ||+------ SYNCUPD
// |+------- CASCADE
// +-------- RUNSTBY
TCB0.CTRLA = 0b00000001; // ←これに変えてクロック源をCLKPERに
そして、Arduino IDEのツール設定を触ります。
こんな結果。
F_CPU=20000000Hz
19283.520kHz
20045.993kHz
20049.757kHz
20049.887kHz
20049.439kHz
20051.664kHz
F_CPU=16000000Hz
15391.121kHz
16001.330kHz
16002.410kHz
16001.726kHz
16002.778kHz
16001.499kHz
16003.342kHz
F_CPU=10000000Hz
9641.545kHz
10023.248kHz
10024.757kHz
10025.080kHz
10024.272kHz
10022.962kHz
F_CPU=5000000Hz
4824.711kHz
5010.271kHz
5010.384kHz
5009.681kHz
5010.371kHz
5009.503kHz
5009.354kHz
けっこう変動していました。
※RTC用水晶の発振周波数(32.768kHz)を調べたい
・ATtiny3224で裸の32bit周波数カウンタ #2
| 固定リンク
「ATtiny」カテゴリの記事
- 備忘録:1Hzパルス発生回路(2026.02.01)
- ATtiny3224で裸の32bit周波数カウンタ #2(2026.01.25)
- ATtiny3224で裸の32bit周波数カウンタ(2026.01.22)
- ATtiny3224のタイマでは・・・言うことを聞かないゾ(2026.01.10)
- ATtiny1614:タイマレジスタの初期設定を見る(2026.01.06)


コメント