« 2025年6月 | トップページ | 2025年8月 »

2025年7月

2025年7月29日 (火)

ATtiny402マイコン サンプル:割り込みでシリアル出力

Arduino 環境でのATtiny402。
UNO R3と同じようにSerial.beginとSerial.print
を使えばシリアル入出力が可能です。

しかし・・・なにせROMが4kバイトしかありません。
できるだけメモリーを節約したい・・・
ということで、
 ・シリアル出力だけで受信はいらない。
 ・でも出力待ちはイヤ。
  出力したらすぐ戻ってくるように割り込み
  で処理したい。
こんなときのサンプルです。

/********************************/
/* 割り込みでシリアル送信を */
/********************************/
// 9600BPSでシリアル出力 受信はなし
// 1秒ごとに2chのA/D値をシリアル出力
/***** タイマデータ *****/
volatile byte tm_10ms; // 10msダウンカウントタイマ
/***** タイマーTCB0周期割り込み *****/
// 1ms割り込み
ISR(TCB0_INT_vect)
{
static byte c1; // 10msカウンタ
TCB0.INTFLAGS = 1; // 割込要求クリア
ADC0_COMMAND = 1; // A/D変換開始指令
VPORTA.OUT |= (1 << 3); // PA3 H (!!!) 7pin
c1++; // 10msチェック
if (c1 >= 10) {
c1 = 0;
if (tm_10ms) tm_10ms--; // 10msダウンカウント
}
}
/**** A/D データ *****/
// A/D変換チャンネル
const byte ad_mpx[] = {
0b00000001, // AIN1:PA1 4pin
0b00000010, // AIN2:PA2 5pin
};
// A/Dデータ(2ch)
volatile word ad_15b[2]; // A/D変換データ 0~32736(32回加算)
// 割込で書き込み
/***** A/D割り込み処理 *****/
ISR(ADC0_RESRDY_vect)
{
static byte ch; // A/D変換チャンネル(ch0,1)
ad_15b[ch] = ADC0_RES; // A/D (0~1023*32) 15bitデータ
ch++; // 次変換チャンネル
if (ch >= 2) ch = 0; // 0に戻す
ADC0.MUXPOS = ad_mpx[ch]; // A/D入力MPX切り替え
VPORTA.OUT &= ~(1 << 3); // PA3 L (!!!) 7pin
}

/***** シリアル出力バッファ *****/
char str_bff[16]; // 文字出力バッファ
/***** 送信割り込みデータ ******/
volatile char tx_bff[32]; // 送信割り込み文字バッファ
volatile byte tx_rdp; // 送信 読み出しポインタ
volatile byte tx_wrp; // 送信 書き込みポインタ
volatile byte tx_cnt; // 送信 データ数
/***** シリアル送信割り込み *****/
// 送信完了で起動
ISR(USART0_TXC_vect)
{
USART0.STATUS = USART_TXCIF_bm; // 送信完了解除
if(tx_cnt == 0){ // バッファに残データ無し
USART0.CTRLA &= ~USART_TXCIE_bm; // 送信完了割り込みオフに
}
else{ // バッファにデータあり
USART0.TXDATAL = tx_bff[tx_rdp]; // 1文字送信
tx_rdp++; // ポインタ+1
if(tx_rdp >= sizeof(tx_bff)){ // 最終?
tx_rdp = 0; // 先頭に
}
tx_cnt--; // データ数-1
}
}
/***** シリアル送信可チェック *****/
// 1:送信可 0:送信できない(バッファいっぱい)
byte txx(void)
{
byte cnt;
cli(); // 割込禁止に
cnt = tx_cnt; // 送信バッファ内データ数
sei();
if(cnt >= sizeof(tx_bff)) return 0; // 送信不可
else return 1; // 送信OK
}
/***** シリアル出力 *****/
void tx(char c)
{
while(!txx()); // 送信OK待ち
// 初めてならいきなり出力
// 2回目以降はバッファに書き込む
cli(); // 割込禁止に
if(!(USART0.CTRLA & USART_TXCIE_bm)){ // 初めての送信
USART0.CTRLA |= USART_TXCIE_bm; // 送信割り込みon
USART0.TXDATAL = c; // 1文字直接送信
}
else{ // 送信継続中
tx_bff[tx_wrp] = c; // 1文字書き込み
tx_wrp++; // 書き込みポインタ+1
if(tx_wrp >= sizeof(tx_bff)){ // 最終?
tx_wrp = 0; // 先頭に
}
tx_cnt++; // データ数+1
}
sei(); // 割込有効に
}
/***** 文字列出力 *****/
void txstr(const char *s)
{
while (*s != '\0') { // Nullまで
tx(*s); // 1文字出力
s++; // 次文字
}
}

