文章目录
- 前要
- 原理
- 脉冲与定位
- 功能
- 硬件设计
- 编程
- 轮询模式
- 定时器Encoder模式
- 结束语
前要
关于EC11编码器的了解可以参考两篇文章,比较详细,在此就不多介绍了:
- 一篇文章带你了解——EC11编码器(关于硬件、原理图、上下拉等都有讲)
- 认识EC11旋转编码器&编写驱动程序
原理
脉冲与定位
- 15脉冲/30定位:每拨动一格,两个电平都相继翻转,是半个脉冲;再拨动一格,电平再相继翻转,也是半个脉冲;两个半脉冲形成一个完整脉冲。静止状态下,两个电平相同,都为高或低
- 20脉冲/20定位:每拨动一格,形成一个完整脉冲
对应下图如下:
示波器抓取部分波形:
(note: 两个脉冲跳变的间隔约为几十ms)
功能
通过2个pin负责编码器的波形检测,顺时针与逆时针波形不同
硬件设计
IO外部上拉与无上拉
编程
硬件条件:
- MCU: stm32f407
- 编码器类型: EC11-15脉冲/30定位
- 连接: IO外部无上拉,设置MCU GPIO的内部上拉
下面使用两种方法来对编码器进行计数和使用。
轮询模式
直接就上代码了,随意两个GPIO
//GPIO初始化
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin : PB6 */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//判断检测
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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// exit_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint32_t count = 0;
uint32_t wait_t = 0;
bool encoder_switch = 0;
uint8_t encoder_a_pre = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
// uint8_t encoder_b_pre = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
int32_t steps = 0;
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
uint32_t t = HAL_GetTick();
uint8_t encoder_a = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
uint8_t encoder_b = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
if (encoder_a_pre != encoder_a && !encoder_switch) {
wait_t = HAL_GetTick();
encoder_switch = true;
}
if (encoder_switch && ((t - wait_t) >= 2 )) {
//a 0->1, b 0 Clockwise; b 1 AntiClockwise
if (encoder_a == 1) {
if (encoder_b == 0) {
steps++;
} else {
steps--;
}
}
//a 1->0, b 1 Clockwise; b 0 AntiClockwise
else {
if (encoder_b == 1) {
steps++;
} else {
steps--;
}
}
encoder_switch = false;
encoder_a_pre = encoder_a;
printf("%d\r\n", steps);
}
}
/* USER CODE END 3 */
}
注意点:编码器电平发生变化时可能存在噪声,类似按键一样需要增加延时防抖,并且考虑到在系统中少加入延时死等这些不友善的代码,所以代码中有如上处理。
定时器Encoder模式
stm32中定时器有自带Encoder的功能,所以可以借助定时器的这个特性来实现我们的需求。
直接撸代码,GPIO必须使用复用功能有定时器的pin。
//定时器及IO初始化
TIM_HandleTypeDef htim4;
/* TIM4 init function */
void MX_TIM4_Init(void)
{
/* USER CODE BEGIN TIM4_Init 0 */
/* USER CODE END TIM4_Init 0 */
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM4_Init 1 */
/* USER CODE END TIM4_Init 1 */
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 65535;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter = 3;
sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter = 3;
if (HAL_TIM_Encoder_Init(&htim4, &sConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM4_Init 2 */
HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
/* USER CODE END TIM4_Init 2 */
}
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(tim_encoderHandle->Instance==TIM4)
{
/* USER CODE BEGIN TIM4_MspInit 0 */
/* USER CODE END TIM4_MspInit 0 */
/* TIM4 clock enable */
__HAL_RCC_TIM4_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM4 GPIO Configuration
PB6 ------> TIM4_CH1
PB7 ------> TIM4_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM4_MspInit 1 */
/* USER CODE END TIM4_MspInit 1 */
}
}
//获取编码器变化
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_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// exit_init();
MX_TIM4_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
int32_t Enc_Count_pre = __HAL_TIM_GET_COUNTER(&htim4);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
int32_t Enc_Count = __HAL_TIM_GET_COUNTER(&htim4);
if (Enc_Count != Enc_Count_pre) {
printf("%d\r\n", Enc_Count);
Enc_Count_pre = Enc_Count;
}
}
/* USER CODE END 3 */
}
结束语
此两种方式已做测试,稳得一批,如果细节问题可沟通。
— 2021.10.22-21:45于广东深圳