编码器(直接了解怎么接线)
电机转动的时候编码器通过编码电机的A相和B相输出两个正交的方波,对电机进行测速和识别电机的方向。AIN1 AIN2 BIN1 BIN2引脚控制正反转,PWM引脚控制占空比;AO1、BO1接电机的正极,AO2、BO2接电机的负极。
让电机转起来,只用设置AIN1、AIN2和PWMA
生成一个1khz的PWM波,启动NVIC。抢占优先级设置为1,1ms一个周期。
AIN1和AIN2设置为GPIO输出模式
直接写一个函数放在main.c里。
初始化定时器,启动定时器和PWM
在while里直接调用moto()函数,设置CCR占空比的值,再用value获取CCR的值显示在OLED上面
OK,电机完美的转起来了
接下来就是编码器测速了
1、编码器概述
将角位移或者角速度转换成一连串点数字脉冲的旋转式传感器
2、单片机如何采集编码器数据
测量A相和B相的上升沿和下降沿,A相或者B相的方波频率就可以得到旋转 速度,方向要看AB两相;可以通过A相上升沿的时候判断B相是什么电平,低电平就是正转(CNT自增)。ARR一般设置为65535,PSC直接给0,不分频。
3、编码器接口(Encode Interface)
可以接受增量的 (正交)编码器的信号,根据旋转编码器旋转产生的正交信号脉冲,自动控制CNT自增和自减,从而只是编码器的位置、旋转方向和旋转速度。两个输入引脚借用了输入捕获的通道一和通道二。编码器接口就相当于一个带方向控制的外部时钟。
4、四倍频技术
开启了两个通道技术,就是倍频技术的四倍频。
比较不幸运的是,电机的编码器坏了,可以正常转动但是编码器芯片一上电就烫。通过用示波器测AB相的波形,和万用表测AB两相转动电机得出,编码器是真的坏了,不过让我也更加深刻了解了这个电机。
cubemx设置
A、B两相是输出了方波,定时器的编码器接口可以获取电机的旋转位移和方向。在cubemx里直接设置Combined Channels为Enconder Mode,PSC不分频,设置为0,ARR就设置为65535。
后面再设置一个100ms定时中断一次读取TIM4编码器模式记录的脉冲值TIM1,PSC设置为720-1,ARR设置为10000-1,打开NVIC中的TIM1 update interrupt。)(好像给10ms电机有点受不了)
还有一个PWM波设置,我用TIM2的CH2生成了一个1khz的PWM波,在Channel2里选择PWMGeneration CH2,PSC为72-1,ARR为1000-1,NVIC启动全局中断。PWM设置同上。
代码部分
main.c
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);//以中断方式启动定时器1
HAL_TIM_PWM_Start_IT(&htim1,TIM_CHANNEL_1);//通道一,启动生成PWM
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);//开启TIM2编码器模式
HAL_TIM_Base_Start_IT(&htim3);//开启TIM3的定时器中断
__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_1,200);//设置CCR的值
OLED_Init();
OLED_Clear();
OLED_Init();
OLED_ShowString(1,1,"com:");
OLED_ShowString(2,1,"cnt:");
OLED_ShowString(3,1,"speed:");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
moto(0);//正转
value=__HAL_TIM_GetCompare(&htim1,TIM_CHANNEL_1);
OLED_ShowString(1,1,"com:");
OLED_ShowString(2,1,"cnt:");
OLED_ShowString(3,1,"speed:");
OLED_ShowNum(1,5,value,4);
OLED_ShowNum(2,5,encoder_counter,5);//在特定位置显示脉冲数
OLED_ShowFNum(4,1,n,4,2);//在特定位置显示4位整数+2位小数的电机转速
// cnt=(short)__HAL_TIM_GET_COUNTER(&htim2); //单独用这一行就可以得到编码器的计数值
// OLED_ShowNum(2,5,cnt,5);
}
/* USER CODE END 3 */
enconder.c(这里面就是获得编码器的计数值和中断回调计算速度的函数,之前直接在中断回调函数里调用OLED显示函数回乱显示,然后我就把显示全放到主函数里了)
#include "enconder.h"
#include "tim.h"
extern float n;//转速,单位为:转/秒
extern int16_t encoder_counter;//STM32编码器模式读取的总脉冲数
/**
* @function: void GET_NUM(void)
* @description: 使用STM32编码器模式,读取编码器产生的脉冲值
* @param {*}
* @return {*}
*/
void GET_NUM(void)
{
encoder_counter=(short) __HAL_TIM_GET_COUNTER(&htim2);
__HAL_TIM_SET_COUNTER(&htim2,0);
}
/**
* @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
* @description: 定时器中断回调函数,0.1S中断一次并计算转速,将电机转速以及编码器产生的脉冲数显示在OLED屏上
* @param {TIM_HandleTypeDef *htim}
* @return {*}
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==&htim3)
{
GET_NUM();//得到所记录的脉冲数
// OLED_ShowNum(2,5,encoder_counter,5);//在特定位置显示脉冲数
n=(float)encoder_counter/60000/0.1;//转速为n,r/s。CNT/(t0*60000)
//编码器单圈500脉冲,减速后单圈为15000脉冲每圈,可以通过stm32编码器模式4倍频至60000脉冲每圈。定时器中断是10ms执行一次
// OLED_ShowFNum(4,1,n,4,2);//在特定位置显示4位整数+2位小数的电机转速
}
}
enconder.h
#ifndef ENCODER_ENCODER_H_
#define ENCODER_ENCODER_H_
#include "stm32f1xx_hal.h" //HAL库文件声明
#include <main.h>
#include "OLED.h"
extern TIM_HandleTypeDef htim3;
extern TIM_HandleTypeDef htim2;
void GET_NUM(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif /* ENCODER_ENCODER_H_ */
遇到的问题
开始获取编码器的脉冲计数,电机一圈转下来先是正数,再是负数。本来正常转一圈应该是60000的,我觉得这个计数有问题。
OLED显示函数里使用signednum传数。
总结
总的来说编码器测数,就是先用一个定时器生成PWM波,然后另一个定时器用来计数,捕获A、B相的脉冲。知道脉冲以后,再使用一个定时器来计算当前电机的速度,编码器的计数就相当于电机的位移,定时的时间就是时间,速度就等于位移除以时间。
下面是我打算做平衡小车的引脚配置,跟上面文章里的不一样。放这方便我后面写闭环。
STM32 | TB6612 | 电机接口 | OLED | 蓝牙模块 | MPU6050 | ||||
PB12 | AIN1 | ||||||||
PB13 | AIN2 | ||||||||
PA8 | PWMA(TIM1_CH1) | ||||||||
PA15 | BIN1 | ||||||||
PA12 | BIN2 | ||||||||
PA11 | PWMB(TIM1_CH4) |
|
AO1 | M+ | ||||||||
AO2 | M- | ||||||||
BO1 | M+ | ||||||||
BO2 | M- | ||||||||
PA0 | A相(motor1)(TIM2_CH1) | ||||||||
PA1 | B相(motor1)(TIM2_CH2) | ||||||||
PB6 | A相(motor2)(TIM4_CH1) | ||||||||
PB7 | B相(motor2)(TIM4_CH2) |
PA4 | SCL | ||||||||
PA5 | SDA | ||||||||
PA9 | RXD(uart1_rx) | ||||||||
PA10 | TXD(uart1_tx) | ||||||||
PB10 | SDA(I2C2—SCL) | ||||||||
PB11 | SCL(I2C2—SDA) |