PICマイコン(PIC16F1827)のSPI通信で音声を再生する

組み込みエンジニア

こんにちは、ENGかぴです。

PIC16F1827はSPI(MSSP)通信機能を持っており音声合成ICであるATP3011(アクエスト)にコマンドを送信することで音声を再生させることができます。MCCを使ってSPI通信を実装し音声を再生させて動作確認しました。

音声合成ICはATP3011F4-PU(アクエスト製:秋月電子で購入)を使用しています。周辺回路で音声を増幅するためにD級アンプモジュールAE-PAM8012モジュール(秋月電子)を使用しています。

PIC16F1827で動作確認したことについてリンクをまとめています。

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

音声を再生する

ATP3011はローマ字で送信したシリアルデータを音声に変換する音声合成ICです。アクエスト社の音声合成ミドルウェアであるAquesTalkをArduino UNOなどで使用されているマイコンに搭載した製品です。

Arduino UNOのマイコン(ATMEGA328P)を使用しているためArduino UNOの基板のマイコンを置き換えることで簡単に音声出力が確認できるのが特徴です。詳細はアクエスト社のHPをご確認ください。

AQUEST-音声合成LSI「AquesTalk pico LSI」

ATP3011シリーズを使用する

ATP3011シリーズはマイコン内蔵の発振子で動作するため外部の発振子は必要ありません。内部クロックはRCクロックであるため電源電圧の変化や温度変化の影響を受けてしまいます。UART(シリアル通信)で音声を再生する場合は正常に動作しないこともあるので注意が必要です。

データシートにはクロックのボーレートに影響されにくいI2C/SPI通信を使用することが推奨されています。

別のシリーズとしてATP3012がありクリスタルなどの発振子で動作しますが、音声のサンプリングが滑らかになるため音声の質がよくなり電源電圧の変化や温度変化の影響を受けにくくなりますが発振子など部品点数が増えてしまうデメリットがあります。

2つのシリーズを比較するとATP3012シリーズの方が音声が聞き取りやすいと感じていますが、ATP3011でも音声が聞き取りにくいことはないので部品点数を減らしたいのならATP3011を選択し少しでも音声を明瞭にしたい場合はATP3012を選択するとよいと思います。

今回使用するピンは以下の通りです。

ピン番号機能内容
4,5SMOD0
SMOD1
動作モードを選択します。
SMOD0及びSMOD1をGNDに接続するとSPI通信(MODE0)が選択される。
12AOUT音声出力端子。D級アンプモジュールで信号を増幅して音声再生する。
スピーカの容量(インピーダンスが高い)によっては直付け可能
13/PLAY発音中にLOWになる。
音声再生待機中はHIGHになるためHIGHになるのを確認して再生スタートする。
17MOSISPIのマスターOUT、スレーブIN
18MISOSPIのマスターIN、スレーブOUT
19SCKSPIのシリアルクロック入力
ATP3011M6で使用するピンの機能まとめ

今回はSPIを使用しますがI2Cやシリアル通信でも音声再生することが可能です。ATP3011シリーズの内蔵クロックは使用環境の影響を受けやすいため、クロックに同期して動作するWire(I2C)やSPI通信がお勧めです。

よく使う音声記号

今回使用したアクセントや区切り記号についてまとめました。音声記号仕様はデータシートに記載されているものを一部抜粋してまとめています。

記号内容
.(0x2E)無音区間が入り、文の終わりを示す。
?(0x3F)無音区間が入り、分の終わりとなるが文末の声が高めになる。
;(0x3B)次のアクセントが高い音で始める。
/(0x2F)アクセントの区切りを指定する。
‘(0x27)アクセント記号で音の高さが「高→低」に変化する部分につける。
_(0x5F)母音のi,uが振動を伴わずに発音させる。
データシートの読み記号表に定義されている音のみ対応
ATP3011でよく使う音声記号まとめ(データシート抜粋)

