はじめに
PSoCというマイコンが好きです。PSoCは、センサーを創るのに適しています。今回は、例として、超音波距離計を作ってみましょう。
オシロスコープがあったら楽しいです。
書き込み部と本体を切り離していな状態で使うことを前提にしています。(接続部のピン以外にUARTが接続されています。)
はじめに
気温を20℃と仮定し、測定距離5m以内、精度1cm程度の距離計を作ってみましょう。
音速の近似式 S=331.5+0.61T (T=20℃の時343.7m/s)
スピーカー・マイクから障害物までの距離xは、超音波を出してマイクに帰ってくるまでの時間差tより x = St / 2 です(Sは音速)。
5m場合の時間差は t = 2x / S より 2 * 5 / 343.7 = 29.1 [ms] です。
1cm計測するのに要する時間を 同様に計算すると 2 * 0.01 / 343.7 = 58.2 [μs] となり、1 / (58.2 * 10-6) [s] = 17.2 * 103[Hz]から、時間差tを17.2 [kHz]以上の周波数の信号でカウントすれば1cm以上の精度で計測できます。今回は20kHzでカウントします。20KHzでカウントすると5mは29.1msなので、29.1 * 10-3 * 20 * 103 = 582 [Count]
なお、今回の測定器の周期Tは250[ms]、発振時間Tsは60[μs]とします。また、計測用カウンターは230[ms]でリセットします。
※なお、今回は計算中の遅れ等は考慮していません。
超音波スピーカー・マイク
使用する超音波スピーカー・マイクは以下の製品です。
https://akizukidenshi.com/catalog/g/g117716/
超音波センサ(送受信セット)秋月電子 (I-00120)
主な仕様
◎送信側(UT1612MPR)
・音圧レベル:115dBmin(0dB =0.02mPa) 測定条件:入力10Vrms、測定距離30cm
・最大入力電圧:20Vp-p
◎受信側(UR1612MPR)
・感度:-65dBA(0dB=1V/ubar)
◎共通仕様
・中心周波数:40k±1kHz
・-6dB 角:50度
超音波を発信するのに、スピーカーに40KHzの信号を入れる必要があります。スピーカーの回路は、信号(振幅・音量)を大きくするため交互に信号を入力します。なお、直流電圧がかかると故障するので、使っていない時は両端子ともOFFにする必要があります。
返ってきた超音波を受信する回路は、マイクで受けた信号を増幅した後、Band Pass Filter(BPF)を通して、コンパレータで検知します。
アプリケーションの作成
それでは作っていきましょう。
接続案
この時点では、まだ接続しませんが、想定してプログラムを作成します。
名称 | PSoC側 | 備考 |
---|---|---|
スピーカー(TX) | P1[7] | 極性なし |
スピーカー(TX) | P1[6] | 極性なし |
マイク(RX) | P1[5] | 極性なし |
マイク(RX) | P1[4] | 極性なし |
UART(RX) | P12[6] | 指定 |
UART(TX) | P12[7] | 指定 |
プロジェクトの作成
PSoC Creatorを起動し、プロジェクトを作成します。
デバイスは、前回のCY8CKIT-059を使用するので、CY8C5888LTI-LP097にしてください。TempleteはEmptyです。
ハードウェア
クロック部
まずは、クロックを指定します。Clocksをダブルクリックします。
今回は時間がシビアだと思うので最高速の48MHzにBus Clockを設定します。ただし、直接IMOを48MHz(±5%)に設定すると、偏差がUARTの範囲内に入らないので以下の様に設定します。(PSoCの内部クロックで利用)
No | Clock種別 | 設定 |
---|---|---|
1 | IMO | OSC 12MHz ± 3% |
2 | PLL | Enable, INPUT:IMO(12MHz), Desired 48MHz |
3 | Master Clock | INPUT: PLL_OUT(48MHz), Divider: 1 |
4 | Bus Clock | Divider: 1 |
以下の画面で設定する項目(どれでも良いです)をダブルクリックするとClockのFlowが表示されます。GUIで上記の設定をしていきます。
全体のタイミング
Counter_1はスケジュールをつかさどるカウンターです。100Hzでカウントし0の時に距離カウンターとマイクをスタートさせます。23の時に距離カウンターのリセットをかけます。25で繰り返します。
Counter_2は超音波を発してから、帰ってくるまでの時間を計測します。
作成
それでは、実際に作成していきましょう。
TopDesignを開き
Digital-->Function-->Counterを2つ置きます。
Digital-->Logicn-->ANDを1つ置きます。
Digital-->Logicn-->logic low '0'を1つ置きます。
Digital-->Logicn-->SR Flip Flapを1つ置きます。
Port and Pins --> Digital Output を2つ置きます。
System --> Clock を5個置きます。
Sheet Connector (左にあります)を2つ置きます。
こんな感じになります。
設定していきましょう。
Counter_1:UDBを使用します。25で繰り返し23の時にCompare信号(Counter_2のReset信号)を立ち上げます。
Clock_1:IMOから100Hzを作ります。Counter_1のカウントクロックになります。
BUS_CLK(Nameが違うだけなので以後省略):48MHzのバスクロックを利用します。(Clock_2,3,4,5は別で使うので他の名前にしましょう)
配線:他の回路と接続するためには、配線の名前を指定します。配線上で右クリックしEdit Name and Widthを選択します。"Use computed name and width"のチェックを外し、名前を指定します。配線に名前が表示されます。
SR-FF回りも同様にしていきます。こんな感じになりますね。
Counter_2:UDBを使用します。出力はプログラムでカウント数を読むだけです。Periodは Clock ModeをUp Counterにした後で Max ボタンを押します。なお、繰り返すのはReset信号が入った時だけですのでOn TCのチェックは外します。
Clock_2:IMOから100kHzを作ります。計測用のカウントクロックになります。
スピーカー部
スピーカーはStart信号が来てから60μsの間、ONにします。またスピーカーは40kHzの信号を入れる必要があります。今回は、前述したように音を大きくするため、交互にONになるような配線をしています。
作成
では作っていきましょう。
まず回路図のページを追加します。回路図のページのタグで右クリックし、"Add Schematic Page"を選択します。
新しいページが出来たので、ここに描いていきます。
Digital-->Function-->Counterを1つ置きます。
Digital-->Logicn-->Notを1つ置きます。
Port and Pins --> Digital Output を3つ置きます。
System --> Clock を3個置きます。
Off-chip タブに移り Electro-Mechanical-->Sperker を1つ置きます。
Counter_3:スピーカーの音を出す時間を決定しています。Start信号が来て最初の60μsほどオンにします。(Compare Valueで設定)
UDBを使います。RunモードはOne Shotです。ReloadしないのでOnTCのチェックは外します。
Clock_3:Counter_3のカウンタクロック信号を作ります。1MHzにします。1周期が1μsになります。
Clock_4:超音波スピーカーの超音波40kHzを作るための信号を生成します。
BUS_Clockは前回と同様です。
Digital OutputのPortの名前をSP_OUT0、SP_OUT1にし、HW connection、Output enable にチェックをします。Output enableにCounter_3からの信号を入力することでスピーカーのON-OFFの制御を行っています。External terminalは、外部回路を描くためにチェックしています。
接続していきます。
最後にSheet Connectorの配線の名前を"start"に変更します。(上手くいかない時は一旦1ページの名前を削除して、再度両方の名前を指定してください。)
できました。
マイク部
Counter_4、Clock_5でマイクからの直接ノイズを外す時間を設定しています。
Vdda/2(約2.5V)をAGNDとするため、Opamp_1を介してアナログ系(PGA_1、ADC_Delsig_1)の基準電圧に入力しています。帯域通過フィルター(Band Pass Filter:BPF)はミニDSPのFilterにデータを入れるためのADコンバータ、計算エンジンのFilter、出力電圧を作成するDA変換器から構成しています。今回はADコンバータとFilterの間はDMA(Direct Memory Access)をFilterとDA変換器間は割り込みを利用しています。FilterからDA変換器(VDAC8_1)を通して出力された信号は、アナログコンパレータ(Comp_1)でVDAC8_2と比較し、信号として出力し、さらにCounter_4の反転信号と論理積をとって、stop信号として出力します。stop信号は、計測終了信号なので割り込みisr_2に入力します。
作成
前回と同様、回路図のページを追加します。
デジタル側
Digital-->Function-->Counterを1つ置きます。
Digital-->Logicn-->Andを1つ置きます。
Digital-->Logicn-->Notを1つ置きます。
System --> Clock を2個置きます。
System --> Interrupt を1個置きます。
アナログ側
off-chip
Sensor-->Microphone を1個置きます。向きを反転させます。
Passive-->Registor を1個置きます。90度回転させます。
Analog-->VRefを1つ置きます。
Analog-->Amplifiers-->Opampを1つ置きます。
Port and Pins --> Analog Pinを4つ置きます。
Analog-->Amplifiers-->PGAを1つ置きます。
Analog-->ADC-->Delta Sigma ADCを1つ置きます。
System --> DMA を1個置きます。
Filters --> Filter を1個置きます。
System --> Interrupt を1個置きます。
Analog-->DAC-->Voltage DACを2つ置きます。
Analog-->Comparators-->Comparatorを1つ置きます。
Seet Connectorを2つ置きます
Clock_5:Counter_4のカウンタクロック信号を生成します。1周期は0.1msです。
BUS_Clkは前と同様に作ります。
Counter_4:スピーカーからの直接音が入る時間を設定し、キャンセルする信号を出します。1ms(0.1ms [Clock_5]が 10回[Compare Value])以下ならcomp出力(キャンセル信号)を出力します。
Andゲート後のInterruptの名前はisr_2 にします。(同じ名前があった場合は、先にそちらの名前を変更しておきます)
isr_2:結果が出ると割り込みをかけます。
デジタルの部分を結線します。
左のSheet Connectorの線はstart
右のSheet Connectorの線はstop
Analog Pinの名前をMic_1とMic_2にします。
Mic_1,2:マイクの信号を入力します。外部回路を描くためにExternal terminalのチェックを入れています。
Opamp_1:負荷に電圧を加えるときのインピーダンス整合に使います。
PGA_1:入力信号を増幅します。今回は最大の50dBに設定。
Pin_2_0:入力し、PGAで増幅した信号を確認用に出力します。
次はフィルター部です。
ADC_DelSig_1:デルタシグマ式のAD変換器です。これでGainをあげると変換速度が下がるため、前段にPGA_1を利用しています。先にCommonタブでconfiguration数を1に設定します。Config1は、Resolutionを8に落として速度をあげます。Input Range等も調整します。
DMA_1:ADC_DelSig_1の出力をDMAに載せるために使用します。Derivedは接続先に応じて自動で変わるモードです。
Filter:BPFのパラメータを入力しています。左側がパラメータです。参考に最低のBUSクロックが表示されますが、設定はされません。今回のSample Rateは、ADCで設定していますので、その値を入力します。なお、Filter gainは、オーバフローして計算不可になる可能性があるので通常0.25を設定するのですが、今回に限り信号が小さいので20に設定しています。Centerは40kHz、Bandwithは10kHzにしています。
isr_1:VDAC8_1に値をWriteするためFilterで計算が終わったら呼ばれます。
設定が終わったら配線していきます。
DMA_1,isr_1は配線し辛かったので記号を反転しています。AD-->Filter間はDMA転送、Filter-->VDAC8_1間はプログラム(割り込み)でするので配線は不用です。
このような感じになります。
最後判定部を作ります。
VDAC8_2:アナログコンパレータ(Comp_1)の比較用の電圧を発生しています。
Comp_1:VDAC8_1の信号がVDAC8_2で発生した閾値電圧を超えると信号を出力します。
終わったら配線をします。
最後の出力の配線をstopにします。
その他の回路
その他の回路として、PCとの通信用にUART_1、確認用にLedPinを設置ています
作成
前回と同様に回路図を1ページ追加します。
Comminucations-->UART を 1つ置きます。
Ports and Pins --> Digital outputを1つ置きます。
UART_1:UARTの通信速度は9600bpsに設定しています。
LedPin:ボード上のLED(P2[1])に接続します。ソフトのみの制御なのでHW Connectionのチェックを外します。
Pins
最終的に以下の様なPinに設定しました。
名称 | Port | 備考 |
---|---|---|
Mic_1 | P1[5] | 超音波マイクと接続 |
Mic_2 | P1[4] | 超音波マイクと接続(AGND側) |
SP_OUT0 | P1[7] | 超音波スピーカーと接続 |
SP_OUT1 | P1[6] | 超音波スピーカーと接続 |
Rx_1 | P12[6] | UARTで使用。指定ポート |
Tx_1 | P12[7] | UARTで使用。指定ポート |
LedPin | P2[1] | ボード上のLEDと接続。 |
Pin_0_0 | P0[0] | 確認用。全体周期 |
Pin_0_1 | P0[1] | 確認用。計測用カウンター(Counter_2)のリセット信号 |
Pin_0_5 | P0[5] | 確認用。スピーカーのON信号 |
Pin_2_0 | P2[0] | 確認用。BPF前の信号 |
Pin_2_3 | P2[3] | 確認用。BPF後の信号 |
割込み
割込みの優先度を変更します。Filter関係の優先度をあげないと、BPFが正常に動作しません。優先度は、0が高く、7が低くなります。
Design Wide Resources --> Interruptsを開きます。
出来ました。プログラムを書く準備をするために、Build Designをクリックして実行してみましょう。
ーーーーーーーーーーーーーーーーーーーーーーー
ソフトウェア
デザインのビルドが上手くいったらコードを作成します。と言ってもほとんどは、モジュールをスタートさせるだけのプログラムで、Stopの割込みが来たら、時間から距離を求めるだけです。
DMAの設定
DMAの設定のコードは、Tools-->DMA Wizerdで簡単に作成できます。
- DMAを選択します。
- データの行先を設定します。
- データの長さや受け渡し先、次のTD#を設定します。今回は8bitなので、AH_PTRの方になります。
- プログラム上に書き加えるコードが表示されるのでコピーします。
プログラム(全)
DMA_Config関数内はDMA Wizardで作成したものです。
#include "project.h"
#include "stdio.h"
CY_ISR(filterVDAC)
{
static int i=0; //for debug
if(i==0){LedPin_Write(0);i=1;}else{LedPin_Write(1);i=0;} //for debug
VDAC8_1_SetValue(Filter_1_Read8(Filter_1_CHANNEL_A) + 128u);
}
CY_ISR(DataRcv)
{
uint16 t=Counter_2_ReadCounter(); //count
float t2=(float)t*0.01; //ms
float x=343.7*t2/2.0; //mm
char str[30];
sprintf(str,"Count=%3d[Count] x=%3d[mm] \r\n",t,(int)(x+0.5));
UART_1_PutString(str);
}
void DMA_Config(void)
{
/* Defines for DMA_1 */
#define DMA_1_BYTES_PER_BURST 1
#define DMA_1_REQUEST_PER_BURST 1
#define DMA_1_SRC_BASE (CYDEV_PERIPH_BASE)
#define DMA_1_DST_BASE (CYDEV_PERIPH_BASE)
/* Variable declarations for DMA_1 */
/* Move these variable declarations to the top of the function */
uint8 DMA_1_Chan;
uint8 DMA_1_TD[1];
/* DMA Configuration for DMA_1 */
DMA_1_Chan = DMA_1_DmaInitialize(DMA_1_BYTES_PER_BURST, DMA_1_REQUEST_PER_BURST,
HI16(DMA_1_SRC_BASE), HI16(DMA_1_DST_BASE));
DMA_1_TD[0] = CyDmaTdAllocate();
CyDmaTdSetConfiguration(DMA_1_TD[0], 1, DMA_1_TD[0], 0);
CyDmaTdSetAddress(DMA_1_TD[0], LO16((uint32)ADC_DelSig_1_DEC_SAMP_PTR), LO16((uint32)Filter_1_STAGEAH_PTR));
CyDmaChSetInitialTd(DMA_1_Chan, DMA_1_TD[0]);
CyDmaChEnable(DMA_1_Chan, 1);
}
int main(void)
{
DMA_Config();
isr_1_StartEx(filterVDAC);
isr_2_StartEx(DataRcv);
Filter_1_Start();
Filter_1_Write24(Filter_1_CHANNEL_A,0x000000);
UART_1_Start();
Counter_1_Start();
Counter_2_Start();
Counter_3_Start();
Counter_4_Start();
Opamp_1_Start();
PGA_1_Start();
ADC_DelSig_1_IRQ_Start();
ADC_DelSig_1_Start();
VDAC8_1_Start();
VDAC8_2_Start();
Comp_1_Start();
ADC_DelSig_1_StartConvert();
CyGlobalIntEnable; /* Enable global interrupts. */
UART_1_PutStringConst("Program Start \n\r");
for(;;)
{
CyDelay(500);
}
}
接続・確認・調整
最後に動かして調整しましょう。
回路
スピーカへの入力信号
スルーホール基板との接続は秋月電子のこの線が便利ですよね。
https://akizukidenshi.com/catalog/g/g109830/
スピーカ(TX)はP1.7とP1.6で出力されます。 こんな感じの40kHz波形が出ていました。
P1.6,P1.7は出力ピンですのでオシロとはP1.6-GND、P1.7-GNDで接続する必要があります。
接続します。
マイクの接続
TeraTerm等でシリアル通信(9600bps)で接続し、上手くいっていれば数値が出てきています。
たまに0が出ているのは、スピーカの反射の音を取っているのではなく、直接音を拾っていたり、空気中の反射音以外の音を拾っているからです。これは後のマイクの無音時間を調整します。
送受信の波形の確認
スピーカの出力はP0.5、マイクのフィルター後の信号はP2.3に出力しているので確認してみましょう。
黄色がスピーカの送信、水色がマイクで拾って処理した後のアナログ信号です。
※オシロの性能が悪く見えませんが、本来は黄色の部分のOnの間に40kHzの信号があります。
マイクの無音時間
スピーカーとマイクを一緒に設置すると音を出すと基板を伝わったり、机からの反射など様々な音を拾います。マイクとスピーカーの距離の影響や何回か反射する影響で、実際の音を出している時間よりも長くなります。その時はCounter_4の値を調整します。
コンパレータの比較電圧の設定
P2[3]の信号をオシロスコープのTrig電圧を変化させ確認しながら閾値を検討します。閾値が決まったらVDAC8_2の値を変更します。
出力がGND中心でないことに注意が必要です。
備考
コードで色々なパラメータを動的に変更できます。
本プログラムでは
- 温度
- Filterの計算遅れ
を考慮していません。
おわりに
自分は、こんなことが出来るマイコンはPSoCしか知りません。他のマイコンなど紹介して頂ければ幸いです。
どうですか。PSoC面白いでしょう。