ATtiny

2025年12月 4日 (木)

ブザー報知周波数でマイコンの動作クロックを探る#2

ブザー報知周波数でマイコンの動作クロックを探る
この続き。

ここでは「コンデンサマイク」を使ってブザーの周波数を
拾ったのですが、マイクじゃなく「発音体」をマイク
がわりに使うほうが良い結果が得られました。

例えば、2kHzあたりなら「マグネチック・サウンダ」。
4kHzあたりなり「圧電サウンダ」と、発声側と同じ
ものがマイクとしても使えるのなら、2倍の高調波成分
を気にせずに、うまく波形を拾ってくれました。

マイクとは別経路で発音体をつなぎます。
Frq31

※関連
「圧電発音体」という呼称

| | コメント (2)

2025年10月31日 (金)

ブザー報知周波数でマイコンの動作クロックを探る

マイコンの動作クロック、水晶発振させていれば
それを信じれば良い(ちゃんと動いていたら)のですが、
内蔵クロックで動いているマイコンの動作周波数を、
回路をケースに組み込んだ後で調べるとなると、
ちょいと面倒です。

そこでこんなジグを考えてみました。
 ・マイコンのクロックでブザー駆動周波数を決めている
  回路に限定。 (ケースに入れてからも音を拾える)
 ・ピーとかプーとかのブザー報知をマイクで拾って
  周波数カウンタで計る。
 ・内蔵クロックからの分周比が分かれば
  報知周波数からクロック周波数を逆算できる。

マイクを増幅して周波数カウンタにつなげばよいのですが、
せっかくですんで、専用回路を考えてみました。
   まだ、バラックでの試作段階

・32.768kHz水晶を乗せたATtiny1614を使う。
  ATtiny1614につないだ32kHz水晶発振子、隣のピンの影響を受けるみたい

・タイマーは16bitのTCBを使う。
  ATtiny1614で(裸の)周波数カウンタ
   これは12bitのTCDを使った

・マイクアンプにMCP6021を使う
  8pinのシングル・オペアンプ MCP6021、MCP6023

・液晶はI2Cインターフェースの8文字x2行の
  8文字×2行のI2C液晶表示器に注意

・マイク入力レベル表示に計装アンプを使った両波整流回路を。

とりあえず、こんな回路。
Aa1_20251031152801

・バラック状態で実験
Bb11_20251031161401
周波数の最大表示は99.999kHz。
4Hz周期で測定して、4回を積み上げて1Hz単位の
表示に。

初めて使ったICが
・MCP6021   VREF付のオペアンプ
   10月30日に紹介
・74LVC1G3157 アナログ・マルチプレクサ
   7月17日に紹介
INA350A  計装アンプ
   https://www.ti.com/jp/lit/ds/symlink/ina350.pdf

INA350Aは10倍あるいは20倍を切り替えできる計装アンプで、
むちゃ安価なんです。 DigiKeyで79円
INA350Cになると30倍と50倍を設定できます。
そしてINA351になるとREF入力にバッファアンプが
入っていて、ベースとなる基準電圧を自由に設定
できます。 これが104円。
使用できる電源電圧範囲が1.8V~5.5Vと低いのが欠点ですが、
差動で電圧を増幅したいとき、便利に使えます。
   使ったのは今回初めて

この計装アンプとマルチプレクサで整流回路を作って
みたのです。
  周波数計測には関係ないけど、入力レベルを
  表示したかったので。

とりあえずスケッチ。 長いので圧縮
  ・ダウンロード - tiny1614_fcnt_lcd02.zip


※例えば
マイコン型導通チェッカーだと、ATtiny25Vの内部クロック
1MHzを1/4して250kHzを作り、それを1/125して2kHzを
作り、定格周波数2048Hzのブザー(GT-111P)を駆動して
います。
この報知周波数を計って500倍すればマイコンの駆動周波数
がわかります。

※あれこれ分かったこと
このGT-111Pですが、定格周波数で鳴らしたとき、
2kHzの波より倍の4kHzの成分のほうが大きく
出るのです。
そのため、作った周波数カウンタだと2kHzじゃなく
4kHzの表示がでます。
ブザーの穴をテープなどで押さえれば2kHzの成分が
勝って、ちゃんと表示するようになります。
これは、回路のせいじゃなく、ブザーの特性です。

・中心穴開放の時
B0000
2kHzより4kHzの成分のほうが大。

・中心穴をセロテープで塞いだとき
B0001

※各部の波形を観察

・発音体の穴を開放→4kHzの成分が大
Cc000_20251102151601
パルス間隔は不連続に見えるが「2ms間に8パルスは安定」
しているので、周波数測定値は安定して4kHzを表示する。

・発音体の穴をセロテープでふさぐ→2kHzに
Cc001_20251102151601

・発振器から4kHzの正弦波を入力
 Cb1

| | コメント (0)

2025年10月20日 (月)

ADCの機能差 ATtiny402とATtiny1614

