ATtiny85 タップテンポ付LFO その2

14_229_1atLFO2.png
前回のLFOをさらに発展させました。主な変更点は下記の通りです。考え方は前回と同じで、一定間隔でPWMのデューティ比を変更します。
・1周期126分割→510分割(PWMの分解能8ビット分をフルに生かせる)
・PWM周波数31.25kHz→250kHz(出力ピンPB0→PB1)
・タイマー割り込みを使ってPWMデューティ比を変更、タップ時間計測

タイマー・割り込みに関する詳細説明は省きます。ATtiny85(日本語データシートpdf)は2つのタイマーがあり、Timer0でデューティ比変更時間の管理とタップテンポの時間計測をし、Timer1でPWMを使います。該当レジスタを変更することにより様々な設定を行うわけですが、見慣れないビット演算が出てくるので最初は戸惑うかもしれません。
参考ページ→C言語入門::付録 ビット演算びんずめ堂)・AVRでのタイマとPWMの使い方うしこlog)・AVRでの割り込みの使い方うしこlog

今回のスケッチでは三角波を生成しています。ATtiny85のPWMを最高速で動作させるため、Arduino IDEのツールメニューから「Timer1 Clock: "64MHz"」を選択し「ブートローダを書き込む」という操作を行っておく必要があります。

▽Arduinoスケッチ(94行)
const int PWMpin = 1;
const int POTpin = 3;
const int SWpin = 4;

volatile byte rateCount = 0; // Timer0用カウンタ = 割り込み回数
volatile byte rateCountNum = 0; // Timer0 割り込み回数決定値
volatile int pwmCount = 0; // PWM用カウンタ 0~509
volatile unsigned long timeCount = 0; // タップ時間取得用カウンタ

unsigned long tapTime = 0; // タップ間隔時間
unsigned long timeTemp = 0; // タップ間隔時間一時保存用
unsigned long swCount = 0; // スイッチ用カウンタ
const byte rateAdj = 99; // 周期補正率 % 実測して設定
int oldPOTvalue = 2000; // 前回ADC読取値
int newPOTvalue = 2000; // 今回ADC読取値

void setup() {
PLLCSR |= (1 << PLLE); // Timer1 PLL許可
delayMicroseconds(200); // Timer1 PLL許可後安定するまで待機
PLLCSR |= (1 << PCKE); // Timer1 PCK(64MHz)許可
TCCR1 = 0b01100001; // Timer1 PWM動作A 分周なし
OCR1A = 100; // Timer1 比較Aの値 = PWMデューティ比
TCCR0A = 0b00000010; // Timer0 CTCモード
TCCR0B = 0b00000010; // Timer0 クロック8分周
TIMSK |= (1 << OCIE0A); // Timer0 比較A割り込み許可
OCR0A = 100; // Timer0 比較Aの値 = 割り込み間隔 us
pinMode(PWMpin, OUTPUT);
pinMode(POTpin, INPUT);
pinMode(SWpin, INPUT_PULLUP);
sei(); // 全割り込み許可
}

// LFO(Timer0 比較A割り込み)---------------------------------------
ISR(TIMER0_COMPA_vect) {
if (rateCount == rateCountNum) {
rateCount = 0;
if (pwmCount == 510) pwmCount = 0;
if (pwmCount < 256) OCR1A = pwmCount; // 三角波(上昇)
else OCR1A = 510 - pwmCount; // 三角波(下降)
pwmCount++;
}
rateCount++;
timeCount++;
}

void loop() { // 1ループ約0.14ms
// タップテンポ ---------------------------------------------------
if (digitalRead(SWpin) == LOW) {
swCount++;
if (swCount == 70) { // チャタリング対策 10msスイッチ押下で検出
// 前回スイッチ検出から経過した時間を記録
tapTime = timeTemp + timeCount * OCR0A * 0.001;
timeTemp = 0; // 経過時間をリセット
timeCount = 0;
if ( 100 < tapTime && tapTime < 3000) { // 周期制限 0.1秒~3秒
// デューティ比変更間隔時間へ換算(*1000/510*周期補正率)
rateCalc(tapTime * 1.96078 * rateAdj * 0.01);
}
}
}
else swCount = 0;

// ADC ------------------------------------------------------------
newPOTvalue = analogRead(POTpin); // 読取値が4以上変化で変更
if (abs(newPOTvalue - oldPOTvalue) > 3) {
rateCalc((100 + 3 * newPOTvalue) * 1.96078 * rateAdj * 0.01);
oldPOTvalue = newPOTvalue;
}
}

