ガレスタさんのDIY日記

電子回路、Web、組み込み、自作エフェクターを語るblog

STM32F767 SAIについてメモ1

今回はSerial Audio Interfaceの話です
タイトルはF7となっているがSTがラインナップごとにCubeHALの仕様変えなければ同じように考えることができると思います。

まずSAIについて基礎的なことはこの辺を読んでくださればわかると思います。
http://www.st.com/resource/ja/product_presentation/37.stm32l4-peripheral-serial-audio-interface-wspc-(sai)-wspc-final_jp.pdf

そんでもって何で今回こういう記事を書いているのかというとペリフェラルInitまではCubeMX*1がやってくれるけどその先でHAL_SAI_Receive_ITのような末尾にITがつく割り込み用のAPIに関して何もわかってなかったのでそれを解決する目的からです。

HAL_SAI_Receive_ITについて

STのCubeHAL*2の通信系のペリフェラルは大きく分けて

  • HAL_SAI_Receive(ポーリング)
  • HAL_SAI_Receive_IT(割り込み)
  • HAL_SAI_Receive_DMA(DMA)

の3つのAPIが用意されている。
下の二つがノンブロッキングAPI(他の処理をブロックしない)になっている。
今回は割り込み駆動についてみていきたいので

  • HAL_SAI_Receive_IT(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)

を見ていくことになる。
引数に関しては設定などもろもろ含まれている構造体SAI_HandleTypeDef とデータバッファのポインタとサイズが必要。

HAL_StatusTypeDef HAL_SAI_Receive_IT(SAI_HandleTypeDef *hsai, uint8_t *pData, uint16_t Size)
{
  if((pData == NULL) || (Size == 0))
  {
    return  HAL_ERROR;
  }

  if(hsai->State == HAL_SAI_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsai);

    hsai->pBuffPtr = pData;
    hsai->XferSize = Size;
    hsai->XferCount = Size;
    hsai->ErrorCode = HAL_SAI_ERROR_NONE;
    hsai->State = HAL_SAI_STATE_BUSY_RX;

    if((hsai->Init.DataSize == SAI_DATASIZE_8) && (hsai->Init.CompandingMode == SAI_NOCOMPANDING))
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT8Bit;
    }
    else if(hsai->Init.DataSize <= SAI_DATASIZE_16)
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT16Bit;
    }
    else
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT32Bit;
    }

    /* Enable TXE and OVRUDR interrupts */
    __HAL_SAI_ENABLE_IT(hsai, SAI_InterruptFlag(hsai, SAI_MODE_IT));

    /* Check if the SAI is already enabled */
    if((hsai->Instance->CR1 & SAI_xCR1_SAIEN) == RESET)
    {
      /* Enable SAI peripheral */
      __HAL_SAI_ENABLE(hsai);
    }

    /* Process Unlocked */
    __HAL_UNLOCK(hsai);
    
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

冒頭のエラー処理を除くとおおまかに4つのことをやっている。

  1. 構造体メンバに対して値の設定
  2. InterruptServiceRoutine の登録
  3. 割り込みの有効化
  4. SAIモジュールの有効化(無効の場合だけ実行)

これだけ見るとデータの受け渡しやってないやん!?ってなるけどまあ耐えてください。

構造体メンバに対して値の設定

    hsai->pBuffPtr = pData;
    hsai->XferSize = Size;
    hsai->XferCount = Size;
    hsai->ErrorCode = HAL_SAI_ERROR_NONE;
    hsai->State = HAL_SAI_STATE_BUSY_RX;

がその部分にあたりバッファのサイズとかその辺を構造体に対して渡してあげるという感じ

InterruptServiceRoutine の登録

    if((hsai->Init.DataSize == SAI_DATASIZE_8) && (hsai->Init.CompandingMode == SAI_NOCOMPANDING))
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT8Bit;
    }
    else if(hsai->Init.DataSize <= SAI_DATASIZE_16)
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT16Bit;
    }
    else
    {
      hsai->InterruptServiceRoutine = SAI_Receive_IT32Bit;
    }

ここが結構重要でRX FIFO -> データバッファ転送を行うための実態を登録する処理をしている。

InterruptServiceRoutineは構造体にて定義されている
f:id:gsmcustomeffects:20180915032512p:plain

SAI_Receive_IT32Bitはstaticで定義されておりここでやっとバッファに対してデータを渡す形になっている。

f:id:gsmcustomeffects:20180915032900p:plain

規定数データの受け渡しが終わったら

  • 割り込みの禁止
  • 割り込みフラグのクリア
  • ステータスの更新(busy -> ready)
  • HAL_SAI_RxCpltCallbackのコール

ここで注意しなければいけないのはHAL_SAI_Receive_ITはISRの登録しかしていないので実際の処理をやっているのはSAI1_IRQHandler内のHAL_SAI_IRQHandler(&hsai_BlockA1);である。
f:id:gsmcustomeffects:20180915212013p:plain

この内部でステータスフラグを確認して何を起爆剤として割り込みに入ったかを判別している。
FIFOの場合は青枠でくくった感じになっており関数ポインタで定義されたInterruptServiceRoutineを呼ぶようにできている。

割り込みの有効化とSAIの有効化

一通りの設定が終了後に__HAL_SAI_ENABLE_ITと__HAL_SAI_ENABLEを読んで一連の動作が終わる。

まとめ

HAL_SAI_Receive_ITは

  • データサイズの登録
  • InterruptServiceRoutineの登録
  • __HAL_SAI_ENABLE_ITにて割り込み有効
  • __HAL_SAI_ENABLEにてペリフェラルを有効

割り込み検出時には

  • 実際のハンドラSAI1_IRQHandlerにてInterruptServiceRoutineをコールして登録した関数を実行

SAI_Receive_IT32Bitが

  • データレジスタから読み出し
  • 規定数読み出し終了後割り込みの無効化、フラグクリア
  • HAL_SAI_RxCpltCallback(受信完了コールバック)をコール

*1:STが出しているマイコンGUIコンフィグツール

*2:STが出しているSDKのことを総じてこう呼んでいる。 CubeF7のようにラインナップごとに名前がついている