ラベル 電気・電子 の投稿を表示しています。 すべての投稿を表示
ラベル 電気・電子 の投稿を表示しています。 すべての投稿を表示

2015年9月2日水曜日

dsPICでmicroSDからwavファイルの再生

dsPICでmicroSDからPCM音源のWAVファイルを再生してみた


1 仕様

マイコン : dsPIC33FJ64GP802
SD :microSDHC Class10 (メーカー: GREEN HOUSE)
再生ファイル : Fs = 44.1kHz 、分解能16bit、ステレオ音源
クロック源 : 外付けの発信器未使用 完全に内部発振のみ

また、今回はとにかくWAVを再生することだけに専念したので、連続して音楽を再生するようなシステムや、フォルダ分けされた階層などは非対応です。

2 実際に音楽を再生している様子



3 MDDFSでのmicroSDの操作

MDDFSとはMicroChiop社が提供している、PIC向けのファイルシステムで、これを使うことでFATに関する知識が要らなくなる。
そういう意味では、MDDFSの使い方をしっかり理解しないと当然エラーで悩みます。

microSDの読み書きについて過去に記事をかいたので、詳しくはそちらを確認してください。
URLはこちら

4 AudioDACでのDAC出力

dsPIC33FJ64GP802は、分解能16bitのDAコンバーターを内臓しています。
それも、これはただのDAコンバーターではなく、AudioDACというオーディオ用のDACです。
そのため、使い方は基本的に固定されています。
手順をまとめます。

4-1 オーバーサンプリングについて

このAudioDACでは、デジタル補間フィルタを備えており、256倍のオーバーサンプリングを行い補完データを作成します。
これにより、アップサンプリング時に発生するノイズを高音域に追いやる事ができるそうです。
これがAudioDACのブロック図になります。
中央にクロックとして[ACLK] が入力されています。
その先、[CLK DIV] で[ACLK]が分周されます。 この周波数を[DACCLK]とよび、音声のサンプリング周波数の256倍に設定する必要があります。


4-2 DACクロックの計算方法

[ACLK]はどのように計算するかというと、それはクロックの設定についてブロック図を確認します。

(文字がマーカーで青く強調されてるのは、ミスですので気にしないでください。)
ブロック図右下のほうを見ると、[DAC] へ伸びる矢印があり、そこに [ACLK]と書いてあります。
今回は、内部発振を使いたいので、内部発振から生成可能なのは一番近くにある [Fosc] です。

SELACK で Foscを選択します。
SELACKというレジスタが有るわけではないのですが、設定方法はプログラム中にかいてりますので、ご確認ください。

また、Foscの設定方法もプログラム中に書いてありますが、作成中に発見した問題があります。

PLLは、M、 N1、 N2の値をレジスタで設定することで求まります。
Fosc = 7.3728MHz / N1 * M / N2
上のような式になるのですが、 N2が2より大きい場合にDAC割込みの周期に狂いが出る可能性があるということです。
私のプログラムに問題がある可能性が当然あるのですが、N2を2に設定することをお薦めします。
不思議なのは、システムクロック (Fcy = Fosc / 2)は正しく、ディレイ関数も問題なく動作するのに、DAC割込みのみが狂うという点です。


ちなみにこちらが、PLLでのクロック生成についてのブロック図です。
これとプログラムを照らしあわせて、計算方法をご確認ください。

4-3 DAC割込み関数の記述について

DAC割込みは、ライトチャネルとレフトチャネルそれぞれの割込みがあります。
今回私は夫々の割込みを丁寧に書きましたが、多くの方は片方の割込み処理内で両チャネル分の処理を書いているようです。
それでも動く上に、プログラムの処理も減るのでメモリの削減にはなると思います。

5.プログラム

こちらに今回使用したファイルをひと通り載せておきました。
URLはこちら

読み解いて真似することもできると思いますが、特に注意するべき点はバッファを2つ用意する点です。
バッファがひとつしかないと、次のデータを読み込む間無音になってしまうため、一方のバッファが再生中もう一方のバッファは読み込みをする仕組みになっています。


6.回路図