// デューティ比変更間隔時間(x)変更計算 x = p * q + r ----------------
void rateCalc(int x) {
byte q; // 商
byte r; // 余り
int pMin;
byte qMin;
byte rMin = 255;
for (int p = 255; p > 99; p--) { // 除数255から100まで計算
q = x / p;
r = x % p;
if (r < rMin) { // 余りrが最小値の時の値を記録
pMin = p;
qMin = q;
rMin = r;
}
}
// OCR0A変更前に経過した時間をtimeTempに代入した後、
// カウンタをリセットして新しいOCR0Aで再度カウント開始
timeTemp = timeTemp + timeCount * OCR0A * 0.001;
timeCount = 0;
OCR0A = pMin;
rateCountNum = qMin;
rateCount = 0;
}



<タップ時間計測について>
Timer0に比較A割り込みを設定している関係で、millis関数で時刻を取得しようとしてもうまくいきません。タップ時間の測定は割り込み回数をカウントすることにより行います。Timer0はクロック8MHz、8分周なので最小時間単位は1usです。OCR0Aを200にすると200usごとに割り込みが発生します。

【例】0.2msごとに割り込みが発生(OCR0A=200)→割り込みが50回発生(timeCount=50)→経過時間10ms(0.2ms×50)

OCR0Aを更新する時に、更新前の時間(timeTemp)を計算しておく必要があります(スケッチ最下部)。TCNT0も加味した方がより正確ですが、そこまでの精度は必要ないでしょう。タップについては1回目と2回目の区別はなく、毎回前回タップからの経過時間を計算します。経過時間が設定可能なLFO周期内であるときだけ、実際に周期を変更することになります。



<デューティ比変更間隔時間の計算処理について>
OCR0Aを200とし、割り込みが発生する度にデューティ比を変更すると、周期は102ms(0.2ms×510)です。OCR0Aは255まで(8ビット)なので、このままでは周期を長く設定できません。分周比を変えることも考えましたが時間分解能が悪くなります。そこで長い周期を得るために、何回か割り込みが発生した時にデューティ比を変更するようにします。

【例】0.2msごとに割り込みが発生(OCR0A=200)→割り込みが10回発生(rateCountNum=10)するごとにデューティ比変更→周期1020ms(0.2ms×10×510)

次は逆に周期から割り込み間隔と回数を計算します(上記スケッチのrateCalc関数)。ちょっと無理やり感がありますが、100~255usの割り込み間隔で割った商(=割り込み回数)と余り(=誤差)を全て計算し、余りが最小になる時を採用します。割り込み間隔はあまりに狭いと問題がありそうなので、100us以上としました。

【例】最適値計算:周期1030ms(1030000us)→デューティ比変更間隔時間2019us(1030000÷510切捨)→224us(割り込み間隔)×9(回数)+3us(誤差)
計算値設定後:0.224msごとに割り込みが発生(OCR0A=224)→割り込みが9回発生(rateCountNum=9)するごとにデューティ比変更→周期1028ms(0.224ms×9×510)


ほとんどの場合で周期の誤差は2ms以内に収まります。実は上記例では2019usを+1usして再計算したほうが誤差が減るのですが、そこまでの計算はしないことにします。

タグ : マイコン 

ATtiny85 タップテンポ付LFO その1

14_228_1atLFO.jpg
モジュレーション系のエフェクターを作る場合Low Frequency Oscillator(LFO)は必須となりますが、タップテンポ機能をつけたかったのでマイコンを利用しました。2ループスイッチャーのときと同じATtiny85で、接続は上図右です。参考サイト→Arduino: LFO Generator
上記サイトのスケッチもなんとなく理解したつもりですが、初心者(私)向けに自分でプログラミングしてみることにしました。※あまり高品質ではないと思います。

