STM32 ピンのオルタネート機能とは?

  • 2020.05.23
  • HAL
STM32 ピンのオルタネート機能とは?

今回はHALを使ったピンのオルタネート機能についてお話します。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.5.0
Board: STM32Nucleo-F401RE

ピンのオルタネート機能とは?

一般的なマイコンでは、あるパッケージのピン番号△△番の機能は〇〇と決まっています。
STM32の場合には「このピン番号にはこの機能を割り当てます」ということができるようになっています。

とはいえ、すべて自由に設定できるわけではなく、あらかじめ決まっている仕様の中からいくつかの選択肢があるということです。

まあ、縛りがあるのですごく便利というほどのものではありません。

それでもこの機能を知っておけば回路を設計する際に役立ちます。

使うボードは STM32Nucleo-F401RE です。

このボードで使っているマイコンのデータシートは こちら になります。

この文書の Table9. Alternate function mapping を眺めてみてください。

例えば Port PA9 のピンには

TIM1_CH2, I2C3_SMBA, USART1_TX, OTG_FS_VBUS, EVENTOUT

の機能が割り振られています。

設定によって、このピン(端子)に対してこれらの中のどの機能を使うかを決めることができるようになっています。

シリアル通信を例に Alternate function について確認してみます。

データシートからUSART1はAF07というグループに属していることがわかります。

USART1_TX を AF07 から探してみると、PA9 と PB6 に割り当てられています。

例えば PA9 をどうしても TIM1_CH2 として使いたい仕様だとします。

そして USART1_TX も使いたい場合には、 USART1_TX を PB6 に割り当てて使うことができます。

それでは実際に試してみましょう。

プロジェクトを作成する

IDEを立ち上げて、File – New – STM32 Project を選択します。
Board Selecter で NUCLEO-F401RE を選択し Next を押します。

Project Name に AlternateTest と入力し Finish を押します。

Initialize all peripherals with their default Mode ? と聞いてくるので Yes を押します。

(周辺機能をデフォルト値で初期化しますか?)

This kind of project is associated with the STM32CubeMx perspective.
Do you want to open this perspective now? と聞いてくるので Yes を押します。

(この種のプロジェクトはSTM32CubeMXパースペクティブに関連づけられています。今開きますか?)

この意味がわかりにくいのですが「コンフィグレーターであるSTM32CubeMXをIDEのレイアウトに取り込みますか?」と解釈しました。

これで、このボード用のある仕様で周辺機能の初期化まで行ったプロジェクトがつくられました。

コメントを省くと main()関数の中身は以下のようになっています。

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  while (1)
  {
  }
}

この段階では USART1_TX の機能は有効ではありません。

Project Explorer の AlternateTest.ioc をダブルクリックするとマイコンのパッケージが表示されます。

このあたりがコンフィグレーターの機能になります。

ここの PA9 と PB6 端子がグレーになっているので、この端子の機能が使われていないことを表しています。

右側の列の PA9 をクリックしてリストから USART1_TX を選択します。

ピンの色がグレーから黄色にかわりました。

次に Pinout & Configuraiton から Categories – Connectivity – USART1 を選択し Mode で Asynchronous を選択します。

Project Explorer で AlternateTest を選択しておいて、Project – Build Project を選択します。

Do you want generate Code ? と聞いてくるので Yes を押します。

これはSTM32CubeMXの機能で初期化コードを生成しますか?という問い合わせです。

そうすると PA9 が黄色から緑色にかわり、隣のPA10 USART1_RX も緑色にかわりました。

送信と受信はペアで使わなくてはならないようです。

main()の中を確認すると MX_USART1_UART_Init() が追加されています。

コンフィグレーターによって USART1 の初期化処理が追加されました。

ピンと機能のひもづけ

それでは設定したピンとUSART1をどのようにひもづけているのか見ていきましょう。

ProjectExplorer で Core – Src – stm32f4xx_hal_msp.c というファイルがあるのでダブルクリックして開きます。

