前回の続きで、ソフトウェアを用意していきます。ちなみに公開しているページはこっち。
Raspberry Piの準備
Raspberry Pi側はRaspberry Pi OSをMicroSDに書き込んで、普通に立ち上げていきます。Raspberry Pi imagerでmicroSDにRaspberry Pi OS Liteのイメージを書き込みます。書き込んだら、コンソール無しでセットアップを進められるようsshを有効化しておくため
$ cd /media/${USER}/boot/ $ touch ssh
としておきます。
Raspberry PiにmicroSDを挿して起動します。起動したら、
$ ssh pi@raspberrypi.local -o PreferredAuthentications=password
でログインします。(普通は -o 以下はなくてもいい)
$ sudo raspi-config
でraspi-configを起動して、パスワード変更、シリアルポートのログインシェル無効化とシリアルインタフェース有効化、localeでja_JP.UTF-8を追加、hostnameの変更を行います。変更して再起動したら、再度ログインして、
$ sudo apt update $ sudo apt upgrade
として、更新をかけておきます。
ATmega328pのファームウェアの準備
ホストPC側でArduino Unoとしてファームウェアを用意しておきます。
今回のソースコードは以下のようになりました。
#define TCCCLK 2000000 // CPUクロックを8分周した値。
#define ICP1 8 // ICP1の端子を定義
// 周期測定ののための変数
volatile uint16_t MaxV,MinV,NumV;
volatile uint32_t SumV;
volatile uint16_t CapV;
volatile uint16_t PreCap,NowCap;
// 1秒間の最大最小平均を格納するための変数
uint16_t tMaxV,tMinV,tNumV;
float tAveV;
uint32_t tSumV;
unsigned long current_time;
// 割り込みハンドラ
ISR(TIMER1_CAPT_vect) {
TIFR1 |= (1<<ICF1);
NowCap = ICR1;
CapV = NowCap - PreCap; // 差分を求めて周期を算出
PreCap = NowCap;
if(CapV > MaxV) MaxV = CapV;
if(CapV < MinV) MinV = CapV;
SumV += CapV;
NumV++;
}
// 割込ごとの電圧測定のための変数
volatile uint32_t Vcnt; // 測定回数
volatile uint16_t Vval; // 測定値読出用
volatile double Vtmp; // 電圧
volatile double Vsqsum; // 自乗和積算用
volatile double Vmax; // 電圧最大値
// 1秒間の計測結果を格納するための変数
uint32_t tVcnt;
double tVsqsum;
double tVol;
double tVmax;
// 割り込みハンドラ
ISR(ADC_vect) {
Vval = (uint16_t)ADCL + ((uint16_t) ADCH << 8); // ADCLを先に読む必要がある。
Vtmp = ((double)Vval-512.); // 単位はAD変換値刻み。実電圧への変換はRasPi側で行う
Vsqsum += Vtmp*Vtmp;
if(abs(Vtmp) > Vmax) Vmax=abs(Vtmp);
Vcnt++;
}
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
// TCC1でICP1信号(PB0端子=IO8)の周期を計測する
TCCR1A = 0; //initialize Timer1
TCCR1B = 0;
TCNT1 = 0;
pinMode(ICP1, INPUT_PULLUP); //ICP1の端子をプルアップあり入力に設定
TCCR1B = 0x02; // 16MHzを8分周
TIMSK1 |= (1 << ICIE1); // キャプチャ割り込み許可
// ADCで入力電圧の実効値と最大値を測定する
DIDR0 &= 0xFE; // ADC0のデジタル入力禁止
ADMUX = 0x00; // AREF=外部、入力=ADC0
ADCSRB = 0; // ADCをFree running modeで使用
ADCSRA = 0xEF; // ADC Enable/ADC Start Conversion/Auto Trigger ENABLE
// Interrupt Enable/Prescaler=128
current_time = millis();
}
void loop()
{
static uint8_t skp = 3; // 最初の何回かはデータを捨てる
if (millis() - current_time >= 1000)
{
// 割り込みで更新している変数をコピー&初期化する
noInterrupts(); //割り込み停止
tMaxV = MaxV; tMinV = MinV; tSumV = SumV; tNumV = NumV;
MaxV = 0; MinV = 65535; SumV = 0; NumV = 0;
tVcnt = Vcnt; tVsqsum = Vsqsum; tVmax = Vmax;
Vcnt = 0; Vsqsum = 0; Vmax = 0;
interrupts(); //割り込み再開
tAveV = (float)tSumV / (float)tNumV;
tVol = sqrt(tVsqsum/tVcnt);
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
current_time = millis();
// PCに送信
if(skp == 0){
Serial.print((double)TCCCLK/(double)tMaxV*1000.);
Serial.print(",");
Serial.print((double)TCCCLK/(double)tMinV*1000.);
Serial.print(",");
Serial.print((double)TCCCLK/(double)tAveV*1000.);
Serial.print(",");
Serial.print(tVol);
Serial.print(",");
Serial.println(tVmax);
} else {
skp--;
}
}
}
ADCはフリーランニングモードでAD変換させ続けて、割り込みで結果を取得します。確か、1秒間に2000回位の変換回数だったと思います。(忘れました・・・)
AD変換した値はdoubleに型変換して、そのまま絶対値の最大値と自乗平均を取っています。実際の電圧への変換はRaspberry Pi側で行うことで、使用する充電器(というかトランス)が変わってもファームウェアの変更は無しでいけるようにしました。
継ぎ接ぎだらけなので、変数名とかメチャクチャですが、めんどくさいのでそのままにしています。
avrdudeのインストール
$ sudo apt install avrdude tio
でavrdude(と後で使うtio)をインストールします。インストールしたら、/etc/avrdude.confを編集して、
# programmer id = "linuxgpio"; desc = "Use the Linux sysfs interface to bitbang GPIO lines"; type = "linuxgpio"; reset = 8; sck = 11; mosi = 10; miso = 9; ;
という感じでもともとコメントアウトされている行を有効化して、AVRのプログラミングに使用する端子を定義します。ファイルを保存したら、
$ sudo avrdude -c linuxgpio -p m328p avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e950f (probably m328p) avrdude: safemode: Fuses OK (E:FF, H:D9, L:62) avrdude done. Thank you. $
という感じで、ATmega328pにアクセスできることを確認します。
FUSE bitの設定
ATmega328pをArduinoとして動作させるにはhardware/arduino/avr/board.txtの中のArduino Unoのところに書かれたFuse bitの値を設定する必要がありますので、ターミナルモードで設定します。
$ sudo avrdude -c linuxgpio -p m328p -t avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e950f (probably m328p) avrdude> w lfuse 0 0xff >>> w lfuse 0 0xff avrdude> w hfuse 0 0xde >>> w hfuse 0 0xde avrdude> w efuse 0 0xfd >>> w efuse 0 0xfd avrdude> q >>> q avrdude: safemode: Fuses OK (E:FD, H:DE, L:FF) avrdude done. Thank you. $
Arduinoバイナリの書き込み
ホストマシンのArduino環境でメニューから「スケッチ」⇒「コンパイルしたバイナリを出力」として、バイナリをHEXファイルで出力します。出力したHEXファイルはscpでRaspberry Piへ持ってきておきます。
Raspberry Pi側で「with_bootloader.standard.hex」が付いた方のファイルをATmega328pに書き込みます。
$ sudo avrdude -c linuxgpio -p m328p -u -e -U flash:w:"スケッチ名称.ino.with_bootloader.standard.hex":a avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e950f (probably m328p) avrdude: erasing chip avrdude: reading input file "スケッチ名称.ino.with_bootloader.standard.hex" avrdude: input file スケッチ名称.ino.with_bootloader.standard.hex auto detected as Intel Hex avrdude: writing flash (32768 bytes): Writing | ################################################## | 100% 1.63s avrdude: 32768 bytes of flash written avrdude: verifying flash memory against スケッチ名称.ino.with_bootloader.standard.hex: avrdude: load data flash data from input file スケッチ名称.ino.with_bootloader.standard.hex: avrdude: input file スケッチ名称.ino.with_bootloader.standard.hex auto detected as Intel Hex avrdude: input file スケッチ名称.ino.with_bootloader.standard.hex contains 32768 bytes avrdude: reading on-chip flash data: Reading | ################################################## | 100% 1.41s avrdude: verifying ... avrdude: 32768 bytes of flash verified avrdude done. Thank you. $
完了したら、tioでコンソール出力を確認します。
$ tio -b 115200 /dev/ttyS0 [tio 18:21:19] tio v1.32 [tio 18:21:19] Press ctrl-t q to quit [tio 18:21:19] Connected inf,30518.04,nan,1.59,1.59 ... ...
(ここでは入力側のトランスを接続していないので、infとかnanとかが出ています)