下図は周期126msの三角波の例です。PWMについての詳細説明は省略します。analogWrite関数でPWM出力のデューティ比を変更することにより、256段階の電圧が出力できると大雑把に考えておけばよいかと思います。
14_228_2atLFOs.png
1周期を126分割して考え、1ms経過するごとに4ずつデューティ比を変化させます。1msではなく1.024ms間隔にしたときは、1.024×126で周期129msです。このようにデューティ比変更間隔時間を変えることで周期を変化させます。実測では少しずれるので補正を入れます。

下記スケッチでは予め計算した配列を用意し、正弦波を生成しています。ATtiny85を8MHzで動作させるため、Arduino IDEのツールメニューから「Clock: "8 MHz (internal)"」を選択し「ブートローダを書き込む」という操作を行っておく必要があります。

▽Arduinoスケッチ(69行)
const int PWMpin = 0;
const int POTpin = 3;
const int SWpin = 4;

unsigned long tapTime1 = 0; // 前回タップ時刻
unsigned long tapTime2 = 0; // 今回タップ時刻
unsigned long SWcount = 0; // スイッチ用カウンタ

int oldPOTvalue = 100; // 前回ADC読取値
int newPOTvalue = 100; // 今回ADC読取値

int i = 0; // PWM用カウンタ 0~125
unsigned long rate = 4000; // デューティ比変更間隔時間 us
int rateAdj = 24; // rate補正値 LFO周期: 126 * ( rate + rateAdj ) us
int rateMin = 760; // rate最小値 us
unsigned long preTime = 0; // 前回デューティ比変更時刻
unsigned long postTime = 0; // 現在時刻
byte sineTable[] = {
0, 0, 1, 1, 3, 4, 6, 8, 10, 13, 15, 19, 22, 26, 30, 34,
38, 43, 48, 53, 58, 64, 69, 75, 81, 87, 93, 99, 105, 111, 118, 124,
130, 136, 143, 149, 155, 161, 167, 173, 179, 185, 191, 196, 201, 206, 211, 216,
220, 224, 228, 232, 235, 239, 241, 244, 246, 248, 250, 251, 253, 253, 254, 254
};

void setup() {
TCCR0B = 0b00000001; // PWM高速化 64倍(分周なし) 31.25kHz
pinMode(PWMpin, OUTPUT);
pinMode(POTpin, INPUT);
pinMode(SWpin, INPUT_PULLUP);
}

void loop() {
// タップテンポ----------------------------------------------------------------
SWcount = 0;
while (digitalRead(SWpin) == LOW) {
SWcount++;
if (SWcount == 10) { // チャタリング対策 10msスイッチ押下で検出
tapTime2 = millis() / 64; // PWM高速化の影響でmillis時間が64倍になる
// タップ間隔制限 0.1秒~3秒
if ( 100 < tapTime2 - tapTime1 && tapTime2 - tapTime1 < 3000) {
rate = (tapTime2 - tapTime1) * 7.9365 - rateAdj; // タップ間隔換算
}
tapTime1 = millis() / 64;
}
delayMicroseconds(625); // 1ms待機(約1.6倍になる?)
}

// ADC-------------------------------------------------------------------------
newPOTvalue = analogRead(POTpin);
if (abs(newPOTvalue - oldPOTvalue) > 2) { // ADC読取値が3以上変化でrate変更
rate = rateMin + 24 * newPOTvalue;
oldPOTvalue = newPOTvalue;
}

// LFO-------------------------------------------------------------------------
if (i == 126) i = 0;
while (micros() / 64 - preTime > rate - 300) {
// 普段はスイッチ検出待ちのためメインループを繰り返すが、
// デューティ比変更予定時刻の300us前になったらこのループ内に入り待機する
postTime = micros() / 64;
if (postTime - preTime >= rate) {
if (i < 64) analogWrite(PWMpin, sineTable[i]); // 正弦波(上昇)
else analogWrite(PWMpin, sineTable[126 - i]); // 正弦波(下降)
preTime = postTime;
i++;
break;
}
}
}
タップテンポについては、単純にスイッチを押した間隔を計算します(間隔が長すぎる場合は設定変更されません)。delayMicrosecondsはPWM高速化の影響なのか実測すると待機時間が1.6倍程度になっていました。割り込み機能も考えたのですが、いろいろと制約があるので今回は使いませんでした。