HAL_UART_MspInit()という関数内で端子の設定が行われています。

MSP とは MCU Support Package の略です。


void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(huart->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

(以降 省略)

コードジェネレーターは HAL_GPIO_Init()関数でGPIOを初期化しています。
第1引数にはポートを指定します。ここではGPIOAです。
第2引数にはGPIO初期化用の構造体アドレスを指定します。

GPIO_INitStruct.Pin メンバにはこのポートのピンを設定します。
ピンの設定内容が同じならば | で OR をとって複数のビットを指定することで、まとめて設定できます。

PA9が USART1_TX, PA10が USART1_RX なので 9 と 10 を OR して同時に設定しています。

そして GPIO_InitStruct.Alternate = GPIO_AF7_USART1 とすることで、これらのピンを USART1 の機能として使えるようになります。

GPIO_AF7_USART1 は Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_gpio_ex.h で定義されています。

いろいろなペリフェラルも定義されているので確認してみてください。

USBシリアル変換モジュール

PCとマイコン間でのシリアル通信の確認をするためにUSBシリアル変換モジュールを使いました。

FTDI社のFT234XDというデバイスを使ったモジュールです。
詳細については こちら を見てください。

接続のイメージ

以下のようにクロスで接続します。

接続

STM32Nucleo-F401RE と USBシリアル変換モジュール を下表のとおりに接続します。

コーディングしてみる

それでは通信部分のプログラムを実装してみましょう。
まずmain()の先頭に変数の定義を行います。


int main(void)
{
  /* USER CODE BEGIN 1 */
  uint8_t buffer[256];
  HAL_StatusTypeDef s;
  /* USER CODE END 1 */

次に while()ループの部分を以下のように記述してください。


  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
    s = HAL_UART_Receive(&huart1, buffer, 1, 10000);
    if (s == HAL_TIMEOUT)
    {
      HAL_UART_Transmit(&huart1, "UART Timeout.\r\n", 15, 10);
    }
    else if (s == HAL_OK)
    {
      HAL_UART_Transmit(&huart1, buffer, 1, 10);
    }
  }
  /* USER CODE END 3 */

コーディングが終わったらビルドしてエラーがないことを確認してください。

PC側の準備

今回も Tera Term を使います。

Tera Term を起動し、起動時の接続で シリアルポートを選択し、ポートは USBシリアルのCOMを選択します。

設定 – シリアルポートでパラメーターを以下の通りに設定します。

スピード : 115200
データ: 8
パリティ: none
ストップビット: 1bit
フロー制御: none

これらのパラメーターがコンフィグレーターが出力したUARTの設定と一致していることを確認しておきます。

動作を確認する

IDEで Run – Debug から F8キー(Resume)してプログラムを実行します。

Tera Term から何かキーを押して1文字ずつ送信します。
ボードは受信したものをそのまま返すので、Tera Termで押したキーが表示されれば成功です。
10秒間何も送られてこないと 「UART Timeout. 」が表示されます。

ピンを入れ替えてみる

本来であればコードジェネレーターが行う部分なので、触るべきではないかも知れませんが実験です。
送信端子を PA9 から PB6 に切り替えるので、HAL_UART_MspInit()の USART1の部分を以下のように書き換えます。
GPIOBのクロックもイネーブルにします。

if(huart->Instance==USART1)
{
__HAL_RCC_USART1_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

ビルドしてエラーがないことを確認しておいてください。

そして USBシリアル変換器の送信側を PA9 から PB6 に入れ替えます。
端子では Nucleo-F401REの CN5-1 につないでいる線を CN5-3 に接続しなおします。

IDEでプログラムを実行し、Tera Term から送ったものが返ってくることを確認できれば成功です。

いかがでしたでしょうか。

USARTだけではなく、さまざまなベリフェラルに対してオルタネート機能を使うことができます。

ぜひ試してみてください。

HALカテゴリの最新記事