首页 > 编程语言 >嵌入式浅谈之“梯形”加减速MCU算法实现

嵌入式浅谈之“梯形”加减速MCU算法实现

时间:2024-06-10 16:28:33浏览次数:22  
标签:TIM3 浅谈 嵌入式 NVIC TIM Motor NowState MCU SetData

书接上回,上章我们讲到原理,本章我们来聊聊实现。

在笔者的实际项目经历中,梯形加减速运用的比较广泛,主要以其优秀的加减速能力、对算法实现资源的需求较小、实现难度适中而被广泛应用。下面就简单介绍一下基于MCU的算法实现过程,以STM32为例。

采用“梯形”加减速算法,在运动过程中分成以下四个状态:空闲状态,加速状态,匀速状态与减速状态。

脉冲产生单元:用来产生指定频率的脉冲信号。

脉冲计数单元:用来记录各个运动状态下输出的脉冲个数。

脉宽计算单元:根据总的脉冲个数及记录的各个运动状态下的脉冲输出个数,进行状态跳转及速度计算。

脉冲产生单元

初始化STM32定时器,在定时器中断里面,翻转IO口,输出脉冲信号。

//初始化定时器功能,用来翻转IO信号,产生脉冲输出信号

//初始化定时器功能,用来翻转IO信号,产生脉冲输出信号
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	
	//定时器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
 
	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级1级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
 
	TIM_Cmd(TIM3, DISABLE);  //不使能TIMx					 
}
//设置定时器值,用来产生指定频率的脉冲信号
void Motor0TimeSet(unsigned int ARR,bool TimeEnable)
{	
	TIM3->ARR = (uint16_t)ARR;   
	if(TimeEnable) TIM_Cmd(TIM3, ENABLE);
	else TIM_Cmd(TIM3, DISABLE);
}
//翻转IO
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  
		MotorPulsePinOut(&MotorControl[0]);
	}
}

脉冲计数单元

用来记录各个阶段输出的脉冲信号;根据设置的值和输出脉冲进行加减速状态切换。

                                          

void MotorRunState(MOTOR_CTRL *Motor)
{
    switch(Motor->NowState.RunState)
    {
		case MIDLE://开始
			Motor->NowState.AllowMaxAddStep = Motor->SetData.RunStep/2;
			Motor->NowState.RunState = MACC;
		break;
		case MACC://加速状态
			Motor->NowState.AddStep ++;
			Motor->NowState.RunStep ++;
			if(Motor->NowState.AddStep >= Motor->NowState.AllowMaxAddStep)//三角形加速
				Motor->NowState.RunState = MDEC;
			else if(Motor->NowState.Speed >= Motor->SetData.SpeedMax)
				Motor->NowState.RunState = MKEEP;
		break;
		case MKEEP://最大速度阶段
			Motor->NowState.RunStep ++;
			if(Motor->NowState.AddStep >= Motor->SetData.RunStep - Motor->NowState.RunStep)
				Motor->NowState.RunState = MDEC;
		break;
		case MDEC://减速阶段
			Motor->NowState.RunStep ++;
			if(Motor->NowState.RunStep >= Motor->SetData.RunStep)//脉冲输出完成
			{
                Motor->TimeSet_Call_Back(Motor->NowState.TimeCountNum,false);//关闭定时器,停止脉冲信号输出
				Motor->NowState.SysState = MOV_IDLE;
				Motor->NowState.Consequence = 0; //执行结果
			}
		}
		if(Motor->NowState.SysState)Motor->TimeSet_Call_Back(Motor->NowState.TimeCountNum,true);//设置下一次脉冲输出频率
  }

速度计算单元

 根据当前处于电机加减速的那个状态,进行速度计算。采用定时1MS计算一次速度的方式。

void MotorCalculateSpeed(MOTOR_CTRL *Motor)
{
	switch(Motor->NowState.RunState)
	{
		case MIDLE:
			Motor->NowState.Speed = Motor->SetData.Speed0;
		break;
		case MACC:
			Motor->NowState.Speed += Motor->SetData.SpeedAdd;
			if(Motor->NowState.Speed > Motor->SetData.SpeedMax)
				Motor->NowState.Speed = Motor->SetData.SpeedMax;
		break;
		case MKEEP:
			Motor->NowState.Speed = Motor->SetData.SpeedMax;
		break;
		case MDEC:
			Motor->NowState.Speed -= Motor->SetData.SpeedAdd;
			if(Motor->NowState.Speed < Motor->SetData.Speed0)
				Motor->NowState.Speed = Motor->SetData.Speed0;
		break;
	}
	
	if(Motor->NowState.Busy)
	{		
		Motor->NowState.TimeCountNum = 1000*1000/Motor->NowState.Speed;//计算定时器应该重装的值
		Motor->NowState.MotorRunTime++;//统计电机运行时间
	}
}