PWM出力に2.2kΩ抵抗器と1μFコンデンサのローパスフィルター(カットオフ周波数72Hz)を通し、オシロスコープで波形を確認しました。
14_228_3atLFOw.png
遠目に見るとよさそうなのですが、拡大すると結構ギザギザです。このままエフェクターに利用できるのかどうかは不明です。

<時間分解能を上げるには>
micros関数での時刻取得には0.01ms程度時間がかかってしまうため、 これが時間計測の最小単位となります。やはり参考サイトにあるようにタイマー割り込みを使うべきでしょう。さらにできれば8ビットではなく16ビットタイマー付きのマイコンを使いたいところです。

---以下2018年8月25日追記---

途中で(おそらくオーバーフロー時)止まる不具合がありましたが、LFO部分の条件式を以下のように修正したところ解決したようです。参考ページ→millis()のオーバーフローの実験
micros() / 64 > preTime + rate - 300

micros() / 64 - preTime > rate - 300

タグ : マイコン 

MOOER Micro Preamp 006 分解

14_227_1mp006p.jpg
前回特性測定したMOOER Micro Preamp 006ですが、ろくに弾きもせずに分解してしまいました。中身がどんなものか記録しておきます。

フットスイッチはバネで基板上のスイッチを押すタイプです。長押し機能が実装されている関係で、オンオフ切替やチャンネル切替はフットスイッチを押した時ではなく離した時になっています。DCジャックは基板直付けではなくコネクタが使われていました。ケースのみの大きさは縦91mm横37mm高さ31mmで、縦横はHAMMOND 1590Aより1.5mmほど小さいです。基板は2枚重ねで、はんだ付けされたピンヘッダを取り除かないと分解できません。無事に元に戻せましたが、結構大変でリスクが高いと思います。

▽基板写真
14_227_2mp006p.jpg 14_227_4mp006p.jpg
左側基板には黒いゴムの円柱があり、基板同士の隙間を保つために取り付けてあるようです。ジャック上側にはバイパス用と思われるリレー(HFD4/3)があります。定電圧レギュレータはμPC29M08とAMS1117です。

▽IC類写真
14_227_3mp006p.jpg 14_227_5mp006p.jpg
左写真右上のIC(印字「415 XTFM」)は415の左隣の文字がかすれていて役割がわかりませんでした。その他のICは下記の通りです。
・MC33078 → オペアンプ
・TLC2262 → オペアンプ
・CS4272 → オーディオコーデック(ADC/DAC)
・GD25Q41BT → フラッシュメモリ
・ADSP-21477 → DSP
・STM32F030F4P6 → マイコン

タグ : 市販エフェクター マイコン 

MOOER Micro Preamp 006 特性測定

14_226_1mp006p.jpg
MOOER Micro Preampは小型デジタルプリアンプということで、中身が気になり購入してみました。元になったモデルはぼかしてあることが多いですが、006の場合「Based on Fender blues deluxe」と公式動画に記載があります。とりあえずろくに弾きもせずに特性を測定しました。以下オーバードライブチャンネル(LEDが赤に点灯)をAch、クリーンチャネル(LEDが青に点灯)をBchと表記しています。

▽波形・倍音(Ach)
14_226_2mp006g.png
真空管の歪みのはずですが、特に偶数次倍音が出やすいというわけではありませんでした。

▽周波数特性
<ゲイン変更・キャビネットシミュレータ>
AchとBchで1kHz時同じ音量になるように調整しています。BASS、MID、TRE全て5(12時の位置)です。
14_226_3mp006g.png
AchではBchより高域と低域が削られていることがわかります。ゲインを上げた時の特性変化はほとんどなく、わずかに高域が落ちる程度です。キャビネットシミュレータをオンにした時の変化幅はAchとBchで変わりません。

<Ach>
14_226_4mp006g.png
<Bch>
14_226_5mp006g.png
トーンコントロールの効き方はAchとBchで変わりません。あまり変化幅は大きくなく、BASSとMIDはグラフィックイコライザの変化のような感じに見えます。マニュアルには下記のように記載があるので、コントロールの変化の仕方は元になったモデルと同じではないということがわかります。
3つのノブを全て12時の位置にするとプリアンプはMooerにてアナライズした時のサウンドになります。時計回りで周波数をブーストし、反時計回りでカットします。各ノブの帯域はモデルごとに最適に調整されています。

