二轮平衡小车2:编码器与MPU6050 加速度计

今日继续我的二轮平衡小车开发之路~~

本文主要贴代码,之前的文章都有原理,代码中相应初始化驱动部分也有注释~~

文章提供源码,解释以及工程下载,测试效果视频。

编码器与陀螺仪相关原理:

 可以看之前的文章,传送:

【MSP432电机驱动学习—上篇】TB6612带稳压电机驱动模块、MG310电机、霍尔编码器_tb6612fng电机驱动模块_NULL指向我的博客-CSDN博客

【MSP432电机驱动设计—下篇】霍尔编码器测车轮运行距离与M/T综合公式法测速概念_msp432编码器_NULL指向我的博客-CSDN博客

STM32 F103C8T6学习笔记6:IIC通信__驱动MPU6050 6轴运动处理组件—一阶互补滤波_NULL指向我的博客-CSDN博客

初始化TIM3编码器信号捕获口:

TIM3的初始化:


//初始化定时器3为编码器信号捕获口
void TIM3_init(void)
{
	//定义相关结构体
	GPIO_InitTypeDef GPIO_InitStructure;            //定义一个GPIO初始化的结构体
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; //定义一个定时器初始化的结构体
	TIM_ICInitTypeDef  TIM3_ICInitStructure;        //定义一个定时器捕获输入初始化的结构体
 	NVIC_InitTypeDef NVIC_InitStructure;            //定义一个中断优先级初始化的结构体

	//使能相关时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	 //使能TIM3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟	
	
	
	//初始化 GPIOB0
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;    //PB0 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入,默认低电平,可以被外部电压拉高为高电平
	GPIO_Init(GPIOB, &GPIO_InitStructure);        //根据GPIO_InitStructure的参数初始化GPIOB0
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);             //初始化PB0为低电平
	//初始化 GPIOB1
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;    //PB1 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入,默认低电平,可以被外部电压拉高为高电平
	GPIO_Init(GPIOB, &GPIO_InitStructure);        //根据GPIO_InitStructure的参数初始化GPIOB1
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);             //初始化PB1为低电平	
	//初始化 GPIOA6
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;    //PA6
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入,默认低电平,可以被外部电压拉高为高电平
	GPIO_Init(GPIOA, &GPIO_InitStructure);        //根据GPIO_InitStructure的参数初始化GPIOA6
	GPIO_ResetBits(GPIOA,GPIO_Pin_6);             //初始化PA6为低电平	
	//初始化 GPIOA7
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_7;    //PA7 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入,默认低电平,可以被外部电压拉高为高电平
	GPIO_Init(GPIOA, &GPIO_InitStructure);        //根据GPIO_InitStructure的参数初始化GPIOA7 
	GPIO_ResetBits(GPIOA,GPIO_Pin_7);             //初始化PA7为低电平	
	
	
	
	//初始化定时器TIM3 
	TIM_TimeBaseStructure.TIM_Period = 0xffff;                 //设定计数器自动重装值,计数上限为最大 65535 
	TIM_TimeBaseStructure.TIM_Prescaler =2;                    //预分频器,计算频率最大为36M   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;    //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);            //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM3
  
	
	//初始化TIM3输入捕获参数
	TIM3_ICInitStructure.TIM_Channel = TIM_Channel_1;                //输入捕获通道1
  TIM3_ICInitStructure.TIM_ICPolarity =  TIM_ICPolarity_BothEdge;	 //上升与下降沿捕获
	TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
	TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	         //配置输入分频为不分频,分频决定几个捕获事件触发中断服务函数
	TIM3_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器为不滤波
	TIM_ICInit(TIM3, &TIM3_ICInitStructure);                         //根TIM3_ICInitStructure参数初始化定时器TIM3输入捕获	
	//初始化TIM3输入捕获参数
	TIM3_ICInitStructure.TIM_Channel = TIM_Channel_2;                //输入捕获通道2
  TIM3_ICInitStructure.TIM_ICPolarity =  TIM_ICPolarity_BothEdge;	 //上升与下降沿捕获
	TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
	TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	         //配置输入分频为不分频,分频决定几个捕获事件触发中断服务函数
	TIM3_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器为不滤波
	TIM_ICInit(TIM3, &TIM3_ICInitStructure);                         //根TIM3_ICInitStructure参数初始化定时器TIM3输入捕获
	//初始化TIM3输入捕获参数
	TIM3_ICInitStructure.TIM_Channel = TIM_Channel_3;                //输入捕获通道3
  TIM3_ICInitStructure.TIM_ICPolarity =  TIM_ICPolarity_BothEdge;	 //上升与下降沿捕获
	TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
	TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	         //配置输入分频为不分频,分频决定几个捕获事件触发中断服务函数
	TIM3_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器为不滤波
	TIM_ICInit(TIM3, &TIM3_ICInitStructure);                         //根TIM3_ICInitStructure参数初始化定时器TIM3输入捕获
	//初始化TIM3输入捕获参数
	TIM3_ICInitStructure.TIM_Channel = TIM_Channel_4;                //输入捕获通道4
  TIM3_ICInitStructure.TIM_ICPolarity =  TIM_ICPolarity_BothEdge;	 //上升与下降沿捕获
	TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI3上
	TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	         //配置输入分频为不分频,分频决定几个捕获事件触发中断服务函数
	TIM3_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器为不滤波
	TIM_ICInit(TIM3, &TIM3_ICInitStructure);                         //根TIM3_ICInitStructure参数初始化定时器TIM3输入捕获	
	
	
	
	
	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;            //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  //先占优先级3级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;         //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //中断使能
	NVIC_Init(&NVIC_InitStructure);                            //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
	TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);//允许TIM3更新中断|允许TIM3通道1~4 捕获中断	
  TIM_Cmd(TIM3, ENABLE ); 	//使能定时器3
	
}

 

 