ATtiny402サンプル:"Wire.h"を使わずI2Cで液晶表示 AQM1602だと
で、
  連続変換モード(自由走行という表現)というのが
  あって、変換を始めたらいつでも最新値を読み出
  せる。
っと、記しましたがAVR1シリーズのATtiny1614には
この機能がありません。
ATtiny402のようにほったらかしではA/D値が得られ
ません。

※関連レジスタ。
0b

「FREERUN」をオンしておくと、勝手にA/D変換が
行われて、変換完了をチェックしなくてもいつでも
最新値(ADC0.RES)が読め出せます。
  ※単一チャンネルの値を読む場合

ところがATtiny1614にはこの制御ビットがありません。

1b
面倒ですが、
 ・A/D変換開始を指令。
 ・変換完了を待つ。
を行わなければなりません。
しかし、「自由走行」の代役として、イベントでの
変換開始指令が行えるようになっています。
インターバル・タイマをイベントとして選択し、
その出力チャンネルをAD変換のトリガに使うの
です。
すると、変換指令を行わなくても周期的にA/D値を
得ることができます。

A/D変換チャンネルを変えながらA/D値を入力すると
なると、割り込み処理で
  チャンネル設定・変換開始・結果入力
を周期的にしなくちゃなりませんが単一チャンネル
で良いのなら、簡略化できます。

このあたり、使えそうな「ライブラリ」を探すより、
ハードウェアマニュアルをにらめっこしながら便利な
機能を見つけるほうが面白いかと。

※重要
ATtinyのADCについて、こんな記事をまとめていたら・・・
英文のATtiny1614データシートには「FREERUN」指定ビット
が出ていました。
ちゃんちゃん。
1c_20251020092901

「データーシートは最新のを読むんだ!」が教訓かと。


| | コメント (0)

2025年10月 6日 (月)

ATtiny1614につないだ32kHz水晶発振子、隣のピンの影響を受けるみたい

ATtiny1614のRTC機能を水晶発振で使う時、
6pin(PB3) 7pin(PB2)に水晶をつなぎます。
Ffc100
14ピンのICなので7ピンの右は空間ですが
6ピンの左にポートPA7が来ます。
水晶を発振させて正確なクロックを期待し
ているとき、PA7にパルスを出すと発振状態が
影響をうけるようで、微妙に周波数が変わる
のです。

RTCの周波数はオーバーフロー信号をイベント出力に
出してオシロとカウンタでチェック。
PA7にパルスを出す(オーバーフロー信号に重なるような)
とひどい変動が生じます。

※経過
2025年10月3日:ATtiny1614で(裸の)周波数カウンタ
では、この図のように
Fc41
XTALと離れた(場所)PB0とPB1に
(8pinと9pin)にタイミングチェック用パルスを出して
いました。
  XTALの隣のPA7はLのまま

そこに液晶でカウント値を表示しようと、PB0とPB1のライン
(SDA、SCL)にI2C接続の液晶をつないだのです。

Fcnt41
チェック用パルスも欲しいので、空きピンのPA6とPA7に
出力する場所を移しました。
すると・・・周波数の変動が発生。
原因を追いかけるとPA7が出力するパルスだったのです。

PA7が静かだとPA2に出力しているRTCクロックを
分周したオーバーフロー信号は変動なく一定の間隔
で出力されます。
Ffc11

ところが、測定クロックを与えてPA7にキャプチャ
タイミングチェック用パルスを出力すると、測定周波数
がパラパラと大きく変動。
追いかけてみるとRTCクロックそのものが動いていました。
Ffc000

RTCオーバーフロー信号の周期だけでなくパルス幅も
変化し、これでは周波数の計測はできません。
PA7を静かにしておくと(チェックパルスを出さないよう)、
この変動はなくなります。
水晶の隣接ピンの影響としか思えないのです。

ATtiny1614を乗せているピッチ変換基板と
ユニバーサル基板の配線を変えてみてどうなるか、
試してみます。
  IPAで洗っただけではダメ。
  もっと短くっということか・・・


※参考
ATtiny1614 Frequency Meter:John BradnamPublished March 22, 2021
ここでの回路を見ますと、水晶の隣のPA7をGNDに落としてます。
Fcnt42
ATtiny1614での32kHz水晶発振、何か「秘技」があるの
かもしれません。

※配線変更
元のピッチ変換基板とユニバーサル基板
Fcd2
ユニバーサル基板側に水晶とコンデンサをハンダしてました。
それを取り外し、ピッチ変換基板側に水晶+コンデンサを
乗せてみました。
6ピン、7ピンのピンヘッダは抜いて水晶の信号が
外に行かないようにします。
Fcd3
裏側
Fcd4
ましにはなるが、PA7パルスの影響を受け、変動が出る。
Fcd5
測定値がダメなくらい不安定になるほどの変動ではないけれど、
大もとのクロックが変動するのは、どう考えてもイヤ。
  ※パルスじゃなくアナログ的変化ならどうだ
  を見てみます。

