スポンサーリンク

Arduino Nano Everyで高速PWMする例

Arduinoをそのまま組み込みで使う時にNanoを良く使ってたのだけど、そろそろ新機種である Nano Every に主力を移すべきかと思ったりもするのですが、完全に上位互換というわけではないので結局はケースバイケースかなと思います。

Arduino Nano Arduino Nano Every
MCU ATMega328p ATMega4809
Flash 32KB 48KB
SRAM 2KB 6KB
EEPROM 1KB 256B
クロック 最大16MHz 最大20MHz
USBコネクタ USB Mini USB Micro
価格 純正品なら Nano Every の方が安い。※ただし互換品を探すと Nano の方が安い。
互換性 Arduino IDE ベースでの互換性はそこそこ。タイマー周りはちょっと怪しい。

MCUが違うのでしょうがないのだけど、特にレジスタ直叩きでオーディオ周波数の波形生成なんかをしている場合は Every に移植するのは結構面倒です。
という事で、Nano Every で高速PWMによるサイン波生成のサンプルをメモしておきます。

ATMega4809 のタイマーは TCA というのが1つと TCB というのが4つで 328P とは全く構成が異なります。
クロック 16MHz を TCA0 で 512 カウントしてサンプリング 31.25kHz、9bit、これに PWM をかけて 440Hz サイン波を生成します。出力は Pin9 (PB0) 固定。

レジスタの定義はどこかでされているような気がするけど、取り合えず ATMega4809 のデータシートを見ながら全部このファイル内に置いてみました。


// Sin Wave Generation for Arduino-Nano-Every
#define PORTMUXBASE (0x05e0)
#define TCA0BASE (0x0a00)

#define TCAROUTEA (*((uint8_t*)(PORTMUXBASE + 0x04)))
#define TCA0CTRLA (*((uint8_t*)(TCA0BASE + 0x00)))
#define TCA0CTRLB (*((uint8_t*)(TCA0BASE + 0x01)))
#define TCA0CTRLC (*((uint8_t*)(TCA0BASE + 0x02)))
#define TCA0CTRLD (*((uint8_t*)(TCA0BASE + 0x03)))
#define TCA0PER (*((uint16_t*)(TCA0BASE + 0x26)))
#define TCA0CMP0 (*((uint16_t*)(TCA0BASE + 0x28)))
#define TCA0CMP0BUF (*((uint16_t*)(TCA0BASE + 0x38)))
#define TCA0INTCTRL (*((uint16_t*)(TCA0BASE + 0x0a)))
#define TCA0INTFLAGS (*((uint16_t*)(TCA0BASE + 0x0b)))
#define TCA0OVF_vect  _VECTOR(7)

float fdelta = 440.0 * 512 / 16000000.0;
uint16_t phase = 0;
uint16_t delta = (uint16_t)(fdelta * 32768);

int16_t sintab[512];

void setup() {
for(int i=0; i<512; ++i){
sintab[i] = 256 + sin(3.14159 * 2 * i / 512) * 255;
}
pinMode(9,OUTPUT);                // Pin9=Output
TCAROUTEA = 0x01;                 // TCA0 PWM to PORTB
TCA0CTRLA = 0x01;                 // PRESCALE : CLKSEL = fclk_per, peripheral Enable
TCA0CTRLB = 0x13;                 // CMP0EN, SINGLESLOPE
TCA0CTRLD = 0;                    // SPLIT mode=off
TCA0INTCTRL = 0x01;               // Enable TCA0 OVF interrupt
TCA0PER = 511;                    // Counter period = CLK / 512 (16MHz:31.25kHz or 20MHz:39.0625kHz)
TCA0CMP0BUF = 100;                // PWM Value (0 - 511)
}
ISR(TCA0OVF_vect){
TCA0INTFLAGS = 1;                 // Clear Interrupt
phase += delta;
if(phase >= 32768)
phase -= 32768;
TCA0CMP0BUF = sintab[phase >> 6]; // Set PWM value
}
void loop() {
// put your main code here, to run repeatedly:
}

結果 :
Pin9 出力に RC 一段入れただけなのでキャリアが乗ってしまっているけどこんな感じ。

未分類
スポンサーリンク