最近买了个平衡小车,为了圆我PID梦,买来玩玩,废话不多说,直接开搞
第一步:让小车的轮子转起来
我用的电机是TB6612FNG,想详细了解的话直接去搜手册,给他输出两路PWM还要两个引脚进行高低电平的转换就可以设置前进或者后退了,规则如下
看一下引脚图吧
引脚连接:
PWMA | PA11 |
---|---|
PWMB | PA8 |
AIN1 | PB13 |
AIN2 | PB12 |
BIN1 | PB14 |
BIN2 | PB15 |
下面开始配置CUBE
首先是SYS:
其次是时钟:
下面开始引脚的设置:
用定时器1,10Hz就够了
生成:
代码部分:
main.h
/* USER CODE BEGIN Private defines */
#define AIN2_Pin GPIO_PIN_12
#define AIN2_GPIO_Port GPIOB
#define AIN1_Pin GPIO_PIN_13
#define AIN1_GPIO_Port GPIOB
#define BIN2_Pin GPIO_PIN_14
#define BIN2_GPIO_Port GPIOB
#define BIN1_Pin GPIO_PIN_15
#define BIN1_GPIO_Port GPIOB
/* USER CODE END Private defines */
main.c
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//开启TIM1_CH1
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);//开启TIM1_CH4
HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_RESET);//AIN1为0
HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_SET);//AIN2位1,反转
HAL_GPIO_WritePin(BIN1_GPIO_Port,BIN1_Pin,GPIO_PIN_RESET);//BIN1为0
HAL_GPIO_WritePin(BIN2_GPIO_Port,BIN2_Pin,GPIO_PIN_SET);//BIN2位1,反转
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(AIN1_GPIO_Port,AIN1_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(AIN2_GPIO_Port,AIN2_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(BIN1_GPIO_Port,BIN1_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(BIN2_GPIO_Port,BIN2_Pin,GPIO_PIN_SET);
for(int i=0;i<7100;i++)
{
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_4,i);//让轮子转起来
HAL_Delay(1);
}
for(int j=7100;j>0;j--)
{
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_4,j);
HAL_Delay(1);
}
for(int i=0;i<7100;i++)
{
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,i);
HAL_Delay(1);
}
for(int j=7100;j>0;j--)
{
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,j);
HAL_Delay(1);
}
}
/* USER CODE END 3 */
第二步:用编码器测出小车的速度
先看原理图
PA0和PA1,PA6和PA7分别是小车的编码器引脚,所以我们在cunbe中将这四个引脚设置为Encoder模式
在cube中设置好编码器之后,我们再设置好串口,异步通信,再打开中断
首先开启编码器
void Encoder_Start(void) //启动两个编码器. 而编码器初始化HAL_TIM_Encoder_Init()和HAL_TIM_Encoder_MspInit()在main()函数中调用的MX_TIM2_Init()中。
{
__HAL_TIM_SET_COUNTER(&htim2,0); //用带参宏设置编码器的初始值为0(涉及正反转的需要)
__HAL_TIM_SET_COUNTER(&htim3,0);
HAL_TIM_Encoder_Start_IT(&htim2,TIM_CHANNEL_ALL); //开启编码器的中断模式,两个定时器通道TI1和TI2是每个编码器的两个信号采集通道.
HAL_TIM_Encoder_Start_IT(&htim3,TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL); //开启两个编码器
HAL_TIM_Encoder_Start(&htim3,TIM_CHANNEL_ALL);
}
读取编码器速度值
int Read_Speed(int TIMx)
{
int Encoder_Value = 0;
switch(TIMx)
{
case 2:
Encoder_Value =(short) __HAL_TIM_GET_COUNTER(&htim2); //保存编码器计数器的值
__HAL_TIM_SET_COUNTER(&htim2,0); //保存之后要清零,以便下次继续读取.另外每次清零后采样值减0,直接用单位时间的话就可以得出速度信息了.不要麻烦还要减去初值了.
break;
case 3:
Encoder_Value =(short) __HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SET_COUNTER(&htim3,0);
break;
default:
Encoder_Value = 0;
}
return Encoder_Value;
}
这里是主函数,记得定义encoder2和encoder3
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
encoder2=Read_Speed(2);
encoder3=Read_Speed(3);
printf("A轮=%d,B轮=%d\n",encoder2,encoder3);
}
}
输出结果,因为是拿手转的,只能一只手转轮子:
第三步:读取MPU6050的数据值
接下来就是最难的MPU6050了,我这里就不详细讲了,因为我自己对底层也不是很了解
左边我们可以看到XYZ的加速度,和XYZ陀螺仪数值,他们经过ADC转换后的数值通过IIC可以直接输出,但是数值就是纯数值,并不是角度值,然后就去DMP了,DMP在输出去FIMO,我理解就是,可以把MPU6050读取的这些东西,变成四元数,然后再通过IIC输出,就可以变成角度值了
首先我们在CUBE中配置好IIC的SDA线和SCL线
MPU6050的读写程序我就不展示了,因为这些从网络上都能找到,我用的是正点原子的,需要注意的就是我这里用的是硬件的HAL库IIC,之前我们老师说硬件的IIC有点bug,让我们用软件的,但是我试了试硬件的IIC,发现也挺好用的,就不用软件的了
读取结果:
可以看到欧拉角的角度值,这样就可以去调整小车的姿态了