/***** SETUP *****/
// 20MHzクロック
void setup()
{
cli(); // 割り込み禁止に
PORTA.OUT = 0b01000000; //ポート出力
PORTA.DIR = 0b11001000;
// || |||+---- PA0 6pin UPDI
// || ||+----- PA1 4pin in AIN1
// || |+------ PA2 5pin out AIN2
// || +------- PA3 7pin out
// |+---------- PA6 2pin out TXD
// +----------- PA7 3pin out
// タイマーA0, 分割モードで初期化されるので16bitモードに
TCA0.SPLIT.CTRLA = 0; // タイマー停止
TCA0.SPLIT.INTCTRL = 0; // 割り込みなしに
PORTMUX.CTRLC = 0; // TCAポート多重切り替えなしに
// タイマーB0 1msタイマー
TCB0.CCMP = 20000 - 1; // 20MHz/20000=1kHz
TCB0.CTRLB = 0b00000000;
// ||| +++---- CNTMODE 周期割り込み
// ||+-------- CCMPEN
// |+--------- CMPINT
// +---------- ASYNC
TCB0.CTRLA = 0b00000001;
// | | ||+---- ENABALE 有効
// | | ++----- CLKSEL 1/1 20MHz
// | +-------- SYNCUPD 同期
// +---------- RUNSTDBY
TCB0.INTCTRL = 1; // 割込許可
// A/D AIN1:PA1 4pin, AIN2:PA2 5pin
ADC0.CTRLA = 0b00000001;
// | ||+--- ENABLE 許可
// | |+---- FREERUN しない
// | +----- RESEL 10bit
// +---------- RUNSTDBY
ADC0.CTRLB = 0b00000101; // サンプル回数
// +++--- 32回 約680usの変換時間
ADC0.CTRLC = 0b01010011;
// ||| +++--- PRESC 20MHz/16=1.25MHz
// |++------- REFSEL VDD
// +--------- SMAPCAP 5PF
ADC0.CTRLD = 0; // 遅延なしに
ADC0.MUXPOS = 0b00000001; // MPX
// +++++--- AIN1 PA1
PORTA.PIN1CTRL = 0b00000100; // AIN1:PA1 4pin
PORTA.PIN2CTRL = 0b00000100; // AIN2:PA2 5pin
// | |+++---- ディジタル入力禁止
// | +------- pullupなし
// +----------- 反転I/Oなし
ADC0.INTCTRL = 0b00000001; // 割り込み
// |+---- RESRDY 変換完了割込許可
// +----- WCMP
// シリアル
USART0.CTRLA = 0; // 送信完了割り込みのセットはtx()で
USART0.CTRLB = 0b01000000;
// || ||||+-- MPCE
// || ||++--- RXMODE
// || |+----- ODME
// || +------ SFDEN
// |+-------- TXEN 送信有効
// +--------- RXEN 受信はしない
USART0.CTRLC = 0b00000011;
// |||||+++-- CHSIZE 8BIT
// ||||+----- SBMODE 1stop
// ||++------ PMODE Parity無し
// ++-------- CMODE 非同期USART
USART0.BAUD = 8333; // 4*20MHz/9600
sei(); // 割り込みイネーブル
}

/***** LOOP ******/
// 1秒ごとに2つのAD値をシリアル出力
void loop() {
byte i;
int d;
while (1) {
VPORTA.IN |= (1 << 7); // PA7 トグル (!!!) 3pin
if(tm_10ms == 0){ // タイムアップ?
tm_10ms = 100; // 1秒セット
for (i = 0; i < 2; i++) { // 2ch loop
cli(); // いったん割込禁止にして
d = ad_15b[i]; // A/D 15bit 32回累積加算値読み出し
sei();
d = d / 32; // 15bitを10bitに(0~1023)
// sprintf(str_bff, "%5d", d); // 5桁文字に
strcpy(str_bff, " ABC"); // ★
txstr(str_bff); // 文字列出力
}
txstr("\r\n"); // CR+LF
}
}
}

 

sprintfを使わなければROMが782バイト、RAMが58バイト
sprintf利用でROM 2559バイト
Serial.begin、Serial.printだとROM 1823バイトなので
ずいぶん軽量化しているかと。

もちろんArduino環境ではなく、Microchip Studioでの
プログラムでも使えます。 
  (setupやloopじゃなく)

10進数値出力だけで良いのなら
ATtiny402マイコン サンプル:RCサーボモータテスター
で示したstrbcd()が役に立つかと。

ATtiny402の内蔵ハードウェア直叩きの例、
あとは液晶表示ルーチンあたりでしょか。

※注目点
・割り込みで処理されるデータ、可能なら1バイトに
 してしまい、cli()、sei()で囲まなくてよいように。
・でも、2バイトのA/D値やらシリアル出力関連
 レジスタを操作する時は割り込み禁止処理が必須。

| | コメント (0)

2025年7月28日 (月)

東芝インパルス TNH-3LE 950mAh 充放電実験 2800cyc目

2024年2月に始めた東芝インパルス TNH-3LE 950mAhの充放電実験

Imp51

2800サイクルを終えました。 内部抵抗は70mΩに。

50サイクルごとに行う0.2C放電のグラフ。
Cap002_20250728160401

毎サイクルでの0.5C充放電のグラフ
Cap001_20250728160501

0.2C放電も0.5C放電も、放電時間が定格の60%
(180分と72分)になると寿命と判断。
だんだんと寿命判断に近づいてきている様子が
出ています。

