2012/01/20

PIC24FJ64GA002にスピーカーを繋げて音を出す

白色LEDに続いて、PICで音を出すことを試みます。

今回はじめてPIC24FJ64GA002を使ってみました。いわゆる16bitのPICです。
こいつの顛末はまた色々とあるのですが、まずは音を出す話の方から。

以前、YMZ294によるFM音源で音を出してましたが、今度はPCMです。回路としてはこちらの方が簡単で、直接PICのPWMで制御しています。最初はDAコンバータとかがないダメなのでは、とか思って色々物色していましたが、音を出すだけならPWMで充分でした。

こんな感じで、ハードウェア的にはしごく単純です。
  1. PICの構成・コンフィグ
    特に何の変哲もありません。
    オシレータはFRCPLL(内蔵オシレータ+PLL)モードにして使います。
    このモードだと、内蔵オシレータだけでFosc=32MHz/16MIPSが出せるので中々偉いです。
    一つびっくりするのが、このPICはMCLRをディセーブルする(I/Oピンにする)ことができません。ちょっとだけ悩みました。
  2. PWMの構成
    PWMは、タイマ1とタイマ2の組み合わせにしました。
    タイマ1は8KHzで回し、Output Compareモジュールとタイマ2でPWMを構成します。
  3. PCMデータの扱い
    このPIC(PIC24FJ64GA002)は、64KBものプログラムメモリを持っています。PIC16Fだと rom 修飾子で指定していたのですが、C30だとconst指定しておくと、勝手にプログラムメモリに入れてくれます。楽チンですね。
  4. ピンのリマッピング
    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 件のコメント: