2017年04月19日

I2S使用方法

今回はPDM出力のデジタルマイクSPH0641LU4Hを
I2S接続で読み込んでみました。



最近のデジタルマイクはMEMSマイクのため、
一般的なマイクに比べ、
高感度かつ広帯域で感度も帯域に癖がなく、
非常にフラットな感度を持ちます。


特に今回紹介するMEMSマイクは
超音波帯域のマイクとしても使用でき、
80kHzまで拾うことができます。

一般的な超音波受信器は共振型のため、
36kHzと書いてあれば36kHz前後周辺の超音波しか拾うことができません。

一方、MEMSマイクは36kHz以外でも拾うことができるため、
例えば、amazonのDashButtonの初期設定のように
普通のマイクとしても、
超音波を使って自在にスマートフォンと機器の間で通信することもできます。



超音波に対応したMEMSマイクはKnowlesから
デジタル版MEMSマイクSPH0641LU4H以外に
アナログ版MEMSマイクSPU0410LR5Hも出ています。


ただ、アナログ版はご存じのようにオペアンプ増幅回路など
周辺回路が必要です。
一方、デジタル版はそのままマイコンに接続できるため、
周辺回路が不要です。
また、アナログ部がセンサ内部のみとなるため、
ノイズにも強い回路にできます。

一般的にはデジタルマイクはPDM出力となっており、
I2S接続でオーバーサンプリングで取得します。



環境は
・STM32F401RENUCLEO
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F4 ver. 1.15.0)
です。


CubeMXの設定は次の通り。

I2S-setting.png

Data and Frame FormatとSelected Audio Frequency
は試しに設定した値です。
ポイントはHalf-Duplex Masterに設定し、
ModeをMaster Recieveに設定することです。


接続は
I2S2_CKをマイクCLK
I2S2_SDをマイクDATA
に接続します。

今回はモノラルで使用するため、
I2S_WSは未使用です。


コードは下記の通り
実際には音を安定して連続に取り込む必要があるため、
DMA転送等を利用すべきですが、
動作確認のため、必要なデータ数を読み込みました。

    uint32_t I2S_RX_BUFFER[24];


  while (1)
  {
     
      int8_t res=HAL_I2S_Receive( &hi2s2,
                            (uint16_t*)&I2S_RX_BUFFER,
                            24,1000);
      HAL_Delay(200);
      //printf("Res: %d ", res);
      printf("Data: %" PRIu32", %"PRIu32", %"PRIu32" , %"PRIu32" \n",
              I2S_RX_BUFFER[0],
        I2S_RX_BUFFER[1],
        I2S_RX_BUFFER[2],
        I2S_RX_BUFFER[3]);
  }
 


今回、テストで使用したKnowles社の
デジタルマイクSPH0641LU4Hは
入力するクロックによってモードが切り替わります。

Low-Power Mode           :351 kHz ≤ fCLOCK ≤ 815 kHz
Standard Performance Mode  :1.024 MHz ≤fCLOCK ≤ 2.475 MHz
Ultrasonic Mode            :3.072 MHz ≤ fCLOCK ≤ 4.8 MHz


HALライブラリのI2Sの設定では
オーディオサンプリングの帯域設定しかありません。
実際に設定によってどのようにクロックが変化するか
確認してみました。


I2Sバスクロック96MHz設定の場合のI2Sクロック結果は下記の通り。

I2S_CLK.png

なお、クロック4.8MHz以上はデジタルマイクSPH0641LU4Hの動作保証外です。
一応、DATA出力としては6MHzまでは出ましたが...


実際にUART出力してみた例は下記の通り。

uart.png

音によって値の桁が変化するため、
正常に読み込んでいると思われます。

今度は読み込んだ値をFFT化したりして、
意図した周波数の信号がとれるか確認してみたいと思います。


なお、I2S_WSは16B幅の場合は
16bit毎にHIGH、LOWが入れ替わり、
それ以外では32bit毎にHIGH、LOWが入れ替わるようです。

96k,16B
16B_96k-WS.png

96k,32B
32B_96k-WS.png


96k,24B
24B_96k-WS.png

96k,16BEX

16BEX_96k-WS.png


1番目がCLK、2番目がDATA、3番目がWSです。