音声記号は半角文字で指定します。文字の横の()内の値は記号に対応したアスキーコード(テキスト)を16進数で示したものです。文字列の例を以下に示します。CRはリターンキーを押すと入力できます。

よろしくおねがいします。:yorosiku;onegaisima_su. CR
元気ですか?:genki+desuka? CR
こんばんは。:konban’wa. CR

アクセント記号を付けない場合は棒読みになってしまいますが、下手にアクセント記号を付けると棒読みよりもイントネーションがおかしくなり不自然な音声になってしまいます。

音声記号やアクセント記号をまだ使いこなせていないため、音声がぎこちない感じになっていますが、うまく使えるようになると聞き取りやすい音声パターンが作れそうです。

すき間時間で資格をゲット【STUDYing(スタディング)】

SPIを実装する

PIC16F1827のI2C(MSSP)の設定はMCCを使って行っています。MCCの使い方については下記記事にまとめています。

MPLAB Code Configurator(MCC)の追加と使い方

今回はMCCを使用してMSSP1(SPI)及びTMR0の設定を行っています。

System Moduleの設定

最初に共通事項であるSystem Moduleの設定を行います。MPLAB X IDEを起動しMCCのアイコンをクリックしてMCCを有効にします。

System Moduleの設定(MCC)
System Moduleの設定(MCC)

内部クロックを使用するためINTOSCを選択しています。クロック周波数は任意でもよいですが4MHz_Hzを選択しています。他にも設定項目はありますがクロックの設定のみとしています。

MSSP1(MSSP1)を設定する

MSSPはMaster Synchronous Serial Port の略称です。PICマイコンのSPI通信はMSSP機能に含まれています。Device Resources内のPeripherals欄のMSSP1を選択して追加します。

MSSP1の設定(MCC)
MSSP1の設定(MCC)

MSSP1を選択しSerial ProtocolをSPIを選択するとModeやSPI Modeの設定画面が表示されます。PICマイコンがATP3011に対してコマンドを送信するためMasterになります。ATP3011が対応しているSPIモードはMode0とMode3ですが今回はMode0を選択しています。

TMR0を設定する

TMR0はDevice Resources内のPeripherals欄のTimerを選択しTMR0をクリックして追加します。

TMR0の設定(MCC)
TMR0の設定(MCC)

Timer Clock内でクロックに関する設定を行います。Clock Sourceは内部クロックを使用しているためFOSC(FOSC/4)を指定します。PrescalerはClock Sourceに対してTMR0のカウントアップする分周比を指定します。

Timer PeriodはTMR0がオーバーフローするタイミングを設定します。1ms毎にオーバーフロー割り込みが発生しますがCallback Function Rateを0x0A(10)を指定しているため10ms毎にコールバック関数が呼び出されます。

void mainTimer(void); //タイマー0割り込みのコールバック関数
//コールバック関数の使用例
TMR0_SetInterruptHandler(mainTimer); //コールバック関数を指定

上記例ではmainTimer()が10ms毎にコールされます。

MCCが生成した関数の使用例

uint8_t data;
uint16_t count;
uint8_t onsei[127];

SSP1CON1bits.SSPEN = 1; //SPI1_Initialize()で許可されないためWhileに入る前にセット

data = SPI1_ReadByte(); //SPIでデータを読み込む場合
//音声のデータを文字列で配列に格納
sprintf(onsei,"onseino;saiseikaisuuwa<NUM VAL=%d>de_su.\r", count );
SPI1_ExchangeBlock(&onsei,45); //SPIでonsetデータを45バイト送信

MCCで生成されたSPI1_Initialize()関数が初期化時にコールされますがSPIが許可されないため別途レジスタを操作してSPIをスタートする必要があります。SPI1_Open()を使用して設定することもできますが、引数に値を指定する必要があります。

SPI1_Initialize()関数で設定した条件から変更しない場合はSSPENのビットに1をセットすることでSPIがスタートします。