ゆっくりしたアナログ電圧の変化は大丈夫でした。
正弦波でも1kHzくらいになると影響が出始めます。
ということは、PA7は
 ・パルスを出力したらRTCの水晶発振に影響が出る。
 ・H/L固定でも、変化させた瞬間に影響が出る。
 ・デジタル入力も同じで、H/Lが変化した瞬間に
  影響がでる。
 ・ゆっくりした変化のアナログ入力は大丈夫そう。

PA7が持つ他の機能は、
 ・AIN7  ADC0  A/D変換
 ・AIN3  ADC1
 ・AINP0  AC0、AC1、AC2 アナログコンパレータ
ですんで、電池運用機器ならアナログコンパレータを使った
電池電圧低下検出入力あたりに使えそうです。

参考になりますかどうか・・・
MICROCHIP AN2648 Selecting and Testing 32.768 kHz Crystal Oscillators for AVR Microcontrollers User Guide

20ピンのになるとPB2、PB3両隣のピンの信号に注意がいる
のでしょう。
Fcd1

※追記
時計用水晶発振回路を内蔵した昔のチップが「敏感」じゃ
ないと感じたのは、発振回路のRfやRdを外付けしていた
からかも。
水晶部分を触ればアウトだけれど、出力段のインピーダンスは
低いので、ちょっとくらいはOKだった・・・のかも。

Rf内蔵タイプでもRdは外付けしていた。
Xx10
NECの78K0マイコンはRf内蔵でRd外付け。
Xx11
  ジャンクボックスから発掘。

三菱のM3851はRdもRfも外付け。
Xx12

4bitマイコンの基板は発掘できず。
  デバッグ用のチップだけ。
Xx13
東芝のTMP74C。 珍しいピギーパック。

※さらに追記
水晶発振子周辺の配線方法注意書き。
  NEC:μPD7801のデータシートより
X11_20251009084101
X12
X13
推奨する配線
X15

そしてμPD7801(64pin SDIP)はこんな足配置になっています。
X14
水晶の足の隣に電源の関連ピンが配置されてます。

| | コメント (9)

2025年10月 4日 (土)

ATtiny1614で周波数カウンタ:キャプチャタイミング

2025年10月3日:ATtiny1614で(裸の)周波数カウンタ

この続き。
TCD0でクロックを数え、RTCの1秒タイミングで
キャプチャして周波数(1秒間のパルス数)を出しました。
この時、周波数が低くなるとキャプチャをミスる
ことが判明。
また、キャプチャ直後に自動で行われるカウンタの
ゼロクリアのタイミングもクロックで刻まれている
ようで、実際のカウント値より1小さい値になるこ
とが分かってきました。

その様子です。
まず100kHz。
Ca000_20251004113101

RTCの1秒信号でキャプチャ。
ch2はその応答でキャプチャ割り込みのパルスです。
1秒信号から3クロック遅れて反応しています。

次が10Hz。
Ca001
同じように3クロック遅れています。
そして、計測値が「9」に。
実際のクロック数より1つ小さいのです。

そして2Hz。
Ca002_20251004113101
1秒間に2パルスですので、キャプチャ処理に
必要な3クロックには足りません。
それで1秒目のキャプチャ処理が抜けてしまい、
2秒目のタイミングに反応しています。
そして計測周波数出力は「0と3が交互に」
出てきます。

クロックの1つ抜けは、カウント値を+1して
対処できます。
周波数が低いときのキャプチャミスはしかた
ありません。
クロック無しの「0Hz」の判定を「1.5秒」に
しているので、「0Hzが間に出たらおかしいで」
という対処になるでしょう。


※追記
TCD0のキャプチャタイミングとオーバーフロー。
Cc51
同時ならオーバーフローが優先される。
TCD0は12bitなので、4096クロックの倍数で発生。
キャプチャの有無をチェックしないと、4096カウント
なのに8192となってしまう。

※注意
2025年10月6日:ATtiny1614につないだ32kHz水晶発振子、隣のピンの影響を受けるみたい

| | コメント (0)

2025年10月 3日 (金)

ATtiny1614で(裸の)周波数カウンタ

ATtiny1614 Frequency Meter:John BradnamPublished March 22, 2021
「A tiny frequency meter built using the ATtiny1614 microprocessor.」
とういうATtiny1614のタイマー「TCD」を使った周波数カウンタの
製作記事があったので、倣って作ってみました。
表示器は無しで、測定値の出力はシリアルで。

Fc41

Arduino IDE環境で「内部のタイマーは使わない」に
しておかないと、タイマーの割り込みベクトルが衝突
しました。
Fc42
 ※AVR1シリーズだと、デフォルトのタイマーがTCD0
  ということですので、これが衝突原因です。
  delayやmillisなどタイマー系の命令を使いたい
  ときはTCAあるいはTCBを使うようにすればOKかと。

こんな具合に、ゲート時間の1秒ごと、シリアル
で測定周波数を出力します。

 ATtiny1614 Frq cnt ←タイトル
 F_CPU:20000000Hz ←動作周波数
 990.547kHz
 1000.015kHz  ←1MHzパルス入力
 1000.014kHz
 1000.014kHz
 0.000kHz  ←パルスオフ
 0.000kHz  1.5秒周期で0Hzを表示
 0.000kHz
 2.767kHz  ←1MHzパルス再接続
 267.531kHz
 1000.014kHz
 1000.014kHz
  :

