PIC24F64GA002とFreeRTOS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
目 次 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
はじめに | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
しばらく見ないうちにPICとかAVRなどのチップマイコンの発展は著しく、32ビット版まで現れる始末です。チップマイコンは単独で使ってこそ 威力を発揮すると考えていたのでRTOSは不要と思っていました。 ところが32bitになればLinuxが動きますし、一般的な PICあるいは AVRには FreeRTOSがある。 16bitの能力であればプリエンティブ制御しても実用的であると思われ、ここでは PIC16bitで 一番安価な PIC24FJ64GA002で試作する。 開発システムは Microchip社製 MPLABとC30コンパイラを使用する。 プログラマとデバッガは同様に Microchip社製 PICkit3を使用する。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
概要 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PICはチップマイコンであり、チップで閉鎖的に完成しているので、外部部品をなるべく使用せずに作る事を目標とする。 何が良いかと思ったが、さし当たってベンチマークのつもりでFFTを動かす。チップのハードとしてはデジタル入出力、UART、アナログ入力、パルス幅変調あたりをサポートする予定です。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FreeRTOS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FreeRTOSの概要はここにあります。 まずFreeRTOSのデモプログラムから出発する。デモとしてPIC24FJ128GA010がある。これをPIC24FJ64GA002で動かさなければならない。デモ・ディレクトリにあるFreeRTOSConfig.hで定義している #include <p24FJ128GA010.h> を次の行で入れ替える。 #include <p24FJ64GA002.h> |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
これで良いかと思われたが、PIC24FJ128GA010の100pinに対してPIC24FJ64GA002は28pinしかない。 GA010ではほとんど必要なかった、ややこしい、pin割付をGA002では行わなければならない。 28pinしかないので複数の機能から一つをpinに割り付けなければならない。 また、一部の機能(JTAG)は使用しない設定にして、PICkit3を用いてデバックしている。 これらはMPLABのConfigure=>Select Device...とConfigure=>Configureation Bits...で指定し直せば 自動的にプロジェクトに取り込まれるが、pin割付は明示的にコードで指定しなければならない。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
このデモの Configuration Bits... 例 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PIC24FJ64GA002のpin割付表 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FreeRTOSのデモプログラムには下記のものがある | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
すっきりさせた状態で各インプリメントを行い、最後にFlashCoRoutineを入れる。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Heap及びstack | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FreeRTOSが使用するHeap及びスタックはFreeRTOSConfig.hにあるconfigTOTAL_HEAP_SIZEで定義する。このHeap範囲でstackとpvPortMalloc等のメモリの取得が実行される。ここでは事実上メモリーマネージメントをしていないHeap1を採用している(説明はここにあります)、これは強く推奨されます。今までのトラブルシュートの経験上、少なからぬトラブルがメモリーマネージメントに起因するもであったからです。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FFT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dsPICにはDSP用FFTがMicrochipから提供されている。これは固定小数点演算でありながら充分なダイナミックレンジを持つQ15,Q16Formatで記述されている。残念ながらPIC24Fでは、このDSP命令は搭載されていない。というより、dsPICからDSP命令をはずしたのがPIC24Fのようだ。 そこで、使えそうなTom Robertsさんの固定小数点演算FFTを載せてみた。色々な理由から複素FFTを使いたいのですが、ここでは、どこまで使用できるかを検証する意味で、同梱されている実数FFTを使い実行時間を計ってみた。 PIC24FJ64GA002のRAM容量は8Kbあり、半分の4kをデータに使用するとすれば、1データは16bit2バイトであるから、2048点までの計算ができることになる。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
あまり早くはないが、クロック周波数並みの速度は出ている。まあまあかな。 ここから、お遊びでアナログ入力から音声256点(40khzで6.4msec)取り込み後リアルタイムで数帯域の周波数強度をPWMを使ってLEDで発光強度で示すものを製作する。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Tom RobertsさんのFFTを移植時の注意点
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
このタスクはローカル変数もそれなりに使用するので、タスクの最小スタック量にプラス100bytes程度増やして置かなければならない。蛇足ながら、必要量ぎりぎりに設定した場合、これに(必要なら)割り込みが使用するスタックも考慮しなければならない。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* Short test program to accompany fix_fft.c */ /*-------- FreeRTOS defines ---------*/ #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #include "fft.h" static void vFFTTask( void *pvParameters ); #define fftQUEUE_SIZE 3 extern xQueueHandle xFFTQueue; __attribute__((far)) short x[N*2]; #define N 256 /* ( FFT_SIZE) */ #define log2N 8 /**/ xQueueHandle xStartFFTTask( void ) { xQueueHandle xFFTQueue; /* データ受信同期用待ち行列の生成 */ xFFTQueue = xQueueCreate( fftQUEUE_SIZE, sizeof( xFFTMessage ) ); /* タスクの生成 */ xTaskCreate( vFFTTask, ( signed portCHAR * ) "FFT", configMINIMAL_STACK_SIZE+100, NULL, tskIDLE_PRIORITY + 1, NULL ); return xFFTQueue; } /*-------------------------------------*/ static void vFFTTask(void *pvParameters) { xFFTMessage xFFT; static int i, j, scale; static unsigned diff; short *fx; /* プライベート・エリアからメモリを取得する */ fx = pvPortMalloc(sizeof(short)*N); /* タスクループ */ while(1){ /* データの収集を待つ */ xQueueReceive( xFFTQueue, &xFFT, portMAX_DELAY ); /* ダブルバッファからデータを取得する */ j = (xFFT.fftSel == 0)? 0 : 256; /* 実数変換用に並べ替える */ /* 符号付き2進に変換し12288に正規化する */ for (i=0; N; i++){ if (i & 0x01)) fx[(N+i)>>1] = (x[i+j]-512)*24;; elsee fx[i>>1] = (x[i+j]-512)*24;; } /* FFT変換 */ fix_fftr(fx, log2N, 0); #if SPECTRUM /* 各周波数領域の二乗総和を求める */ for(i=0; i<4; i++) sp.spec[i] = 0; for (i= 1; i< 4; i++) sp.spec[0] += ((long)fx[i]*fx[i]+fx[i+N/2]*fx[i+N/2]); for (i= 4; i<16; i++) sp.spec[1] += ((long)fx[i]*fx[i]+fx[i+N/2]*fx[i+N/2]); for (i=16; i<32; i++) sp.spec[2] += ((long)fx[i]*fx[i]+fx[i+N/2]*fx[i+N/2]); for (i=32; i<64; i++) sp.spec[3] += ((long)fx[i]*fx[i]+fx[i+N/2]*fx[i+N/2]); /* 平均及び平方根(RMS)を求め明度表示するタスクへ渡す */ xQueueSend( xFFTWQueue, &sp, portMAX_DELAY ); #endif /* 逆変換で元に戻す */ scale = fix_fftr(fx, log2N, 1); /* 実数変換用のデータを並べ替えて元に戻す 正規化(12288)を戻し符号なし2進数に戻す */ int k = (j == 0)? 512 : 768; for (i=0; i<N; i++) { if (i & 0x01) x[i+k] = (fx[(N+i)>>1] << scale)/24+512; else x[i+k] = (fx[ i >>1] << scale)/24+512; } } } /* 平均及び平方根(RMS)を求め明度表示するタスク これは時間内に終わらない。そこで溜まったQUEUEを 間引き最新のデータに同期する。 */ static void vPwrTask(void *pvParameters) { Spectrum sp; while(1){ if (xQueueReceive( xFFTWQueue, &sp, portMAX_DELAY) == pdTRUE){ while( xQueueReceive( xFFTWQueue, &sp, 0) == pdTRUE); sp.spec[0] = sqrt(sp.spec[0] / 3); sp.spec[1] = sqrt(sp.spec[1] / 12); sp.spec[2] = sqrt(sp.spec[2] / 16); sp.spec[3] = sqrt(sp.spec[3] / 32); SetDCOC2PWM((long)(PR2+1)*(PR2-1-sp.spec[0])/160); SetDCOC3PWM((long)(PR2+1)*(PR2-1-sp.spec[1])/160); SetDCOC4PWM((long)(PR2+1)*(PR2-1-sp.spec[2])/160); SetDCOC5PWM((long)(PR2+1)*(PR2-1-sp.spec[3])/160); } } } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
おや、パワースペクトルを計算するには窓関数がかかっていない?これはテスト用に逆変換して音声出力するためであり、どこまで使えるかの検証なので悪しからず。 なお、fix_fftrの修正したものを次に示す。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int fix_fftr(short f[], int m, int inverse) { int i, N = 1<<(m-1), scale = 0; short tt, *fr=&f[0], *fi=&f[N]; short n, n2, tsin, tcos; short real1, imag1, real2, imag2; long tr, ti, sr, si; n = N*2; n2 = n/2; if (inverse){ for(i=1; i<n/4; i++){ tsin = Sinewave[(1024/n)*i]; tcos = Sinewave[(1024/n)*i+256]; tr = fr[i]-fr[n2-i]; ti = fi[i]+fi[n2-i]; sr = ((tr*(32767-tsin)+ti*tcos)/2) >> 15; si = ((ti*(32767-tsin)-tr*tcos)/2) >> 15; real1 = fr[ i] - sr; imag1 = fi[ i] - si; real2 = fr[n2-i] + sr; imag2 = fi[n2-i] - si; fr[ i] = real1; fi[ i] = imag1; fr[n2-i] = real2; fi[n2-i] = imag2; } fr[0] = (fr[0] + fr[n2])/2; fi[0] = (fi[0] - fi[n2])/2; scale = fix_fft(fi, fr, m-1, inverse); } if (! inverse){ scale = fix_fft(fi, fr, m-1, inverse); for(i=1; i<n/4; i++){ tsin = Sinewave[(1024/n)*i]; tcos = Sinewave[(1024/n)*i+256]; tr = fr[i]-fr[n2-i]; ti = fi[i]+fi[n2-i]; sr = ((tr*(32767-tsin)-ti*tcos)/2) >> 15; si = ((ti*(32767-tsin)+tr*tcos)/2) >> 15; real1 = fr[ i] - sr; imag1 = fi[ i] - si; real2 = fr[n2-i] + sr; imag2 = fi[n2-i] - si; fr[ i] = real1; fi[ i] = imag1; fr[n2-i] = real2; fi[n2-i] = imag2; } fr[n2] = fr[0]; fi[n2] = -fi[0]; } return scale; } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
アナログ入力 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PIC24FのADコンバータは多くのモードを備えていあるが、ここではタイマーに依る周期取り込みを例として取り上げる。 タイマ3を取り込み周期発生源として使用する。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ADコンバータ設定関数 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#define USE_AND_OR #include "adc.h" #include "timer.h" int adc_setup(unsigned portBASE_TYPE ports) { /* タイマ3を内部クロック16MHz、プリスケーラ8 (=>2Mhz)に設定 トリガ周期を2Mhz/200=10Khzとする*/ OpenTimer3(T3_ON | T3_PS_1_8 | T3_SOURCE_INT, 200-1); /* 各トリガ時、CH0及びCH1のADCサンプリングを実行する 16回の変換(2chを8回)ごとに割り込みを発生する。 */ if(ports==2){ OpenADC10( ADC_MODULE_ON | ADC_CLK_TMR3 | ADC_AUTO_SAMPLING_ON , ADC_SCAN_ON | ADC_INTR_8_CONV, ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_6Tcy, ~(ENABLE_AN1_DIG | ENABLE_AN0_DIG), ADC_SCAN_AN1 | ADC_SCAN_AN0); } SetChanADC10(0); /* 割り込み初期化 */ ConfigIntADC10(ADC_INT_ENABLE | ADC_INT_PRI_2); } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
アナログ入力割り込み関数 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
例えば音声であれば40Khz程度のサンプリング周波数ですので、もしその頻度で割り込めば、処理時間は25usec程になります。ということはC言語で見て数ステップしか実行できない。しかし、ADCは最大16個のバッファを持っているので、これを使用すれば1桁ほど余裕が出てくる。 それでも、タスクにデータを送信する余裕は無いので、ダブルバッファでタスクとのデータの受け渡しを行う。 バッファがFULLになると(ブロックしている)該当タスクへバッファを指定してシグナル(待ち行列)を送信する。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
__attribute__((far)) extern short dbuff[2][256]; void __attribute__((interrupt, no_auto_psv)) _ADC1Interrupt(void) { static short AnIdx=0; static short AnSel=0; portBASE_TYPE xHPTaskWoken; xFFTMessage xFFT; int i; IFS0bits.AD1IF = 0; /* 割り込みクリア */ if (AnIdx >= N){ xFFT.fftSel = AnSel; xHPTaskWoken = pdFALSE; xQueueSendFromISR( xFFTQueue, &xFFT, &xHPTaskWoken ); if( xHPTaskWoken ) { /* |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PWM | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PIC24FJ64GA002は5chの16Bit Compare/PWM 出力を持っています。これを使って気軽にパルス幅変調が出来ます。ここではPWM周波数として音声も再生できる100khzを選択します。なんか、すごい事になっています。 変調自体は他のタスク等のタイミングで行いますので、ここではドライバのみを説明する。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#include "timer.h" #include "outcompare.h" /* パルス幅変調初期化設定関数 */ int pwm_setup(void) { /* タイマ2を内部クロック16MHz、プリスケーラ1、 PWM周期160(=100khz) PWM周期はPR2に記憶される */ OpenTimer2(T2_ON | T2_PS_1_1 | T2_SOURCE_INT, 160-1); /* pin割付 */ RPOR2bits.RP4R = 18; /*OC1 -> RP4*/ /* OC1をPWMモードで初期化 */ OpenOC1(OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, PR2+1, 0); RPOR2bits.RP5R = 19; /*OC2 -> RP5*/ OpenOC2(OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, PR2+1, 0); } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
変調ステートメントの例として音声を取り込みそのままPWM出力する例を下に示す。ADCの0chからUnSigned10bitデータ(MAX1024)を読み込み、タイマ2のPWM周期(PR2)で正規化します。なお、この計算は16bitだとオーバーフローするのでlongにキャストして計算する。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SetDCOC1PWM((long)(PR2+1)*ReadADC10(0)/1024); |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
UART | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PIC24FJ64GA002はUART2chを持っています。 コンソールはPICでは一般的にLCDを使うようですが、外付け部品を最小にして作るというコンセプトから、UARTをコンソールに使用する。 なお、レベルコンバータが必要であるが、MAX202で作ったコンバータをケーブル側で用意した。 便利なので一つ持っていると便利です。
タスク構成はデモプログラム(comtest.c)そのままです。これに送信用及び受信用待ち行列を付け加えて、送受信タスクとしています。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
疑問なんですが_U2TXInterruptで1文字送出毎に割り込む設定になっている。キューにデータが無い場合等、以後割り込みが掛からなくなりそうですが・・・ 実はxSerialPutCharのxQueueSend()の後ろで小細工して割り込みを発生するようにしてあります。それで余計なxserialPut/GetChar()がはいっています。(serial.cを参照) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xQueueHandle xComTxQueue; xQueueHandle xComRxQueue; #define ComRxQUEUE_SIZE 16 #define ComTxQUEUE_SIZE 3 /*-----------------------------------------------------------*/ void vAltStartComTasks( unsigned portBASE_TYPE uxPriority, unsigned long ulBaudRate, unsigned portBASE_TYPE uxLED ) { /* ポートの初期化 */ xSerialPortInitMinimal( ulBaudRate, comBUFFER_LEN ); xComTxQueue = xQueueCreate( ComTxQUEUE_SIZE, sizeof( xComTxMessage ) ); xComRxQueue = xQueueCreate( ComRxQUEUE_SIZE, sizeof( xComRxMessage ) ); /* タスクを生成する。Txの優先度はRxより低く設定する。 */ xTaskCreate( vComTxTask, ( signed char * ) "COMTx", comSTACK_SIZE, NULL, uxPriority - 1, ( xTaskHandle * ) NULL ); xTaskCreate( vComRxTask, ( signed char * ) "COMRx", comSTACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL ); } /*-----------------------------------------------------------*/ /* UART送信タスク */ static portTASK_FUNCTION( vComTxTask, pvParameters ) { signed char cByteToSend; portTickType xTimeToWait; xComTxMessage xMessage; portCHAR *pcstring; for( ;; ){ while( xQueueReceive( xComTxQueue, &xMessage, portMAX_DELAY ) != pdPASS ); pcstring = xMessage.pcMessage; while(*pcstring){ xSerialPutChar( NULL/*xPort*/, *pcstring++, comNO_BLOCK ); } } } /*lint !e715 !e818 pvParameters is required for a task function */ /* even if it is not referenced. */ /*-----------------------------------------------------------*/ /* UART受信タスク */ static portTASK_FUNCTION( vComRxTask, pvParameters ) { signed char cExpectedByte, cByteRxed; portBASE_TYPE xResyncRequired = pdFALSE, xErrorOccurred = pdFALSE; xComRxMessage xMessage; for( ;; ){ /* 1文字入力後無条件に待ち行列に出力する。受信タスクが無ければ、 このループはいずれブロックする。 */ xSerialGetChar( xPort, &cByteRxed, comRX_BLOCK_TIME ); xMessage.pcMessage = &cByteRxed; xQueueSend( xComRxQueue, &xMessage, portMAX_DELAY ); } } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
タイマー割り込み | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
タイマー割り込みの例として音声データの出力タイミングの発生を取上げる。 音声データを40khzでPWM出力する。本来はリングバッファを構成するのであるが、40khzでは最大で25usec程の割り込み処理時間しか無いので、まともなバッファは構成できない。 一般に、この様な場合シリアルインターフェースを持つMCP4922等とSPIを使用すると思う。SPIはFIFOがあり、その分余裕が生まれるので、バッファ構成も色々考えられる。 ここでは音声入力及びその処理と同期して出力データが生成されるので、これを単純に入出力が同期しているとして制御なしで使用する。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/************************** * タイマー初期化設定関数 ***************************/ int timer_setup(void) { /* 音声出力用 */ /* タイマ4の初期設定 プリスケーラ1/8 内部クロック 16Mhz/8/50=40kc */ OpenTimer4(T4_ON | T4_PS_1_8 | T4_SOURCE_INT, 50-1); ConfigIntTimer4(T4_INT_PRIOR_2 | T4_INT_ON); } /*************************** 音声用timer割り込み処理 ****************************/ void __attribute__((interrupt, shadow)) _T4Interrupt(void) { /* 追っかけるように再生するため、少し手前からスタートする*/ static short PlayIdx=480; IFS1bits.T4IF = 0; /* 割り込みクリア */ PlayIdx &= 0x1ff; /* PWM出力を行う */ SetDCOC1PWM((long)(PR2+1)*x[PlayIdx++]/1024); } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ソースとプロジェクトファイル | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MPLABをデフォルトでインストールした場合、周辺機器のヘッダは C:\Program Files\Microchip\MPLAB C30\support\peripheral_24F にあり、ドライバは C:\Program Files\Microchip\MPLAB C30\src\peripheral_24F\libpPIC24F\pmc にあります。 しかし、あまりに大量であり、かつ細分化されているので必要なものをカレントディレクトリへ転送して使用しています。 これらはLib形式にして使用すれば手間が無いのですが、大規模なプログラムで無い限り、ソース・デバッグを可能にしておくのが得策だと思う。 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
感想 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
リソースの少ないCPUのRTOSはプリミッティブなAPIを持つものが望ましいと考えていた。FreeRTOSはより小さな核を持ち、その上で、少ないリソースを活用しようとしていると見られる。しかし、クリティカルな場面では、まだ動作が重い。そのような時は、単純な割り込み処理とかアッセンブラを使え、ということなのだろう。しかし、100%の負荷を与えても、もくろみ通りの動作をけなげにもこなしているのにはちょっと感心した。おそらくオーバヘッドと機能のバランスは良いところにあるのではないだろうか。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
44khzの音声を(途中FFTみたいな重い処理を入れて)取り扱おうと思ったが、結局16Khz〜20khzが限界だった。主に、その周波数での割り込み処理を必要とする音声出力でのCPUリソースの消費量だった。チップ内で全て処理するのであれば当然だと思う。一般的にはSPI機能とSPIを持つDAコンバーターを使うのであろう。その場合もう少し処理周波数を上げることが出来る。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PICは扱い難いとの先入観を持っていたが | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ご意見ご質問はここ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Guest No. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||