2025年3月15日:ニッ水電池のJIS規格C8708が2019→2024に

| | コメント (0)

2025年7月25日 (金)

ATtiny402マイコン サンプル:RCサーボモータテスター

Arduino IDE下でのATtiny402、そのサンプルです。
 ・2つのボリュームをA/Dコンバータで入力。
 ・その値を元にRCサーボ用のPWMパルスを2ch出力。
 ・1秒ごとに2chの回転角をシリアル出力。
こんな回路です。
Servo_test
ソフト的には、
 ・PWM出力はTCAタイマーで。
   20MHzクロックを1/8して0.4usクロックで
   カウント。
 ・TCBタイマーで1msタイマー割り込み。
 ・A/D変換は割り込み処理で。
   32回累積させて15bit値で読み出し。
    analogReadは使わない。
 ・シリアル出力は独自ルーチンで。
    ArduinoのSerial.printは使わない。
    ただし、割り込み処理していないので
    文字出力中は出力ルーチンから抜け
    出せない。

Arduino環境ですが、ATtiny402を裸にして使っています。

/*********************************************/
/* 小型サーボモータテスト用パルス発生回路 */
/*********************************************/
// ATtiny402 20MHz Arduino IDE環境
// ボードマネージャー:
// http://drazzy.com/package_drazzy.com_index.json
// TCA0 20MHz/8=2.5MHz=0.4us
// サーボの周波数50Hz=20ms 20000/0.4=50000clk
// -90° 0.50ms 1250clk
// 0° 1.45ms 3625clk
// -90° 2.40ms 6000clk
#define SVP_PR 50000 // サーボ周波数clk数
#define SVP_M90 1250 // サーボ-90°位置clk
#define SVP_00 3625 // サーボ 0°位置clk
#define SVP_P90 6000 // サーボ+90°位置clk
/***** タイマーTCB0周期割り込み *****/
// 1ms割り込み
ISR(TCB0_INT_vect) {
TCB0.INTFLAGS = 1; // 割込要求クリア
ADC0_COMMAND = 1; // A/D変換開始指令
}
/**** A/D データ *****/
// A/D変換チャンネル
const byte ad_mpx[] ={
0b00000001, // AIN1:PA1 4pin
0b00000111, // AIN7:PA7 3pin
};
// A/Dデータ(2ch)
volatile word ad_15b[2]; // A/D変換データ 0~32736(32回加算)
// 割込で書き込み
word ad_10b[2]; // 10bit A/D値 0~1023
// mainで読み出し
// TCAデータ サーボの角度パルスを発生
volatile word *const TCA0CMP[]={
&TCA0.SINGLE.CMP0BUF, // WO0出力 PA3 0.4usクロック
&TCA0.SINGLE.CMP2BUF, // WO2出力 PA2
};
word tca_pwm[2]; // TCAに対するPWM設定値
// -90°~0°~+90°のデューティを設定
/***** A/D割り込み処理 *****/
ISR(ADC0_RESRDY_vect)
{
static byte ch; // A/D変換チャンネル
ad_15b[ch] = ADC0_RES; // A/D (0~1023*32) 15bitデータ
// 次変換チャンネル
ch++; // ch0,1だけ
if(ch >= 2) ch = 0; // 0に戻す
ADC0.MUXPOS = ad_mpx[ch]; // A/D入力MPX切り替え
}

/***** BCD文字出力処理 *****/
// BCD出力用文字バッファ
char bcd_bff[16]; // 16文字 終端のnull含めて
// longは10桁
/***** 桁指定BCD文字出力 *****/
// d:変換データ long値
// n:整数部文字数(-を含めて)
// p:小数部文字数(.は含まない)
// 数値は整数を入力 浮動小数点ではない
// 小数部は整数の基数が0.01などの時に用いる
// 12345が123.56を意味する時 (d,3,2)と指定
// -1234を-123.4と表示するときは(d,4,1)と-を含めた文字数に
// bcd_bffに入った文字の先頭アドレスを持ってリターン
char *strbcd(int32_t d, byte n, byte p)
{
byte i;
byte sgn = 0; // 符号フラグ
byte zr = 0; // ゼロデータ処理済みフラグ
char *s; // 0~9書込みバッファのアドレス
if(d < 0){ // マイナス?
d = -d; // +の値にして
sgn = 1; // -フラグをオン
}
i = n + p; // 文字数
if(p) i++; // 小数点あれば+1
s = bcd_bff + i; // バッファの最後尾
*s = '\0'; // 最後にnullをセット
// 小数部 '.'まで
if(p){ // 小数部あり
for(i = 0; i < p; i++){ // loop
if(d){ // 0でないときは計算
*--s = (byte)(d % 10) + '0'; // 下位桁から0~9に
d = d / 10; // 1/10して上位桁の処理
}
else{ // 0なら'0'を
*--s = '0';
}
}
*--s = '.'; // 小数点
}
// 整数部 上位ゼロサプレス
for(i = 0; i < n; i++){ // loop
if((i == 0) || // 1桁目あるいは
(d != 0)){ // ゼロ以外
*--s = (byte)(d % 10) + '0'; // 下位桁から0~9に
d = d / 10; // 1/10して上位桁の処理
}
else if((sgn != 0) && // ゼロでマイナス
(zr == 0)){ // -符号処理まだ
*--s = '-'; // マイナスを付加
zr = 1; // 始めてのゼロを処理した
}
else{ // 2桁目以降でゼロ
*--s = ' '; // ゼロサプレス
}
}
return s; // 文字列の先頭アドレスを持ってリターン
}

