NCOでDCOにできないか
PIC 16F18313に内蔵されてるNCOの出力周波数は20bitで指定できる。これだけ分解能があればシンセサイザーのオシレータ部分に使用できるかも。EXCELで0〜127のMIDIノート番号に対する周波数を計算して、NCOで得られるはずの周波数との誤差を求めてみた。
Foscが1MHzの場合だとノート番号77以降で誤差が0セントになる。実際には10セント程度は人の耳では識別できないし、これはこれで味があるとも言えるが、せっかくデジタルなんだからもう少し頑張りたい。
源振の1MHzを下げられれば周波数分解能が上がり、誤差が出にくくなるのだが、NCOへの入力は以下の3つしか選べない。
- HFINTOSC
- Fosc
- LC1_out
実質的に上の2つはほぼ同じなので、LC1_outしか選択肢が無いが、鬼門のCLCモジュールを使わなくてはいけなくなる。ここでCLCモジュールにLFINTOSC(31kHz)が入力できればいい。
CLCモジュール
CLCモジュールはとにかく意味がわからない。データシートを見ても機能がピンと来ないわけで、これはツールに頼るしかない!と、これも苦手なMCC(Classic)で設定してみた。
とにかく入力元はLFINTOSC(31kHz)に設定し、4-input ANDに突っ込んだ。RA5をPPS経由で出力モニタにすれば、LFINTOSCが本当は何Hzなのかが測れる。
pragma-config.h
将来的にUSARTからMIDI信号を受けたいので、動作周波数は速い方が通信エラーが少ない。今回はとりあえず1MHz駆動にしたが、32MHz駆動の方が妥当かもしれない。(いつもと同じなので省略)
mainルーチン
まずは冒頭はxc.hとpragma_config.hを忘れずにインクルードし、出力バッファ無効、アナログ無効にする。テンプレートのようなものだ。
#include <xc.h>
#include "pragma_config.h"
void main(void) {
TRISA=0xff;
ANSELA=0;
CLCモジュールの設定はMCC classicに頼ったが、出来た中身を見てみるとなんとなく作法がわかってくる。4つのCLC1SELyには全部LFINOSCを指定、4つのCLC1GLSyもゲートを一つだけ選ぶことで、ここは単なるバッファになっている。最後にCLC1CONでモジュールを有効にして、RA5にPPS経由で出力するだけだ。
//
CLC1POL = 0x00;
CLC1SEL0 = 0x1D;
CLC1SEL1 = 0x1D;
CLC1SEL2 = 0x1D;
CLC1SEL3 = 0x1D;
CLC1GLS0 = 0x02;
CLC1GLS1 = 0x02;
CLC1GLS2 = 0x02;
CLC1GLS3 = 0x02;
CLC1CON = 0x82;
RA5PPSbits.RA5PPS=0b00100; // CLC1OUT
TRISAbits.TRISA5=0;
あとはNCO1を設定すればいい。入力はCLCにしてRA2に出力する。インクリメントの0x07358はLFINTOSC周波数が31.25kHzの場合の440Hz出力のための値であり、測定するとかなりいいとこまでイケてるし、微調整も可能だろう。
今回は最後は空の無限ループだが、UARTからMIDIノートを受け取ってNCO 1INCに設定する割り込みルーチンを入れればある程度までは良さそうだ。実際はポルタメントとか変調をどう受け取るかが難しそうだが。
//
NCO1INC=0x07358;
NCO1CLKbits.N1CKS=0b10; // CLC1OUT
NCO1CONbits.N1PFM=0;
NCO1CONbits.N1EN=1;
RA2PPSbits.RA2PPS=0b11101; // NCO1
TRISAbits.TRISA2=0;
while(1) { }
}
投稿者の人気記事
-
akira.kei
さんが
2025/02/10
に
編集
をしました。
(メッセージ: 初版)
-
akira.kei
さんが
2025/02/10
に
編集
をしました。
-
akira.kei
さんが
2025/02/10
に
編集
をしました。
-
akira.kei
さんが
2025/02/10
に
編集
をしました。
-
akira.kei
さんが
2025/02/10
に
編集
をしました。
ログインしてコメントを投稿する