PWMを用いた8kHz8bitサンプルの音声信号の出力
Dec2008
coskx
1.はじめに
8kHz8bitサンプルのリニアPCM音声データを出力するには,通常は8kHzのタイマ割り込みでDACより出力すればよいが,PWM信号としてD級動作するアンプで動作させたい場合はPWM信号で出力するのがよい。
考え方としては,H8/3048の16MHzクロックを用いて,4分周の4MHzでITUを動作させ,500カウントで割り込みをかけると8kHの割り込みになるが,この時同じITUでPWMを発生させる。PWM信号のH区間の長さは,音響信号が8bitであることに由来して0から255の長さを持つことになるのだが,定数を加えて0+α〜255+αの長さを持ってもよい。
ITUでPWM信号を作るにはPWMモードとし,GRAに周期クロック数である500をセットし,GRBにH区間終了の値をセットすればよい。
たとえば GRAに500(正確には499),GRBに200がセットされている時,カウンタTCNTは0からカウントアップし,500に達するとカウンタTCNTはゼロに戻り,再びカウントを開始する。この時TIOCAはTCNT=0の時立ち上がり(H区間開始),TCNT=200の時立ち下がる(H区間終了)
TCNT=0
TCNT=GRB TCNT=GRA(ここでTCNT=0にクリアされる)
↓ ↓ ↓
┌────┐ ┌──
│ │ │ PWM信号(ITOCA)
──┘ └──────┘
↑
割り込み(GRBの書き換え)
ここで大事なことは,割り込み作業中でGRBの書き換えは,TCNT=GRBになる前に行われていなければならないことである。
これを実現するため,GRBには音響信号のサンプル値はそのまま使わずに,音響信号サンプル値に200加算した値をセットする。
そうすると,0から255の音声データについては,200から455の値をGRBにセットするようにすればよい。
(最大値が500を超えないようになっているからこれでOK)
サンプルプログラムのダウンロード
DownLoad |
2.テストプログラム1
のこぎり波状の値を割り込み関数内で生成してPWM信号を発生するプログラムを次に示す。
音を出してみると,256サンプル周期なので8000/256=31.25Hzののこぎり波音となる。
(実際聞いてみるとブツブツブツ・・・のように聞こえる)
割り込み関数が正常に動作していることを視覚的に確かめるために,LED0の点滅を行うようにしている。
ITUはch3を使用している。
のこぎり波生成 normalPWM.c |
/********************************************************** 時間割り込みによって8KHzにてPWM信号を出力 AKI-H8/3048の場合はTIOCA3はCN1-16になる
main() #pragma asm #define clearTimer3Flag() (ITU3.TSR.BIT.IMFA=0) /*この先は割り込み動作検証のためのLED点滅*/ |
3.テストプログラム2
windowsのリニアwavファイル(モノラル8kHz8bit)を変換してテキストデータファイルをつくり,cの定数配列データにして,
これをPWM信号として出力するプログラムを作成する。音声信号は8kHzでPWM出力されるが,全音声データを出力した後は,
1秒間何もせず,再び音声信号が出力され,,また1秒の休みを行うというように循環動作を行う。
「よい天気・・・」という音声が聞こえたらOK。
8kHzのピーという音が聞こえるので,4kHzカットオフのローパスフィルタを通すとよい。
サンプル音響信号の出力 normalPWMvoice.c /********************************************************** 時間割り込みによって8KHzにてPWM信号を出力 コンペアマッチAでカウンタクリア AKI-H8/3048の場合はTIOCA3はCN1-16になる #define Padd 200 const int numberDatas=sizeof(samplewave)/sizeof(char); void initTimer3Int(void) void msecwait(int
msec) main() #pragma asm #define clearTimer3Flag() (ITU3.TSR.BIT.IMFA=0) /*この先は割り込み動作検証のためのLED点滅*/
「8kHzサンプリングのリニアPCM音声データ」のPWMによる出力
8kHzのPWM周波数をもちいるので8kHzのベース音はざわりである
カットオフ=4kHzのアナログローパスフィルタが必要
ITU ch3 を用いる
クロックは4MHzに設定する
TCNT=0; TIOCA3立ち上がり
TCNT=GRB:
TIOCA3立ち下がり
TCNT=GRA:
TCNT=0
4000000/8000=500 →
4MHzクロック500サンプルで8.00KHzサンプル音 ○
500クロック中最初の200サンプルのHを置く これは割り込み処理のための時間稼ぎ
8ビットの音声データは0から255の値を持つので,
音声データ
0 200のH区間+300のL区間
音声データ255 455のH区間+
45のL区間
となる
割り込み周波数は8kHzになる
**********************************************************/
#include
<3048f.h>
#include "h8_3048.h"
/*led関係しか使っていない*/
#include "wavsample.h" /*音響信号データの配列*/
#define PWMPERIOD 500
volatile int
ptr;
{
ITU3.TCR.BIT.CCLR=1; /*GRAのコンペアマッチでTCNTをクリア
これはなくてもよいはず*/
ITU3.TCR.BIT.CKEG=0;
/*立ち上がりエッジでカウント*/
ITU3.TCR.BIT.TPSC=2;
/*内部クロックφ/4でカウント 4MHz*/
ITU3.GRA=PWMPERIOD-1;
/*割り込みの周期を4MHzで500カウント:8kHz(割り込み周波数)に指定*/
ITU3.GRB=Padd;
/*最少PWMデューティ4MHzでPに指定*/
ITU3.TIER.BIT.IMIEA=1;
/*TCNT=GRAとなったときの割り込み要求を許可*/
ITU3.TIER.BIT.OVIE=0;
/*オーバー・アンダーフロー発生時の割り込みを禁止*/
ITU3.TIER.BIT.IMIEB=0; /*TCNT=GRBとなったときの割り込みを禁止*/
ITU.TMDR.BIT.PWM3=1;
/*ITUのPWMに使うchをPWMモードに設定*/
ITU3.TIOR.BIT.IOB=0;
/*TIOCB3にPWM信号を出力しないように設定*/
/*TIOCA3にPWM信号が出てくる*/
ITU.TSTR.BIT.STR3=1;
/*カウントスタート(PWM信号が出力される)*/
}
/*msec間なにもしない時間稼ぎ関数*/
{
int
i,j;
for (i=0;i<msec;i++)
{
for
(j=0;j<2646;j++);
/*2646は実測によって求めた値*/
}
}
{
initLed();
initTimer3Int(); /*PWM出力初期化 TIOCA3*にPWM信号が出てくる*/
E_INT();
/*CPU割り込み許可*/
while(1)
{
ptr=0;
turnOnLed(1);
while
(ptr<numberDatas);
turnOffLed(1);
msecwait(1000);
}
}
.SECTION MYVEC,
DATA, LOCATE=H'000090
.ORG H'000090
;IMIA3
.DATA.L
_TimerIntFunc
.SECTION
P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm
#pragma interrupt
(TimerIntFunc)
void TimerIntFunc()
/*タイマ割り込みルーチン*/
{
static int
counter=0;
clearTimer3Flag(); /*タイマ割り込みフラグのクリア
忘れないこと*/
/*ここで ITU3.GRB にPWM値+Paddを与える*/
if
(ptr<numberDatas) {
ITU3.GRB=(unsigned int)((unsigned char)samplewave[ptr])+(unsigned
int)Padd;
ptr++;
}
counter++;
if (counter&0x1000)
turnOnLed(0);
else
turnOffLed(0);
}
上記プログラムにインクルードされているリニアPCM音声データファイルは次のようになっている。
このファイルはwindowsのリニアwavファイル(モノラル8kHz8bit)を変換して作成されている。
(ダウンロードしたサンプルプログラム中に変換ツールが入っている)
注意 H8/3048のROMに書き込む場合はサイズがROMサイズを超えないようにすること
ROMサイズは128kbyteだから元の音声データは16秒程度が限界のはず
音響信号データファイル wavsample.h |
/*音響信号の配列 const宣言になっているのでROM上に定数として置かれる*/ |
4.テストプログラム3
windowsのリニアwavファイル(モノラル8kHz8bit)を変換してテキストデータファイルをつくり,cの定数配列データにして,
これをPWM信号として出力するプログラムを作成する。音声信号は16kHzでPWM出力されるが,全音声データを出力した後は,
1秒間何もせず,再び音声信号が出力され,,また1秒の休みを行うというように循環動作を行う。
16kHzのピーという音が聞こえるが,あまり気にならないと思われるが,気になる場合は8kHzカットオフのローパスフィルタ
を用いればよい。
8kHzサンプリングデータを16kHzで出力するので,ちょっと工夫が必要。コードを読むこと。
サンプル音響信号の出力 normalPWMvoice16k.c |
/********************************************************** 時間割り込みによって16KHzにてPWM信号を出力 AKI-H8/3048の場合はTIOCA3はCN1-16になる #define Padd 200 const unsigned int
numberDatas=sizeof(samplewave)/sizeof(char); void
initTimer3Int(void) unsigned int numberDatas2; void msecwait(int
msec) main() #pragma asm #define clearTimer3Flag()
(ITU3.TSR.BIT.IMFA=0)
/*この先は割り込み動作検証のためのLED点滅*/ |
上記プログラムにインクルードされているリニアPCM音声データファイルは「3.テストプログラム2」で用いたものと同じである。