/***** シリアル出力 *****/
void tx(char c)
{
while(!(USART0.STATUS & USART_DREIF_bm)); // 送信できるまで待つ
USART0.TXDATAL = c; // 1文字出力
}
/***** 文字列出力 *****/
void txstr(const char *s)
{
while(*s != '\0'){ // Nullまで
tx(*s); // 1文字出力
s++; // 次文字
}
}

/***** SETUP *****/
void setup() {
cli();
PORTA.OUT = 0b01000000; //ポート出力
PORTA.DIR = 0b01001100;
// || |||+---- PA0 6pin UPDI
// || ||+----- PA1 4pin in AIN1
// || |+------ PA2 5pin out PWM WO2
// || +------- PA3 7pin out PWM WO0
// |+---------- PA6 2pin out TXD
// +----------- PA7 3pin in AIN7
// タイマーA0, 分割モードで初期化されるので16bitモードに
TCA0.SPLIT.CTRLA = 0; // タイマー停止
TCA0.SPLIT.INTCTRL = 0; // 割り込みなしに
PORTMUX.CTRLC = 0; // TCAポート多重切り替えなしに
TCA0.SINGLE.CTRLD = 0; // TCA0を16bitモードに
TCA0.SINGLE.PER = SVP_PR - 1; // 周期 20MHz/8/50000 = 50Hz(20ms)
TCA0.SINGLE.CMP0BUF = SVP_00; // WO0出力 PA3 0°位置 1.45ms
TCA0.SINGLE.CMP2BUF = SVP_00; // WO2出力 PA2
TCA0.SINGLE.CTRLB = 0b01010011;
// ||||+++----- WGM 1傾斜PWMモード
// |||+-------- ALUPD
// ||+--------- CMP0EN PA3 7pin 出力
// |+---------- CMP1EN PA1 4pin (AIN1)
// +----------- CMP2EN PA2 5pin 出力
TCA0.SINGLE.CTRLA = 0b00000111;
// |||+----- ENABLE 動作開始
// +++------ CLKSEL プリスケーラ1/8
// タイマーB 1msタイマー
TCB0.CCMP = 20000-1; // 20MHz/20000=1kHz
TCB0.CTRLB = 0b00000000;
// ||| +++---- CNTMODE 周期割り込み
// ||+-------- CCMPEN
// |+--------- CMPINT
// +---------- ASYNC
TCB0.CTRLA = 0b00000001;
// | | ||+---- ENABALE 有効
// | | ++----- CLKSEL 1/1 20MHz
// | +-------- SYNCUPD 同期
// +---------- RUNSTDBY
TCB0.INTCTRL = 1; // 割込許可
// A/D AIN1:PA1 4pin, AIN7:PA7 3pin
ADC0.CTRLA = 0b00000001;
// | ||+--- ENABLE 許可
// | |+---- FREERUN しない
// | +----- RESEL 10bit
// +---------- RUNSTDBY
ADC0.CTRLB = 0b00000101; // サンプル回数
// +++--- 32回 変換時間約0.6ms
ADC0.CTRLC = 0b01010011;
// ||| +++--- PRESC 20MHz/16=1.25MHz
// |++------- REFSEL VDD
// +--------- SMAPCAP 5PF
ADC0.MUXPOS = 0b00000001; // MPX
// +++++--- AIN1 PA1
PORTA.PIN1CTRL = 0b00000100; // AIN1:PA1 4pin
PORTA.PIN7CTRL = 0b00000100; // AIN7:PA7 3pin
// | |+++---- ディジタル入力禁止
// | +------- pullupなし
// +----------- 反転I/Oなし
ADC0.INTCTRL = 0b00000001; // 割り込み
// |+---- RESRDY 変換完了割込許可
// +----- WCMP
// シリアル
USART0.CTRLB = 0b01000000;
// || ||||+-- MPCE
// || ||++--- RXMODE
// || |+----- ODME
// || +------ SFDEN
// |+-------- TXEN 送信有効
// +--------- RXEN 受信はしない
USART0.CTRLC = 0b00000011;
// |||||+++-- CHSIZE 8BIT
// ||||+----- SBMODE 1stop
// ||++------ PMODE Parity無し
// ++-------- CMODE 非同期USART
USART0.BAUD = 8333; // (64*20MHz)/(9600/16)
sei();
}