AudioDACは、さすがAudio用、差動出力になっているのですが、今回は音をだすことだけをまず確認したかったので、ステレオ、ポジティブ出力のみで音を出してみました。
AMPは HT82V739です。
非常に音が大きいので近所迷惑にならないよう…。


7.問題点

電源のノイズが乗っているようで、出力に「ぽうぽうぽうぽうぽう」という方形波のノイズが入ります。
これは、電源ラインにフィルタを入れれば恐らく解決すると思いますが、現段階では未解決です。
解決したら報告します。

8.参考文献+ご協力くださった方

・フルート吹きのMIDI工房 様
・picfun 様
・Twitter 、FaceBookの皆様

2015年8月2日日曜日

dsPIC33FJ64GP802 16bit DAC 動作確認

備忘録として作業過程をメモしつつまとめていきます。

この記事は現段階で未解決です

今回私は次の条件でDACを使います。

・外付発振器をつかわ
ず、全て内蔵FRCとPLLで使う
・wavを適当に再生できれば良いので差動出力はポジティブ出力のみを使用

右チャネル・左チャネルとそれぞれ16bitのFIFOがあり、そこにデータをセットすることで自動でアナログ出力が出る。
出力されるとFIFOが空になり、また割込みが入る。

この割込み周期をDACCLKとする。
DACCLKは外付けの補助クロックをつけ、DAC専用とすれば非常に簡単に出来そうだけど、回路をなるべく簡素にしたので内蔵FRCPLLを使う。

とりあえずクロックの設定ができてんのか、超高速Lチカをしてみる。
次のプログラムは、まだ必要ないDACあたりの設定もかなり書かれているけど、DACの初期化を行うinit_dac()はコメントアウトしてあるのでDACは動作してない。
DAC割込み処理とかも無視してください。

/*
/*
 * File:   main_15.08.02.01.c
 * Author: kota
 *
 * Created on 2015/07/17, 17:15
 */


/***********************************************************************
 *         プログラム全体にかかわる、初期設定
 ***********************************************************************/

/********************************
 *   ヘッダーのインクルード
 ********************************/
#define FCY 24413125UL

#include <libpic30.h>
#include <xc.h>
/********************************
 *   コンフィギュレーション
 ********************************/
#pragma config BWRP = WRPROTECT_OFF
#pragma config BSS = NO_BOOT_CODE
#pragma config RBS = NO_BOOT_RAM
#pragma config SSS = NO_SEC_CODE
#pragma config GWRP = OFF
#pragma config GSS = OFF
#pragma config FNOSC = FRCPLL
#pragma config IESO = OFF
#pragma config POSCMD = NONE
#pragma config OSCIOFNC = ON
#pragma config FCKSM = CSECMD
#pragma config FWDTEN = OFF
#pragma config FPWRT = PWR16
#pragma config ALTI2C = ON
#pragma config ICS = PGD1
#pragma config JTAGEN = OFF

/********************************
 * ユーザー定義関数 プロトタイプ
 ********************************/
void init_gpio(void);
void init_clock(void);
void init_dac(void);


/***********************************************************************
 *               関 数 群
 ***********************************************************************/

/********************************
 *     メイン関数
 ********************************/
int main(void) {
    init_clock();
    init_gpio();
    //init_dac();

    while(1){
        LATAbits.LATA0 = 1;
        __delay_ms(1);
        LATAbits.LATA0 = 0;
        __delay_ms(1);
    }
    return 0;
}

/********************************
 *    動作クロックの設定
 ********************************/
void init_clock()
{
    CLKDIVbits.ROI = 0;         // Interrupts have no effect on the DOZEN bit
    CLKDIVbits.DOZE = 0;        // Fcy / 1
    CLKDIVbits.DOZEN = 0;       // disable DOZE
    CLKDIVbits.FRCDIV = 0;      // FRC/1(default)
    // Initialize and configure Primary PLL, and enable Secondary Oscillator
    PLLFBDbits.PLLDIV = 51;     // M = 53
    CLKDIVbits.PLLPOST = 1;     // N2 = 4
    CLKDIVbits.PLLPRE = 0;      // N1 = 2
    // Fvco = 7.3728MHz / N1 * M = 195.3792MHz
    // Fosc = Fvco / N2 = 48.845MHz
    // Fcy = Fosc / 2 = 24.422MHz

    ACLKCONbits.SELACLK = 0;    // PLL出力(Fvco参照)
    //ACLKCONbits.ASRCSEL = 1;    // DAC用クロックは主クロックを使用
    ACLKCONbits.APSTSCLR = 0b111;
}

