<前の記事 : 次の記事>
別にPIC12F1571じゃなくても…
前の記事でシンセサイザーを作るみたいなことを書いたが、一般にシンセサイザーの音源はまずはノイズジェネレーターから作るべきなのだ。そしてデジタルノイズといえばM系列乱数でLFSRなんだ(用語がよくわかっていないので並べてみた)。いずれにしろピンにランダムタイミングでパルスを出すみたいなことで実質的なホワイトノイズを出してみる。
今回はPIC12F1571を選んでみたが、フルデジタルなので8pinで32MHz駆動であれば何でもいける。なお末尾が[1]ではなく[2]の方のPIC12F1572 はシリアルモジュールがあるので、MIDI信号を受けて16bitPWMでCVを出しつつゲート信号を出す、みたいなことに使えるはずだ。末尾が[1]のやつは通信機能がないのに知らずに沢山買ってしまい、今回みたいな単純作業というか力技に使ってやる予定だ。
8ピンしかなくても大丈夫
電源を入れたらひたすらノイズを出すだけ、という潔い設計でやってみようか。
VDDとVSSは5V電源、RA0とRA1にはPickit4プログラマの通信線(ICSPDATとCLK)、RA3はリセットに接続するので、ノイズを出すのはRA2、RA4及びRA5の3本にする。
今回もMCC Classicを使って楽をしてみることにするが、フルデジタルでCPUしか使わないためFOSCを32MHzにして、RA0/1/3を入力に、残りを出力にするだけで設定は終了だ。
メインルーチンは
Copilotに書かせたので中身がよくわからない。まずlfsr32には0ではない初期値を入れる(0x12345678)。そして第31、21、1及び0ビットをXORをとって末端に入れたらシフトする、というのを繰り返すと疑似乱数が得られるらしい。
#include "mcc_generated_files/mcc.h"
static uint32_t lfsr32 = 0x12345678;
void main(void) {
SYSTEM_Initialize();
uint32_t bit;
while (1) {
bit = ((lfsr32 >> 31) ^
(lfsr32 >> 21) ^
(lfsr32 >> 1) ^
(lfsr32 >> 0)) & 1;
lfsr32 = (lfsr32 << 1) | bit;
LATA=(uint8_t)lfsr32;
}
}
ちょっと8bitPICで32ビット整数を馬鹿正直にシフトしなくてもいいだろ、とも思うがこれでやってみた。するとどうも10kHzまではほぼ平坦なんだが、16kHzくらいに落ち込みがある。もう少しなんとかならんかな。
uint8_tに分解
Copilotの提案はどうもWikipediaと似たような感じだが、8bitのPICなので8bit毎に分解する。ただし、32bitシフトもできるようにしておかないといけないので、ちょっとunionしてみた。
#include "mcc_generated_files/mcc.h"
typedef union {
uint32_t ll;
struct {
uint8_t b0;
uint8_t b1;
uint8_t b2;
uint8_t b3;
};
} LFSR_u;
static LFSR_u lfsr32;
void main(void) {
SYSTEM_Initialize();
lfsr32.ll=0x12345678;
bool b0,b1,b2,b3;
while (1) {
b3=(lfsr32.b3 & 0b10000000)?1:0;
b2=(lfsr32.b2 & 0b00100000)?1:0;
b1=(lfsr32.b0 & 0b00000010)?1:0;
b0=(lfsr32.b0 & 0b00000001)?1:0;
lfsr32.ll= (lfsr32.ll << 1) | ((b3^b2^b1^b0) & 1);
LATA=lfsr32.b0;
}
}
こうするとlfsr32 >> 31などどいう無駄シフトを考えなくていい。ノッチも16kHzから80kHzまで上がった(黄色い線の方を参照)。20kHzくらいまでフラットだと思っていい。
もしかしてアセンブラ
こんな変数に代入してシフトしながら無限ループする、なんて単純作業はアセンブラでも書けるんじゃね?と考えてやってみた。ソースは恥ずかしいし内容をよく理解していないので貼らないが、なんとノッチが258kHzまで伸びている。最小パルスの幅がこれくらいの周波数の逆数くらいになるということで、ほぼループの速さでもある。
結局、これを採用
Wikipedia通りに書いて100kHzまで行けた。短くて分かりやすいしこれでいいだろう。
void main(void) {
SYSTEM_Initialize();
uint32_t lfsr=1;
while (1) {
lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
LATA = (uint8_t)lfsr;
}
}
投稿者の人気記事

-
akira.kei
さんが
前の金曜日の0:46
に
編集
をしました。
(メッセージ: 初版)
-
akira.kei
さんが
前の金曜日の6:59
に
編集
をしました。
-
akira.kei
さんが
前の土曜日の1:20
に
編集
をしました。
ログインしてコメントを投稿する