/***** LOOP ******/
// 20ms周期でA/D値を読んでPWM値を設定
// 1秒ごとに2つの角度をシリアル出力
void loop() {
byte i;
int d;
byte cnt = 0; // 処理カウンタ 1秒をチェック
while(1){
if(TCA0.SINGLE.INTFLAGS & TCA_SINGLE_OVF_bm){ // TCA 1サイクル? (20ms)
TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // フラグクリア
for(i=0; i < 2; i++){ // 2ch
cli(); // 割込禁止にして
d = ad_15b[i]; // A/D 15bit 32回累積加算値読み出し
sei();
ad_10b[i] = d / 32; // 15bit値を10bitに
tca_pwm[i] = map(ad_10b[i], // 0~1023を
0, 1023, SVP_M90, SVP_P90); // -90°~+90°のPWM値に
*TCA0CMP[i] = tca_pwm[i]; // PWM設定
}
cnt++; // 20msごとに+1
}
if(cnt >= 50){ // 1秒経過
cnt = 0;
for(i=0; i < 2; i++){ // 2ch
d = map(ad_10b[i], // A/D値 0~1023を
0, 1023, -900, 900); // -90.0°~+90.0°
strbcd(d, 4, 1); // xxxx.x 6文字に
txstr(bcd_bff); // 文字列出力
}
txstr("\r\n"); // CR+LF
}
}
}

メモリーの使用量、こんな具合です。
  最大4096バイトのフラッシュメモリのうち、スケッチが
   1135バイト(27%)を使っています。
  最大256バイトのRAMのうち、グローバル変数が35バイト
   (13%)を使っていて、ローカル変数で221バイト使うことができます。

例えば、シリアル出力するためにSerial.begin()、Serial.print()を
使うと、
  フラッシュメモリ 1823バイト(44%)
  RAM 59バイト(23%)
となってしまいます。
さらに、sprintf(); で書式指定すると
  フラッシュメモリ 3014バイト(73%)
となり、処理プログラムのエリアを圧迫しはじめます。

「4kバイト:0x0000~0x0FFF」というプログラム領域、
昔なら「2732」(24ピンの紫外線消去ROM)です。

Arduinoの内部タイマとしてTCAが8bitの分割モードで
使われています。
これを16bitで使うには、という手順の参考になるかと。

A/Dコンバータの累積加算機能はありがたいかと。

※サンプルプログラムといえば「Lチカ」して
「動いたよ」というパターンが多いかと思います。
しかし・・・マイコンが持つ能力を引き出したいとき
(内蔵のハードウェアを堪能したい)は、出来合いの
ライブラリには頼れません。
ハードウェアの直叩きが必要です。

※関連
ATtiny402 備忘録
その後のMPLAB@Snap:Arduino IDEで使えるぞ
Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
割り込みで処理させるwordデータの扱い
数値をBCD出力(表示)するルーチン #3



| | コメント (0)

2025年7月23日 (水)

ATtiny402 備忘録

ATtiny402、まだ本格的には使っていませんが、
ちょいとメモを残しておきます。
  8pinのだけじゃなくtinyAVR0系 AVR1系 AVR2系
  についてもあれこれと。

・Arduino IDEでAttiny402が使える。
  その後のMPLAB@Snap:Arduino IDEで使えるぞ
    ただし、そのままではArduinoの環境で
    動くようになるので、I/Oまわりを触ろうと
    すると注意が必要。

・書き込み方法がUPDIに。
  電源+GND+UPDIの3線だけでライタと接続。
    ATmega328Pだと6線が必要。
  しかし、UPDI線にスイッチをつないでもリセットはできない。
    30kΩくらいでプルアップされている。
  UPDI:PA0を出力にしても出力制御(H/L出力)はできない。
  しかし、入力はできる(ようだ)。
    この入力にパルスを与えた後はUPDI書き込みに
    失敗するのでいったん電源を再投入。

・I/Oレジスタのアクセス方法が変わった。
  Atmega328Pでは、ポートの入出力と初期H/L出力レベル、
  それとプルアップの有無をまとめて指定できた。
  ATtiny402では、VPORTに対して同様の操作はできるが、
  プルアップ抵抗をオンするには別の命令で指定しなけれ
  ばならない。

これまでは入出力とプルアップを同時に指定できた。
PORTB = 0b00110000; // out data, pull up
DDRB = 0b00001111; // portI/O
// |||||+---- PB0 out
// ||||+----- PB1 out
// |||+------ PB2 out
// ||+------- PB3 out
// |+-------- PB4 in (pull up)
// +--------- PB5 in (pull up)

ATtiny402だとプルアップは別命令で。
VPORTA.OUT = 0b11000000; // 出力データ
VPORTA.DIR = 0b01001100; // ポート入出力
// || |||+---- PA0 6pin in UPDI
// || ||+----- PA1 4pin in
// || |+------ PA2 5pin out
// || +------- PA3 7pin out
// |+---------- PA6 2pin out H出力
// +----------- PA7 3pin in 【pull upされない
PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // PA7 pull upは別命令で

  出力にした入力ポートに1を書くと出力のH/Lが反転
  する操作はこれまでと同じよう可能。
  これはオシロで見るためのテスト的パルスを出すのに便利。