タイマーTCDのキャプチャタイミングは計測する
クロックに同期します。
そのせいでクロックがないとキャプチャが行われず、
表示が止まってしまいます。
そこで、1.5秒内にキャプチャできなかったときは
クロック無しとして「0.000kHz」を出力するように
してあります。

制御スケッチです。 (ちょっと長い)
※10-03のスケッチを変更
 TCD0の2つの割り込み、オーバーフローとキャプチャが
 同時に入ったとき、オーバーフローが優先されるので
 計測カウント値が「+4096」されてしまう。
   4.096kHzを入れてるのに8.192kHになってしまう。
 そこでOVF処理でCAPをチェックしてCAPがあるときは
 cnt_ovfを+1しないようにした。
   ATmega328PではOVFよりCAPのほうが優先される。
   ATtiny1614とはちょっと違うので注意。

/*****  ATtiny1614を使った周波数カウンタ  *****/
// その叩き台 計測値はシリアル出力
// "tiny1614_fcnt_td0.ino" 2025-10-03 JH3DBO
// "tiny1614_fcnt_td0a.ino" 10-05
// TCD0のイベント:キャプチャ割り込みと
// オーバーフロー割り込みの部分を修正
// 同時ならオーバーフローが優先させる
// PA3 13pin 測定クロック入力
// PA1 11pin シリアル出力 9600BPS
// PA2 12pin RTC 1Hzパルス出力 キャプチャタイミング
// PB0 9pin TCD0オーバーフロータイミング チェック出力
// PB1 8pin TCD0キャプチャタイミング チェック出力
// PB2(7),PB3(6) 32.768kHz時計用水晶を接続
// クロックが無いとキャプチャデータが上がってこない
// PITを使って1.5秒間をチェックしてキャプチャがないと0Hzと表示
// タイトル
const char pgm_ttl[] = "Test ATtiny1614 Frq cnt (2025-10-05)\r\n";
/***** カウントデータ *****/
volatile uint32_t cnt_cap; // カウントキャプチャ値(32bit)
volatile uint16_t cnt_ovf; // TCD0は12bitなので上位桁を
// オーバーフロー値として累積カウント
volatile byte f_cap; // キャプチャ完了フラグ
/***** 文字出力バッファ *****/
char str_bff[40]; // 40文字 終端のnull含めて
// longは10桁
/***** シリアル出力 *****/
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++; // 次文字
}
}
/***** 書式付シリアル出力 *****/
void txprintf(const char *s, ...)
{
va_list vp;
va_start(vp,s);
vsnprintf(str_bff, sizeof str_bff, s, vp);
txstr(str_bff);
va_end(vp);
}
/***** RTC,PIT busy待ち *****/
// 両方とも0になるのを待つ
void rtcbusy(void) {
while((RTC.STATUS & 0x0F) || // RTC busy ?
(RTC.PITSTATUS & 1)); // PIT busy ?
}
/***** TCD0 オーバーフロー割り込み *****/
// カウント値の12bit以上のMSB側を計数
// OVFとCAPが同時ならOVFが優先されるので
// CAPフラグがオンなら+1しない
// PB0でチェック
ISR(TCD0_OVF_vect)
{
PORTB.OUTSET = (1 << 0); // PB0 H 9pin
TCD0.INTFLAGS = 1; // TCD0オーバーフローフラグをクリア
if(!(TCD0.INTFLAGS & (1 << 3))){ // CAPフラグonなら+1しない
cnt_ovf++; // MSB側カウント値を+1
}
PORTB.OUTCLR = (1 << 0); // PB0 L
}
/***** TCD0 トリガ割り込み *****/
// トリガB割り込みでカウント値をキャプチャ
// 上位のオーバーフローカウント値を加算して総クロック数を計算
// カウントクリアのタイミングで1 clk少なくなるのを+1して補正
// PB1でチェック
ISR(TCD0_TRIG_vect)
{
static uint16_t d;
PORTB.OUTSET = (1 << 1); // PB1 H 8pin
TCD0.INTFLAGS = (1 << 3); // TCD0トリガB割り込みフラグをクリア
d = TCD0.CAPTUREB & 0x0FFF; // 12bitキャプチャデータ
cnt_cap = ((uint32_t)cnt_ovf << 12) + (uint32_t)d; // 32bitで加算
cnt_cap += 1; // クリアタイミングの1 clkを補正
cnt_ovf = 0; // 結果が出たのでMSB値をクリア
f_cap = 1; // キャプチャok
TCD0.INTFLAGS = 1; // TCD0オーバーフローフラグをクリア
PORTB.OUTCLR = (1 << 1); // PB1 L
}
/***** SETUP *****/
void setup() {
cli(); // 割込禁止
PORTA.OUT = 0b00000010;
PORTA.DIR = 0b11110110;
// |||||||+---- PA0 10pin UPDI
// ||||||+----- PA1 11pin TXD
// |||||+------ PA2 12pin EVOUT0 (!!!)
// ||||+------- PA3 13pin 測定クロック入力 ←
// |||+-------- PA4 2pin
// ||+--------- PA5 3pin
// |+---------- PA6 4pin
// +----------- PA7 5pin
PORTB.DIR = 0b00000011;
// |||+---- PB0 9pin out (!!!)
// ||+----- PB1 8pin out (!!!)
// |+------ PB2 7pin TOSC2 32.768kHz
// +------- PB3 6pin TOSC1 XTAL
// プルアップ
PORTA_PIN3CTRL = PORT_PULLUPEN_bm; // PA3 pull up
// 代替ポート
PORTMUX.CTRLA = 0b00000001; // 代替ポート
// || ||+--- EVOUT0 PA2 イベント出力
// || |+---- EVOUT1 PB2
// || +----- EVOUT2 PC2
// |+------- LUT0out PB4
// +-------- LUT1out PC1
PORTMUX.CTRLB = 0b00000001; // 代替ポート
// | +-- USART0 TXをPA1に
// +---- SPIO
// 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サイクルの起動時間待ち
// RTC
rtcbusy(); // RTC,PIT ビジー待ち
RTC.CLKSEL = 0b00000010;
// ++---- XOSC32K 32.768kHzHz水晶
RTC.CTRLA = 0b00101001;
// ||||| +---- RTCEN RTC周辺機能許可
// |++++------- CLK_RTC 1/32 →1024Hz
// +----------- RUNSTBY
RTC.PER = 1024 - 1; // 1/1024 →1Hz
RTC.PITCTRLA = 0b01010001;
// |||| +-- PITEN PIT有効
// ++++----- PERIOD 32.768kHz/2048 →16Hz
// タイマーD ※データシート 22.3.2.6参
TCD0.CMPBCLR = 4096 - 1; // カウント最大値
TCD0.CTRLB = 0b00000000; // 波形制御
// ++--- WGMODE 1ランプモード
TCD0.INPUTCTRLB = 0b00001000; // 入力制御
// ++++--- EDGETTRIG キャプチャとクリア
TCD0.EVCTRLB = 0b10000101; // イベント制御
// || | | +--- トリガイベント入力有効
// || | +----- イベントはキャプチャをトリガ
// || +------- イベントは↓エッジ
// ++--------- 非同期イベントで
TCD0.INTCTRL = 0b00001001; // 割り込み
// || +--- オーバーフロー割り込みon
// |+----- トリガA
// +------ トリガB キャプチャ割り込みon
while(!(TCD0.STATUS & 1)); // ENRDY イネーブルレディを待つ
TCD0.CTRLA = 0b01000001; // 制御開始
// ||||||+---- ENABLE
// ||||++----- SYNC分周比 1/1
// ||++------- プリスケーラ1/1
// ++--------- 外部クロック PA3を入力
// イベント
EVSYS.ASYNCCH1 = 0x08; // RTCの1秒オーバーフロー
EVSYS.ASYNCUSER7 = 0x04; // TCD0のイベント1でキャプチャを実行
EVSYS.ASYNCUSER8 = 0x04; // チェックのためEVOUT0 (PA2)にも出力
// シリアル
// 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
sei(); // 割込有効
}
/***** LOOP *****/
// 1秒周期のキャプチャを待ってシリアル出力
// 1234.567kHz
// 1.5秒以上パルスが来ないと0.000kHzを出力
void loop() {
uint32_t d; // 周波数データ
static byte f_disp; // 周波数表示(出力)フラグ
static byte pit_cnt; // 16Hz PITカウンタ
// 1.5秒経過(24カウント)で0Hz出力
txstr(pgm_ttl); // タイトル出力
txprintf("F_CPU:%luHz\r\n", F_CPU); // 動作周波数
while(!(CLKCTRL.MCLKSTATUS & (1 << 6))); // XOSC32KSの1を待つ
//while(!(TCD0.STATUS & 1)); // ENRDY イネーブルレディを待つ
//TCD0.CTRLE = (1 << 2); // TCD0再スタート (clkが必要)
cli(); // 割り込み禁止で
TCD0.INTFLAGS = 1; // TCD0オーバーフローフラグをクリア
TCD0.INTFLAGS = (1 << 3); // TCD0トリガB割り込みフラグをクリア
f_cap = 0; // キャプチャフラグをクリア
cnt_ovf = 0; // オーバーフローカウンタクリア
sei(); // 割込有効に
// loop
while(1){
if(f_cap){ // キャプチャした?
f_cap = 0; // キャプチャフラグをクリア
cli(); // 割り込み禁止で
d = cnt_cap; // キャプチャデータを読み出し
sei(); // 割込有効に
f_disp = 1; // 周波数確定 表示フラグをオン
pit_cnt = 0; // PITカウンタをゼロに 24回で0Hz表示
}
if(RTC.PITINTFLAGS & 1){ // PIT 16Hz?
RTC.PITINTFLAGS = 1;
pit_cnt++; // PITカウンタ +1
if(pit_cnt >= 24){ // 24/16サイクル=1.5秒経過
cli(); // 割り込み禁止で
cnt_ovf = 0; // オバーフローカウンタクリア
sei(); // 割込有効に
d = 0; // カウント値ゼロで
f_disp = 1; // 表示指令
pit_cnt = 0; // PITカウンタクリア
}
}
if(f_disp){ // 周波数表示指令?
f_disp = 0; // 表示フラグクリア
txprintf("%5lu.%03lukHz\r\n", // シリアル出力
d / 1000L, d %1000L);
}
}
}

