dsPIC使用のDSP CWデジタル・フィルター

投稿日 2014/01/17

dsPICを使用したCW用DSPフィルタを作ってみました。

33fj128gp802_DSP_Filter1_2.jpg

実装の様子 CPU 28pin DIP dsPIC33FJ128GP802 CODEC WM8510 

 

まず、数あるPICデバイスモデルの中から、どれにするかですが、今回はdsPIC33FJ128GP802を選びました。

 

dsPIC3FJ128GP802はDAコンバータを内蔵しているので、何かに使えるかと購入しておいたものですが、今回はDAコンバータを使用せずにWolfsonのCODEC WM8510を使用することにしました。

 

CODECを使用するとADコンバータも不要となりますが、代わりにDCIが必要です。

 

幸いdsPIC3FJ128GP802はDCIもサポートしています。

 

CODECを使用すると入力は簡単なラインアンプ、出力にはヘッドホンアンプが必要になるだけで、部品数の多いアンチエリアシング・フィルタなどは省略できます。(CODECに内蔵されています)

 

回路は非常にシンプルで、

 

ラインアンプ -> CODEC入力 -> DSPフィルタ -> CODEC出力 -> ヘッドホンアンプ

 

という構成です。

 

その他の回路は3.3V LDO、5つのLEDランプ、3個のプッシュスイッチ、リセット回路、ICSP等です。

 

LCDで情報表示するのもよいのですが、今回はシンプルに、5つのLEDで5種類のフィルターのうち選択されているフィルターを示すことにしました。

 

ラインアンプは無線機のオーディオ信号を、0 - 3.3Vの信号にレベルシフトする回路です。

 

DSPフィルターの入出力はDMA転送です。

 

フィルターの選択はフィルター選択UP/DOWNスイッチで行います。

 

また、いつでもスルー(フィルター処理しない)にできるよう、スルースイッチも用意しました。

 

デバッグ用にキャラクタLCDもつなげられるようにしてあります。

 

フィルターは、好きなように作れるのですが、以下の5種類を標準としました。

 

タイプ 中心周波数 帯域幅
BPF  650Hz    550Hz
BPF  700Hz    750Hz
BPF  650Hz    500Hz
BPF  650Hz    300Hz
BPF  650Hz    200Hz

 

すべて64タップのFIRフィルタです。
フィルターの係数はDigital Filter Designer Liteで作成しています。

33fj128gp802_DSP_Filter1_3.jpg

フィルター特性 中心周波数650Hz -60dB 通過帯域200HzのBPF

 

このPIC 33FJ128GP802の主なスペックは以下の通りです。(microchip社より引用(一部削除))

 

Architecture 16-bit 
CPU Speed (MIPS) 40 
Memory Type Flash 
Program Memory (KB) 128 
RAM Bytes 16,384 
Temperature Range C -40 to 125 
Operating Voltage Range (V) 3 to 3.6 
I/O Pins 21 
Pin Count 28 
Internal Oscillator 7.37 MHz, 512 kHz 
nanoWatt Features Fast Wake/Fast Control 
Digital Communication Peripherals 2-UART
2-SPI
1-I2C

 

Codec Interface NO <- NOになってますがDCIはサポートしています。
Analog Peripherals 1-A/D 10x12-bit @ 500(ksps) 1-D/A 2x16-bit @ 100(ksps) 
Comparators 2 
Capture/Compare/PWM Peripherals 4/4 
PWM Resolution bits 16 
Timers 5 x 16-bit 2 x 32-bit 
Hardware RTCC Yes 
DMA 8 

 

ADコンバータは12bit 500Ksps 10ch, DAコンバータは16bit 100Ksps 2chです。
DMAを8chサポートしています。
OCPWMのPWMモードをサポートしているので、PWM方式での出力も可能です。
DCIをサポートしているのでI2SインターフェースなどでCODEC等も使えます。
RTCをサポートしています。

 

いろいろ遊べそうなデバイス。

 

回路は以下のようになりました。

33fj128gp802_DSP_Filter1_1.jpg

実際に無線機につないでCWの交信を聞いてみました。

 

スパッと切れるところはさすがにDSPフィルターですが、デジタルフィルター特有の音は仕方がありません。

 

ソースコード
main.c以外(初期化、I2C, I2C LCD、フィルター係数ファイル、ヘッダファイル等)は省略

 

//main.c
#include "..\h\p33FJ64GP802.h"
#include "..\h\WM8510CodecDrv.h"
#include "..\h\sask.h"
#include "dsp.h"

 

//Protection off
_FGS(GWRP_OFF & GCP_OFF);

 

//POR起動時のオシレータはFRC
_FOSCSEL(FNOSC_FRC);

 

//プライマリオシレータ HS 12MHz
_FOSC(FCKSM_CSECMD & OSCIOFNC_ON & POSCMD_NONE);

 

//ウオッチドッグタイマ off
_FWDT(FWDTEN_OFF);

 

//I2C Enable
_FPOR(ALTI2C_OFF);

 

//JTAG off, ICSP PGD2
_FICD(JTAGEN_OFF & ICS_PGD2);

 

// DSP Filter Struct (CW Filter)
extern FIRStruct FIR_HPF_600_550Filter;
extern FIRStruct FIR_LPF_700_750Filter;
extern FIRStruct FIR_BPF_650_500Filter;
extern FIRStruct FIR_BPF_650_300Filter;
extern FIRStruct FIR_BPF_650_200Filter;

 

/* FRAME_SIZE - Size of each audio frame */
#define FRAME_SIZE 128

 