・さらばPROGMEM。 (PSTRやPGM_P、Fマクロ)
  Atmega328PではROMに置いてあるデータを読み出すのに、
  「LPM命令」しか使えなかった。
  このため、PROGMEM、PSTR、PGM_P、Fマクロを駆使して
  ROMエリアに固定データ(文字列を含む)を配置していた。
  ATtiny402では、「LD命令」でRAMのデータだけでなくROMの
  データも読めるようになり、LPMを使わなくてすむようになった。
  これでPROGMEMから開放されるのだ!

・AD変換で変換データの累積ができる。
  1回だけの10bit変換だけでなく、2、4、8、16、32、64回の
  自動的累積加算が設定できるようになった。
  64回にセットすると、10bitのAD値が64回加算され、最大値
  で65472のデータが得られる。
  ソフト的に加算平均しなくても、勝手に加算してくれる。

・どうしてもリセットしたいとき。
  ウオッチドッグを使ってのリセット操作だろう。
  プルアップ付きの入力ポート(これにPA0:UPDI線が使えれば
  エエんだが、どうだか)にスイッチを付け、押されたらloop
  を回してWDR命令の実行(このloop外のメインループでのWDR)
  を阻止してリセット。

・RSTCTRL.SWPRでもリセットできる。
  データシートのRSTCTRLを見ると、ソフトウェアリセット
  の機能を持っている。
    // Reset immdiately using software reset.
    inline void ResetViaSoftware() {
      _PROTECTED_WRITE(RSTCTRL.SWRR, 1);
    }
  これでリセット。

・PWM出力のデューティ、0でLを、maxでHを出力できる。
  Atmega328Pの偽AnalogWriteとおさらば。

・delayやmillisのためにタイマーTCAを8bit分割モードで使っ
 ている。(Arduino環境のとき)
 このためTCAを16bitで使いたいときは、とうぜん、delay、
 milllisは使えなくなる。

・RTC、PIT設定時の注意 《データシート 22.13~》
 RTC.CTRLA、RTC.CNT、RTC.PER、RTC.CMP、RTC.PITCTRLA
 これらのレジスタを操作する時は
 RTC.STATUSおよびRTC.PITSTATUSレジスタにある
 それぞれのbusyビットをチェック(0の確認)をすること。
 さもないと、うまいこと動かない。
   下記記事のように、RTCとPIT関連レジスタを触る
   ときはビジー待ちをチェックのこと。
 ・RTC機能付ATtiny RTCとPITの初期化注意点
 ・ATtiny1614に時計用水晶発振子をつなぐ

・ATtiny402はタイマAとBの2種類だけだが、
 ATtiny1614ではタイマDが増える。
 周波数カウンタではタイマBとタイマDが
 使えるのだが、両方ともキャプチャ信号が
 計数する入力クロックで刻まれている。
 このため、クロック信号が無い状態では
 キャプチャできない。
 周波数ゼロの状態を計られない。

・ATtinyを含め、AVRマイコンのデータシートなどの
 情報はここ。
  ・AVR日本語情報サイト

・14pinのtiny1604,1614,1624を比較。
  AVR0系:ATtiny1604のピン配列
1604
  AVR1系:ATtiny1614のピン配列
1614
  AVR2系:ATtiny1624のピン配列
1624

・14pinのtiny1604,1614,1624,3224の価格(デジキー調べ)
  ATtiny1604   SOP:@124
  ATtiny1614   SOP:@145
  ATtiny1624   SOP:@162
  ATtiny1624  SSOP:@148
  ATtiny3224   SOP:@159
  ATtiny3224  SSOP:@169
    ROM,RAMの大きな(32k/3k)ATtiny3224を買って
    おくほうが幸せになれる。
    UARTが2つ乗っているし。

・SSOP(0.65mm)はATtiny1624,3224だけ。
  SSOPを使うと、秋月の
   AE-TSSOP14 0.65mmピッチ表面実装用14ピン DIP変換基板
  が使えて、狭ピッチ(14pin DIP幅)で配線できる。

・tinyAVR2系は、12bitのタイマTCDが無いかわりに、
 2つのTCBを連結して、32bitタイマを構成できる。
   これは楽しみ!
 TCBのクロック入力はイベントで得ることができ、
 tinyAVR1のようにタイマTCAからもらうことを
 しなくてすむようになっている。 とデータシートから。


| | コメント (0)

2025年7月21日 (月)

その後のMPLAB@Snap:Arduino IDEで使えるぞ

あれこれネットをさまよっていたら、
 JH7UBCブログ
ATtiny402 ArduinoでLチカまで(2023-07-18)
USBシリアル変換器でATtiny402への書き込み(2023-07-23)
Arduinon IDEでATtiny402を手懐ける方法が出てきました。

その中で「書き込み装置の選択」にMPLAB SNAP(UPDI mode)
出ています。
うまく行くのかどうか分かりませんでしたが、
試してみると・・・書けました。

設定、確認するとことろ。
Ss12_20250721101601

  a.書き込み装置 MPLAB SNAP(UPDI mode)
  b.チップの種別 ATtiny402
  c.使用するクロック周波数
  d.milliesやdelayを使いたいならd.
  e.タイマー割り込みは要らないぜというのならe.