/********************************
 *      GPIOの設定
 ********************************/
void init_gpio()
{
    AD1PCFGL = 0xFFFF;          // アナログ入出力をオフに
    TRISA = 0x0000;             // RA0-
    LATA = 0x0000;              // 出力を0に初期化
}

/********************************
 *   Audio DACの設定
 ********************************/
void init_dac()
{
    DAC1STATbits.ROEN = 1;      // Right Channel DAC Output Enabled
    DAC1STATbits.LOEN = 1;      // Left Channel DAC Output Enabled

    DAC1STATbits.RITYPE = 0;    // Right Channel Interrupt if FIFO is empty
    DAC1STATbits.LITYPE = 0;    // Left  Channel Interrupt if FIFO is empty

//    DAC1STATbits.LMVOEN = 0;    // Midpoint DAC output is anabled
//    DAC1STATbits.RMVOEN = 0;    // Midpoint DAC output is anabled

    DAC1CONbits.AMPON = 0;      // Amplifier Disabled During Sleep and Idle Modes
    // Fs = 44.1kHz
    // Fs(kHz) * 256 = 11.2896MHz
    // Fvco(MHz) / 11.2896MHz =  17.31 ≒ 17
    DAC1CONbits.DACFDIV = 16;    // Divide Clock by 17
    DAC1CONbits.FORM = 0;       // Data Format is Unsigned

    DAC1DFLT = 0x8000;          // Default Value set to Midpoint when FORM

    IFS4bits.DAC1RIF = 0;       // Clear Right Channel Interrupt Flag
    IFS4bits.DAC1LIF = 0;       // Clear Left  Channel Interrupt Flag

    IEC4bits.DAC1RIE = 1;       // Right Channel Interrupt Enabled
    IEC4bits.DAC1LIE = 1;       // Left  Channel Interrupt Enabled

    DAC1CONbits.DACEN = 1;      // DAC1 Module Enabled
}

/********************************
 *   Audio DAC 割込み関数
 ********************************/
//-- ライトチャネル
void __attribute__((interrupt, no_auto_psv))_DAC1RInterrupt(void)
{
    IFS4bits.DAC1RIF = 0;       // Clear Right Channel Interrupt Flag
    LATAbits.LATA0 = ~LATAbits.LATA0;
    DAC1RDAT = 0x0000;          // User Code to Write to FIFO Goes Here
}

//-- レフトチャネル
void __attribute__((interrupt, no_auto_psv))_DAC1LInterrupt(void)
{
    IFS4bits.DAC1LIF = 0;       // Clear Right Channel Interrupt Flag
    DAC1LDAT = 0x8000;          // User Code to Write to FIFO Goes Here
}


RA0をオシロで確認すると1kHz周期で出力が反転しているのでFcyの計算はあっていると考えられます。

ここで、次のブロック図を確認してみる。


一番下、右端にACLKという出力がある。これをいくらか分周したものがADCLKとなる。

この出力を登っていくと、Auxiliary Oscillator にたどり着くがちょっと登りすぎて、その二つくらい下のマルチプレクサちゃんに Fvco という選択肢が与えられてる。

これは、まさしく先ほどLチカをした時に使ったシステムクロックFcyを作る過程の信号である。
1kHzのLチカが出来たってことはこれもあっているはず。


・SELACLK
これはデフォルトで’0’です。よってPLL出力であるFvcoがマルチプレクサの出力となります。

・APSTSCLR
分周器です。こいつは 0b111 を指定することで 分周比が1となります(÷1)。

よって、ACLK DACはFvcoのまま、ということになります。

Fvcoは次の画像のように求まります。


Fvco = FIN / N1 × M

先ほどのプログラムの値だと
Fvco = 7.3728MHz / 2 × 53 = 195.3792MHz

となる。

AudioDACのブロック図を見てみる。


中央、左の方を見るとACLKの入力がある。
その次に分周器 CLK DIVがある。
この分周後の周波数、DACCLKは