#define FILTER_NO_MAX 5
#define FILTER1 1
#define FILTER2 2
#define FILTER3 3
#define FILTER4 4
#define FILTER5 5

 

/* Allocate memory for buffers and drivers */
int codecBuffer[WM8510DRV_DRV_BUFFER_SIZE];
fractional Signal_in[FRAME_SIZE];
fractional Signal_out[FRAME_SIZE];

 

int i;
int mode;
int filter_through;
int filter_select;

 

WM8510Handle codec;
WM8510Handle * codecHandle = &codec;

 

int main(void){
  // Configure Oscillator to operate the device at 40MHz.
  // Fosc= Fin*M/(N1*N2), Fcy=Fosc/2
  // Fosc= 7.37M*40/(2*2)=80Mhz for 7.37M input clock

 

  PLLFBD=41; // M=39
  CLKDIVbits.PLLPOST=0; // N1=2
  CLKDIVbits.PLLPRE=0; // N2=2
  OSCTUN=0;

 

  __builtin_write_OSCCONH(0x01); // Initiate Clock Switch to FRC with PLL
  __builtin_write_OSCCONL(0x01);
  while (OSCCONbits.COSC != 0b01); // Wait for Clock switch to occur
  while(!OSCCONbits.LOCK);

 

  // Intialize the board and the drivers
  SASKInit();
  i2c_init();
  i2c_enable();

 

  lcd_init();
  lcd_write(0, 0, "DSP CW Filter " );
  lcd_write(0, 1, "v1.00A by JF1VRR" );

 

  mode = mode_set();

 

  Delay_ms(1000);

 

  // LED test
  led_test();

 

  WM8510Init(codecHandle,codecBuffer);

 

  // Start Audio input and output function
  WM8510Start(codecHandle);

 

  // Configure codec for 8K operation
  WM8510SampleRate8KConfig(codecHandle);

 

  // Initialize FIR Filter (CW Filter)
  FIRDelayInit(&FIR_BPF_650_500Filter);
  FIRDelayInit(&FIR_BPF_650_300Filter);
  FIRDelayInit(&FIR_BPF_650_200Filter);
  FIRDelayInit(&FIR_HPF_600_550Filter);
  FIRDelayInit(&FIR_LPF_700_750Filter);

 

  filter_through = ON;
  filter_select = FILTER3;

 

  // Main processing loop. Executed for every input and output frame
  while(1){
    // Obtain Audio Samples
    while(WM8510IsReadBusy(codecHandle));

 

    WM8510Read(codecHandle,Signal_in,FRAME_SIZE);

 

    FLT1LED = OFF;
    FLT2LED = OFF;
    FLT3LED = OFF;
    FLT4LED = OFF;
    FLT5LED = OFF;
    case MODE_CW:
      if(filter_through == OFF) {
        // Processing DSP filter
        switch (filter_select){
          case FILTER1:
            FLT1LED = ON; 
            lcd_write(0, 0, "FLT# 1 PB: 600Hz" );
            lcd_write(0, 1, "CW HPF SB: 550Hz" );
            FIR(FRAME_SIZE, &Signal_out[0], &Signal_in[0], &FIR_HPF_600_550Filter);
            break;
          case FILTER2:
            FLT2LED = ON; 
            lcd_write(0, 0, "FLT# 2 PB: 700Hz" );
            lcd_write(0, 1, "CW LPF SB: 750Hz" );
            FIR(FRAME_SIZE, &Signal_out[0], &Signal_in[0], &FIR_LPF_700_750Filter);
            break;
          case FILTER3:
            FLT3LED = ON;
            lcd_write(0, 0, "FLT# 3 F: 650Hz" );
            lcd_write(0, 1, "CW BPF W: 500Hz" );
            FIR(FRAME_SIZE, &Signal_out[0], &Signal_in[0], &FIR_BPF_650_500Filter);
            break;
          case FILTER4:
            FLT4LED = ON; 
            lcd_write(0, 0, "FLT# 4 F: 650Hz" );
            lcd_write(0, 1, "CW BPF W: 300Hz" );
            FIR(FRAME_SIZE, &Signal_out[0], &Signal_in[0], &FIR_BPF_650_300Filter);
            break;
          case FILTER5:
            FLT5LED = ON; 
            lcd_write(0, 0, "FLT# 5 F: 650Hz" );
            lcd_write(0, 1, "CW BPF W: 200Hz" );
            FIR(FRAME_SIZE, &Signal_out[0], &Signal_in[0], &FIR_BPF_650_200Filter);
            break;
        } //switch filter select
      } else { //Through
        lcd_write(0, 0, "FLT# 0 F: ---Hz" );
        lcd_write(0, 1, "THROUGH W: ---Hz" );
        for(i = 0; i < FRAME_SIZE; i++) {
          Signal_out[i] = Signal_in[i];  // Copy Signal Buffer
        }
      }
    // Wait till the codec is available for a new frame
    while(WM8510IsWriteBusy(codecHandle));

 

    // Write the frame to the output
    WM8510Write (codecHandle,Signal_out,FRAME_SIZE);

 

    // The CheckSwitch functions are defined in sask.c
    if(Check_THROUGHSW() == 1){
      if(filter_through == OFF) filter_through = ON;
      else filter_through = OFF;
    }

 

    if(Check_FLT_SELSW_UP() == 1 && filter_through == OFF){
      filter_select++;
      if(filter_select > FILTER_NO_MAX) filter_select = 1;
    }

 

    if(Check_FLT_SELSW_DN() == 1 && filter_through == OFF){
      filter_select--;
      if(filter_select < 1) filter_select = FILTER_NO_MAX;
    }

 

  } //while
}




 

(JF1VRR)