以上是设计的核心思想和算法,旨在抛砖引玉,各位看官有更好的算法实现请在评论区留言,多多交流。若各位看官需要完整工程代码,请关注公众号或评论区私信留言。

好了,今天的分享就到这里。

各位看官,我们下期再见。

标签:TIM3,浅谈,嵌入式,NVIC,TIM,Motor,NowState,MCU,SetData
From: https://blog.csdn.net/2401_84369443/article/details/139578067

相关文章

  • 嵌入式Linux系统编程 — 3.5 utime、utimes、futimens、utimensat函数修改文件时间属
    目录1文件的时间属性简介2utime()函数2.1 utime()函数简介2.2示例程序3 utimes()函数3.1 utimes()函数简介3.2示例程序 4  futimens()函数4.1 futimens()函数简介4.2示例程序5 utimensat()函数5.1 utimensat()函数简介5.2 示例程序1文件的时间......
  • 【转载】ARM嵌入式系统为什么要做内存对齐
    做嵌入式系统软件开发,经常在代码中看到各种各样的对齐,很多时候我们都是知其然不知其所以然,知道要做好各种对齐,但是不明白为什么要对齐,不对齐会有哪些后果,这篇文章大概总结了内存对齐的理由。CPU体系结构和MMU的要求目前有一些RISC指令集的CPU不支持非对齐的内存变量访问操作,比......
  • 浅谈2024年全国中学生生物学联赛
    坐标gd谈谈联赛流水账day-n吃了蛋糕感谢家乐哥、圆领姐、刘sir和黄豆前来捧场,不过忘记叫盛哥来了教练把已经退役的人拉回来考试?然后在教学楼大发雷霆这几天身体一直不太好,先是发烧,退烧了之后就开始感冒+腹泻,十分难受。保济丸狂闷随便看了点比解day0感冒好得差不多,疯狂......
  • 嵌入式Linux中驱动程序的基本框架
    在“嵌入式Linux中内核模块的基本框架”一文中,已经构建好了内核模块的基本框架结构,现在在该框架的基础上进一步扩展,就可以形成Linux下的字符型设备驱动基本框架,下面就详细进行讨论。在Linux系统中,设备驱动共分为三种类型,即字符型、块型和网络型。字符型设备以字节为最小操作单位,......
  • 【机器学习】与【数据挖掘】技术下【C++】驱动的【嵌入式】智能系统优化
    目录一、嵌入式系统简介二、C++在嵌入式系统中的优势三、机器学习在嵌入式系统中的挑战四、C++实现机器学习模型的基本步骤五、实例分析:使用C++在嵌入式系统中实现手写数字识别1.数据准备2.模型训练与压缩3.模型部署六、优化与分析1.模型优化模型量化模型剪枝......
  • Renesas MCU之SCI_SPI接口驱动LCD
    目录概述1软硬件介绍1.1软件版本信息1.2 ST7796-LCD1.3 MCUIO与LCDPIN对应关系2FSP配置项目2.1配置项目参数2.2生成项目框架3代码实现 3.1SPI的库函数3.1.1R_SCI_SPI_Open()3.1.2 R_SCI_SPI_Read()3.1.3  R_SCI_SPI_Write()3.2应用函数接口3.......
  • 作为嵌入式/软件开发工程师你需要知道的东西
    大型软件开发的基本素养中国科学技术大学软件开发规范软件开发规范(试行版)(ustc.edu.cn) 清华软件工程样张标题(tup.com.cn)软件工程这个概念,并将其定义为“为了经济地获得可靠的和能在实际机器上高效运行的软件,而建立和使用的健全的工程规则”(1)将系统化的、严格约......
  • 嵌入式Linux系统编程 — 3.1 Linux系统中的文件类型
    目录1Linux系统中的文件类型简介2普通文件2.1什么是普通文件2.2普通文件分类3 目录文件4 字符设备文件和块设备文件4.1什么是设备文件4.2查看设备文件的方法:5 符号链接文件(link)5.1什么是符号链接文件5.2如何查看和创建符号链接文件6 管道文件7 套接......
  • 嵌入式Linux系统编程 — 1.5 文件描述符详解
    目录1文件描述符简介1.1文件描述符特点1.2标准文件描述符1.3文件描述符的生命周期2 fcntl()函数2.1fcntl()函数简介2.2复制文件描述符(F_DUPFD)2.3获取/设置文件状态标志(F_GETFL/F_SETFL)1文件描述符简介文件描述符(FileDescriptor)是Linux和UNIX系统编......
  • RT-Thread和Infineon主持的嵌入式网络应用开发沙龙
    主题会议由RT-Thread&&Infineon共同主持,PSoc62开发板现场演示从0到1搭建智能数据网关RT-Thread介绍rt-thread社区负责人郭占鑫郭工介绍RT-Thread英飞凌合作伙伴介绍英飞凌产品负责人介绍英飞凌的产品动态、分享未来的一些嵌入式技术发展方向以及应用案例技术分享(钩子函......