本次分享stm32对多个舵机的控制,之前讲解过单个舵机的控制。以及控制原理,定时器的使用和pwm的输出来控制一个舵机的角度转向。这次就和大家分享一下多个舵机的控制以及调速。利用单片机实现对 8 个舵机的同时控制,掌握多个舵机控制程序实现方法。从单个舵机控制到多个舵机控制,理解定时器的分时复用。为扩展控制 16 路,24 路舵机打下坚实的基础。
多个舵机控制如下图所示
16路舵机控制板子(维特智能)
多个舵机控制方案
利用单个定时器中断法。该方案的实现方法是将舵机 20ms 的周期分解成若干份(由于舵机控制信号的最大高电平时间为 2.5 毫秒,故一般分成 8 份以上), 每一份时间完成一个舵机的控制。以将 20ms 平均分解成 8 份为例进行说明:单片机上电初始化时,先将所有的 IO 端口电平拉低;当定时器产生第一次溢出中断时,在中断服务程序中对定时器计数寄存器重新赋值,所赋值为第一个受控舵机控制信号的高电平时间值,并将 IO 口拉高;当定时器产生第二次中断时,继续给寄存器重新赋值,此次所赋值为 2.5ms 减去高电平所得时间值;剩下的17.5ms 时间内重复七次上述操作,这样即可同时完成 8 个舵机的控制。
每20mm的周期,把每个周期分为8分,每份2.5mm 控制一个舵机,总共控制8个舵机
参考demo 本次使用stm32f103 c8t6
主函数
int main(void)
{
SysTick_Init(); //系统滴答定时器初始化
Servor_GPIO_Config();
Timer_Init();
Timer_ON();
while (1)
{
for(i=0;i<8;i++) //每个舵机到0度
{
CPWM[i]=500; //给PWM舵机一个0.5ms的高电平脉冲,传入到定时器2
}
Delay_ms(1000);
for(i=0;i<8;i++) //每个舵机到180度
{
CPWM[i]=2500;//给PWM舵机一个2.5ms的高电平脉冲,传入到定时器2
}
Delay_ms(1000);
}
}
初始化i/o口
void Servor_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
GPIO电平反转函数
//整个周期20ms,把每个周期分为8分,每份2.5mm 控制一个舵机,总共控制8个舵机,2.5mm 分为两个动作去做,每两个动作控制一个舵机一共16个动作,就是分时复用法,把时间分成几份去做单独的控制。
void Flip_GPIO_One(void)
{
switch(count1) //将20ms的舵机控制周期分成8份,每2.5ms控制一个舵机运转
{ //每个定时器控制8路舵机运转,3个定时器控制24路舵机运转
case 1:
TIM2->ARR=CPWM[1]; //0.5ms //将第一个舵机脉冲宽度值赋值给定时器2
GPIO_SetBits(GPIOB,GPIO_Pin_15); //同时拉高控制舵机1的引脚的电
break;
case 2:
TIM2->ARR=MAXPWM-CPWM[1]; //2ms //将2.5ms减去PWM脉宽值的数据赋值定时器2
GPIO_ResetBits(GPIOB,GPIO_Pin_15);//同时拉低控制舵机1引脚的电平
break; //控制舵机1的引脚在剩下20ms-CPM[0]时间内将一直保持低电平,舵机1按照CPWM值转动
case 3:
TIM2->ARR=CPWM[2];
GPIO_SetBits(GPIOA,GPIO_Pin_8);
break;
case 4:
TIM2->ARR=MAXPWM-CPWM[2];
GPIO_ResetBits(GPIOA,GPIO_Pin_8);
break;
case 5:
TIM2->ARR=CPWM[3];
GPIO_SetBits(GPIOB,GPIO_Pin_5);
break;
case 6:
TIM2->ARR=MAXPWM-CPWM[3];
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
break;
case 7:
TIM2->ARR=CPWM[4];
GPIO_SetBits(GPIOB,GPIO_Pin_4);
break;
case 8:
TIM2->ARR=MAXPWM-CPWM[4];
GPIO_ResetBits(GPIOB,GPIO_Pin_4);
break;
case 9:
TIM2->ARR=CPWM[5];
GPIO_SetBits(GPIOB,GPIO_Pin_3);
break;
case 10:
TIM2->ARR=MAXPWM-CPWM[5];
GPIO_ResetBits(GPIOB,GPIO_Pin_3);
break;
case 11:
TIM2->ARR=CPWM[6];
GPIO_SetBits(GPIOA,GPIO_Pin_15);
break;
case 12:
TIM2->ARR=MAXPWM-CPWM[6];
GPIO_ResetBits(GPIOA,GPIO_Pin_15);
break;
case 13:
TIM2->ARR=CPWM[7];
break;
case 14:
TIM2->ARR=MAXPWM-CPWM[7];
break;
case 15:
TIM2->ARR=CPWM[8];
break;
case 16:
TIM2->ARR=MAXPWM-CPWM[8];
count1=0;
break;
default:break;
}
//count1++;
}
舵机控制函数1
void Servo1(void) //time2 中断里面去调用
{
count1++;
Flip_GPIO_One(); //反转IO电平
}
控制舵机速度方案
脉冲细分法。改方案的具体实现操作位:将预期 PWM 值分解成若干 PWM 值,每个 PWM 值对应舵机一个角度,即让舵机从起始位置转动若干次后到达预期位置。由于舵机控制信号的周期为 20ms,忽略舵机的实际转动时间,每产生一次预期 PWM 值,舵机将转动 N 次,将需要 20*Nms 的时间,如此可达到调速的目的。
控制舵机从500-2500之间运行
每次脉宽控制到(2500-500)/n次=脉宽值
通过调整脉宽值进行平滑运行。
/***************************************************************************************************************
函 数 名:作业初位置,末尾置更新函数
功能描述:从缓存中取一个新的目标位置替换原来的目标位置,原来的目标位置变为新的初位置,一次更替
:有效的数据是插补增量,和插补次数,知道这两个量,和当前初位置即可
备 注: 先进先出,循环访问
****************************************************************************************************************/
void change(void)
{
unsigned char s;
if(point_aim==1)
{
point_aim=0;
point_now=1;
}
else
{
point_aim=1;
point_now=0;
}
n=pos[point_aim][0]/20; //计算新的插补次数
for(s=1;s<9;s++) //计算新的插补增量
{
if(pos[point_aim][s]>pos[point_now][s])
{
dp=pos[point_aim][s]-pos[point_now][s];
dp0[s]=dp/n;
}
if(pos[point_aim][s]<=pos[point_now][s])
{
dp=pos[point_now][s]-pos[point_aim][s];
dp0[s]=dp/n;
dp0[s]=-dp0[s];
}
}
m=0; //m清0
}
/***************************************************************************************************************
函 数 名:vpwm()
功能描述:数据插补,插补时间间隔为20/12ms,由timer0控制,使舵机平滑实现速度控制
:另一个功能是执行完一行后去更新下一行数据,即调用change()
备 注:
****************************************************************************************************************/
void vpwm(void)
{
unsigned char j=0;
static unsigned char flag_Tover;
m++; //用来累加插补过的次数
if(m==n) //n是本行作业要插补的总次数
{
flag_Tover=1; //一行数据的执行时间已经完成
}
for(j=1;j<9;j++)
{
if(abs(CPWM[j]-pos[point_aim][j])<5)
{ //检测靠近终点位置
// how++; //是,则累加一个
CPWM[j]=pos[point_aim][j];//并且直接过度到终点位置
}
else //不靠近终点,继续插补
{
CPWM[j]=pos[point_now][j]+m*dp0[j];
}
}
if(flag_Tover==1)
{ //从插补次数,和脉宽宽度两方面都到达终点,本作业行完成
flag_Tover=0;
change();
}
//return;
}
总结
分时复用法
脉冲细分法
对于stm32针对单个舵机控制的原理上一次已经分享大家可以看一下上一期