ATtiny1614のレジスタへの書き込み、「わけわかめ」の
信号の英文名称を記述するのではなく、データシートを
見ればわかる「ビット位置」を使って書いています。
信号名称を使うと、
 「使いたいんは何ちゅう名前やねん」
 「それはどこに書いたんねん」
 「そいつの値はなんぼやねん」
 「なにやねんそれ」
 「えらい長い名前やなぁ」
を検索するのがうっとうしく、結局はデータシートを
見なけりゃならないので、8bitなら「0b00000001」
という記述でエエやんっと、独自路線で仕上げました。

もう一つ。
タイマーTCD、キャプチャと同時にゼロクリアしてくれるので
周波数カウンタの用途に便利なんですが、このゼロクリアの
タイミングも入力クロックで刻まれているようです。
そのため、周波数が低いと(10Hz以下)、ゲートとなる
RTCからの1Hzクロックとの差で、ミスが発生します。
  低めの周波数値が出る
キャプチャしてからクリアという流れがクロックで刻まれ
るのでカウントをミスるようです。

20MHz動作で8MHzは数えていました。

2019年12月20日:Arduino UNOで周波数カウンタ
2021年1月15日:アナデバの電力計用IC、arduinoで周波数(周期)を計る
のようにキャプチャ機能だけを使ってカウントする方が良い
かもしれません。