HALライブラリを利用したI2Sの場合、
HALの設定でオーディオサンプリング周波数に応じて
いい感じにクロックを出力してくれることが分かりました。
ただ、今回のセンサのようにクロックによって動作が変わる場合、
I2Sクロック周波数が直感的に分からないため、
オシロスコープ等で確認する必要がありそうです。


ラベル:STM32 HAL
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2017年03月08日

Kicad プロジェクトレスキューヘルパー

電子回路設計ツールのKicadについてメモ書き。


電子回路設計ツールとしてKicadの他にEagleやQuadceptなど
使用しています。



先日、古いKicadプロジェクトから転用して
新しいプロジェクトとして別回路を設計、委託製造しました。


届いた基板の火入れすると、動作がおかしく、Fetのピンアサインのミスが発覚。
古いKicadプロジェクトでも同じFetwo使用していましたが、
そちらではピンアサインは正しく設計されていました。


ERCチェックにも問題がなかったにも関わらず、
一体、どこでピンアサインがおかしくなったのかと調査すると...


そいう言えば、あるタイミングで回路図エディタを開いた際に下記のようなメッセージが出ていました。


kicad.png

「プロジェクトレスキューヘルパー」
ライブラリの情報とキャッシュの情報が一致しない場合に表示されます。
この画面で「OK」をクリックすると、
ライブラリの情報を優先し、
回路図エディタのコンポーネントが更新されてしまうようです。


この画面で左下「再度表示しない」or「キャンセル」を押すと
勝手に更新されずに元のコンポーネントが維持されるようです。


原因としては
無精して適当にライブラリの情報を追記しながら使用していたためです。
ライブラリとキャッシュで同じコンポーネント名にも関わらず、
異なるピンアサインのため、このような画面が出たようです。



ライブラリを追記せすに別名で作成することが正しい使い方ですが、
もし「プロジェクトレスキューヘルパー」が表示された場合は
「再度表示しない」or「キャンセル」で
コンポーネントが更新されてしまうという問題が回避できます。




今回は基板委託製造後の火入れで気付いたため、
手遅れでした、残念。
まあ、試作基板で1つや2つのミスは良くあることですが笑

無精なKicadユーザーさんは「プロジェクトレスキューヘルパー」に要注意です。
ラベル:KiCad
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2017年02月20日

デジタルシグマ型ADC読込

今回はデルタシグマ型ADC(SDADC)について紹介させて頂きます。

デルタシグマ型ADC(SDADC)は、
一般的な内臓の逐次比較型に比べ、
変換速度は遅いものの、ノイズが少なく、高分解能にAD変換できる特徴があります。

・デルタシグマ型ADC(SDADC)
16bit
16.6 ksps @マルチチャンネル変換
50 ksps @1チャンネル変換
差動信号入力可

・逐次比較型ADC
12bit
最大1Msps
シングル信号のみ


今回はSTM32F373に内臓のSDADCを使用してみました。
HALライブラリで公開されているサンプルは1chのみですが、
2ch読み込みを実験してみました。




環境はSDADC内臓の373シリーズ
・STM32F373CC
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F3 ver. 1.60)
です。


◆CubeMXの設定としては、
PB1→SDADC1_AIN5P
PB2→SDADC1_AIN4P


CubeMXで自動生成される初期化関数は下記の通りです。
static void MX_SDADC1_Init(void)
{

  SDADC_ConfParamTypeDef ConfParamStruct;

  hsdadc1.Instance = SDADC1;
  hsdadc1.Init.IdleLowPowerMode = SDADC_LOWPOWER_NONE;
  hsdadc1.Init.FastConversionMode = SDADC_FAST_CONV_DISABLE;
  hsdadc1.Init.SlowClockMode = SDADC_SLOW_CLOCK_DISABLE;
  hsdadc1.Init.ReferenceVoltage = SDADC_VREF_VDDA;
  if (HAL_SDADC_Init(&hsdadc1) != HAL_OK)
  {
    Error_Handler();
  }


  ConfParamStruct.InputMode = SDADC_INPUT_MODE_SE_ZERO_REFERENCE;
  ConfParamStruct.Gain = SDADC_GAIN_1;
  ConfParamStruct.CommonMode = SDADC_COMMON_MODE_VSSA;
  ConfParamStruct.Offset = 0;
  if (HAL_SDADC_PrepareChannelConfig(&hsdadc1,
                      SDADC_CONF_INDEX_0,
                      &ConfParamStruct) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_SDADC_PrepareChannelConfig(&hsdadc1,
                     SDADC_CONF_INDEX_1,
                     &ConfParamStruct) != HAL_OK)
  {
    Error_Handler();
  }

}


