ししかわです。
社員研修の一環で、自作マウスを作って大会に出場します。
記事一覧 – 元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)やマルチタスク処理が有効です。
その方法は今後紹介していきたいと思います。