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