SPIでデータを受信する場合はSPI1_ReadByte()を使用します。読み込んだ値が戻り値として返されるため変数に入れて使用します。SPIでデータを複数バイトを送信する場合はSPI1_ExchangeBlock()を使用します。1バイトずつ送信する場合はSPI1_ExchangeByte()を使用します。

Half Duplex SPI Functions用の関数も実装されているため用途によって使い分けることができます。

テックジム|ゼロからはじめるPython入門講座の申込

動作確認

PIC16F1827のSPI通信の動作確認回路
PIC16F1827のSPI通信の動作確認回路

PIC16F1827とATP3011をSPIポートに接続して動作確認を行います。PIC16F1827とATP3011をSPI通信で接続します。ATP3011のAOUTはD級アンプであるPAM8012と組み合わせて音声を増幅して再生します。SPIスレーブ選択ピンの/SSは常に使用するためGNDに接続しています。

電源をONすると「音声の再生回数はXXXです」と音声を発声させます。XXXは再生した回数ですが10以上になると音声はイチゼロのように番号を順番に発声します。

ATP3011F4の12ピンのAOUT部分にノイズ除去のためにR1とC2を実装しています。C2は0.047uF程度がデータシートで回路例として示されていますが手持ちのものがなかったため0.47uFで代用しています。

音声のボリュームを調整したい場合はC2よりPAM8012モジュール側に可変抵抗を入れて信号電圧を調整するとよいでしょう。

スピーカのインピーダンスが高いものであれば直接接続することもできますが、大きな音を出したい場合はインピーダンスが低い8Ωまたは4Ωのスピーカを接続する場合があります。

インピーダンスが低いスピーカを接続すると電流を多く引っ張る必要があるため直接接続するとマイコンに負担がかかるため注意が必要です。

インピーダンスが低いスピーカで大きな音を出したい場合はアンプを使用してATP3011の音声出力を信号増幅する必要があります。今回はPAM8012を使って信号増幅しています。PAM8012は負荷が8Ωであれば最大で2Wの出力が可能なD級アンプモジュールです。PIC16F1827のピン配置は以下の通りです。

PIC16F1827のピン設定(MCC)
PIC16F1827のピン設定(MCC)

PB5はINPUTでウィークプルアップ(WPU)設定しています。WPUを有効にするためにはRegistersのnWPUENをenabledにする必要があります。

Easy SetupでWPUにチェックするのみでは有効にならないので注意が必要です。Notificationsにワーニングで通知されます。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include "mcc_generated_files/mcc.h"

#define ONSEI_TIM_MAX 50
#define TIME_OFF -1         //タイマーを使用しない場合
#define TIME_UP 0           //タイムアップ

uint8_t onsei[127];
uint16_t count;
int16_t timOnseiWait;

void mainTimer(void);

void main(void)
{
    SYSTEM_Initialize();
    TMR0_SetInterruptHandler(mainTimer);
    SSP1CON1bits.SSPEN = 1; //SPI1_Initialize()で許可されないのでここでセット
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();
    count = 1;
    timOnseiWait = ONSEI_TIM_MAX*2; //1s

    while (1)
    {
        if( timOnseiWait == TIME_UP || SPI1_ReadByte() == '>' ){
            if( IO_RB5_GetValue()){
                //音声再生の回数を音声で伝える
                sprintf(onsei,"onseino;saiseikaisuuwa<NUM VAL=%d>de_su.\r", count );
                SPI1_ExchangeBlock(&onsei,45);
                ++count;
                timOnseiWait = ONSEI_TIM_MAX;
            }
            else{
                timOnseiWait = ONSEI_TIM_MAX;
            }
        }
    }
}
/* タイマ管理関数 */
void mainTimer(void){
    
    if( timOnseiWait > TIME_UP ){
        --timOnseiWait;
    } 
}

本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていないと使用できません。

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

あなたのキャリアのお供に「生涯学習のユーキャン」

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました