今回はじめてPIC24FJ64GA002を使ってみました。いわゆる16bitのPICです。
こいつの顛末はまた色々とあるのですが、まずは音を出す話の方から。
以前、YMZ294によるFM音源で音を出してましたが、今度はPCMです。回路としてはこちらの方が簡単で、直接PICのPWMで制御しています。最初はDAコンバータとかがないダメなのでは、とか思って色々物色していましたが、音を出すだけならPWMで充分でした。
- PICの構成・コンフィグ
特に何の変哲もありません。
オシレータはFRCPLL(内蔵オシレータ+PLL)モードにして使います。
このモードだと、内蔵オシレータだけでFosc=32MHz/16MIPSが出せるので中々偉いです。
一つびっくりするのが、このPICはMCLRをディセーブルする(I/Oピンにする)ことができません。ちょっとだけ悩みました。 - PWMの構成
PWMは、タイマ1とタイマ2の組み合わせにしました。
タイマ1は8KHzで回し、Output Compareモジュールとタイマ2でPWMを構成します。 - PCMデータの扱い
このPIC(PIC24FJ64GA002)は、64KBものプログラムメモリを持っています。PIC16Fだと rom 修飾子で指定していたのですが、C30だとconst指定しておくと、勝手にプログラムメモリに入れてくれます。楽チンですね。 - ピンのリマッピング
PIC24Fシリーズの特徴となる機能として、動的にペリフェラルのピン位置を変えることができる、というものがあります(割り当て先のピンには制約がありますが)。
マニュアル中のピン配置のページに "RPn"と記されているのが割り当て可能なピンです。
今回は、RP4(ピン11)にPWM出力(OC2=アウトプット番号19番)をアサインしています。あと動作確認用LEDをRB5に。
ただ、割り当て時にIOUNLOCK/IOLOCKという操作をしないといけないのがちょっと面倒ですね。不用意にピンが変わっちゃったら困るので、ロックするのはしょうがないのですが。
#include "p24FJ64GA002.h"
#include <libpic30.h>
#define Clock 32000000
_CONFIG1( JTAGEN_OFF &
GCP_OFF &
BKBUG_OFF &
COE_OFF &
GWRP_OFF &
ICS_PGx1&
FWDTEN_OFF )
_CONFIG2( IESO_OFF &
FNOSC_FRCPLL &
FCKSM_CSDCMD &
OSCIOFNC_OFF &
IOL1WAY_OFF &
I2C1SEL_PRI &
POSCMOD_NONE)
void delay_ms(unsigned int N)
{
__delay32(Clock/4000*N);
}
#include "samples.c"
#define LEN sizeof(samples)
void play(int pos)
{
unsigned char s;
s=samples[pos];
OC2RS=s;
}
// 8KHz tick
int current=0;
void tick(void)
{
if (IFS0bits.T1IF == 1) {
IFS0bits.T1IF = 0;
}
play(current);
current = (current+1)%LEN;
}
void _ISR _T1Interrupt(void)
{
tick();
}
// Timer1 settings (8KHz clock)
// 1tick = 1/8000 = 125us
// Fosc = 32MHz for 24F internal osc.
// 1Fosc_2 = 16MHz = 62.5ns
// 1tick = 2000
// Prescaler none
// start tick = 0x10000-2000 = 0xF830
void init(void)
{
// Default of RC is 4MHz by CLKDIV!
// Make it 8MHz.
CLKDIVbits.RCDIV = 0;
// Setup OC2 to RP4 (pin 11)
// Unlock IOLOCK
asm volatile ( "MOV #OSCCON, w1 \n"
"MOV #0x46, w2 \n"
"MOV #0x57, w3 \n"
"MOV.b w2, [w1] \n"
"MOV.b w3, [w1] \n"
"BCLR OSCCON,#6");
// __builtin_write_OSCCONL(0);
RPOR2bits.RP4R = 19; // OC2
// __builtin_write_OSCCONL(0x40);
// Re-lock IOLOCK
asm volatile ( "MOV #OSCCON, w1 \n"
"MOV #0x46, w2 \n"
"MOV #0x57, w3 \n"
"MOV.b w2, [w1] \n"
"MOV.b w3, [w1] \n"
"BSET OSCCON, #6" );
// PIC24FJ64GA002 does not have TIMER0!!!
PR1 = 2000;
T1CONbits.TSIDL = 0; // Continue on idle
T1CONbits.TGATE = 0; // No gate accumulation mode
T1CONbits.TCKPS = 0; // Do not use prescaler
T1CONbits.TSYNC = 0; // DC
T1CONbits.TCS = 0; // Internal clock (Fosc/2)
IEC0bits.T1IE = 1; // Interrupt Enabled
// PWM settings
// Use PWM2/RB3
PR2 = 0xFF; // 8bit resolution
OC2RS = 0x80;
T2CONbits.TSIDL = 0;
T2CONbits.TGATE = 0;
T2CONbits.TCKPS = 0;
T2CONbits.TCS = 0; // Internal Clock
IEC0bits.T2IE = 0; // No Interrupt
OC2CONbits.OCM=6; // PWM, Fault pin, OFCx, disabled
PMD2bits.OC2MD = 0;
T2CONbits.TON = 1; // On
T1CONbits.TON = 1; // On
}//end UserInit
int main (void)
{
init();
TRISB = 0; // configure PORTB for output
while(1) {
LATBbits.LATB5 ^= 1; // LED blinker
delay_ms(1000);
}
return 0;
}
こちらはサンプリングデータを格納するファイルです。8bit/8KHz。
// file len 3946
// data section @ 44
const unsigned char samples[] = {
0x7e,
0x7d,
0x7b,
0x7e,
0x80,
0x83,
0x82,
0x7e,
....
0x34,
0x2e,
0x36,
0x30,
0x00,
};
0 件のコメント:
コメントを投稿