2020年10月31日

CO2センサ比較

以前にCO2センサを紹介しました。混み具合や換気状態を監視する用途としても昨今、CO2センサが注目されています。

今回は手持ちの下記のCO2センサを夕方から夜、夜から朝方の2回、日常生活の中でCO2センサを設置して比較してみました。NDIR(Non Dispersive Infrared)方式とMOX(Metal Oxide)方式の2種類で比較する予定でしたが、手持ちのNDIR式のMH-Z14Aの調子が悪いため、MOX(Metal Oxide)方式内で比較しました。

 Adafruit SGP30 2.0.0ライブラリを使用
 ZMOD4410_IAQ_2nd_Gen_2.1使用
・AMS製 CCS811 (生産終了)
 Adafruit CCS811 1.0.3ライブラリ使用


18時頃から0時頃までリビングのダイニングテーブルにCO2センサを3つ並べて、TeratermのLog機能で連続測定しました。

・19時頃から夕食の準備でガスレンジを使用
・20時頃にダイニングテーブルで食事
・20時半頃に窓を開けて換気
・22時頃にダイニングテーブルでお茶


day1.jpg

今回のセンサはすべて400ppm〜測定可能なため、最小が400ppmとなっています。一般的には屋外のCO2濃度は400ppm前後、閉め切った人の多い部屋で800ppm前後、1000~2000ppm以上で眠気や不快を感じると言われています。MOX(Metal Oxide)方式は直接CO2を検出しているのでなく、間接的にCO2に変換しているため、メーカにより差が出ています。ただ、大まかな傾向としては同じ結果となりした。基準となるCO2センサがないため、根拠はありませんが、24時間換気を弱設定で11畳くらいの一般室内のため、最大でも1000ppm程度だと思います。SGP30とCSS811で1000ppmを超えるタイミングが2、3か所ありますが、大きな値ダイニングテーブルに設置したため、呼気や人の動きの風等の影響を受けたと考えられます。

見ずらいため、拡大した推移が下記の通りです。

day1_zoom.jpg
ZMOD4410は少し鈍い推移ではありますが、呼気や人の動きの風等の影響を受けずに比較的現実的なCO2濃度で推移した結果となりました。


続いて、呼気や人の動きの風等の影響を低減させるため、0時から6時まで寝室の足元に設置して測定してみました。0時に換気をして空気を入れ替えた状態で測定を開始しています。

night.jpg

6畳程度の閉め切った寝室では時間経過とともにCO2が増加しました。今回の条件では呼気や人の動きの風等の影響を受けないため、比較的どのセンサも近い傾向となりました。

センサにもよりますが、CO2を安定して測定するためには呼気や人の動きの影響を受けない場所に設置する必要があることが分かります。また、閉め切った部屋では人の呼気でCO2が上昇するため、定期的な換気が大切ということが分かります。今後はNDIR式のセンサも合わせてどのような傾向になるのか検証してみたいと思います。

posted by Crescent at 00:00| Comment(0) | 電子部品 | このブログの読者になる | 更新情報をチェックする

2020年10月24日

ST Acoustic SLライブラリ

STマイクロから提供されているCubeMX群の中にX-CUBE-MEMSMIC1があります。X-CUBE-MEMSMIC1はMEMSマイクに関するサンプルファイルや応用ライブラリが用意されています。例えば、PDM出力をPCM出力に変換するライブラリやFFT処理、USBオーディオ、音の位置検出(Sound Source Localization)、音の向きに応じた増幅(Beamforming)といったライブラリが準備されています。

今回は音の位置検出(Sound Source Localization)、Acoustic SLライブラリについて紹介します。Acoustic SLライブラリを使用して、2つor4つのマイクの情報を入力すると音の方向を角度として出力することができます。マイク2つの場合は-90~90度で検出され、4つの場合は0~360度で検出できます。

Acoustic SLライブラリでは、XCORR cross correlationアルゴリズム、GCC-PHATアルゴリズム、BMPHアルゴリズムが実装されており、3つからアルゴリズムを選択できます。Acoustic SLライブラリの紹介PDFに記載されている各アルゴリズムの違いについて下記に記します。