◆割り込み関数
追加する割り込み処理として下記の関数を追加します。
void HAL_SDADC_InjectedConvCpltCallback(SDADC_HandleTypeDef *hsdadc)
{
     int16_t tmp=HAL_SDADC_InjectedGetValue(hsdadc,
                      (uint32_t *) &Channel);
    if(Channel==5)InjectedConvData=tmp;       //PB1
    else if(Channel==4)InjectedConvData2=tmp;  //PB2
}



◆define例として

#define SDADC_RESOL           (uint32_t) 65535
#define SDADC_INIT_TIMEOUT    30
#define SDADC_CAL_TIMEOUT     4*30720
#define SDADC_VREF2            (float) 3.300
#define SDADC_GAIN            (uint32_t) 1 

◆グローバル変数

 float inputVoltage = 0;
 int16_t ConvData = 0;
 uint32_t Channel = 0;

 float inputVoltage2 = 0;
 int16_t ConvData2 = 0;
 uint32_t Channel2 = 1;

mainコード内の例として、

MX_SDADC1_Init();
初期化実行後、詳細設定とキャリブレーション、割り込み開始を実行します。

if (HAL_SDADC_AssociateChannelConfig(&hsdadc1,
                SDADC_CHANNEL_4|SDADC_CHANNEL_5,
                SDADC_CONF_INDEX_0) != HAL_OK)
       {
                  printf("ERROR:  HAL_SDADC_AssociateChannelConfig");
       }

   
       if (HAL_SDADC_InjectedConfigChannel(&hsdadc1,
               SDADC_CHANNEL_4|SDADC_CHANNEL_5,
               SDADC_CONTINUOUS_CONV_ON) != HAL_OK)
       {
                printf("ERROR:  HAL_SDADC_InjectedConfigChannel");
       }


      if (HAL_SDADC_SelectInjectedTrigger(&hsdadc1,
              SDADC_SOFTWARE_TRIGGER) != HAL_OK)
    {
              printf("ERROR:  HAL_SDADC_SelectInjectedTrigger");
    }

  
    if (HAL_SDADC_CalibrationStart(&hsdadc1,
              SDADC_CALIBRATION_SEQ_2) != HAL_OK)
    {
              printf("ERROR:  HAL_SDADC_CalibrationStart");
    }

  
    if (HAL_SDADC_PollForCalibEvent(&hsdadc1,
              HAL_MAX_DELAY) != HAL_OK)
    {
           printf("ERROR:  HAL_SDADC_PollForCalibEvent");
    }

 
    if (HAL_SDADC_InjectedStart_IT(&hsdadc1) != HAL_OK)
    {
         printf("ERROR:  HAL_SDADC_InjectedStart_IT");
    }


while関数内では
 inputVoltage = (((ConvData + 32768) * SDADC_VREF2)
              / (SDADC_GAIN * SDADC_RESOL));
 inputVoltage2 = (((ConvData2 + 32768) * SDADC_VREF2)
              / (SDADC_GAIN * SDADC_RESOL));

 printf("SDADC PB1:%d %1.3f, PB2:%d %1.3f\n\r",
                                 ConvData,
                                 inputVoltage,
                                 ConvData2,
                                 inputVoltage2);
 HAL_Delay(300);



可変抵抗をつけて実際に読み込んでみました。

SDADC.png


確かに値は安定して変化しているようです。

今回は差動入力でなく、シングル入力ですが、
値としては差動として読み込まれるようで、
0V入力で-32768、3.3入力で32768となりました。

温度計など大きく値がぶれない処理によいと思います。

ラベル:STM32 HAL 部品
posted by Crescent at 13:16| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2017年01月26日

内臓DAC出力

今回はSTM32でのDMAなしのDA変換について紹介します。


環境はこれまで同様、
・STM32F303K8
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(HAL ライブラリ、F3 ver. 1.60)
です。

マイコン内蔵のDACというと、
キャリブレーションやブザー用などに付いてるオマケ程度で
応答や分解能など性能は全く期待できないという印象でした。