▽レイテンシー→測定方法はこちら
14_226_6mp006w.png
約0.6msです。1ms以下というのがもはや当たり前になっているのかもしれません。

ノイズも測定しようとしましたが、私の環境では測定限界以下だったので特に問題ないでしょう。ハードウェアについては別記事にしました。→MOOER Micro Preamp 006 分解

タグ : 歪み 周波数特性 波形・倍音 市販エフェクター 

MOS FETリレー G3VM-21GR 特性測定

SPSTモーメンタリースイッチでエフェクトのバイパスをする場合、リレーとマイコンを使うのが簡単だと思います。しかしながらメカニカルリレーは入手性や電力消費の点でイマイチかなと考え、MOS FETリレーを試すことにしました。

MOS FETリレーはソリッドステートリレーの一種で、各メーカーで同様の商品がありますが名称が違います(フォトリレー、Photo MOSリレー等)。参考ページ→オムロン リレー 技術解説
通常のものはオン抵抗RONや端子間容量COFFが大きいため、バイパスに使用する場合はバッファーが必要となってしまいます。そこで今回は低オン抵抗・低端子間容量タイプのG3VM-21GR(RON=5Ω、COFF=1pF)というMOS FETリレーを選びました。エフェクターでよく使われる青い3PDTフットスイッチと比較検討します。
14_222_1G3VMp.jpg
G3VM-21GRは表面実装部品なので丸ピンソケットにはんだづけしました。フットスイッチは同じ大きさの黒いものも測定しましたが、青のフットスイッチと大差なかったので結果からは省いています。

ハイインピーダンス条件下での使用を考慮し、以下の接続としました。
  [擬似ギター出力]→[リレーG3VM-21GR]または[フットスイッチ]→[バッファー(入力インピーダンス1MΩ)]→[PCマイク入力]

スイッチオン時に音質が変化しないのはもちろん重要ですが、スイッチオフ時も下図のようにハイパスフィルターを形成して高域が漏れることが考えられるので、そのあたりについても確認します。
14_222_2G3VMs.png
※配線が近いだけでも容量が増加してしまうので注意が必要です。

▽結果
14_222_3G3VMd.png

<スイッチオン時の特性変化>
周波数特性はほとんど重なっていますが、よく見るとリレーでは高域が下がっています。まぁごくわずかなので大丈夫でしょう。歪率についてはほぼ変化はありません。

<スイッチオフ時の音漏れ>
リレーではオンオフの差が-21dB(10kHz)となっており、ブースターやハイゲインエフェクターでは問題が出てくるかもしれません。エフェクターに組み込んだ後、どの程度影響があるか測定する予定です。フットスイッチでもわずかに漏れがあることがわかりましたが、実際のトゥルーバイパス配線ではオフ時にエフェクト回路の入力をアースに落とすので、問題になることはないと思います。

---以下2018年6月14日追記---
14_222_4G3VMb.png
まず上図上側のバイパス方法を試しましたが、音漏れがあり発振しやすい上、切替時に少しポップノイズが出ました。その後下側の回路に変え音漏れや発振はなくなりましたが、ポップノイズは消えませんでした。スイッチングの順番をいろいろ変えてみましたがダメなようです。バッファーを入れて考え直すことにします。

タグ : 周波数特性 歪率 擬似ギター出力 

管理人

管理人

自己紹介のページ
記事一覧
Twitter
GitHub

ブログ内検索
カテゴリー
タグ

自作エフェクター   レイアウト   回路図   歪み   PureData   周波数特性   マイコン   波形・倍音   RaspberryPi   エレキギター   アンプ   エフェクター自作方法   歪率   エレキベース   真空管   コーラス   ピックアップ   静音ギター   ヘッドフォンアンプ   擬似ギター出力   市販エフェクター   アコースティックギター   ブースター   コンデンサ   ソロギター   ポールピース   イコライザー   コンプレッサー   ビブラート   フェイザー   トレモロ   TAB譜   ディレイ   DIY_Layout_Creator   ワウ   オートワウ   バッファー   

最近の記事
最新コメント
Twitter
RSS
メールフォーム
当ブログに関するお問い合わせはこちらからお願いします。 ※FAQ(よくある質問)もお読みください。

お名前
メールアドレス
件名
本文

アクセスカウンター