・XCORR cross correlation
 -時間領域で処理
 -計算処理が軽い
 -角度分解能は低い
   -マイクの距離を離す必要がある

・GCC-PHATアルゴリズム
 -周波数領域で処理
 -XCORRより計算処理が必要
 -角度分解能は比較的高い
 -マイクの距離に影響しない

・BMPHアルゴリズム
 -周波数領域で処理
 -XCORRよりも計算処理が必要だが、PHATよりも軽い
 -マイクの距離に影響しない
 -安定性を高めるために過去データに基づいた処理

Acoustic SLライブラリはソースコード形式でなく、*.a形式のライブラリとして提供されており、ライブラリを組み込んで使用します。組み込み方については以前に紹介したこちらを参照してください。また、使用できるSTM32マイコンの中でもM4、M7コアのみ対応と記載されており、STM32シリーズだとF3、F4、F7、G4、H7、L4のみ対応ということになります。

角度検出で使用する関数は下記の通りです。

@メモリ設定関数AcousticSL_getMemorySize
A初期化関数AcousticSL_Init
B閾値設定関数AcousticSL_setConfig
C音データ入力関数AcousticSL_Data_Input
D音源角度取得関数AcousticSL_Process

各関数の設定例を紹介します。

@メモリ設定関数AcousticSL_getMemorySizeとA初期化関数AcousticSL_Init
設定のポイントは下記の通りです。
・音データ配列インクリメント数
 音データの入力時にどのような配列かに応じて数が変わります。
 例えば、4つの音データが1つの配列に順番に入る場合は4つずつポインタを進めるため、4となります。
 int16_t val[512]={マイク1,マイク2,マイク3,マイク4,マイク1,マイク2,マイク3,マイク4,...}

 例えば、マイクごとに配列を準備して音データを入力する場合はポインタを1つずつ進めるため、1となります。
 int16_t mic1[16]={マイク1,マイク1,...}
 int16_t mic2[16]={マイク1,マイク1,...}

・処理までのサンプリング数
 角度検出するためのサンプリング数の設定です。4つのマイク合計でのサンプリング数を設定します。
 なお、サンプリング周波数16kHz、サンプリング数512の場合、AcousticSL_Data_Inputで1回あたり、各マイク16個のデータ(計64個)を入力し、同様のAcousticSL_Data_Inputを32サイクル繰り返すとAcousticSL_Processで角度を得ることができます。
 16個x4chx32cycle=512サンプル

 サンプリング周波数16kHz、サンプリング数256の場合は16cycle、サンプリング周波数16kHz、サンプリング数128の場合は8cycleとなります。 

uint32_t error_value = 0;
AcousticSL_Handler_t SSL_Handler_Instance;
AcousticSL_Config_t SSL_Config_Instance;

SSL_Handler_Instance.channel_number = 4;//マイク数2or4
SSL_Handler_Instance.M12_distance =50;//マイク同士の距離 単位mm
SSL_Handler_Instance.M34_distance =50;//マイク同士の距離 単位mm
SSL_Handler_Instance.sampling_frequency = 16000;//サンプリング周波数
SSL_Handler_Instance.algorithm = ACOUSTIC_SL_ALGORITHM_GCCP;//処理アルゴリズム
SSL_Handler_Instance.ptr_M1_channels = 1;//音データ配列インクリメント数
SSL_Handler_Instance.ptr_M2_channels = 1;//音データ配列インクリメント数
SSL_Handler_Instance.ptr_M3_channels = 1;//音データ配列インクリメント数
SSL_Handler_Instance.ptr_M4_channels = 1;//音データ配列インクリメント数
SSL_Handler_Instance.samples_to_process = 512;//処理までのサンプリング数 全マイク合計 32,64,128...
AcousticSL_getMemorySize( &SSL_Handler_Instance);//サンプリング数に応じた必要メモリ領域
SSL_Handler_Instance.pInternalMemory=(uint32_t *)malloc(SSL_Handler_Instance.internal_memory_size);//メモリ確保
if(SSL_Handler_Instance.pInternalMemory == NULL)
{
  printf("SSL Malloc Error\n\r");
}

error_value = AcousticSL_Init( &SSL_Handler_Instance);//初期化
if(error_value != 0)
{
  printf("SSL Config Error\n\r");
}

