完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
前言
第一步当然是驱动电机啦! 材料包含 L298N 两个带霍尔编码器电机 一、硬件初始化 1.pwm与电机正反转 我这里是PA8(TIM1 CH1),PA9((TIM1 CH2)输出pwm波控制,硬件接线自己定义吧,板子不一样肯定不一样,挑一个合适的修改我的代码即可 #include "pwm.h" /*****************************电机控制*************************/ //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM1_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO, ENABLE);// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIO外设时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //CH2 GPIO_Pin_9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TIM_CH2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //电机A口控制 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 |GPIO_Pin_12 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_ResetBits(GPIOA,GPIO_Pin_11 |GPIO_Pin_12); //所有引脚拉低 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_ResetBits(GPIOB,GPIO_Pin_14 | GPIO_Pin_13); //引脚拉低 TIM_TimeBaseStructure.TIM_Period = 899; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K TIM_TimeBaseStructure.TIM_Prescaler =0; //设置用来作为TIMx时钟频率除数的预分频值 不分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC2Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能 TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM1, ENABLE); //使能TIM1 } void motor_R_move(void) { PBout(13) = 0; PBout(14) = 1; } void motor_R_back(void) { PBout(13) = 1; PBout(14) = 0; } void motor_R_stop(void) { PBout(13) = 0; PBout(14) = 0; } void motor_L_move(void) { PAout(11) = 0; PAout(12) = 1; } void motor_L_back(void) { PAout(11) = 1; PAout(12) = 0; } void motor_L_stop(void) { PAout(11) = 0; PAout(12) = 0; } 代码解释 我这里除了初始化了pwm通道,还初始化了四个引脚作为L298N的逻辑控制,L298N是可以由两个引脚的0或者1能使输出颠倒从而实现电机正反转,具体谁1谁0自己试试就行,然后根据实际修改motor_R_move等四个函数控制电机的正反转。 至于PAout();就是使引脚输出的宏定义,你自己改一下只要能使得引脚输出高低电平即可。 2.编码器初始化 encoder.c #include "encoder.h" /************************************************************************** 函数功能:把TIM2初始化为编码器接口模式 右轮 入口参数:无 返回 值:无 **************************************************************************/ void Encoder_Init_TIM3(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频器 TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除TIM的更新标志位 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //Reset counter TIM_SetCounter(TIM3,0); //=============================================== TIM3->CNT = 0x7fff; //=============================================== TIM_Cmd(TIM3, ENABLE); } /************************************************************************** 函数功能:把TIM5初始化为编码器接口模式 左轮 入口参数:无 返回 值:无 **************************************************************************/ void Encoder_Init_TIM5(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能定时器4的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PB端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_0; //端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(TIM5, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 10; TIM_ICInit(TIM5, &TIM_ICInitStructure); TIM_ClearFlag(TIM5, TIM_FLAG_Update); //清除TIM的更新标志位 TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE); //Reset counter TIM_SetCounter(TIM5,0); //=============================================== TIM5->CNT = 0x7fff; //=============================================== TIM_Cmd(TIM5, ENABLE); } 代码解释 这里就是编码器初始化,每个霍尔电机编码器都需要AB两相来测速,我这里选择了TIM3与TIM5,至于AB接那个,先随便接,你只需安装好电机后到时候使电机正转,测速也是正数即可,反了的话换一下就行。 二、pwm控制与编码器使用 1.pwm使电机 很简单,只需要设定指定占空比,通俗来说当时初始化总的可以看成900,我们这里填0-900其比例就是电机转快慢 TIM_SetCompare1(TIM1,500);//让tim1的CH1输出相应占空比pwm波 TIM_SetCompare2(TIM1,500);//让tim1的CH2输出相应占空比pwm波 2.读入编码器速度 /************************************************************************** 函数功能:读取编码器脉冲差值 入口参数:TIM_TypeDef * TIMx 返回 值:无 **************************************************************************/ s16 getTIMx_DetaCnt(TIM_TypeDef * TIMx) { s16 cnt; cnt = TIMx->CNT-0x7fff; //这一点默认认为,单位时间内编码器不会出现0x7fff的脉冲变化,事实上也是这样 TIMx->CNT = 0x7fff; return cnt; } /************************************************************************** 函数功能:计算左右轮速 入口参数:int *leftSpeed,int *rightSpeed 返回 值:无 //计算左右车轮线速度,正向速度为正值 ,反向速度为负值 //一定时间内的编码器变化值*转化率(转化为直线上的距离m)*200s(5ms计算一次) 得到 m/s *1000转化为int数据 右1975.6 = 11 * 4 * 44.9 左1975.6 = 11 * 4 * 44.9 一圈的脉冲数: 左: 11 右: 11 减速比 左: 44.9 右: 44.9 轮子周长:13cm **************************************************************************/ void Get_Motor_Speed(int *leftSpeed,int *rightSpeed) { static int leftWheelEncoderNow = 0; static int rightWheelEncoderNow = 0; static int leftWheelEncoderLast = 0; static int rightWheelEncoderLast = 0; //记录本次左右编码器数据 leftWheelEncoderNow += getTIMx_DetaCnt(TIM5); rightWheelEncoderNow+= getTIMx_DetaCnt(TIM3); //5ms测速 *leftSpeed = 13 * (leftWheelEncoderNow - leftWheelEncoderLast)*200 / 1975.6; //速度为cm/s *rightSpeed = 13 * (rightWheelEncoderNow - rightWheelEncoderLast)*200/ 1975.6; //记录上次编码器数据 leftWheelEncoderLast = leftWheelEncoderNow; rightWheelEncoderLast = rightWheelEncoderNow; } /************************************************************************** 函数功能:开启TIM6用以定时测速 入口参数:无 返回 值:无 **************************************************************************/ void TIM6_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitStructure.TIM_Period = 50-1; TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure); TIM_ARRPreloadConfig(TIM6, ENABLE); //使能TIMx在ARR上的预装载寄存器 NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE); TIM_Cmd(TIM6, ENABLE); } void TIM6_IRQHandler(void) //TIM6中断 { if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 { TIM_ClearITPendingBit(TIM6, TIM_IT_Update); //清除TIMx的中断待处理位:TIM 中断源 Get_Motor_Speed(&F103RC_chassis.leftSpeedNow,&F103RC_chassis.rightSpeedNow); } 代码解释 我这里设置了tim6为5ms中断,5ms进入中断调用函数Get_Motor_Speed测一次速度,测速公式也在代码注释给好了,原理可以自己百度查一下,这里是算出来的直接的,实际中摩擦等原因,我们可以在测速的结果乘一个系数来更符合实际,这里先提示一下。 总结 今天讲了基础的电机驱动与编码器测速 |
|
|
|
只有小组成员才能发言,加入小组>>
2438 浏览 0 评论
9107 浏览 4 评论
36786 浏览 19 评论
5029 浏览 0 评论
24757 浏览 34 评论
1536浏览 2评论
1755浏览 1评论
2202浏览 1评论
1561浏览 0评论
532浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 03:07 , Processed in 1.425471 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号