草庐IT

快速学习Stm32舵机控制板控制多个舵机运动以及调速

闰土小蒋 2023-04-08 原文

本次分享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针对单个舵机控制的原理上一次已经分享大家可以看一下上一期

有关快速学习Stm32舵机控制板控制多个舵机运动以及调速的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  3. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  4. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  5. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  6. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  7. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