B閾値設定関数AcousticSL_setConfig
分解能は角度分解能を設定します。分解能は内部的に処理されます。
環境音変化閾値はどれくらいの音の変化で音源検出を実行するかを設定します。0~1000の範囲で設定します。
例えば、テストデータとしてsin波等を与えて検証する際は環境音(ノイズ)がないため、0を設定すると常に角度検出されます。
環境音が大きく、大きな音だけで反応させたい場合は値を大きく設定します。

SSL_Config_Instance.resolution=10;//分解能
SSL_Config_Instance.threshold=24;//環境音変化閾値
error_value = AcousticSL_setConfig(&SSL_Handler_Instance, &SSL_Config_Instance);
if(error_value != 0)
{
  printf("SSL Config Error\n\r");
}

C音データ入力関数AcousticSL_Data_Input
サンプリング周波数16kHz、サンプリング数512の場合、1回のAcousticSL_Data_Inputでは16個x4chデータを入力します。これを32cycle分実行するとサンプリング数512となり、角度データを取得できます。サンプリング数に達していない状態でAcousticSL_Processを実行すると処理が異常終了します。

int16_t val[4][16];
//各チャンネル16個分の音データを変数に入力
res=AcousticSL_Data_Input( (int16_t *)&val[0][0], (int16_t *)&val[1][0],
                                           (int16_t *)&val[2][0], (int16_t *)&val[3][0],
                                           &SSL_Handler_Instance );

resが1の場合は処理に必要なサンプリング数に達したため、AcousticSL_Processで角度データを取得できます。
resが0の場合は処理に必要なサンプリング数に達していないため、サンプリング数に達するまでAcousticSL_Data_Inputを繰り返して音データを入力します。

D音源角度取得関数AcousticSL_Process
AcousticSL_Data_Inputの返数が1であることを確認後にAcousticSL_Processを実行して音源角度情報を取得します。検出された角度は配列の1番目に0~360(マイク2つの場合は-90~90)の範囲でそのまま角度として出力されます。環境音変化閾値に0以外を設定した場合で環境音変化閾値以下の場合は角度検出されず、-100が返されます。

int32_t result[2];
res=AcousticSL_Process((int32_t *)&result, &SSL_Handler_Instance);
if(result[0]==ACOUSTIC_SL_NO_AUDIO_DETECTED)
{
  printf("SoundSource is not detected!::%lo\n\r",result[0]);
}
else
{
  printf("SoundSource is detected!::%lo \n\r",result[0]);
}


マイクの配置は下記の通りに配置します。


mic_array.jpg


X-CUBE-MEMSMIC1ライブラリは様々な応用例が準備されている一方でPDFやヘルプ資料だけでは情報が少なく、サンプルコードや実際の関数を使用しながら調べる必要があります。使いこなすまでに苦労したため、情報としてまとめてみました。


posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2020年10月17日

STM32F3 FPU設定の注意点

以前にリンカライブラリ(*.lib)ファイルや静的ライブラリ(*.a)ファイル等のライブラリを追加する方法について紹介しました。その際にライブラリに合わせてFPUなしのMPUとしてビルドする設定について、STM32F3シリーズでの注意点について紹介します。

 プロジェクトファイルのプロパティを開き(Alt+Enter)、「C/C++Build」の「Settings」、「MPU Settings」内の「Floating-point ABI」を「hard」から「soft」もしくは「softfp」に切り替えます。「hard」、「soft」、「softfp」の違いは下記の通りです。

soft浮動小数点の演算に整数命令のみで構成された浮動小数点演算ライブラリ(soft-float)を使用
softfp浮動小数点の演算に浮動小数点演算命令(hard-FPU)を使用するが、floatを引数にする関数の呼び出しはsoft-floatと同じく汎用レジスタを使用
hard浮動小数点の演算に浮動小数点演算命令(hard-FPU)を使用し、floatを引数にする関数は浮動小数点レジスタを使用

softのみFPUを使用しません。一方、softfp、hardは内蔵のFPUを使用します。STM32Fシリーズの場合は「soft」を選択した場合は「Floating point hardware」を「No unit」に合わせて設定する必要があります。逆に 「softfp」、「hard」を選択した場合は「fpv4-sp-d16」のFPUを選択する必要があります。この設定をチグハグにした場合、Hard Faultが起動直後に発生し、マイコンが停止する場合があります。