Arduino UNO R3のATmega328pとはちょっと違う動き
でした。

※関連
2021年1月15日:アナデバの電力計用IC、arduinoで周波数(周期)を計る
2025年5月16日:Arduino UNO R3で周波数を計る

※続き
2025年10月4日:ATtiny1614で周波数カウンタ:キャプチャタイミング

※注意
2025年10月6日:ATtiny1614につないだ32kHz水晶発振子、隣のピンの影響を受けるみたい

| | コメント (0)

2025年9月30日 (火)

ATtiny1614に時計用水晶発振子をつなぐ

同じ14ピンでもATtiny1604(やtiny804、tiny404
などのtinyAVR0系統)は時計用水晶発振回路を持っ
ていません。
32kHzの発振回路は内蔵してるのですが、時計と
して使うには精度が不十分です。
マイコン自身では発振できないので、精度の良い
クロックが欲しい時は別置きの発振器から外部
クロックを突っ込むしかありません。

対して、tinyAVR1シリーズになると、時計用の
水晶を発振できる発振回路を搭載しています。
精度の良いクロックがあれば、時計や周波数カウンタが
作れます。

ATtiny1614を使って、その動作を確かめてみました。
・32.768KHz水晶発振回路の始動方法。
  データシートにはややこしいことが
  書いてあるけど、リセット起動なら
  簡単に始動。
・発振周波数の確認方法。
  発振させた32.768kHzは直接取り出せない。
  イベントシステムを使ってRTCのオーバー
  フロー信号をイベント出力0(ポートPA2)に
  出して周波数を確認。(1/2の16.384kHz)
・発振安定タイミング確認のためポート出力。
・PITの動作確認のため1024Hzをソフトで出力。

こんな回路です。
Tt14_20251002175801
PA2を周波数カウンタ読むと16.38418kHzだったんで、
「+11PPMほど」の周波数でした。

制御スケッチ。 Arduino IDE環境です。

