通过STM32单片机计算并控制小车速度,通过控制速度的思想扩展到控制其它变化量 您所在的位置:网站首页 怎样测当前车速 通过STM32单片机计算并控制小车速度,通过控制速度的思想扩展到控制其它变化量

通过STM32单片机计算并控制小车速度,通过控制速度的思想扩展到控制其它变化量

2023-07-23 21:08| 来源: 网络整理| 查看: 265

说明:如果有哪里说错了或者说得不好的话还请大家指出来,及时纠正错误,或者哪里有更好的解决方法也可以提出来,我们一起学习交流。

目录

一、编码电机

二、单片机相关定时器的作用以及配置

1、TIM2的配置

2、TIM3的配置

3、TIM4的配置

4、PID函数

5、读定时器的计数值

6、calc_motor_rotate_speed()函数

三、2020电赛C题

四、扩展

五、效果展示

一、编码电机

如果想要控制小车的速度就需要得到小车的速度,想要得到小车的速度就需要用到编码电机,下面先来大概看一下编码电机。

大概就是这个样子了,这里就不介绍它的原理了,可以去查阅相关资料。

二、单片机相关定时器的作用以及配置

本次用到了3个定时器,分别是TIM2、TIM3、TIM4,定时器2用于定时,也就是定时10ms产生中断,然后到中断服务函数中去计算速度,定时器3用于输出PWM波来控制小车的行走,定时器4用来配置成编码器模式,用来读取编码器的数据。

1、TIM2的配置 #include "encoderTim.h" /********************* 通用定时器6初始化 arr:自动重装值。 psc:时钟预分频数 定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us. Ft=定时器工作频率,单位:Mhz ************************/ void TIM2_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); ///使能TIM时钟 TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM7 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允许定时器6更新中断 定时器溢出中断 TIM_Cmd(TIM2,ENABLE); //使能定时器6 开始工作 NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //定时器6中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //中断使能 NVIC_Init(&NVIC_InitStructure); }

TIM2中断服务函数

void TIM2_IRQHandler(void) // { if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){ //检测是否发送中断 calc_motor_rotate_speed(); } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断 }

TIM2用于定时10ms,然后进入中断服务函数,执行calc_motor_rotate_speed()函数,对于这个函数的作用,后面再说明。

2、TIM3的配置 #include "iopwm.h" void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStruct; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA外设 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能定时器3时钟 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //输出 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7|GPIO_Pin_6; // GPIOA.7;GPIOA.6 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; //TIM3_CH2 TIM3_CH1 GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIO //定时器TIM3初始化 TIM_TimeBaseInitStruct.TIM_Period=arr;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseInitStruct.TIM_Prescaler=psc;//设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseInitStruct.TIM_ClockDivision=0;//设置时钟分割:TDTS = Tck_tim TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//TIM向上计数模式 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 //初始化TIM3 Channel 2 PWM模式 TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM2;//选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;//比较输出使能 TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_Low;//输出极性:TIM输出比较极性 TIM_OC2Init(TIM3,&TIM_OCInitStruct);//根据T指定的参数初始化外设TIM3 OC2 TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);使能TIM3在CCR2上的预装载寄存器 TIM_OC1Init(TIM3,&TIM_OCInitStruct);//根据T指定的参数初始化外设TIM3 OC1 TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);使能TIM3在CCR1上的预装载寄存器 TIM_Cmd(TIM3,ENABLE);//使能定时器 }

TIM3主要就是输出PWM信号来控制电机

3、TIM4的配置 #include "encoder.h" //TIM4 通道1通道2 正交编码器 void TIM4_ENCODER_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; TIM_ICInitTypeDef TIM_ICInitStruct; //输入捕获 //PB6 ch1 A,PB7 ch2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能TIM4时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能GPIOA时钟 GPIO_StructInit(&GPIO_InitStructure);//将GPIO_InitStruct中的参数按缺省值输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//PA6 PA7浮空输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); /*时基初始化*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); /*使能定时器时钟 APB1*/ TIM_DeInit(TIM4); //TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Prescaler = ENCODER_TIM_PSC; /*预分频 0*/ TIM_TimeBaseStruct.TIM_Period = ENCODER_TIM_PERIOD; /*周期(重装载值)65535*/ TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; /*连续向上计数模式*/ TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStruct); /*编码器模式配置:同时捕获通道1与通道2(即4倍频),极性均为Rising*/ TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICStructInit(&TIM_ICInitStruct); TIM_ICInitStruct.TIM_ICFilter = 0; /*输入通道的滤波参数*/ TIM_ICInit(TIM4, &TIM_ICInitStruct); /*输入通道初始化*/ TIM_SetCounter(TIM4, CNT_INIT); /*CNT设初值 0 */ TIM_ClearFlag(TIM4,TIM_IT_Update); /*中断标志清0*/ TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); /*中断使能*/ TIM_Cmd(TIM4,ENABLE); /*使能CR寄存器*/ } //定时器4中断服务函数 void TIM4_IRQHandler(void) { if(TIM4->SR&0X0001){}//溢出中断 TIM4->SR&=~(1PrevError=0; //Error[-2] sptr->Proportion=P_DATA; //比例常数 sptr->Integral=I_DATA; //积分常数 sptr->Derivative=D_DATA; //微分常数 } /************** 入口参数:NextPoint:当前输出值 SetPoint: 设定值 返回值:PID调整输出 ***************/ int PID_Calc(int NextPoint,int SetPoint) { int iError,Outpid; //当前误差 iError=SetPoint-NextPoint; //增量计算 Outpid=(sptr->Proportion * iError) //E[k]项 -(sptr->Integral * sptr->LastError) //E[k-1]项 +(sptr->Derivative * sptr->PrevError); //E[k-2]项 sptr->PrevError=sptr->LastError; //存储误差,用于下次计算 sptr->LastError=iError; return Outpid; //返回增量值 }

PID函数就是用来不断调整的,使当前值逐渐接近目标值

5、读定时器的计数值 // 读取定时器计数值 int read_encoder(void) { int encoderNum = 0; encoderNum = (int)((int16_t)(TIM4->CNT)); /*CNT为uint32, 转为int16*/ TIM_SetCounter(TIM4, CNT_INIT);/*CNT设初值*/ return encoderNum; } 6、calc_motor_rotate_speed()函数

calc_motor_rotate_speed()函数在TIM2的中断服务函数里面,10ms时间到了就执行这个函数

//计算电机转速(被另一个定时器100ms调用1次) void calc_motor_rotate_speed() { /*读取编码器的值,正负代表旋转方向*/ encoderNum = read_encoder(); /* 转速(1秒钟转多少圈)=单位时间内的计数值/总分辨率*时间系数 */ // rotateSpeed = (int)encoderNum/TOTAL_RESOLUTION*10; // Speed2 = (rotateSpeed * 6.0 * 3.14)/1.0; //速度 1秒钟转的圈数 X 一圈的距离(轮子周长)/1s Speed = ((encoderNum/(48.4*4)) * 18.84)/0.1;//((总的脉冲数/电机一圈的脉冲*减数比*4倍频)*轮子周长)/10ms para_L = PID_Calc(Speed,SetPoint); //,计数得到增量式PID的增量数值 if((para_L < -3) || (para_L > 3)){ // 不做 PID 调整,避免误差较小时频繁调节引起震荡。 Moto_Left += para_L; } if(Moto_Left>19000)Moto_Left=19000;//限幅 if(Moto_Left


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有