タイマーはタイマーAを8bit分割で使い、ベクタ番号9
のオーバーフロー割り込みで計時しています。

「e.」だと、この処理が無くなって、使用RAMバイト数
はゼロになって、ほぼ裸のTiny402が使えます。


たとえば、こんなテストスケッチ。

//  ATtiny402 : 20MHz, millis() micros()タイマーイネーブル
void setup() {
VPORTA.DIR = 0b11001110;
// || |||+---- PA0 6pin UPDI
// || ||+----- PA1 4pin
// || |+------ PA2 5pin
// || +------- PA3 7pin 20ms経過でトグル
// |+---------- PA6 2pin millis変化でトグル
// +----------- PA7 3pin loopでトグル
}

void loop() {
uint32_t ms, ms20, tnow;
tnow = millis(); // 現ms値
ms = ms20 = tnow;
while(1){
VPORTA.IN |= (1 << 7); // PA7 3pin トグル
tnow = millis(); // 現ms値
if(ms != tnow){ // ms値変化?
VPORTA.IN |= (1 << 6); // PA6 2pin トグル
ms = tnow;
}
if((tnow - ms20) >= 20){ // 20ms経過
VPORTA.IN |= (1 << 3); // PA3 7pin トグル
ms20 = tnow;
}
}
}

・loopでPA7をトグル
・millis()で1ms変化を調べてPA6をトグル。
・20ms経過を調べてPA3をトグル。

すると、こんな波形が出てきます。
Ms11a
もうちょい拡大すると・・・
Ms12a
「millis()値変化でトグル」が1msきっちりにならない
のに注意が必要です。
  Arduino UNO R3でもきっちり1msじゃ
  ありません。

| | コメント (0)

2025年7月17日 (木)

シングルゲート・マルチプレクサIC「74LVC2G53」

DDS IC「AD9833」をArduino UNO R3で制御:箱入れ #2
でマルチプレクサIC「74LVC2G53」は、
  2Gなんで2ゲートかと思いきや機能ブロックは一つだけ
と書きました。

その後、ちょいと調べてみました。

まず、東芝のから。
 ・東芝 TC4W53
  4000番C-MOS 8pin
  ±5V電源で使える

 ・東芝 TC7W53
  74HCタイプ 8pin
  ±5V電源で使える

そして件の74LVC2G53一家
 ・TI  74LVC2G53
  2Gで1回路 8pin 1.65V~5.5V

 ・Nexperia  74LVC2G53
  2Gで1回路 8pin 1.65V~5.5V

ところが・・・Nexperiaにはこんな「1G」品が。
 ・Nexperia  74LVC1G53
  型番が1Gになってますが機能的には
  「2G53」と同じです。

さらに調べると、6ピンのが見つかります。
INH端子をなくして切り替え入力だけになっています。
 ・TI  SN74LVC1G3157
 ・ON semi  NC7SB3157

さらに、これの「2G」が見つかります。
 ・Nexperia  74LVC2G3157
  2回路入り 10pin INH端子無

これは2Gという型番でちゃんと2回路に
なってます。

どれを使うか・・・悩ましいところです。


「53」は4000番シリーズの4053が元型番。
  16ピンの4053は3ゲート入ってる。
「3157」の157はディジタル・マルチプレクサ(データセレクタ)
の74157から来てるのかな?
Nexperiaのでは、INH端子の名称が「/E」になっています。


※関連
PWMでD/A変換:アナログマルチプレクサの応用で
PWMでD/A変換:アナログマルチプレクサの応用、解決方法

| | コメント (0)

2025年7月16日 (水)

今年の梅酒

梅の不作が聞こえてきて心配していたんですが、「梅」が
たんまりとやってきました。

Ume1

氷砂糖をほうりこんで、アルコール分は「ウイスキー」。
Ume2
使ったのはウイスキー 365(さんろくご)
4ltr入りのを3本。


| | コメント (2)

その後のMPLAB@Snap

UPDI」で書き込む新しいタイプのAVRマイコン、
  ・Microchip StudioでMPLAB@Snapが動かない
ということで、MPLAB@SnapはMicrochip Studioで
使うのはあきらめて、「MPLAB IPE」を使って
書いていました。

フラッシュメモリの書き込みはちゃんとできていたんですが、
困ったぞが「ヒューズビット」の書き込み。
MPLAB IPEでは、うまいこといかないのです。
 ・起動時のクロックを20MHz→16MHzにしたい
 ・BOD電圧を触りたい 1.8V→2.6V
など。

そこで・・・あれこれ検索すると
ATTINY202/402のFUSEビットをAVRDUDESSを使って書く方法
[Part2] ATTINY202への書き込みとプログラムをやっています!

AVRDUDESSが使えそうということで、試してみました。

起動するとMPLAB@Snapをそのまま認識してくれました。
Sn1

コンパイル結果のHEXファイルを読み込むようにして、
右下の追加コマンド。
 ・-x mode=avr    AVRモードにせよ
             これは必須!

 ・-U fuse2:w:0x01:m ヒューズ02の値を01に
          変更したいときだけ

