今日继续我的二轮平衡小车开发之路~~
本文主要贴代码,之前的文章都有原理,代码中相应初始化驱动部分也有注释~~
文章提供源码,解释以及工程下载,测试效果视频。
可以看之前的文章,传送:
【MSP432电机驱动学习—上篇】TB6612带稳压电机驱动模块、MG310电机、霍尔编码器_tb6612fng电机驱动模块_NULL指向我的博客-CSDN博客
【MSP432电机驱动设计—下篇】霍尔编码器测车轮运行距离与M/T综合公式法测速概念_msp432编码器_NULL指向我的博客-CSDN博客
STM32 F103C8T6学习笔记6:IIC通信__驱动MPU6050 6轴运动处理组件—一阶互补滤波_NULL指向我的博客-CSDN博客
//初始化定时器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
}
中断服务函数没啥多需要注意的,就是每个通道的中断标志需要清除,否则计数会混乱。
//定时器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
关于MPU6050的驱动就不多赘述了,可以自己缝合,不会缝合的直接下工程:
https://download.csdn.net/download/qq_64257614/88291511?spm=1001.2014.3001.5503