UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを
「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、
その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、
「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を
作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。
各種確認プログラム | 左記に必要な部品の追加例 |
---|---|
とくに必要ありません。 (D1のLEDは、あるとよい) | |
PWM対応の部品追加例 | |
ADC 対応の部品追加例 | |
BEEP SWITCH 対応の部品追加例 | |
Reset SW, Type-A, CN2 部品追加例 | |
U20,D4,D5,NPN, D3 部品追加例 | |
CN11,CN-12 部品追加例 | |
U19 部品追加例 | |
U17にRN4020の部品を追加する例 |
[UMEHOSHI ITA]単体で動作させる時に必要な部品の追加です。
以下のコードでは、[UMEHOSHI ITA]のPORTB5の端子をON・OFFするパルス変調で、音を鳴らしています。
(PORTB5の端子の端子には、別途にLED D2のON・OFFも兼用しています。)
[UMEHOSHI ITA]の「テスト・ウメ・フラッシュ」を使うプログラムは、変数として使える総メモリ数が 4096byte程度です。
それ以外で自由に使えるRAM領域は、_MEMO3を先頭アドレスとする0x0F00byte(3840byte)しか在りません。
また自由に使えるEEPROM領域は、(0x9D03FC00-0x9D020000)の範囲で、130048byteです。
よって、メモリに記憶したデータで鳴らす場合、音のデータの格納数を少しでも減らす工夫を一番に考えて作る必要があります。
(音の品質が多少悪くても、データ量を抑える目標で考えます。)
#include <xc.h> #include <stdlib.h> #include "common.h" #define TARAGET_PIN 0x0020 // スイッチング対象のPORT B端子 char buffer[100]=""; char bin_data_array[] = { // 5μのスイッチング時に1KHzで1サイクル分のバイト数は「 (1/1e3) / 5e-6 / 8 = 25 」と算出 // 25byteの半分が1、残り半分が1であれば、矩形波形りなり、それは 5μのスイッチング周期で、1KHzの周波数になります。 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,//10byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//10byte 0x00,0x00, 0x0f, 0xff,0xff, }; // スイッチングデータが並ぶバイナリ char *bin_data = bin_data_array; int bin_mask = 1; int bin_idx = 0; int switching_count=0;// switching の実行回数をカウント void switching() // スイッチング関数で、 timer4() 割り込み関数 { switching_count++;//スイッチング回数計測用 if( bin_idx >= sizeof(bin_data_array) ) { bin_idx = 0;// bin_data のデータをループ再生 // return; //returnするとループしない。(コメントになっているとループ再生) } if (bin_data[bin_idx] & bin_mask) PORTBSET = TARAGET_PIN; // ON else PORTBCLR = TARAGET_PIN; // OFF bin_mask <<= 1; if( bin_mask >= 256 ){ bin_idx+=1; bin_mask=1; } } // コアタイマーで作る1秒周期の割り込みに使う関数。 // これを、これで10回実行で10秒間の期間を判定し、その間だけswitching() の割り込みを可能にする。 // この間で、switching() を実行した回数を得て、その回数からswitching() 周期が正しいかを検証する。 void core_TIMER_SUB_FUNC() { static int count=0; if( count == 0) { //最初の割り込み PORTBCLR = 0x8000;// LED1をOFF T4CONbits.ON = 1; // timer4のswitching()の割り込み スタート }else if(count == 10){ //10秒後の割り込み PORTBSET = 0x8000;// LED1をON T4CONbits.ON = 0; // switching()の割り込み ストップ _send_decimal(switching_count,10);// PR4=199;の時、このカウント数は2000002であった。 _send_string("\nEnd program\r\n"); //PR4=399; // 割り込み周期 (399+1)*(1/40e6) = 10μ 秒(10秒間のカウント数は1000001) //PR4=199; // 割り込み周期 (199+1)*(1/40e6) = 5μ 秒(10秒間のカウント数は2000002) //PR4=159; // 割り込み周期 (159+1)*(1/40e6) = 4μ 秒(10秒間のカウント数は2499993)本来は2500000のはず //PR4=119; // 割り込み周期 (119+1)*(1/40e6) = 3μ 秒(10秒間のカウント数は2666659)本来は3333333のはず //PR4=100; // 割り込み周期 (100+1)*(1/40e6) = 2.5μ秒(10秒間のカウント数は2712983)本来は4000000のはず //PR4= 79; // 割り込み周期 ( 79+1)*(1/40e6) = 2 μ秒(10秒間のカウント数は2714110)本来は5000000のはず PR4=199; // 割り込み周期 (199+1)*(1/40e6) = 5μ 秒(10秒間のカウント数は2000002) } count++; } __attribute__((address( 0x80005000 ))) void main(void); void main() { _send_string("start program\r\n"); // Timer4の割り込み初期化------------------ T4CON =0x00000000;//typeB,16bit, [1:1]プリスケール TMR4=0x00000000; //16bitタイマの設定値(レジスタがカウントアップ) //IPC4bits.T4IP = 6; // Set priority level = 6(2番に大きな優先度) //IPC4bits.T4IS = 0;// Set sub-priority level = 3(最大値) IFS0bits.T4IF = 0;// Clear the timer interrupt status flag IEC0bits.T4IE = 1;// Timer4 Enable(割込み許可). _HANDLES[_IDX_TIMER_4_FUNC] = switching;// timer4割り込み関数を登録 _UM_CP0_SET_COMPARE=(long)(1.0/(1/40e6)/2); // コアタイマを1秒周期の割り込み間隔に変更 // (コアタイマは、2 システムクロック周期でインクリメントします) _HANDLES[_IDX_CORE_TIMER_SUB_FUNC]= core_TIMER_SUB_FUNC;// コアタイマの割り込み関数を登録 }のコメント範囲は、TMR4の設定値を変更して実験した値の結果です。
#include <xc.h>
#include <stdlib.h>
#include "common.h"
#define PERIOD_N 100 // コアタイマーのカウントで指定するswitchingの呼び出し周期:5μ秒周期
//コアタイマーは (1/40e6)*2 =50ナノ秒で一つカウントします。(20であれば1μ秒周期を意味する)
// 10/((1/40e6)*2 * PERIOD_N )=目標のカウント値で、実行値がこれに近いほど正しい周期でswitching()をしている。
//10μ秒周期:10/((1/40e6)*2 *200 )= 1000000 に対して、実行値は 953207
// 5μ秒周期:10/((1/40e6)*2 *100 )= 2000000 に対して、実行値は1834739
// 4μ秒周期:10/((1/40e6)*2 * 80 )= 2500000 に対して、実行値は2223505
// 3μ秒周期:10/((1/40e6)*2 * 60 )=3333333.33に対して、実行値は2899525
// 2μ秒周期:10/((1/40e6)*2 * 40 )= 5000000 に対して、実行値は4057275
// 1μ秒周期:10/((1/40e6)*2 * 20 )=10000000 に対して、実行値は4839274
// 以上が、PERIOD_Nを20,40,60,80,100,200と変更した場合の実行結果
#define TARAGET_PIN 0x0020 // スイッチング対象のPORT B端子
char buffer[100]="";
char bin_data_array[] = { // 5μのスイッチング時に1KHzで1サイクル分のバイト数は「 (1/1e3) / 5e-6 / 8 = 25 」と算出
// 25byteの半分が1、残り半分が1であれば、矩形波形りなり、それは 5μのスイッチング周期で、1KHzの周波数になります。
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,//10byte
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//10byte
0x00,0x00, 0x0f, 0xff,0xff,
}; // スイッチングデータが並ぶバイナリ
char *bin_data = bin_data_array;
int bin_mask = 1;
int bin_idx = 0;
int switching_count=0;// switching の実行回数をカウント
unsigned long next_count = 0;
int switching() // bin_dataの配列内容でスイッチングする関数。
{
switching_count++;//スイッチング回数計測用
if( bin_idx >= sizeof(bin_data_array) ) {
bin_idx = 0;// bin_data のデータをループ再生
// return 0; //returnするとループしない。(コメントになっているとループ再生)
}
if (bin_data[bin_idx] & bin_mask) PORTBSET = TARAGET_PIN; // ON
else PORTBCLR = TARAGET_PIN; // OFF
bin_mask <<= 1;
if( bin_mask >= 256 ){
bin_idx+=1;
bin_mask=1;
}
return 1;
}
// SW2のスイッチのエッジトリガーのINT0で実行させる関数
// これを、これで10回実行で10秒間の期間を判定し、その間だけswitching() の割り込みを可能にする。
// この間で、switching() を実行した回数を得て、その回数からswitching() 周期が正しいかを検証する。
void int0_FUNC()
{
IEC0CLR = _IEC0_INT0IE_MASK;// INT0割り込みを無効化(これが無いと、ちゃちたリングで再帰処理が生じる)
long count_end = _CP0_GET_COUNT() + 10/(1/40e6)/2; //200000000の追加が10秒後のカウント値
PORTBINV = 0x8000;// LED1を反転
while(_CP0_GET_COUNT() < count_end){ // 10秒間の実行
next_count = _CP0_GET_COUNT() + PERIOD_N;
switching();
while( _CP0_GET_COUNT() < next_count) { }
}
_send_decimal(switching_count,10);
_send_string(" <==switching_count\r\n");
//IFS0CLR = _IFS0_INT0IF_MASK;// 割り込みフラグをクリアする
}
__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
IEC0CLR = _IEC0_INT0IE_MASK;// INT0割り込みを無効化
_HANDLES[_IDX_EXTERNAL_0_VECTOR] = int0_FUNC; // RB7 がSW2(白)に繋がっている。これが押された割り込み
INTCONSET = _INTCON_INT0EP_MASK; // INT0をエッジトリガとして設定
IPC0bits.INT0IP = 3; // 割り込みの優先度を設定(0から7の範囲で指定され、0が最も高い)
IFS0CLR = _IFS0_INT0IF_MASK; // INT0割り込みフラグをクリア
IEC0SET = _IEC0_INT0IE_MASK; // INT0割り込みを有効化
_send_string("start program\r\n");
}
のコメント範囲は、PERIOD_N の設定値を変更して実験した値の結果です。start program +1834739 <==switching_countこれは、10秒間でswitching() を2000000回実行できるはずの所を、1834739回しか実行できていないという結果です。
#include <xc.h> #include <stdlib.h> #include "common.h" #define PERIOD_N 100 // コアタイマーのカウントで指定するswitchingの呼び出し周期:5μ秒周期 //コアタイマーは (1/40e6)*2 =50ナノ秒で一つカウントします。(20であれば1μ秒周期を意味する) #define TARAGET_PIN 0x0020 // スイッチング対象のPORT B5端子 char buffer[100]=""; char bin_data_array[] = { // 5μのスイッチング時に1KHzで1サイクル分のバイト数は「 (1/1e3) / 5e-6 / 8 = 25 」と算出 // 25byteの半分が1、残り半分が1であれば、矩形波形りなり、それは 5μのスイッチング周期で、1KHzの周波数になります。 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,//10byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//10byte 0x00,0x00, 0x0f, 0xff,0xff, }; // スイッチングデータが並ぶバイナリ char *bin_data = bin_data_array; int bin_mask = 1; int bin_idx = 0; int switching_count=0;// switching の実行回数をカウント int switching() // bin_dataの配列内容でスイッチングする関数。 { switching_count++;//スイッチング回数計測用 if( bin_idx >= sizeof(bin_data_array) ) { bin_idx = 0;// bin_data のデータをループ再生 // return 0; //returnするとループしない。(コメントになっているとループ再生) } if (bin_data[bin_idx] & bin_mask) PORTBSET = TARAGET_PIN; // ON else PORTBCLR = TARAGET_PIN; // OFF bin_mask <<= 1; if( bin_mask >= 256 ){ bin_idx+=1; bin_mask=1; } return 1; } __attribute__((address( 0x80005000 ))) void main(void); void main() { PORTBINV = 0x8000;// LED1を反転 switching_count = 0; unsigned long next_count = 0; unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得 unsigned long count_end = count_now + 200000000;//の追加が10秒後のカウント値(10/50e-9=200000000) while(count_now < count_end){ // 10秒間の実行 while( count_now < next_count) { count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得 } switching();// switching_countをインクリメントしてPORT B5の端子をスイッチング //switching_count++;//スイッチング回数計測用 next_count = count_now + PERIOD_N; // PERIOD_Nが100であれば5μ秒でswitching_count++を実行 } PORTBINV = 0x8000;// LED1を反転 _send_decimal(switching_count,10); _send_string(" <==switching_count\r\n"); }上記の実行例です。
+1957696 <==switching_countこれは、10秒間でswitching();を2000000回実行できるはずの所を、1957696回しか実行できていないという結果で、前述のコードより正確のはずですが、42304カウント足りません。
10μ秒周期:10/((1/40e6)*2 *200 )= 1000000 に対して、実行値は 979202 この誤差は 20798カウント 5μ秒周期:10/((1/40e6)*2 *100 )= 2000000 に対して、実行値は1957696 この誤差は 42304カウント 4μ秒周期:10/((1/40e6)*2 * 80 )= 2500000 に対して、実行値は2380332 この誤差は 119668カウント 3μ秒周期:10/((1/40e6)*2 * 60 )= 3333333.3に対して、実行値は3099891 この誤差は 233442カウント 2μ秒周期:10/((1/40e6)*2 * 40 )= 5000000 に対して、実行値は4263107 この誤差は 736893カウント 1μ秒周期:10/((1/40e6)*2 * 20 )=10000000 に対して、実行値は4263111 この誤差は5736889カウントこれは、この手法で正確な制御は出来ないという結果です。
+1939003 <==switching_count
オリジナルファイル(doremi.wav) 左リンクよりダウンロード可能 44100Hz,16BitPCM |
目標となる音(音質を落としたデータ音) 「約7350Hz、分解能4ビット」 |
#include <xc.h> #include "common.h" int change_count = 0; // change()の割り込み回数計数用 void change() // timer2() 割り込み関数 (PWMのデューティ サイクルの変更予定 { change_count++;//割り込み回数計測用 (約44100Hzの周期でカウント) IFS0CLR = 0x00000200; // Clear the timer interrupt status flag } __attribute__((address( 0x80005000 ))) void main(void); void main() { // Timer2の割り込み初期化------------------ T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み) PR2=906; //16bitタイマの設定値(周期設定用 =int((1/44100)/(1/40e6)-1) _HANDLES[_IDX_TIMER_2_FUNC] = change;// timer2割り込み関数を登録 PORTBINV = 0x8000;// LED1を反転 unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得 unsigned long count_end = count_now + 20000000;//の追加が1秒後のカウント値(1/50e-9=20000000) change_count = 0; T2CONbits.ON = 1; // Timer2の動作スタート while(count_now < count_end){ // 1秒間の実行 count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得 } PORTBINV = 0x8000;// LED1を反転 _send_decimal(change_count,10); _send_string(" <==change_count\r\n"); }この実行結果は次のようになり、想定内です。(1/44100)/(1/40e6)-1 =906.02947845805の値を、総数点以下を切り捨ててPR2に設定のため)
START:80005000 +44101 <==change_count
import math import matplotlib.pyplot as plt y=[ int((-math.cos(x * 2*math.pi/44)+1)/2 *903+2 ) for x in range(44)] plt.plot(y) plt.show() for n,v in enumerate(y): if n > 0 and n % 10 == 0 : print() print(v, end=", ") |
2, 6, 20, 42, 73, 112, 157, 209, 265, 326, 389, 453, 517, 580, 641, 697, 749, 794, 833, 864, 886, 900, 905, 900, 886, 864, 833, 794, 749, 697, 641, 580, 517, 453, 389, 326, 265, 209, 157, 112, 73, 42, 20, 6, |
#include <xc.h> #include "common.h" int change_count = 0; // change()の割り込み回数計数用 short int pwd_datas[]={ 2, 6, 20, 42, 73, 112, 157, 209, 265, 326, 389, 453, 517, 580, 641, 697, 749, 794, 833, 864, 886, 900, 905, 900, 886, 864, 833, 794, 749, 697, 641, 580, 517, 453, 389, 326, 265, 209, 157, 112, 73, 42, 20, 6, }; int pwd_idx=0; // 上記アクセス用添え字 void change() // timer2() 割り込み関数 (PWMのデューティ サイクルの変更予定 { change_count++;//割り込み回数計測用 (約44100Hzの周期でカウント) if( pwd_idx >= sizeof(pwd_datas)/2 ) pwd_idx = 0; OC2RS = pwd_datas[pwd_idx++]; IFS0CLR = 0x00000200; // Clear the timer interrupt status flag } __attribute__((address( 0x80005000 ))) void main(void); void main() { // Timer2の初期化---(割り込み指定を含む)--------------- T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み) PR2=906; //16bitタイマの設定値(周期設定用 =int((1/44100)/(1/40e6)-1) _HANDLES[_IDX_TIMER_2_FUNC] = change;// timer2割り込み関数を登録 IPC2SET = 0x0000000C; // Set priority level = 3 IEC0SET = 0x00000200;//T2IE Timer2 Enable(割込み許可) // Output Compare module 2の初期化------------------ OC2CON = 0x0000;// Turn off OC2 while doing setup. RPB5Rbits.RPB5R = 0x5; // RB5をOC2の出力にする。 OC2CONbits.SIDL = 0; // アイドルモード中も動作を継続する OC2CONbits.OC32 = 0; //OC2R<15:0> およびOC2RS<15:0> を使って16 ビットタイマ源と比較する OC2CONbits.OCTSEL = 0; // Timer2 をこの出力コンペア モジュールのクロック源として使う OC2CONbits.OCM = 5; // OC2 をPWM モードにし、フォルトピンを無効にする //OC2R = 200;//出力コンペアx コンペアレジスタの初期値 OC2RS = 905;//出力コンペアx セカンダリコンペアレジスタ(デューティー比設定用で上記設定の最大値 ) T2CONbits.ON = 1; // Timer2の動作スタート OC2CONbits.ON =1; // OC2の動作スタート PORTBINV = 0x8000;// LED1を反転 unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得 unsigned long count_end = count_now + 200000000;//の追加が10秒後のカウント値(10/50e-9=200000000) change_count = 0; while(count_now < count_end){ // 10秒間の実行 count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得 } PORTBINV = 0x8000;// LED1を反転 OC2RS=2; OC2CONbits.ON =0; // OC2の動作OFF _send_decimal(change_count,10); _send_string(" <==change_count\r\n"); }これを使って生成された音は余計な周期的と思われる雑音が出て、期待した正弦波の1KHzとは程遠い結果になりました。
#include <xc.h> #include "common.h" int change_count = 0; // change()の割り込み回数計数用 short int pwd_datas[]={ 2, 6, 20, 42, 73, 112, 157, 209, 265, 326, 389, 453, 517, 580, 641, 697, 749, 794, 833, 864, 886, 900, 905, 900, 886, 864, 833, 794, 749, 697, 641, 580, 517, 453, 389, 326, 265, 209, 157, 112, 73, 42, 20, 6, }; int pwd_idx=0; // 上記アクセス用添え字 void change() // OC2割り込み関数 (PWMのデューティ サイクルの変更 { change_count++;//割り込み回数計測用 (約44100Hzの周期でカウント) if( pwd_idx >= sizeof(pwd_datas)/2 ) pwd_idx = 0; OC2RS = pwd_datas[pwd_idx++]; //IFS0CLR = 0x00000200; // Clear the timer interrupt status flag IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag★ } __attribute__((address( 0x80005000 ))) void main(void); void main() { // Timer2の初期化------------------ T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み) PR2=906; //16bitタイマの設定値(周期設定用 =int((1/44100)/(1/40e6)-1) //_HANDLES[_IDX_TIMER_2_FUNC] = change;// timer2割り込み関数を登録 //IPC2SET = 0x0000000C; // Set priority level = 3 IEC0CLR = 0x00000200;//T2IE Timer2 desable(割込み不許可)★ // Output Compare module 2の初期化-(割り込み指定を含む)----------------- OC2CON = 0x0000;// Turn off OC2 while doing setup. RPB5Rbits.RPB5R = 0x5; // RB5をOC2の出力にする。 OC2CONbits.SIDL = 0; // アイドルモード中も動作を継続する OC2CONbits.OC32 = 0; //OC2R<15:0> およびOC2RS<15:0> を使って16 ビットタイマ源と比較する OC2CONbits.OCTSEL = 0; // Timer2 をこの出力コンペア モジュールのクロック源として使う OC2CONbits.OCM = 5; // OC2 をPWM モードにし、フォルトピンを無効にする _HANDLES[_IDX_OUTPUT_COMPARE_2_VECTOR] = change;// OC2割り込み関数を登録 IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag IEC0SET = _IEC0_OC2IE_MASK; // Enable OC2 interrupt IPC2bits.OC2IP = 7;// Set OC1 interrupt priority to 7 IPC2bits.OC2IS = 3;// Set Subpriority to 3, maximum //OC2R = 0;//出力コンペア1 コンペアレジスタの初期値 OC2RS = 905;//出力コンペア1 セカンダリコンペアレジスタ(デューティー比設定用用 ) T2CONbits.ON = 1; // Timer2の動作スタート OC2CONbits.ON =1; // OC2の動作スタート PORTBINV = 0x8000;// LED1を反転 unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得 unsigned long count_end = count_now + 200000000;//の追加が1秒後のカウント値(10/50e-9=200000000) change_count = 0; while(count_now < count_end){ // 10秒間の実行 count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得 } PORTBINV = 0x8000;// LED1を反転 _send_decimal(change_count,10); _send_string(" <==change_count\r\n"); }の部分のTimer2割り込みを止めて、 のOC2割り込み設定を追加しています。
pdata=[int(n*903/15)+2 for n in range(16)] print(pdata) # 結果「[2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905]」これを4ビットで選んでデューティ比に指定します。(16ビットの音を4bitにする→必要メモリを1/4にできる)
#include <xc.h> #include "common.h" int change_count = 0; // change()の割り込み回数計数用 #define NREP 6 // 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ) short int pwd_base[]={2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905}; int idx_base=0; char bin_data_array[]={// 下位4ビットと、上位4ビットで1byteが2つデータ(1byteで0.2721ミリ秒) 0x40, 0xb8, 0xaf, 0x26, // 8つのデータ };// 上記4byteで1つのデータを(NREP=6)回使う。0.272*4=約0.92ミリ(約919Hzの波形) char *pwd_datas=bin_data_array; int pwd_idx=0; // 上記アクセス用添え字 int count_out=0; // pwd_datasの各要素の上位、下位それぞれのデータを(NREP=6)回使い、(NREP+NREP)までのカウンタ void change() // OC2割り込み関数 (PWMのデューティ サイクルの変更 { change_count++;//割り込み回数計測用 (約44100Hzの周期でカウント) if(count_out == 0){ // 下位4ビットの周期へ idx_base = pwd_datas[pwd_idx] & 0x0f; OC2RS = pwd_base[idx_base]; }else if(count_out == NREP){ // 上位4ビットの周期へ idx_base = (pwd_datas[pwd_idx] >> 4) & 0x0f; OC2RS = pwd_base[idx_base]; } if(++count_out == (NREP+NREP)){ //上位4ビットが終わって次の要素周期へ count_out=0; pwd_idx++; if( pwd_idx >= sizeof(bin_data_array) ) {//周期要素を配列を使い終わった pwd_idx = 0; // OC2CONbits.ON =0; // OC2の動作OFF } } IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag } __attribute__((address( 0x80005000 ))) void main(void); void main() { // Timer2の初期化------------------ T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ T2CONbits.TCKPS = 0;// 1:1プリスケール値 TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み) PR2=906; //16bitタイマの設定値(周期設定用 =int((1/44100)/(1/40e6)-1) IEC0CLR = 0x00000200;//T2IE Timer2 desable(割込み不許可) // Output Compare module 2の初期化-(割り込み指定を含む)----------------- OC2CON = 0x0000;// Turn off OC2 while doing setup. RPB5Rbits.RPB5R = 0x5; // RB5をOC2の出力にする。 OC2CONbits.SIDL = 0; // アイドルモード中も動作を継続する OC2CONbits.OC32 = 0; //OC2R<15:0> およびOC2RS<15:0> を使って16 ビットタイマ源と比較する OC2CONbits.OCTSEL = 0; // Timer2 をこの出力コンペア モジュールのクロック源として使う OC2CONbits.OCM = 5; // OC2 をPWM モードにし、フォルトピンを無効にする _HANDLES[_IDX_OUTPUT_COMPARE_2_VECTOR] = change;// OC2割り込み関数を登録 IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag IEC0SET = _IEC0_OC2IE_MASK; // Enable OC2 interrupt IPC2bits.OC2IP = 7;// Set OC1 interrupt priority to 7 IPC2bits.OC2IS = 3;// Set Subpriority to 3, maximum //OC2R = 0;//出力コンペア1 コンペアレジスタの初期値 //OC2RS = 905;//出力コンペア1 セカンダリコンペアレジスタ(デューティー比設定用用 ) T2CONbits.ON = 1; // Timer2の動作スタート OC2CONbits.ON =1; // OC2の動作スタート PORTBINV = 0x8000;// LED1を反転 unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得 unsigned long count_end = count_now + 200000000;//の追加が10秒後のカウント値(10/50e-9=200000000) change_count = 0; while(count_now < count_end){ // 10秒間の実行 count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得 } PORTBINV = 0x8000;// LED1を反転 OC2RS=0; OC2CONbits.ON =0; // OC2の動作OFF _send_decimal(change_count,10); _send_string(" <==change_count\r\n"); }pwd_datasが指し示す文字列の1byteの「下位4ビットと上位4ビット」で2つデータ指定し、それぞれのデータを(NN=6)回使うパルス変調です。
import math import matplotlib.pyplot as plt NREP=6 # 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数 pwd_base=[2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905] pwd_datas=[0x40, 0xb8, 0xaf, 0x26,] # 4bitで上記テーブル内パルス幅を指定する(4byteなので、8個のデータ) pwd_idx=0; # 上記アクセス用添え字 count_out=0; y=[] while pwd_idx < len(pwd_datas): if(count_out == 0): # 下位4ビットの周期へ idx_base = pwd_datas[pwd_idx] & 0x0f elif(count_out == NREP): # 上位4ビットの周期へ idx_base = (pwd_datas[pwd_idx] >> 4) & 0x0f y.append( pwd_base[idx_base]) if(count_out == (NREP+NREP)):# 上位4ビットが終わって次の要素周期へ pwd_idx+=1 count_out=-1 count_out+=1 plt.plot(y) plt.show() |
2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905 | |
12, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 805 |
# wav_to_UME.py 『wavファイルからパルス変調用ファイルを作成する』
import wave
# 参考https://docs.python.org/ja/3/library/wave.html
import numpy as np
import matplotlib.pyplot as plt
WAV_FILE_PATH="doremi.wav"# 変換対象のこの音声が記憶されるサウンドファイル
def getframedatas_by_wav(wav_file_path:str): # wavファイルからフレームレートと、16bitPCMバイナリーを取得
print(f"{wav_file_path}サウンドファイルから、再生用のバイト列を取得")
wavefile = wave.open(wav_file_path, "r")
framerate = wavefile.getframerate() # フレームレート(1秒間のデータ出力数)
print(f"フレームレート:{framerate}Hz、サンプリング周期:{1/framerate*1e6:6.2f}u秒")
nchannel=wavefile.getnchannels()
print("チャンネル数:", nchannel)
sampwidth=wavefile.getsampwidth()
print("サンプル幅数:", sampwidth)
if sampwidth != 2:
print(f"サンプル幅数が2(PCM 16bit)以外に対応していません")
exit()
nframes = wavefile.getnframes() # 全フレーム内データ数
print(f"フレームの数:{nframes}, 時間:{nframes/framerate}秒")
buffer = wavefile.readframes(nframes) # 全フレームを読み取る
wavefile.close() # ファイルを閉じる
if nchannel == 2: # 2チャンネルであれば、左チャンネルだけ抽出
ba=bytearray()
for i,c in enumerate(list(buffer)):
if i%4==0: ba.append(c)# 左チャンネル抽出(0,1)、右なら(3,4)
if i%4==1: ba.append(c)
buffer = bytes(ba)
return framerate, buffer
framerate, buffer=getframedatas_by_wav(WAV_FILE_PATH) # wavファイルからフレームレートと、16bitPCMバイナリーを取得
print(f"オリジナルのサウンドの{len(buffer)/2/framerate} 秒を再生する。")
import pyaudio # サウンド再生用
audio = pyaudio.PyAudio()
format=audio.get_format_from_width(width=2) # ストリームを読み書きするときのデータ型
stream = audio.open(format=format,channels=1,rate=framerate,output=True)
stream.write(buffer) # オリジナルのサウンドの音を確認
print(f"サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換")
int16_np=np.frombuffer(buffer, dtype="int16")
uint16_np=int16_np+(2<<14) # int16をunsigned int 16へ
#plt.plot(uint16_np) # オリジナルのサウンド(uint16)の視覚化
#plt.show()
minV=min(uint16_np)
value_width=max(uint16_np)-minV
print(f"最小値:{minV}, 値の幅:{value_width}")
print(f"データ数:{uint16_np.shape[0]}")
NREP=6 # 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)
count_out=0 # NREPの回数カウント用で、この回数分のデータ
nrep_list =[]
prev_val=0
print(f"サウンドデータ(16bitPCMバイナリー)を4bitデータに変換し、{NREP}個のサンプル数を1つまとめる")
datas_4bit=[] # 4bitに変換したデータの記憶用リスト
for i in range(uint16_np.shape[0]):
val= uint16_np[i]
nrep_list.append(val)
count_out+=1
if count_out == NREP:
val=nrep_list[0]
#val=sum(nrep_list)/len(nrep_list) # ここで、データの変化に対応した特殊抽出の制御検討が可能
#if val > prev_val : val = max(nrep_list)
#elif val < prev_val : val = min(nrep_list)
#prev_val=val
idx_v= int( (val-minV)/value_width*16 )
idx_v = 15 if idx_v >= 16 else idx_v
datas_4bit.append(idx_v)
count_out=0
nrep_list =[]
print(f"最小値:{min(datas_4bit)}, 最大値:{max(datas_4bit)}")
print(f"データ数:{uint16_np.shape[0]}から{len(datas_4bit)}個になった。")
plt.plot(datas_4bit)
plt.show()
# datas_4bitの4bitデータリストを内容を4096倍してバイナリ化し、その音を再生
buffer2=(np.array( [[x<<12]*NREP for x in datas_4bit] ).flatten()-(2<<14)).astype(np.int16).tobytes()
stream.write(buffer2) # 変換した音を確認
print("4ビット要素の2つを、1バイトにまとめて、リストにする。")
bin_data_array=[]
for n,bit4 in enumerate(datas_4bit): # (249600バイト以下にしないとリンクエラーの可能性あり)
if n % 2 == 0:
v = bit4
else:
v |= bit4<<4
bin_data_array.append(v)
#print(f" '\\x{v:02X}',", end="")
print(f"生成したデータ:{len(bin_data_array)}byte")
num_maxdata=2950 # UMEHOSHI ITAにおいて3000の配列要素数のデータはビルドで失敗した。
count_byte=0
print(f"C言語用の配列初期化データを、テキストファイルとして出力(最大で{num_maxdata}バイトの出力制限)")
with open("bin_data_array.txt", "w") as f: # C言語配列用パルス変調のデータを書き込み
for n,v in enumerate(bin_data_array):
if n > 0 and n % 10 == 0 :
f.write("\n")
if count_byte >= num_maxdata: break
s=f" '\\x{v:02X}',"
#print(s, end="")
f.write(s)
count_byte += 1
starting_address=0x9D020000 #領域EEPPOMデータ設定開始アドレス # RAM領域データ設定開始アドレスであれば(0x80009000)
setting_limit_address=0x9D03E400 # 設定限界アドレス # RAM領域データ設定開始アドレスであれば(0x80009000+0x0F00)
path=r"C:\Microchip\umehoshiEdit\samples\_20240305\6_timer\data.bin.hex"
print(f"starting_address:{setting_limit_address:016X}で「UME専用Hexファイル:{path}」を出力")
def data_to_ume_hex_faile(path, datas_list, starting_address, limit_address):
'datas_listが8bitデータのリストのファイル化で、starting_addressの番地から'
'UMEHOSHI ITA用の「S」から始まる設定データ群を「UME専用Hexファイル」として出力'
idx=0
address16 = starting_address
with open(path, "w") as f: # UMEHOSHI C言語配列用パルス変調のデータを書き込み
while idx < len(datas_list) and address16 < limit_address:
nn = 16
if idx+nn >= len(datas_list):nn = len(datas_list)-idx
if address16+nn >= limit_address:nn = limit_address-address16
s=f"S{nn:02X}{address16:08X}00"
for n in range(nn):
s+=f"{datas_list[idx+n]:02X}"
checksum=sum([ord(c) for c in s])
s+=f"{((0x100-checksum)) & 0x0ff:02X}"
#print(s)
f.write(f"{s}\n")
idx += nn
address16+=nn
return address16
last_address=data_to_ume_hex_faile(path, bin_data_array, starting_address, setting_limit_address)
print(f"last_address:{last_address:016X}, {last_address-starting_address}byteのデータ")
上記の実行例です。
doremi.wavサウンドファイルから、再生用のバイト列を取得 フレームレート:44100Hz、サンプリング周期: 22.68u秒 チャンネル数: 2 サンプル幅数: 2 フレームの数:132372, 時間:3.0016326530612245秒 オリジナルのサウンドの3.0016326530612245 秒を再生する。 サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換 最小値:8029, 値の幅:50405 データ数:132372 サウンドデータ(16bitPCMバイナリー)を4bitデータに変換し、6個のサンプル数を1つまとめる 最小値:0, 最大値:15 データ数:132372から22062個になった。 4ビット要素の2つを、1バイトにまとめて、リストにする。 生成したデータ:11031byte C言語用の配列初期化データを、テキストファイルとして出力(最大で2950バイトの出力制限) starting_address:000000009D03E400で「UME専用Hexファイル:data.bin.hex」を出力 last_address:000000009D022B17, 11031byteのデータの部分コード実行で、bin_data_array.txtのファイルが生成されます。
'\x77', '\x77', '\x77', '\x77', '\x77', '\x77', '\x77', '\x77', '\x77', '\x87', '\x67', '\x86', '\x68', '\x76', '\x98', '\x9A', '\x88', '\x77', '\x77', '\x87',このようなデータが、この例では11031個生成されるのですが、実際には2950個だけ制限されて出力されます。
S109D020000007777777777777777778767866876989A5F S109D02001000887777877878988956865754767767776Dこれは0x9D020000番地から始まる[UMEHOSHI ITA]へのメモリ設定コードなので、「umehoshiEditツール」など使って転送することで記憶域を設定する手法です。
doremi.wavサウンドファイルから、再生用のバイト列を取得 フレームレート:44100Hz、サンプリング周期: 22.68u秒 チャンネル数: 2 サンプル幅数: 2 フレームの数:132372, 時間:3.0016326530612245秒 オリジナルのサウンドの3.0016326530612245 秒を再生する。 サウンドデータ(16bitPCMバイナリー)からpulse_modulationで使う値(符号なし16bitのnumpy配列)に変換 最小値:8029, 値の幅:50405 データ数:132372 サウンドデータ(16bitPCMバイナリー)を4bitデータに変換し、3個のサンプル数を1つまとめる 最小値:0, 最大値:15 データ数:132372から44124個になった。 4ビット要素の2つを、1バイトにまとめて、リストにする。 生成したデータ:22062byte C言語用の配列初期化データを、テキストファイルとして出力(最大で2950バイトの出力制限) starting_address:000000009D03E400で「UME専用Hexファイル:C:\Microchip\umehoshiEdit\samples\_20240305\6_timer\data.bin.hex」を出力 last_address:000000009D02562E, 22062byteのデータ
以下が0x9D020000〜0x9D03F000の範囲と、 0x9D03F800〜0x9D03FC00の範囲を消去するコードです。 この範囲が「ウメ・エディットプログラム」において、 データやプログラムコードを自由に配置できるEEPROM領域です。 下記が、erase.cのソースで、 右が、実行用「UME専用Hexコマンド」です。 これをUSBを介して転送することで、メモリに書き込めます。 #include <xc.h> #include <stdlib.h> #include "common.h" __attribute__((address( 0x80005000 ))) void main(void); void main() { unsigned int clradr[] ={ // for x in range(0x9D020000, 0x9D040000, 0x400): print(f"0x{x:8X},") 0x9D020000,0x9D020400,0x9D020800,0x9D020C00,0x9D021000,0x9D021400, 0x9D021800,0x9D021C00,0x9D022000,0x9D022400,0x9D022800,0x9D022C00, 0x9D023000,0x9D023400,0x9D023800,0x9D023C00,0x9D024000,0x9D024400, 0x9D024800,0x9D024C00,0x9D025000,0x9D025400,0x9D025800,0x9D025C00, 0x9D026000,0x9D026400,0x9D026800,0x9D026C00,0x9D027000,0x9D027400, 0x9D027800,0x9D027C00,0x9D028000,0x9D028400,0x9D028800,0x9D028C00, 0x9D029000,0x9D029400,0x9D029800,0x9D029C00,0x9D02A000,0x9D02A400, 0x9D02A800,0x9D02AC00,0x9D02B000,0x9D02B400,0x9D02B800,0x9D02BC00, 0x9D02C000,0x9D02C400,0x9D02C800,0x9D02CC00,0x9D02D000,0x9D02D400, 0x9D02D800,0x9D02DC00,0x9D02E000,0x9D02E400,0x9D02E800,0x9D02EC00, 0x9D02F000,0x9D02F400,0x9D02F800,0x9D02FC00,0x9D030000,0x9D030400, 0x9D030800,0x9D030C00,0x9D031000,0x9D031400,0x9D031800,0x9D031C00, 0x9D032000,0x9D032400,0x9D032800,0x9D032C00,0x9D033000,0x9D033400, 0x9D033800,0x9D033C00,0x9D034000,0x9D034400,0x9D034800,0x9D034C00, 0x9D035000,0x9D035400,0x9D035800,0x9D035C00,0x9D036000,0x9D036400, 0x9D036800,0x9D036C00,0x9D037000,0x9D037400,0x9D037800,0x9D037C00, 0x9D038000,0x9D038400,0x9D038800,0x9D038C00,0x9D039000,0x9D039400, 0x9D039800,0x9D039C00,0x9D03A000,0x9D03A400,0x9D03A800,0x9D03AC00, 0x9D03B000,0x9D03B400,0x9D03B800,0x9D03BC00,0x9D03C000,0x9D03C400, 0x9D03C800,0x9D03CC00,0x9D03D000,0x9D03D400,0x9D03D800,0x9D03DC00, 0x9D03E000,0x9D03E400, //0x9D03F000,この部分は__vector_dispatchで消去してはいけない。 //0x9D03F400,この部分は__vector_dispatchで消去してはいけない。 0x9D03F800, //0x9D03FC00, パワーオン起動用などの起動ポインタなどがある。 }; PORTBCLR = 0x8000;// LED1を消去 int i; for(i=0; i < sizeof(clradr)/sizeof(unsigned int); i++){ _nvm_erase_page(clradr[i]); } PORTBSET = 0x8000;// LED1を点灯 _send_string("END program\r\n"); } |
S108000500000F0FDBD270C02BFAF0802BEAF21F0A00307 S1080005010000080023C1400C427E0504324F0010224D3 S1080005020002128600021304000B414000C0000000012 S10800050300088BF023C00800334246143AC1000C0AF87 S108000504000201400080000000000A0023CF0404234FC S1080005050000000438C1000C28F801002001000C427D8 S108000506000211082000400428C2120400009F86000F0 S108000507000000000001000C28F010042241000C2AFDA S1080005080001000C28F7C00422CEFFF4014000000008E S10800050900088BF023C00800334286143AC00A0023C8F S10800050A000A44142340000428C0080033CD0506424BC S10800050B00009F840000000000021E8C0030C02BF8F91 S10800050C0000802BE8F1002BD270800E00300000000AB S10800050D000454E442070726F6772616D0D0A00000097 S10800050E0000000029D0004029D0008029D000C029DAF S10800050F0000010029D0014029D0018029D001C029DAA S1080005100000020029D0024029D0028029D002C029DBB S1080005110000030029D0034029D0038029D003C029DB6 S1080005120000040029D0044029D0048029D004C029DB1 S1080005130000050029D0054029D0058029D005C029DAC S1080005140000060029D0064029D0068029D006C029DA7 S1080005150000070029D0074029D0078029D007C029DA2 S1080005160000080029D0084029D0088029D008C029D9D S1080005170000090029D0094029D0098029D009C029D98 S10800051800000A0029D00A4029D00A8029D00AC029D77 S10800051900000B0029D00B4029D00B8029D00BC029D72 S10800051A00000C0029D00C4029D00C8029D00CC029D66 S10800051B00000D0029D00D4029D00D8029D00DC029D61 S10800051C00000E0029D00E4029D00E8029D00EC029D5C S10800051D00000F0029D00F4029D00F8029D00FC029D57 S10800051E0000000039D0004039D0008039D000C039DAA S10800051F0000010039D0014039D0018039D001C039DA5 S1080005200000020039D0024039D0028039D002C039DB6 S1080005210000030039D0034039D0038039D003C039DB1 S1080005220000040039D0044039D0048039D004C039DAC S1080005230000050039D0054039D0058039D005C039DA7 S1080005240000060039D0064039D0068039D006C039DA2 S1080005250000070039D0074039D0078039D007C039D9D S1080005260000080039D0084039D0088039D008C039D98 S1080005270000090039D0094039D0098039D009C039D93 S10800052800000A0039D00A4039D00A8039D00AC039D72 S10800052900000B0039D00B4039D00B8039D00BC039D6D S10800052A00000C0039D00C4039D00C8039D00CC039D61 S10800052B00000D0039D00D4039D00D8039D00DC039D5C S10800052C00000E0039D00E4039D00F8039D00FC039D55 S10800052D000211080005600C0100C00CA2C4D004015B0 S10800052E0002618A400030063302338040029006010ED S10800052F0000300E7300600E0102330C7000000A398C2 S1080005300000300A3882128A700000083B821208700D7 S1080005310000F00C3302338C3001100E01021306000E2 S1080005320002138E5000000A3980300A3880400A898B8 S1080005330000700A8880800A9980B00A9880C00AA9874 S1080005340000F00AA88000083AC040088AC080089AC6C S1080005350001000A52410008424F2FFA714FCFF8AAC34 S1080005360000300C3302338C3002A00E01021306000DF S1080005370002138E5000000A3980300A3880400A524C1 S10800053800004008424FBFFA714FCFF83AC2100001045 S108000539000000000000500E0102330C7000000A398F1 S10800053A0002128A700000083B8212087000F00C330BE S10800053B0002338C3000D00E010213060002138E500C6 S10800053C0000000A38C0400A88C0800A98C0C00AA8C55 S10800053D000000083AC040088AC080089AC1000A52487 S10800053E00010008424F6FFA714FCFF8AAC0300C33021 S10800053F0002338C3000700E010213060002138E500CF S1080005400000000A38C0400A52404008424FCFFA71484 S108000541000FCFF83AC0600C0102138C5000000A39079 S1080005420000100A52401008424FCFFA714FFFF83A043 S0880005430000800E0030000000031 |
オリジナルファイル(doremi.wav)44100Hz,16BitPCM (264.744Kbyteの再生音) |
「44100Hz÷6=約7350Hz、分解能4ビット」(11.031Kbyteの再生音) PCでオリジナルファイル16bitを4bitにして、オリジナルのデータ6個から1個を抽出した目標となる音 目標となる音(音質を落としたデータでPC上のPythonで鳴らした音) |
UMEHOSHI ITA で鳴らした音を録音した再生音(11.031Kbyteの再生音) 「44100Hz÷6=約7350Hz、分解能4ビット」相当の音を、PWDのベースクロックを40MHzで鳴らした音 株式会社村田製作所の(PKM13EPYH4000-A0)圧電サンダーを使った場合、 音質を落としてなるべく音量を上げる検討の結果、使う場合に許容しがたいのですが、ここで使った設定が最も良いと分かりました。 |
UMEHOSHI ITA で鳴らした音を録音した再生音(11.031Kbyteの再生音) 「44100Hz÷6=約7350Hz、分解能4ビット」相当の音を、PWDのベースクロックを5MHzで鳴らした音 上記の40MHzの音と聞き比べて、音質や音量の違いが分からなかった。 その結果より、将来的にデータを細かく制御できる可能性から、上記の40MHzのスイッチング周期の方が良いと判断できた。 |
UMEHOSHI ITA で鳴らした音を録音した再生音 (上記よりサンプリング周期を2分の1:データサイスを2倍にして精度を上げた22.062Kbyteの再生音) 「44100Hz÷3=約14700Hz、分解能4ビット」相当の音を、PWDのベースクロックを40MHzで鳴らした音 上の7350Hzの音と比べて、音のメリハリが無くなっって、聴きにくくなった。(音量が小さくなったが、雑音も少し減ったように感じる) |
UMEHOSHI ITA で鳴らした音を録音した再生音(上記よりサンプリング周期を3分の1:データサイスを3倍にして精度を上げた66.186Kbyteの再生音) 「約44100Hz、分解能4ビット」相当の音を、PWDのベースクロックを40MHzで鳴らした音 上の14700Hzの音と比べて、より音のメリハリが無くなっって、聴きにくくなった。(音量が小さくなったが、雑音も少し減ったように感じる) |
#include <xc.h>
#include "common.h"
int change_count = 0; // change()の割り込み回数計数用
#define NREP 6 // 1つ(4bit)のデータを、約(1/4410)秒周期で繰り返し使う回数(水平分解能指定パラメタ)
short int pwd_base[]={2, 62, 122, 182, 242, 303, 363, 423, 483, 543, 604, 664, 724, 784, 844, 905};
int idx_base=0;
char *pwd_datas=(char *)(0x9D020000);
int pwd_idx=0; // 上記アクセス用添え字
int count_out=0; // pwd_datasの各要素の上位、下位それぞれのデータを(NREP=6)回使い、(NREP+NREP)までのカウンタ
void change() // OC2割り込み関数 (PWMのデューティ サイクルの変更
{
change_count++;//割り込み回数計測用 (約44100Hzの周期でカウント)
if(count_out == 0){ // 下位4ビットの周期へ
idx_base = pwd_datas[pwd_idx] & 0x0f;
OC2RS = pwd_base[idx_base];
}else if(count_out == NREP){ // 上位4ビットの周期へ
idx_base = (pwd_datas[pwd_idx] >> 4) & 0x0f;
OC2RS = pwd_base[idx_base];
}
if(++count_out == (NREP+NREP)){ //上位4ビットが終わって次の要素周期へ
count_out=0;
pwd_idx++;
if( pwd_idx >= 11031 ) {//周期要素を配列を使い終わった
pwd_idx = 0;
// OC2CONbits.ON =0; // OC2の動作OFF
}
}
IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag
}
__attribute__((address( 0x80005000 ))) void main(void);
void main()
{
ODCBSET = 0x0020; // スイッチング対象のPORT B5出力端子をオープンドレインに設定
CNPUBCLR = 0x0020; // スイッチング対象のPORT B5出力端子をpull-up不使用に設定
CNPDBCLR = 0x0020; // スイッチング対象のPORT B5出力端子をpull-down不使用に設定
// Timer2の初期化------------------
T2CON =0x00000000;//typeB,16bit, [1:1]プリスケール これで(1/40e6)の周期で、TMR2をカウントアップ
T2CONbits.TCKPS = 0;// 1:1プリスケール値
TMR2=0x0000; //16bitタイマの設初期値(上記設定周期でカウントして、PR2と一致するとクリアして割り込み)
PR2=906; //16bitタイマの設定値(周期設定用 =int((1/44100)/(1/40e6)-1)
IEC0CLR = 0x00000200;//T2IE Timer2 desable(割込み不許可)
// Output Compare module 2の初期化-(割り込み指定を含む)-----------------
OC2CON = 0x0000;// Turn off OC2 while doing setup.
RPB5Rbits.RPB5R = 0x5; // RB5をOC2の出力にする。
OC2CONbits.SIDL = 0; // アイドルモード中も動作を継続する
OC2CONbits.OC32 = 0; //OC2R<15:0> およびOC2RS<15:0> を使って16 ビットタイマ源と比較する
OC2CONbits.OCTSEL = 0; // Timer2 をこの出力コンペア モジュールのクロック源として使う
OC2CONbits.OCM = 5; // OC2 をPWM モードにし、フォルトピンを無効にする
_HANDLES[_IDX_OUTPUT_COMPARE_2_VECTOR] = change;// OC2割り込み関数を登録
IFS0CLR = _IFS0_OC2IF_MASK; // Clear the OC2 interrupt flag
IEC0SET = _IEC0_OC2IE_MASK; // Enable OC2 interrupt
IPC2bits.OC2IP = 7;// Set OC1 interrupt priority to 7
IPC2bits.OC2IS = 3;// Set Subpriority to 3, maximum
//OC2R = 0;//出力コンペア1 コンペアレジスタの初期値
//OC2RS = 905;//出力コンペア1 セカンダリコンペアレジスタ(デューティー比設定用で上記設定の最大値 )
T2CONbits.ON = 1; // Timer2の動作スタート
OC2CONbits.ON =1; // OC2の動作スタート
PORTBINV = 0x8000;// LED1を反転
unsigned long count_now = _CP0_GET_COUNT();//(1/40e6)*2 =50ナノ秒でカウントするコアタイマーの値を取得
unsigned long count_end = count_now + 200000000;//の追加が10秒後のカウント値(10/50e-9=200000000)
change_count = 0;
while(count_now < count_end){ // 10秒間の実行
count_now = _CP0_GET_COUNT();//50ナノでカウントするコアタイマーの値を取得
}
PORTBINV = 0x8000;// LED1を反転
OC2RS=0;
OC2CONbits.ON =0; // OC2の動作OFF
_send_decimal(change_count,10);
_send_string(" <==change_count\r\n");
count_out = 0;//ウメ・エディットプログラムのグローバル変数はリセットで初期化しないので、連続実行のために初期化
}
[UMEHOSHI ITA]のPortB5のデフォルトは、オープンドレインでなくトーテムポール出力型です。ODCBSET = 0x0020; // スイッチング対象のPORT B5出力端子をオープンドレインに設定 CNPUBCLR = 0x0020; // スイッチング対象のPORT B5出力端子をpull-up不使用に設定 CNPDBCLR = 0x0020; // スイッチング対象のPORT B5出力端子をpull-down不使用に設定 ODCBCLR = 0x0020; // スイッチング対象のPORT B5出力端子をオープンドレイン不使用に設定 CNPDBSET = 0x0020; // スイッチング対象のPORT B5出力端子をpull-downに設定 CNPUBSET = 0x0020; // スイッチング対象のPORT B5出力端子をpull-upに設定(参考: https://ww1.microchip.com/downloads/jp/DeviceDoc/61120E_JP.pdf#page=3、
SYSKEY = 0xAA996655; // キー1を書き込む SYSKEY = 0x556699AA; // キー2を書き込む
CFGCONbits.IOLOCK=0; // ロック解除 DEVCFG3bits.IOL1WAY = 1;// これ以降で、FGCONbits.IOLOCK=1にすると、変更できなくなる。 // RPnR および [pin name]R レジスタへの書き込み FGCONbits.IOLOCK=1;