とりあえずこれでMPLAB@Snapを使っています。

・書き込み装置選択画面
Sn2

| | コメント (0)

2025年7月10日 (木)

ガレージPCがぁ~

私の帰宅より先にガレージやってきて
(とうぜん、一杯呑みが目的)、PCを
立ち上げた佐藤テック君からの入電。
「PCがアカンで」っと。
Ggpc1
画面がきれいに流れてて、アナログテレビ様。

もうずいぶん使っています。
こんなことからももう5年。
2020年9月17日:CR2032バックアップ用リチウム電池・・・0V

ビデオ出力は、外付けグラボじゃなくCPU内蔵型。
「メモリの接触不良じゃなかろうか」という判断。
再立ち上げで復活してますが、ちょっとこわい。
今日にでも、メモリの端子をIPAで拭ってみます。

| | コメント (1)

2025年7月 3日 (木)

回路屋にとっては不気味な音:「プチっ」

ついさっき、私の作業机の後方で「プチっ」という音が一発。
ちょいと離れたところにいる同僚も「んっ!?」っと反応
するくらいの音の大きさ。
あれこれ電子回路が作動しているときの「プチっ」音は
なかなか不気味です。
  デバイスやコンデンサの破裂の可能性。
「どこや?!」っと耳を澄ましているともう一発「プチっ」。
しかし、臭いは感じません。 音だけ。

単発の音ですので、場所の特定が困難。
「このあたりか?」を見てみると、充電中のニッ水電池
がありました。

この記事にある
 ・パナの充電器BQ-321は劣化ニッ水でも充電してくれる(かも)
ということで、BQ-321死亡電池HHR-3XPS(2400mAh)を充電して
いたのです。 (2005年5月の刻印)

Bq333
Bq334

コンセントから充電器を外してから、電池を触っても
びっくりするくらいの熱さではありません。
充電してたらまぁこんなくらいやろの熱。
液体が漏れているということもありませんでした。
充電はしっかりされてました。

その後、音は聞こえずで、おそらくこれが原因。
「プチっ」音、電池の中の圧力逃がし弁が働いた
音なのかと推定。

※関連
アルカリ電池の液漏れで「音」!?
  TVのリモコンから小さな「プチプチ」音が


| | コメント (0)

DDS IC「AD9833」をArduino UNO R3で制御:箱入れ #2

DDS IC「AD9833」をArduino UNO R3で制御:箱に入れる
ここからちょっと手直し。
 ・高い周波数で方形波を出力するとちゃんとした
  波形が出てこない。原因はアンプの飽和。
 ・AD9833の出力、正弦波と三角波だとおよそ
  0.6V(p-p)なのが、方形波だと電源電圧フルスイング
  になってしまう。
 ・このため、前の回路では前段のアンプ(4V(p-p)まで増幅)
  が飽和。
 ・そこで、方形波の時にレベルを下げるような細工
  を入れ込む。
 ・使ったのは2入力のアナログマルチプレクサ。
    74LVC2G53 8pinのIC
      「2G」なんで2ゲートかと思いきや
      機能ブロック的には一つだけ。
      なんかへんな感じ。
 ・方形波を出力する時は、出力レベルを1/9した側に
  切り替える。
 ・飽和しないしLPFを通らないので、まっとうな
  方形波(今度はアンプでの帯域制限)が出るように。

ということで、こんな回路になりました。
  ※回路変更あり(一連のコメントを参照)
Ad9833
・BSch3Vのデータ
   ・ダウンロード - ad9833_04.ce3

切り替え機能を追加したスケッチ。
   ・ダウンロード - ad9833_03.zip

AD9833を使う時の参考にどうぞ。

周波数を高くするときにいるのは、やっぱ、フィルタです。
これ以上の高速アンプは電気食いだし、電池運用はちょい
としんどいか。

※回路図をちょっと訂正
LVC2G53の手前の2段CRフィルタ。
マルチプレクサの入力容量がけっこうあるようなので
(データシートでオン時約20PF)フィルタ部を変更しました。
9833a
  入力部の抵抗を200Ω→1Kに。
  コンデンサをなしに。
1MHzあたりからレベルが落ち始めます。
4MHzになると出力電圧はほぼ半分に。

※追記
AD9833の出力を
  ・出力レベルを可変したい
  ・出力ののDCオフセット電圧を可変したい
ときの方法、面倒でもこんな構成かと。
Pm4
方形波の時はLPFを通さずできるだけ「生」信号
を使えるよう、MPXで分けます。
正弦波、三角波の時だけLPFを通します。
12dB/octので描きましたが、2段にして24dB/octや
LCフィルタでも良いわけです。
方形波出力のとき、後段アンプを飽和させないように
します。
  飽和させると電源ラインが汚くなります。
周波数が高くなると、高抵抗が使えなくなるので
ボリュームの入力部が面倒になります。

現スケッチのままで動くように
 マルチプレクサのプルアップ抵抗をプルダウンに。

04a


| | コメント (22)

« 2025年6月 | トップページ | 2025年8月 »