ししかわのマウス研修マウス自作研修

STM32でロータリエンコーダを読む – 元Web屋のマイクロマウス製作記 Part.19

ししかわのマウス研修

ししかわです。

社員研修の一環で、自作マウスを作って大会に出場します。

記事一覧 – 元Web屋のマイクロマウス製作記 | RT MicroMouse

今回はロータリエンコーダをSTM32で読み取りました。


車輪の回転を検知するには

前回はSTM32からDCモータを回しました
DCモータはかけた電圧に比例して回ります。マイクロマウスのように指定した距離だけ走らせるには、実際にモータが何回転したかを知る必要があります。そこで ロータリエンコーダ を使います。

M5MouseのDCモータはエンコーダをセットで搭載しています。
このエンコーダの値をSTM32から読み取ってみましょう。

本記事執筆にあたりSoraさんのブログ記事を参考にしました。

STM32 +HALでエンコーダモードを使用してみる | Sora’s Activity Record

ロータリエンコーダはモータが一定角度回転する毎にパルス波を発生させます。(ステッピングモータの逆のイメージ)
STM32のタイマーは「エンコーダモード」といって、このパルス波をカウントする機能を持っています。

CubeMXでエンコーダモードを設定し、プログラムから使ってみましょう。

STM32CubeMXの設定

  • 左メニューから「Timers->TIM1」を選択します
  • 「Combined Channels」を「Encoder Mode」に変更します
    • このとき自動的に適当なピンに機能が割り当てられます(PA8にTIM1_CH1、PA9にTIM1_CH2)
  • 「Configuration->Parameter Settings」を次の通り変更します
    • Counter Period: 65535
    • Encoder Mode: Encoder Mode TI1 and TI2

雛形の出力

「GENERATE CODE」をクリックして雛形を出力します。

生成されたエンコーダモード設定の処理がmain.cに出力されています。

/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC1Filter = 0;
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */

}

処理の記述

プログラム中からエンコーダの機能にアクセスするにはHALの次の関数を使います。

  • HAL_TIM_Encoder_Start: エンコーダ開始
  • HAL_TIM_Encoder_Stop: エンコーダ停止

エンコーダを開始すると、モータの正回転、逆回転に応じてTIM1->CNTレジスタの値が増減します。
この値を定期的に読み取りつつ、同時にレジスタをリセットすることで、ある周期毎の回転を積算できます。
この処理は関数にまとめておくとよいです。

int16_t read_encoder_value(void)
{
  uint16_t enc_buff = TIM1->CNT;
  TIM1->CNT = 0;
  if (enc_buff > 32767)
  {
    return (int16_t)enc_buff * -1;
  }
  else
  {
    return (int16_t)enc_buff;
  }
}

main()
{
  char usr_buf[1000];
  uint16_t count;
  HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL);
  
  // ...
  while(1)
  {
    // Encoder
    count += read_encoder_value();
    sprintf(usr_buf, "Encoder: %d\n\r", count);
    CDC_Transmit_FS((uint8_t *)usr_buf, strlen(usr_buf));
    HAL_Delay(100);
  }
}

動作確認

データシートのピン配置を参考に、エンコーダとSTM32を次のように接続します。

STM32 エンコーダ
5V Vcc
GND GND
PA8 ChannelA
PA9 ChannelB

接続できたらプログラムを実行します。

モータの軸を手で回すと、回転に応じて出力値が増減していることがわかります。

今回まで、マイクロマウスの主要な部品である光センサ、モータ、ロータリエンコーダを一つずつ、個別に動作させてきました。
マイクロマウスでは光センサを4つ、モータとロータリエンコーダを2つずつ、同時に動かす必要があります。
この制御を限られたCPU資源で効率よく行うためにDMA(Direct Memory Access)やマルチタスク処理が有効です。
その方法は今後紹介していきたいと思います。

タイトルとURLをコピーしました