DACCLK = Fs × 256 とある。

DACCLK = 44.1kHz×256 = 11.2896MHz

DACCLK = ACLK / CLK DIV

11.2896MHz = 195.3792MHz / CLK DIV

CLK DIV = 195.3792MHz / 11.2896MHz = 17.31 ≒ 17分周

DACFDIV = 16に設定すれば DACFDIV + 1の分周比となる。


これで、DACCLKの設定は完了のはず。

また、
Fosc = Fvco / N2
また  Fosc < 80MHz という条件があるので
PLLPOST = 1 (N2 : 4分周) とし、

Fosc = 195.3792MHz / 4 = 48.8098MHz
Fcy = Fosc / 2
となる。

8行↑くらいの説明からは、もはやDACCLKには無関係の話であるはず。(ブロック図を見る限り)
では、DAC割込みの周期を見てみる。

 プログラムは先程のプログラムで init_dac() のコメントアウトを外しただけ。


/*
 * File:   main_15.08.02.01.c
 * Author: Chatteringok
 *
 * Created on 2015/07/17, 17:15
 */


/***********************************************************************
 *         プログラム全体にかかわる、初期設定
 ***********************************************************************/

/********************************
 *   ヘッダーのインクルード
 ********************************/
#define FCY 24413125UL

#include <libpic30.h>
#include <xc.h>
/********************************
 *   コンフィギュレーション
 ********************************/
#pragma config BWRP = WRPROTECT_OFF
#pragma config BSS = NO_BOOT_CODE
#pragma config RBS = NO_BOOT_RAM
#pragma config SSS = NO_SEC_CODE
#pragma config GWRP = OFF
#pragma config GSS = OFF
#pragma config FNOSC = FRCPLL
#pragma config IESO = OFF
#pragma config POSCMD = NONE
#pragma config OSCIOFNC = ON
#pragma config FCKSM = CSECMD
#pragma config FWDTEN = OFF
#pragma config FPWRT = PWR16
#pragma config ALTI2C = ON
#pragma config ICS = PGD1
#pragma config JTAGEN = OFF

/********************************
 * ユーザー定義関数 プロトタイプ
 ********************************/
void init_gpio(void);
void init_clock(void);
void init_dac(void);


/***********************************************************************
 *               関 数 群
 ***********************************************************************/

/********************************
 *     メイン関数
 ********************************/
int main(void) {
    init_clock();
    init_gpio();
    init_dac();

    while(1){
    }
    return 0;
}

/********************************
 *    動作クロックの設定
 ********************************/
void init_clock()
{
    CLKDIVbits.ROI = 0;         // Interrupts have no effect on the DOZEN bit
    CLKDIVbits.DOZE = 0;        // Fcy / 1
    CLKDIVbits.DOZEN = 0;       // disable DOZE
    CLKDIVbits.FRCDIV = 0;      // FRC/1(default)
    // Initialize and configure Primary PLL, and enable Secondary Oscillator
    PLLFBDbits.PLLDIV = 51;     // M = 53
    CLKDIVbits.PLLPOST = 1;     // N2 = 4
    CLKDIVbits.PLLPRE = 0;      // N1 = 2
    // Fvco = 7.3728MHz / N1 * M = 195.3792MHz
    // Fosc = Fvco / N2 = 48.845MHz
    // Fcy = Fosc / 2 = 24.422MHz

    ACLKCONbits.SELACLK = 0;    // PLL出力(Fvco参照)
    ACLKCONbits.APSTSCLR = 0b111;
}

/********************************
 *      GPIOの設定
 ********************************/
void init_gpio()
{
    AD1PCFGL = 0xFFFF;          // アナログ入出力をオフに
    TRISA = 0x0000;             // RA0-
    LATA = 0x0000;              // 出力を0に初期化
}

/********************************
 *   Audio DACの設定
 ********************************/
