PWM を用いた8kHz8bitサンプルの音声信号の出力
Dec2008
coskx
1.はじめに
8kHz8bitサンプルのリニアPCM音声データを出力するには,通常は8kHzのタイマ
割り込みでDACより出力すればよいが,PWM信号としてD級動作するアンプで動作させたい場合はPWM信号で出力するのがよい。
考え方としては,H8/3048foneの25MHzクロックを用いて,8分周の3.125MHzでITUを動作させ,391カウント
で割り込みをかけると8kHの割り込みになるが,この時同じITUでPWMを発生させる。PWM信号のH区間の長さは,音響信号が
8bitであることに由来して0から255の長さを持つことになるのだが,定数を加えて0+α〜255+αの長さを持ってもよい。
ITUでPWM信号を作るにはPWMモードとし,GRAに周期クロック数である500をセットし,GRBにH区間終了の値をセットすれ
ばよい。
たとえば GRAに391(正確には390),GRBに200がセットされている時,カウン タTCNTは0からカウントアップし,391に達するとカウンタTCNTはゼロに戻り,再びカウントを開始する。この時TIOCAは TCNT=0の時立ち上がり(H区間開始),TCNT=200の時立ち下がる(H区間終了)
TCNT=0
TCNT=GRB TCNT=GRA(ここでTCNT=0にクリアされる)
↓ ↓ ↓
┌────┐ ┌──
│ │ │ PWM信号(ITOCA)
──┘ └──────┘
↑
割り込み(GRBの書き換え)
ここで大事なことは,割り込み作業中でGRBの書き換えは,TCNT=GRBになる前に行わ
れていなければならないことである。
これを実現するため,GRBには音響信号のサンプル値はそのまま使わずに,音響信号サンプル値に50加算した値をセットする。
そうすると,0から255の音声データについては,50から305の値をGRBにセットするようにすればよい。
(最大値が390を超えないようになっているからこれでOK)
サンプルプログラムのダウンロード
DownLoad |
2.テストプログラム1
のこぎり波状の値を割り込み関数内で生成してPWM信号を発生するプログラムを次に示す。
音を出してみると,256サンプル周期なので8000/256=31.25Hzののこぎり波音となる。
(実際聞いてみるとブツブツブツ・・・のように聞こえる)
割り込み関数が正常に動作していることを視覚的に確かめるために,LED0の点滅を行うようにしている。
ITUはch3を使用している。
のこぎり波生成 normalPWM.c |
/********************************************************** ITU ch3 を用いる AKI-H8/3048の場合はTIOCA3はCN1-16になる
void initTimer3Int(void) 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信号を出力 ITU ch3 を用いる AKI-H8/3048の場合はTIOCA3はCN1-16になる #define Padd 50 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のアナログローパスフィルタが必要
クロックは3.125MHz(φ/8)に設定する
コンペアマッチAでカウンタクリア
TCNT=0; TIOCA3立ち上がり
TCNT=GRB: TIOCA3立ち下がり
TCNT=GRA: TCNT=0
3125/8=390.625 →
3.125MHzクロック391サンプルで8.00KHzサンプル音 ○
391クロック中最初の50サンプルのHを置く これは割り込み処理のための時間稼ぎ
8ビットの音声データは0から255の値を持つので,
音声データ 0 50のH区間+341のL区間
音声データ255 305のH区間+ 86のL区間
となる
割り込み周波数は8kHzになる
**********************************************************/
#include <3048fone.h>
#include "h8_3048fone.h"
/*led関係しか使っていない*/
#include "wavsample.h" /*音響信号データの配列*/
#define PWMPERIOD 391
volatile int ptr;
{
ITU3.TCR.BIT.CCLR=1;
/*GRAのコンペアマッチでTCNTをクリア これはなくてもよいはず*/
ITU3.TCR.BIT.CKEG=0;
/*立ち上がりエッジでカウント*/
ITU3.TCR.BIT.TPSC=3;
/*内部クロックφ/8でカウント 3.125MHz*/
ITU3.GRA=PWMPERIOD-1;
/*割り込みの周期を3.125MHzで391カウント:8kHz(割り込み周波数)に指定*/
ITU3.GRB=Padd;
/*最少PWMデューティ3.125MHzで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信号として出力するプログラムを作成する。音声信号は32kHzでPWM出力されるが,全音声データを出力した後は,
1秒間何もせず,再び音声信号が出力され,,また1秒の休みを行うというように循環動作を行う。
32kHzの音が聞こえるはずだが,人間には聞こえない。
8kHzサンプリングデータを32kHzで出力するので,同じ値を4回出している。
サンプル音響信号の出力 normalPWMvoice32k.c |
/********************************************************** 時間割り込みによって32KHzにてPWM信号を出力 AKI-H8/3048の場合はTIOCA3はCN1-16になる #define Padd 50 const unsigned int
numberDatas=sizeof(samplewave)/sizeof(char); void initTimer3Int(void) void msecwait(int msec) main() #pragma asm #define clearTimer3Flag()
(ITU3.TSR.BIT.IMFA=0)
/*この先は割り込み動作検証のためのLED点滅*/ |
上記プログラムにインクルードされているリニアPCM音声データファイルは「3.テストプログ ラム2」で用いたものと同じである。