//  ATtiny1614 (14pin)
// 32.768kHz水晶の発振を確認
/***** RTC,PIT busy待ち *****/
// 両方とも0になるのを待つ
void rtcbusy(void) {
while((RTC.STATUS & 0x0F) || // RTC busy ?
(RTC.PITSTATUS & 1)); // PIT busy ?
}
/***** SETUP *****/
void setup() {
cli(); // 割込禁止
PORTA.DIR = 0b11111110;
// |||||||+---- PA0 10pin UPDI
// ||||||+----- PA1 11pin loopでHに
// |||||+------ PA2 12pin EVOUT0(16384Hz)
// ||||+------- PA3 13pin PITパルス(1024Hz)
// |||+-------- PA4 2pin 発振安定でH
// ||+--------- PA5 3pin
// |+---------- PA6 4pin
// +----------- PA7 5pin
PORTB.DIR = 0b00000011;
// |||+---- PB0 9pin out
// ||+----- PB1 8pin out
// |+------ PB2 7pin TOSC2 32.768kHz
// +------- PB3 6pin TOSC1 XTAL
// 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サイクルの起動時間待ち
// RTC,PIT
rtcbusy(); // RTC,PIT ビジー待ち
RTC.CLKSEL = 0b00000010;
// ++---- XOSC32K 32.768kHzHz
RTC.CTRLA = 0b00000001;
// ||||| +---- RTCEN RTC周辺機能許可
// |++++------- CLK_RTC 1/1 →32.768kHz
// +----------- RUNSTBY
RTC.PER = 2 - 1; // 1/2 →16.384kHz
RTC.PITCTRLA = 0b00011001;
// |||| +-- PITEN PIT有効
// ++++----- PERIOD 32768Hz/16 →2048Hz
// RTCのオーバーフローをPA2(EVOUT0)に出力 16.384kHz
PORTMUX.CTRLA = 0b00000001; // PA2ポートをEVOUT0に
EVSYS.ASYNCCH1 = 0x08; // ch1入力:RTC_OVF
EVSYS.ASYNCUSER8 = 0x04; // ch1出力:EVOUT0(PA2:12pin) 出力
sei(); // 割込有効
}
/***** LOOP *****/
void loop() {
PORTA.OUTSET = (1 << 1); // PA1(11pin)をHにしてloopに来たことを通知
// RTC安定待ち
while(!(CLKCTRL.MCLKSTATUS & (1 << 6))); // XOSC32KSの1を待つ【1】
PORTA.OUTSET = (1 << 4); // PA4(2pin)をHに
while(1){
if(RTC.PITINTFLAGS & 1){ // PIT 2048Hz
RTC.PITINTFLAGS = 1;
PORTA.OUTTGL = (1 << 3); // PA3:13pinトグル (1024Hz)
}
}
}

とりあえず32.768kHzの発振ができるようになりました。
このクロックを使って何をするかは・・・これから考えます。

※発振タイミングのオシロ波形

電源オンから発振開始まで
Ca000_20251001084001
  (安定待ち=16kサイクルで)

発振安定から波形出力まで
Ca002_20251001084101

実験用手組み基板
Cc21_20251001084101

※ATtiny1614のタイマ・カウンタ、ちょっと不便なところが
 あります。
 周波数カウンタを作ろうとすると・・・
・16bitのタイマBで周波数計測機能があるんだけど、オーバーフローの
 検出ができないんで、カウント数を上げられない。
・タイマBのクロック入力はタイマAからイベントリンク機能で引っ張って
 来なくちゃならない。
・カウントクロックが無いと、キャプチャパルスを認識してく
 れない。
・タイマDは周波数カウントに使える機能があるんだけど、
 12bit(4095カウント)。

※注
リストの【1】の部分、XOSC32KS(発振安定)のチェックは、
32kHzクロックをどこかのモジュールが使い初めてからしか
チェックできない。使い始める前は「0」になっている。

※注意
2025年10月6日:ATtiny1614につないだ32kHz水晶発振子、隣のピンの影響を受けるみたい

| | コメント (0)

2025年9月29日 (月)

RTC機能付ATtiny RTCとPITの初期化注意点

RTC(リアルタイムクロック)の使用方法、
こんな注意点が記されています。

 注: RTC周辺機能はデバイス始動の間で内部的に
 使われます。
 常に及び初期構成設定で、状態(RTC.STATUS)
 と周期割り込み計時器状態(RTC.PITSTATUS)
 のレジスタで同期中多忙ビットを調べてください。


RTCとPITは「ビジーをチェックしてから触るんだ」っと。
関連するのはこのレジスタ。
Pi10
Pi11_20250929102001
RTCが4つ。PITが1つ。
Hならビジーで、Lになるのを待つ。

電源オン時、どのくらいの待ちが生じるのか調べて
みました。
こんなスケッチ。 (チップは14ピンのATtiny404)

//  ATtiny404 (14pin)
// I/O MACRO
#define PA5_H PORTA.OUTSET = (1 << 5); // PA5 3pin H/L
#define PA5_L PORTA.OUTCLR = (1 << 5);
#define PA6_H PORTA.OUTSET = (1 << 6); // PA6 4pin H/L
#define PA6_L PORTA.OUTCLR = (1 << 6);
/***** SETUP *****/
void setup() {
VPORTA.DIR = 0b11111110;
// |||||||+---- PA0 10pin UPDI
// +++++++----- PA7~PA1 out
VPORTB.DIR = 0b00001111;
// ++++---- PB3~PB0 out
PA5_H; // 3pin H
}

/***** LOOP *****/
void loop() {
if((RTC.STATUS & 0x0F) || // RTC busy ?
(RTC.PITSTATUS & 1)){ // PIT busy ?
PA6_H; // 4pin H/L
}
else{
PA6_L;
}
}

I/Oポートの初期化完了でPA5をH。
RTCとPITがビジーならPA6をH。
レディーならPA6をLに。
こんな波形が得られました。
Im000

電源のオン・オフを繰り返してビジー時間の変動を見ています。
Im001
Im002

ビジーをチェックせずにRTCを初期化しようとすると
失敗してちゃんと動きません。
初期化成功後のアクセスでビジー/レディー状態がどう
なるのかはもうちょいスケッチを触らなくちゃなりません。