FPU2.jpg

FPU1.jpg

STM32マイコンのシリーズによっては「Floating point hardware」で「fpv4-sp-d16」のFPUを選択した状態でも「soft」で動作しましたが、STM32F3シリーズではHard Faultになって正常に動作しませんでした(SW4STM32+STM32F3 V1.11.0環境)。シリーズやバージョンによっては挙動が異なるかもしれませんが、「Floating-point ABI」の設定と「Floating point hardware」の設定は合わせてした方が良さそうです。
posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2020年10月10日

STM32 USB CDC注意点

STM32マイコンでUSBデバイス(USB_HSやUSB_FS)に対応している場合、CubeMX上で容易にUSB-CDC(Communications Device Class:CDCもしくはVirtual COM Port:VCP)を利用することができます。CubeMXの設定については今回は割愛しますが、CDCを利用する際に知っておきたい挙動、注意点について下記の3点を紹介します。

@usbd_cdc_if.cの関数ポインタ
Aバッファは割込み専用バッファ
B64byte以上のデータの送受信


@usbd_cdc_if.cの関数ポインタ
 CubeMX上で生成されてユーザがCDC機能を利用する場合、usbd_cdc_if.c内の関数を利用することになります。usbd_cdc_if.c内ではCDCの送受信の関数の他にバッファの初期化設定やCDCクラスの要求処理等の関数があります。これらの関数は関数ポインタで呼び出されるため、プロジェクト内で関数をgrepしても個々の関数は見つかりません。USBD_Interface_fops_FSでポインタが管理されています。そのため、usbd_cdc_if.cの関数の初期化や受信処理等の一通りの処理はCubeMX生成時点で既に実装されており、自動的に関数が実行されます。関数ポインタで管理、呼び出されるため、usbd_cdc_if.c内に安易に自作関数を配置することは避けたほうが賢明です。自作関数を置く場合、関数ポインタにも自作関数を加えて、さらに他のUSBライブラリとの整合を取る必要があります。関数ポインタの整合を取らないと関数が正常に呼び出されず、挙動がおかしくなります。

Aバッファは割込み専用バッファ
CubeMX生成時点で自動でバッファUserRxBufferFS、UserTxBufferFSが生成されます。このバッファは割込みorDMAのバッファとして使用されるため、ユーザー用途のデータバッファとして利用するのは避けた方が賢明です。バッファは受信時に読み込み、送信時の書き込みだけで利用します。このバッファは受信時はUserRxBufferFSにデータが入り、次の受信で自動的にクリアされて新しいデータが上書きされます。送信のUserTxBufferFSについても同様です。MX_USB_DEVICE_Init()の初期化の際にバッファの設定が有効化されます。CDC受信時のCDC_Receive_FS関数内のUSBD_CDC_SetRxBufferでバッファを再度設定していますが、例えば受信の都度、バッファをクリアやバッファの位置を変更、他のバッファに設定するといった自動で生成されたバッファに手を加えると2回目以降の送受信ができないといったCDCの不具合が発生しました。

B64byte以上のデータの送受信
USB_FSの場合、USBの仕様上、通信1回のデータサイズが最大64byteに制限されます。通常は相手側(通常はPC)ではデータの分割、結合が自動で行われるため、無理に独自規格で64byte以上に実装し直す必要はありません。一方、STM32マイコン側はデータの分割、結合の処理が記述されていないため、64byte以上の送受信をするとデータが欠落します。Aで少し触れましたが、CDCの通信バッファであるUserRxBufferFSとUserTxBufferFSは毎回の通信で自動的に上書きされます。例えば受信の場合は毎回上書きされるため、64byte以上のデータを受信した場合は64byteの端数(余り)のデータがバッファに残ります。

64byte以上を送信する際にはUSBD_StatusTypeDefを64byte毎にCDC_Transmit_FSを呼び出して、USBの状態を見てバッファが空になってから次の64byteを送信するようにします。

