ATtiny202 正弦波のノイズについて調べてみた。 [Arduino]
以前、当ブログで、
202duinoで正弦波 → ノイズがのる → ATtiny412でDAC使用:放課後マイコンクラブ:SSブログ
https://hello-world.blog.ss-blog.jp/2023-06-10
というので、PWMでの疑似DACをあきらめ、1シリーズのDACでしのぎましたが、こちらの方もATtiny202のPWMでノイズが出たというのを確認しました。
ATtiny202で正弦波を作ったらノイズが入る
https://www.jh4vaj.com/archives/39505
正弦波の下り坂でノイズが入っています。
つまり、TCB0.CCMPH を減らしたときです。
いろいろ想像して、確認して、原因がわかりました。
簡単にいうと、、、
ランナー(カウンタ)が、ゴール(TCB0.CCMPH)に向かって走っているときに、ゴールが先になる場合(TCB0.CCMPHが増える場合)は問題なし。
ゴールした後に変更しても、すでに出力はLOWになっているので大きな問題はない。
だけど、ゴール(TCB0.CCMPH)が手前に来る場合(TCB0.CCMPHが減る場合)は注意。
ゴールした後(カウンタがTCB0.CCMPHを過ぎた後)にゴールを変更しても出力はLOWのままで問題なし。
あるいは、走者がゴールする前で、変更後のゴールも走者の前のときもHIGHのままで問題なし。
問題なのは、まだ走者がゴールしていないのに(カウンタがTCB0.CCMPHに達していないのに)、走者よりも後ろにゴールを設定してしまったとき(TCB0.CCMPHをカウンタよりも少ない値にしたとき)です。出力がHIGHのまま、カウンタはTOP値まで行ってしまいます。
ということで正弦波のとき限定のパッチを当ててみました。
なぜ正弦波限定なのかというと、正弦波は連続性があり前の値と大きく変わることがない(と思われる)からです。(高い音だとサンプリングの関係で値がとぶと思う。)
新しいループに入ったときに、ゴールの変更はすぐにでもできるのですが、ゴールが近い(TCB0.CCMPHの値が小さい)ときは、上記の事故がおこりうるので、ゴールした後(カウンタがTCB0.CCMPHを過ぎた後)にゴールを変更(TCB0.CCMPHを変更)すればいいということで、適当にウェイトをいれてみたところノイズのない正弦波になりました。
TCB0.CCMPHの更新タイミングが、カウンタが0になったときではなく、即時行われるので起こる事故(ノイズ)だったようです。
原因はわかりましたが、一般的な解決法はまだ思いつきません。
202duinoで正弦波 → ノイズがのる → ATtiny412でDAC使用:放課後マイコンクラブ:SSブログ
https://hello-world.blog.ss-blog.jp/2023-06-10
というので、PWMでの疑似DACをあきらめ、1シリーズのDACでしのぎましたが、こちらの方もATtiny202のPWMでノイズが出たというのを確認しました。
ATtiny202で正弦波を作ったらノイズが入る
https://www.jh4vaj.com/archives/39505
正弦波の下り坂でノイズが入っています。
つまり、TCB0.CCMPH を減らしたときです。
いろいろ想像して、確認して、原因がわかりました。
簡単にいうと、、、
ランナー(カウンタ)が、ゴール(TCB0.CCMPH)に向かって走っているときに、ゴールが先になる場合(TCB0.CCMPHが増える場合)は問題なし。
ゴールした後に変更しても、すでに出力はLOWになっているので大きな問題はない。
だけど、ゴール(TCB0.CCMPH)が手前に来る場合(TCB0.CCMPHが減る場合)は注意。
ゴールした後(カウンタがTCB0.CCMPHを過ぎた後)にゴールを変更しても出力はLOWのままで問題なし。
あるいは、走者がゴールする前で、変更後のゴールも走者の前のときもHIGHのままで問題なし。
問題なのは、まだ走者がゴールしていないのに(カウンタがTCB0.CCMPHに達していないのに)、走者よりも後ろにゴールを設定してしまったとき(TCB0.CCMPHをカウンタよりも少ない値にしたとき)です。出力がHIGHのまま、カウンタはTOP値まで行ってしまいます。
ということで正弦波のとき限定のパッチを当ててみました。
なぜ正弦波限定なのかというと、正弦波は連続性があり前の値と大きく変わることがない(と思われる)からです。(高い音だとサンプリングの関係で値がとぶと思う。)
新しいループに入ったときに、ゴールの変更はすぐにでもできるのですが、ゴールが近い(TCB0.CCMPHの値が小さい)ときは、上記の事故がおこりうるので、ゴールした後(カウンタがTCB0.CCMPHを過ぎた後)にゴールを変更(TCB0.CCMPHを変更)すればいいということで、適当にウェイトをいれてみたところノイズのない正弦波になりました。
TCB0.CCMPHの更新タイミングが、カウンタが0になったときではなく、即時行われるので起こる事故(ノイズ)だったようです。
原因はわかりましたが、一般的な解決法はまだ思いつきません。
// megaTinyCore ATtiny202/212/402/412 Sine Wave - PWM version
#include <util/delay_basic.h>
static const uint16_t OCTAVE9[] = { 7023, 7441, 7883, 8352, 8848, 9375, 9932, 10523,11148,11811,12513,13258 };
static const uint8_t SINE256[] = {
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124,
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0 };
void setup(){ // Register Settings
TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; // 20MHz / 1 / 256 -> 78,125Hz
TCB0.CTRLB = TCB_CNTMODE_PWM8_gc | TCB_CCMPEN_bm; // 8-Bit PWM mode, Output Enable (WO,PA6,megaTinyCore:P0)
TCB0.CCMPL = 0xff; // top value = 255
TCB0.CCMPH = 0; // output value
}
void loop(){
sineWave( 60, 500); // C4
sineWave( 64, 500); // E4
sineWave( 67, 500); // G4
sineWave( 72, 2000); // C5
delay(1000);
}
void sineWave(uint8_t midiNum, uint16_t msDuration) {
uint16_t i, di = OCTAVE9[ midiNum % 12 ] >> (10 - midiNum / 12); // 256 times the Wave Table subscript to advance in one cycle
uint32_t cycDuration = F_CPU / 256 / 1000 * msDuration; // Convert duration to number of cycles
static uint8_t pwNew, pwNow;
do {
pwNew = SINE256[ ( (i += di) >> 8 ) ];
if( pwNew < pwNow && pwNow <120) _delay_loop_1(40); // <== Noise Reduction (wait 3x40=120cycle)
TCB0.CCMPH = pwNow = pwNew; // 8bit PWM (78,125Hz)
while( !TCB0.INTFLAGS ); // Waiting for TCB0 overflow (every 12.8usec(78,125Hz))
TCB0.INTFLAGS = TCB_CAPT_bm; // cleared by writing a '1'
} while( --cycDuration ); // Exit if note data is 0
TCB0.CCMPH = 0; // Set output to 0
}
2023-11-13 23:39
nice!(0)
コメント(0)
コメント 0