void init_dac()
{
    DAC1STATbits.ROEN = 1;      // Right Channel DAC Output Enabled
    DAC1STATbits.LOEN = 1;      // Left Channel DAC Output Enabled

    DAC1STATbits.RITYPE = 0;    // Right Channel Interrupt if FIFO is empty
    DAC1STATbits.LITYPE = 0;    // Left  Channel Interrupt if FIFO is empty

//    DAC1STATbits.LMVOEN = 0;    // Midpoint DAC output is anabled
//    DAC1STATbits.RMVOEN = 0;    // Midpoint DAC output is anabled

    DAC1CONbits.AMPON = 0;      // Amplifier Disabled During Sleep and Idle Modes
    // Fs = 44.1kHz
    // Fs(kHz) * 256 = 11.2896MHz
    // Fvco(MHz) / 11.2896MHz =  17.31 ≒ 17
    DAC1CONbits.DACFDIV = 16;    // Divide Clock by 17
    DAC1CONbits.FORM = 0;       // Data Format is Unsigned

    DAC1DFLT = 0x8000;          // Default Value set to Midpoint when FORM

    IFS4bits.DAC1RIF = 0;       // Clear Right Channel Interrupt Flag
    IFS4bits.DAC1LIF = 0;       // Clear Left  Channel Interrupt Flag

    IEC4bits.DAC1RIE = 1;       // Right Channel Interrupt Enabled
    IEC4bits.DAC1LIE = 1;       // Left  Channel Interrupt Enabled

    DAC1CONbits.DACEN = 1;      // DAC1 Module Enabled
}

/********************************
 *   Audio DAC 割込み関数
 ********************************/
//-- ライトチャネル
void __attribute__((interrupt, no_auto_psv))_DAC1RInterrupt(void)
{
    IFS4bits.DAC1RIF = 0;       // Clear Right Channel Interrupt Flag
    LATAbits.LATA0 = ~LATAbits.LATA0;
    DAC1RDAT = 0x0000;          // User Code to Write to FIFO Goes Here
}

//-- レフトチャネル
void __attribute__((interrupt, no_auto_psv))_DAC1LInterrupt(void)
{
    IFS4bits.DAC1LIF = 0;       // Clear Right Channel Interrupt Flag
    DAC1LDAT = 0x8000;          // User Code to Write to FIFO Goes Here
}

早速、RA0のポート変化の周期をみたら、22.388kHzでした。
目的の44.1kHzのやく半分です。

なぜ、分周比が上がってるのでしょうか。
17分周をその半分にすれば44.1kHzは作れますが、スッキリしない。

現状ではわからないことがわかった。

2015年7月17日金曜日

dsPIC33FJ64GP802でmicroSDの読み書きをしてみた


MDDFS (Microchip's Memory Disk Drive File System) を用いてmicroSDの操作をしました。

既に、MDDFSを用いた操作についていくつかのサイトでまとめられていますが、その時、その時のバージョンや環境での説明なので、手元にあるライブラリと異なる点が多々ありました。

その点混乱しないよう、違いがある可能性もあるよという言葉を添えながらまとめたいと思います。
では、極力箇条書きで説明していきます。


2014年3月25日火曜日

DE0-Nano 7セグデコーダ回路 1/2


ロジック回路の復習の意味も込めてやってみました
DE0-Nanoには7セグがないので7つのLEDで動作テスト
GPIOへの出力方法を知らないので、分かり次第 2/2 の記事で投稿します

2014年3月23日日曜日

工事担任者総合種21年度第2回計算問題解説

工事担任者総合種21年度第2回
計算問題の解答です
計算問題は結構定着してきたのであっていると思いますが、
間違いがあったらご指摘ください。

2014年3月12日水曜日

DE0-Nanoの統合開発環境のインスコ及びLED点灯


ALTERA 社のFPGA学習ボード DE0-Nanoを買った
統合開発環境(Quartus ii)のインスコ及びLED点灯までの手順をまとめます
FPGAにこんなことさせるのは申し訳ないのですが、まずはここから…。

2014年3月7日金曜日

工事担任者総合種21年度第1回計算問題解説














21年度第1回の計算問題系の計算手順を載せてみました
第2回の問題が終わるまで少し日にちがあくと思うので先に載せます
20年度に比べて難易度が上がった気がしますが、頑張りました
参考になれば幸いです

2014年2月28日金曜日

工事担任者総合種20年度計算問題解説

工事担任者総合種20年度の計算問題解説です。
私が少しでも悩んだ問題だけ解法を載せました。解放が正しいかはわかりません。