STM32F3のDACは8bit、12bitの分解能が選択でき、
Settlingタイムも3usと高速で外付け汎用DACと変わらない性能が
内臓DACで実現されています。

つまり、DACを外付けしなくとも、
モータ制御等に使えそうです。




STM32CubeMXは下記のように設定してコードを出力しました。


PA4 DAC1OUT1
PA5 DAC1OUT2

dac.png



割り込み等を使わずに単に出力するだけの場合は非常に簡単です。

サンプル出力例は下記の通りです。
階段状の出力を2ch独立に出してみました。

MX_DAC1_Init();

HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Start(&hdac1, DAC_CHANNEL_2);


 int i=0;

  while (1)
  {
 
      if(i>3600)i=0;
      else i=i+300;
      HAL_DAC_SetValue(&hdac1, 
                   DAC_CHANNEL_1,
                   DAC_ALIGN_12B_R,
                   i);
      HAL_DAC_SetValue(&hdac1, 
                   DAC_CHANNEL_2,
                   DAC_ALIGN_12B_R,
                   4096-i);

      printf("DA TEST: %d \n",i);
      HAL_Delay(200);
 

  }


思った以上にシンプルな関数構成でDAC出力することができました。

内臓DACを使用してサーボアンプの制御なども色々出来そうです。
そのうち、試してみたいと思います。
posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする

2017年01月16日

RTC使い方

今回はSTM32のRTC機能について紹介します。


環境は、
・STM32F373CCT6
 +SW4STM32(System Workbench for STM32)
 +STM32CubeMX(F3_1.6.0)
です。

48ピン以上の多くのMPUにはVBAT端子がついており、
32.768kHz水晶発振子と電池を接続するとRTC機能が使用可能です。

また、RTCの他に64byte程度のバックアップレジスタがあり、
電池で値を保持することが可能です。
例えば、ボタンや機器の状態等を保持することに使えそうです。


STM32CubeMX上でRTCを有効化し、
ActivateClockSourceとActivateCalenderにチェックを入れます。



◆設定

RTC_TimeTypeDef sTime;
sTime.Hours = 11;
sTime.Minutes =12;
sTime.Seconds = 13;
sTime.SubSeconds = 0;
sTime.TimeFormat = RTC_HOURFORMAT_24;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;

RTC_DateTypeDef sDate;
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 15;
sDate.Year = 0;//YEARは2017でなく、ある年を基準として設定します


HAL_RTC_SetTime(&hrtc, &sTime, FORMAT_BCD);
HAL_RTC_SetDate(&hrtc, &sDate, FORMAT_BCD);

で時刻や日付をセットします。


◆読み出し

RTC_TimeTypeDef gTime;
RTC_DateTypeDef gDate;
HAL_RTC_GetTime(&hrtc, &gTime, FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &gDate, FORMAT_BIN);
printf("YY MM DD : %d , %d, %d,  ",gDate.Year,gDate.Month,gDate.Date);
printf("HH MM SS : %d , %d, %d \n",gTime.Hours,gTime.Minutes,gTime.Seconds);

で時刻や日付を読み出し可能です。

◆バックアップレジスタ
バックアップレジスタは下記の関数で読み書き可能です。

書き込み
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0,0x01234567);
読み出し
uint32_t ROM=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);



外付けでRTCを別途つける必要なく、
水晶発振子と電池だけで簡単にRTC機能とその周辺の機能を実装可能です。
他にADCの設定でVBATの電源電圧の読み込みが可能なようです。


実験していて驚いたこととして、
一度、基板上の電池と32.768kHz水晶発振子を接続しておけば、
コードに直しても設定が保持され、時刻も進んでいることです。

MPU内のプログラムをRTCとは関係のないコードを書き込んでも
再度、RTCのプログラムに戻すと保持されていました。


一般的なMPUのRTCは別のプログラムを書き込んだ時点でRTCが停止し、
時刻やデータが消えてしまうのが一般的です。
実験のボードで使うにもSTM32は時刻やバックアップレジスタが保持されるのは
再設定の手間が省けて便利な機能だと思いました。
VBATとして電池端子が独立しているのも扱いが楽だと思いました。

posted by Crescent at 00:00| Comment(0) | TrackBack(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする