STM32のTIMモジュールのエンコーダモードを試してみましたので使い方を説明します
ルネサスのRXでいうところのMTUの位相計数モードです
使用するボードはnucleo-G474RE、開発環境は下記記事で紹介しているPlatformIO+STM32CubeMXです

STM32CubeMXの設定(TIMモジュール)
まず最初はCubeMXにて周辺機能の設定です
今回取り扱うエンコーダはブラシレスDCモータに搭載されているインクリメンタルエンコーダ(AB相タイプ)です
インクリメンタルエンコーダの説明については色々なサイトでわかりやすく説明されているのでそちらを引用させて頂きます
上記サイトで紹介されているエンコーダは原点を知らせるZ相がありますが、今回使用するものはAB相のみとなります

また、取り込んだカウンタ値はシリアル通信(標準出力)で確認します
シリアル通信の設定については下記記事を参照ください


動作確認用コードの作成
エンコーダモードはブロッキング処理のHAL_TIM_Encoder_Start関数とノンブロッキング処理のHAL_TIM_Encoder_Start_IT関数とHAL_TIM_Encoder_Start_DMA関数があります
しかし、DMAの処理が上手く行きませんでしたので今回の記事はのHAL_TIM_Encoder_Start関数とHAL_TIM_Encoder_Start_IT関数を紹介します
HAL_TIM_Encoder_Start関数を実行後、__HAL_TIM_GET_COUNTERでカウンタにアクセスできます
下記例ではカウンタが更新されたらカウンタをシリアル通信(標準出力)で外部に出力する仕様としました
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(void)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int f getc(FILE* f)
#endif
/* USER CODE END PD */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
setbuf( stdout, NULL );
setbuf( stdin, NULL );
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
uint32_t encCnt = 0;
while (1)
{
if( encCnt != __HAL_TIM_GET_COUNTER(&htim3) )
{
encCnt = __HAL_TIM_GET_COUNTER(&htim3);
printf("%5ld\r",encCnt);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void __io_putchar(uint8_t ch)
{
HAL_UART_Transmit(&hlpuart1, &ch, 1, 0xFFFFFFFF);
}
int __io_getchar(void)
{
uint8_t rxBuf;
while(HAL_UART_Receive(&hlpuart1, &rxBuf, 1, 0xFFFFFFFF) != HAL_OK);
return(rxBuf);
}
/* USER CODE END 4 */
いつものようにtera termで確認します
今回はエンコーダを接続する必要があるのでブラシレスDCモータのエンコーダ信号をnucleoのPC6とPC7に接続します
モータのエンコーダは5V仕様でしたが、3.3Vでも動いたのでとりあえず良しとしています

エンコーダの仕様はモータ軸1回転で300パルスなので、1回転で1200カウント検出できればOKと判断します(AB相の立上り/立下りエッジでカウントなので300×4)
今回使ったモータのエンコーダはCCWでA相が先行するの仕様のようなので、CCW方向でカウントアップ、CW方向でカウントダウンとなります(先ほどの参考サイトの例とは逆)
初期状態からCCW方向にモータ軸を1回転させたところ、1200カウントとなりましたのでOKと判断します

末尾”IT”なので割り込み処理となります
前準備としてSTM32CubeMXで割り込みを許可設定します

カウンタが更新される度に割り込みが発生するようなので下記例では通信処理を割り込みコールバック関数に記述しています
動作としては先ほどと全く同じ挙動となることを確認しました
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "tim.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(void)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int f getc(FILE* f)
#endif
/* USER CODE END PD */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
MX_TIM3_Init();
/* USER CODE BEGIN 2 */
setbuf( stdout, NULL );
setbuf( stdin, NULL );
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_TIM_Encoder_Start_IT(&htim3, TIM_CHANNEL_ALL);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void __io_putchar(uint8_t ch)
{
HAL_UART_Transmit(&hlpuart1, &ch, 1, 0xFFFFFFFF);
}
int __io_getchar(void)
{
uint8_t rxBuf;
while(HAL_UART_Receive(&hlpuart1, &rxBuf, 1, 0xFFFFFFFF) != HAL_OK);
return(rxBuf);
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if( htim == &htim3 )
{
printf("%5ld\r",__HAL_TIM_GET_COUNTER(&htim3);
}
}
/* USER CODE END 4 */
TIMモジュールにはZ相を取り込んでカウンタをリセットする機能がありますので試してみました
まずはCubeMXでEncoder Mode + indexを選択します

次にGPIOの設定でZ相を取り込むポート(今回はPD2)をプルダウンします
TIMモジュールの初期設定ではindex信号はLowからHighになったときに検出されるようなので、通常時Low、検出時Highという論理なので端子をプルダウンします

その他のソースは変更不要です
カウンタUP/DOWN中に手動でPD2をVcc(3.3V)に接続するとカウンタがゼロにクリアされることを確認しました