Stack Overflow 社区您好,
我目前正在开展一个项目,涉及配备四个旋转编码器的定制 PCB,并且我使用的是带有 16MHz 晶体的 STM32F103CBT6 微控制器。每个编码器都连接到其各自定时器的通道 1 和 2(例如,定时器 1 上的编码器 1、定时器 2 上的编码器 2 等等)。
项目目标: 该项目的目标是创建一个游戏控制器,其中 PCB 按钮和旋转编码器的输入将映射到游戏中的功能。我已经成功实现了按钮功能,现在我专注于让旋转编码器像某种按钮一样工作,每次顺时针或逆时针旋转代表一次独特的按钮按下,总共有 8 个按钮。
目前进展: 在 STM32CubeIDE 中,我使用 TI1 和 TI2 输入在编码器模式下配置了所有四个定时器,并且生成了默认代码并制作了自己的代码。但是,我实现的当前代码不会产生预期的输入。没有输入发送到电脑。
我将非常感谢有关如何修改代码以确保我的游戏控制器项目具有正确的旋转编码器输入功能的任何指导或建议。
#include "main.h"
#include "usb_device.h"
#include "usbd_customhid.h"
#include "stm32f1xx_hal.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;
TIM_HandleTypeDef htim4;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM2_Init(void);
static void MX_TIM3_Init(void);
static void MX_TIM4_Init(void);
// Declare de rapportstructuur
typedef struct
{
uint8_t report_id;
uint8_t data[7];
} HID_Report_TypeDef;
HID_Report_TypeDef buttonReport;
int encoder1_state = 0;
int encoder2_state = 0;
int encoder3_state = 0;
int encoder4_state = 0;
void updateButtonStatus(int encoder_number, int direction)
{
buttonReport.report_id = 1;
buttonReport.data[0] = encoder_number;
buttonReport.data[1] = direction;
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&buttonReport, sizeof(buttonReport));
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_USB_DEVICE_Init();
// Start de timers
HAL_TIM_Encoder_Start_IT(&htim1, TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start_IT(&htim2, TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start_IT(&htim3, TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start_IT(&htim4, TIM_CHANNEL_ALL);
while (1)
{
}
}
void TIM1_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim1);
}
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2);
}
void TIM3_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim3);
}
void TIM4_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim4);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim1)
{
int current_state = __HAL_TIM_GET_COUNTER(&htim1);
int direction = current_state - encoder1_state;
encoder1_state = current_state;
if (direction > 0)
{
updateButtonStatus(1, 1); // Encoder 1, clockwise
}
else if (direction < 0)
{
updateButtonStatus(1, 2); // Encoder 1, counter-clockwise
}
}
else if (htim == &htim2)
{
// Lees de encoderstatus voor Timer2
int current_state = __HAL_TIM_GET_COUNTER(&htim2);
int direction = current_state - encoder2_state;
encoder2_state = current_state;
if (direction > 0)
{
updateButtonStatus(2, 1); // Encoder 2, clockwise
}
else if (direction < 0)
{
updateButtonStatus(2, 2); // Encoder 2, counter-clockwise
}
}
else if (htim == &htim3)
{
int current_state = __HAL_TIM_GET_COUNTER(&htim3);
int direction = current_state - encoder3_state;
encoder3_state = current_state;
if (direction > 0)
{
updateButtonStatus(3, 1); // Encoder 3, clockwise
}
else if (direction < 0)
{
updateButtonStatus(3, 2); // Encoder 3, counter-clockwise
}
}
else if (htim == &htim4)
{
int current_state = __HAL_TIM_GET_COUNTER(&htim4);
int direction = current_state - encoder4_state;
encoder4_state = current_state;
if (direction > 0)
{
updateButtonStatus(4, 1); // Encoder 4, clockwise
}
else if (direction < 0)
{
updateButtonStatus(4, 2); // Encoder 4, counter-clockwise
}
}
}
这是定时器初始化之一:
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 65535;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 0;
sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 0;
if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
Cube/HAL 在更新事件(即计数器翻转)时调用 HAL_TIM_PeriodElapsedCallback(),因此根据您的设置,当编码器朝一个方向转动时需要 65535 个脉冲才能实现这一目标。
通常/推荐的程序是定期对计数器进行采样,例如在 main() 的定时循环中。中断对于编码器来说没那么有用。
顺便说一句,TIM1 特别为其各种中断源拥有单独的中断向量,请参阅启动文件中的向量表(更新为 TIM1_UP_IRQHandler)。