64byte以上を受信する際には下記のようにUserRxBufferFSとは別にバッファ(CDC_RxBuffer、CDC_RxBufferLen)を用意し、上書きされる前にグローバル変数の受信バッファにデータを退避させます。必要に応じてユーザープログラム側でCDC_RxBufferの読み出し、CDC_RxBufferLenのクリアなどの処理を行ってください。


//グローバル変数としての受信バッファ
uint16_t CDC_RxBufferLen=0;
uint8_t CDC_RxBuffer[2048];


static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, Buf);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
memcpy(CDC_RxBuffer+CDC_RxBufferLen,Buf,*Len);//データ退避処理追加
CDC_RxBufferLen=CDC_RxBufferLen+*Len;//データ長さ処理追加
return (USBD_OK);
/* USER CODE END 6 */
}


STM32マイコンのCDCに関する記事は多くありますが、CDC機能を応用しようとするとCubeMXで生成された関数の挙動を理解する必要があります。その他にもUSBの接続状態やポートをソフトウェアで開いたタイミングの検知などもできるため、機会があれば紹介したいと思います。
posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする

2020年10月03日

RTC水晶発振子

多くのSTM32マイコンではRTC機能を備えており、VBATにリチウム電池と32.768kHzの水晶発振子を接続することでVCC電源断でも時刻を保持することが容易に実現できます。一方で設計によっては精度20ppm前後の一般的な水晶発振子を使用したにも関わらず、20ppmよりも時刻が経時でずれるといった場合があります。

時刻が経時でずれるといった場合、環境温度や周辺ノイズ、不安定な電源を除くと、多くは水晶発振子の外部負荷容量が適切でないことが原因です。安物の時計で時刻が進む、遅れるということが生じる原因も多くはこれが原因です。安物の時計の場合、水晶発振子の外部負荷容量のコンデンサ自体のばらつきが大きい部品を使用していることも一因です。


水晶発振子をRTCで使用する場合には水晶発振子と外部負荷容量の調整としてコンデンサを回路に入れます。一部のRTC ICでは外部負荷容量のコンデンサを内蔵しているものもありますが、使用する水晶発振子と接続先に合わせて外部負荷容量を調整する必要があります。STM32マイコンの評価ボードとして販売されているNucleoボードでは下記のように負荷容量6pFの水晶発振子X2に対して4.3pFのコンデンサC31,C32が外部負荷容量として接続されています。

STM32_NUCLEO64.jpg



負荷容量と周波数偏差の関係は下記のような反比例のような関係があります。水晶発振子によって適切な負荷容量は異なりますが、下記の例では6pF前後が周波数偏差0付近となり、適切な負荷容量と分かります。

Crystal.jpg

負荷容量と周波数偏差の関係から負荷容量が適切でない場合は周波数偏差がプラスにもマイナスにもずれることを意味します。

例えば、周波数偏差マイナスの場合は1秒間の発振回数が減少することになります。32.768kHzの水晶発振子で32768回カウントして1秒になるはずが、32768回カウントで1秒よりも多く秒数を要するため、経時で時計が遅れることになります。反対に周波数偏差プラスの場合は1秒間の発振回数が増加することになります。32768回カウントして1秒になるはずが、1秒よりも少ない秒数で32768回カウントされるため、経時で時計が進むことになります。

つまり、安い時計で時計が遅れるという場合は外部負荷容量を交換して容量の少ないコンデンサに置き換える必要があります。一方、時計が進む場合は外部負荷容量を容量の大きいコンデンサに置き換える必要があります。

実際に手持ちの時計が1か月で数分遅れるため、12pF程度から8pFに変更したところ、遅れがほぼなくなりました。外部負荷容量は接続する先のマイコンデータシート等から計算で求められますが、構成によっては理論通りにならないこともあります。なお、STM32マイコンの場合はソフトウェア側で周波数偏差を微調整、キャリブレーションすることが可能ですが、ハードウェア側で可能な限り対応した上でソフトウェアで微調整する方が確実です。RTCの水晶発振子は時刻のずれとして明確に影響が出るため、最終的には数台の実物を準備し、個体差を含めて半月や1か月で何秒くらいずれるか負荷容量を数pF単位で調整した方が良さそうです。
posted by Crescent at 00:00| Comment(0) | 電子工作 | このブログの読者になる | 更新情報をチェックする