TIM3中断服务函数:

 中断服务函数没啥多需要注意的,就是每个通道的中断标志需要清除,否则计数会混乱。

//定时器3中断服务程序	 
void TIM3_IRQHandler(void)
{ 
	if(TIM_GetITStatus(TIM3, TIM_IT_CC1)) //通道1发生捕获事件
		{CAP1++;}	
	if(TIM_GetITStatus(TIM3, TIM_IT_CC2)) //通道2发生捕获事件
		{CAP2++;}			
	if(TIM_GetITStatus(TIM3, TIM_IT_CC3)) //通道3发生捕获事件
		{CAP3++;}			
	if(TIM_GetITStatus(TIM3, TIM_IT_CC4)) //通道4发生捕获事件
		{CAP4++;}	
  TIM_ClearITPendingBit(TIM3, TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4|TIM_IT_Update);//每次进入中断都要清空每个通道的中断标志,否则主函数将无法正常执行
}

 

主函数测试代码:

 说明:

定义变量CAP1~4,分别对应TIM3的通道1~4的捕获事件计数值,

每秒通过串口1打印这四个变量的值:

#include "main.h"

uint16_t T,t;

uint16_t CAP1,CAP2,CAP3,CAP4;//四个通道的捕获值

int main(void)
{	
	init_ALL();     //初始化所有函数
  while(1)
	{	

	}
}


//初始化所有函数:
void init_ALL(void)
{
	SysTick_Init(72);         //初始化滴答计时器
	Usart1_Init(115200);      //串口1初始化
	
//	i2c_GPIO_Config();	      //IIC初始化
//	Usart2_Init(115200);      //串口2初始化
//	Usart3_Init(115200);      //串口3初始化
//	OLED_Init();              //初始化OLED屏幕
//	OLED_Clear();             //清空屏幕数据
//	RTC_init();               //初始化RTC实时时钟
	
	TIM1_init();              //定时器1初始化为 通用定时器 (周期1ms)
	TIM2_init();              //定时器2初始化为 电机PWM (频率 18K HZ,重载值 1000)
	TIM4_init();              //定时器4初始化为 舵机PWM (频率333 hz ,重载值3000)
	TIM3_init();              //定时器3初始化为 编码器信号捕获口
	TB6612_init();  	        //电机正反转控制引脚初始化:
	
	printf("HELLO");          //开机测试 串口1
}


//通用定时器 定时器1 中断服务函数
void TIM1_UP_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
	{	
		if(++t==1000)		
		{
			t=0;
			printf("CAP1=%d,CAP2=%d,CAP3=%d,CAP4=%d\r\n",CAP1,CAP2,CAP3,CAP4);
		}
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
	}
}

//定时器3中断服务程序	 
void TIM3_IRQHandler(void)
{ 
	if(TIM_GetITStatus(TIM3, TIM_IT_CC1)) //通道1发生捕获事件
		{CAP1++;}	
	if(TIM_GetITStatus(TIM3, TIM_IT_CC2)) //通道2发生捕获事件
		{CAP2++;}			
	if(TIM_GetITStatus(TIM3, TIM_IT_CC3)) //通道3发生捕获事件
		{CAP3++;}			
	if(TIM_GetITStatus(TIM3, TIM_IT_CC4)) //通道4发生捕获事件
		{CAP4++;}	
  TIM_ClearITPendingBit(TIM3, TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4|TIM_IT_Update);//每次进入中断都要清空中断标志,否则主函数将无法正常执行
}

 

测试效果:

测试视频如下,经过实际测试,车轮一圈脉冲数应为630~660个,(上升沿下降沿都捕获:倍频2),倍频1的话就是315~330个,编码器线数一般为11~13线,据此猜测是30减速比的电机配置了11线的霍尔编码器。

二轮平衡小车2:编码器测试

 

阶段测试工程下载:

https://download.csdn.net/download/qq_64257614/88290838?spm=1001.2014.3001.5503

初始化iic解码陀螺仪数据:

 关于MPU6050的驱动就不多赘述了,可以自己缝合,不会缝合的直接下工程:

https://download.csdn.net/download/qq_64257614/88291511?spm=1001.2014.3001.5503

 

 

你可能感兴趣的:(STM32,F103,C8T6笔记,stm32,嵌入式硬件)