| | コメント (0)

2025年9月26日 (金)

ATtinyのデータシートに「1/1023」が出現

ArduinoのA/D変換値(10bitなら0~1023)から
入力電圧を求めるときは「1/1024」でっせを
何度も言ってきました。
   トラ技にも書いたし。
ところが・・・
8ピンのAVRマイコン「ATtiny402」のデータシート
を見ていたら「1/1023」になっていたのです。

まずこれがArduino UNO R3のマイコンATmega328Pの
データシートでの記述。
正しく「1/1024」です。
T13_20250926090601

それがATtiny402(202~406まとめての)のデータシート
では「1/1023」と示されているのです。
T11_20250926090601

追跡しきってはいませんが、「アトメル」時代からあるチップは
正しく「1/1024」と。
「マイクロチップ」になってからできたチップが「1/1023」と
なっているような感じかと。

※追記 3・1/2桁電圧計IC(モトローラのMC14433)の資料から
大昔のデータシートを引っ張ってきました。
モトローラの3・1/2桁電圧計IC、BCDでの表示出力が±1999。
  ※関連
   ・1/2桁とは
   ・1/2桁とは 「セグメント説」
基準電圧が2.000Vだと±1.999Vがフルスケール。
0.2000Vだと±フルスケールは1999mVになります。
このあたりの関係、データシートではこのように
表現されています。

14433a
ごくあたりまえですが、このVref=2.000Vで
1.000Vが入力されたら
表示出力は1.000Vになります。
(1.000V ÷ 1.999) × Vref = 1.00050V
ではありません。
  出力桁外になる0.00050VがこのICで
  どういう挙動になるかは別問題。
(1.000V ÷ 2.000) × Vref = 1.000V
がまっとうな計算。
  Vrefがジャスト2.000Vじゃないとき、
  入力電圧に対して何Vが表示されるか
  の検証計算。

| | コメント (2)

2025年9月 9日 (火)

ATtiny402サンプル:"Wire.h"を使わずI2Cで液晶表示 AQM1602だと

ATtiny402サンプル:I2Cで液晶表示 SCL周波数を設定できるようにしたら
で使った液晶AC0802は、コントラストを外付け
ボリュームで調整します。

AQM1602Y-NLW(FLW、FLB)やAQM1602XA-RNは
内部レジスタの操作でコントラストが変わります。

電源電圧3.3Vなら内部ブースター回路をオンに
して、液晶の駆動電圧を上げるので、5Vで使う
時とで微妙にコントラストが変わりますが
両方とも「35」あたりでOKのようです。

バックライト付のAQM1602Y-FLW-FBWを試したのが
このページ。 ※ずいぶん前だぁ
 ・秋月電子16文字x2行のI2Cインターフェース液晶AQM1602Y

今回はATtiny402でバックライト無しの
AQM1602Y-RN-GBWを試してみました。
  電源電圧は3.3Vでこんな回路。
Sch21
VRを回すとコントラストが変わります。
Cc01_20250909165301

※制御スケッチ
  ・ダウンロード - lcd_test2a.zip
    ※BCD変換ルーチンを別ファイルにしてあります。

これで、
  スケッチが1537バイト(37%)
  グローバル変数が37バイト(14%)
というメモリー使用量。

"Wire.h”をやめたおかげでRAMへの圧迫が
取り除かれています。

初期化のところで、文字数と行数、SCL周波数、
電源電圧を設定します。

  LCD.begin(16, 2, 100, LCD_V3R3);
   // I2C液晶 ★wire.hは使わない
   // 液晶初期化 16文字x2行,100kHz,3.3V
   // (5V:LCD_V5R0,3.3VならLCD_V3R3に)

3.3Vならブースター回路をオン。
5Vならオフに。

tiny402の動作周波数はArduino IDEの画面で設定
します。
3.3V動作なら10MHzが最大。
Ide02

電源電圧5Vでは必ず電圧ブースターをオフにという
のが注意点です。
  LCD.begin()での電圧設定をLCD_V5R0
  にしたらオフになります。

Arduinoでのアナログ入力といえば「analogRead() 」ですが、
tiny402には連続変換モード(自由走行という表現)というのが
あって、変換を始めたらいつでも最新値を読み出せるのです。
  入力チャンネルを切り替えるとなると
  タイミングがちょっとややこしいけど。
累積加算の機能と合わせて、割り込みで処理しなくて
良いのでなかなか便利。

もうひとつ。
内蔵クロック32.768kHzベースのRTCタイマに付随する
PIT(Periodic Interrupt Timer)が、メインクロック周波数が
変わっても一定時間を数えてくれるので、これも便利。
  通常はF_CPUで動作クロックが分かるのですが、
  例えばこのサンプルでは0.25秒(1/4秒)サイクル
  でPITフラグが立つようにしています。
  メイン周波数が変わっても、一定タイミングが
  得られます。
    delay()を使わなくてもエエんです。

| | コメント (0)