STM32LL库编程系列第五讲——定时器PWM输出+DS3115舵机控制

第五讲——定时器PWM输出+DS3115舵机控制



前言

本文是STM32LL库编程系列第五讲,主要分享如何使用定时器输出PWM,本文使用的芯片型号是STM32F407
编程逻辑:手机APP发送数据——>控制板蓝牙接收数据——>DMA传输数据置内存——>到内存提取需要数据——>经过换算转化成PWM占空比——>控制舵机
说明一下,每次遇到新配置我都会详细说明,如果直接个答案,没解析的都是往期文章解析过的,有需要可以考古。我每次工程会开一下搭配外设,比如串口、DMA,如果不需要自行省略。


一、什么是舵机?

舵机是一种电机装置,用来控制机械装置的方向和位置。它通常由电机、传动装置和位置反馈装置组成,可以通过控制信号来精确地控制其位置和角度。舵机通常用来控制舵面、方向盘、摆动臂等部件的运动,从而实现精确的定位和控制。舵机的工作原理类似于普通的电机,但其精度和稳定性更高,适用于需要精确控制的场合。舵机主要分为模拟舵机和数字舵机。

1.模拟舵机和数字舵机的区别

在电路上:数字舵机的控制电路比模拟舵机多了微处理器和晶振,电路设计也更加复杂。
信号驱动上:数字舵机只需发送1次PWM信号就能保持在规定的某个位置,会自动按设定速度转动要控制角度。而模拟舵机是需要多次发送PWM信号才能够保持在规定的位置上,要实现对舵机的控制,还要按照规定的要求速度进行转动。
工作模式:数字舵机分辨率更高,能产生更大的固定力量。模拟舵机是脉冲动力,这种动力实际上每秒传递50次,被调制成开/关脉冲的最大电压,并产生小段小段的动力。
总之数字舵机各方面优于模拟舵机(浅显理解)

二、什么是PWM?

PWM(Pulse Width Modulation,脉宽调制)是一种常用的调制技术,通过改变信号的脉冲宽度来实现对模拟信号的模拟。PWM信号由一个固定频率的周期性脉冲组成,每个周期内包含一个高电平(通常称为"on"状态)和一个低电平(通常称为"off"状态)。通过改变高电平的持续时间(即脉冲宽度),可以实现对信号的调制。

1.PWM输出原理

在这里插入图片描述
图 14.1.1 就是一个简单的 PWM 原理示意图。图中,我们假定定时器工作在向上计数 PWM模式,且当 CNT<CCRx 时,输出 0,当 CNT>=CCRx 时输出 1。那么就可以得到如上的 PWM示意图:当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。

根据PWM模式、输出极性、计数方向,共有8种PWM输出状态,如下图所示:
在这里插入图片描述

二、使用CubeMX建立工程

这里只说明关于定时器部分的配置介绍,其他外设配置说明见往期文章
首先选择时钟和PWM输出通道,这里用的是内部时钟,选择TIM2的CH2、CH3、CH4输出PWM。
在这里插入图片描述
设置参数
TIM2时钟频率为84MHz,预分频设置为84,从而 f(CK_INT) =1MHz。(84-1是因为重装载时会加1)
选择向上计数
计数重装载值设置为5000,则一个周期耗时5000/1000000=5ms,频率为200Hz,DS3115舵机的控制频率为50Hz~330Hz,频率越高舵机响应速度越快,位置控制精度也会更高。同样热量与功耗也会更高。故200Hz足够。
CKD是的定时器时钟 (CK_INT) 频率与数字滤波器所使用的采样时钟 (TIx) 之间的分频比,这里不分频。(上一章有详细解释)
使能计数自动重装载
三个通道PWM输出设置一致
设置PWM模式1
Pulse是用来设置初始占空比,占空比=Pulse/ARR,这里初始为0
Fast mode 顾名思义为快速模式,适合非常高频率PWM开启,这里不需要
输出极性为高
在这里插入图片描述
接着设置好
UART5(用于蓝牙数据收发);
UART5_RX的DMA(用于搬运数据,注意开启中断);
USART1(用于串口助手显示);
对以上设置有疑问可以参考往期文章

将PWM输出引脚设置下拉,
对于输入引脚,如果没有外部上下拉,就一定要内部上下拉,避免空闲时误触发,具体上拉还是下拉根据需要的空闲状态而定。而输出引脚可以不上下拉,当然,如果有空闲状态,最好拉置空闲状态。
配置LL库输出,至此CubeMX配置结束(这里跳了很多中间步骤,如果结合往期文章还是无法理解,结尾会给出工程下载地址,可以下载查看)

三、keil工程代码编写

这是DS3115舵机控制逻辑,可知占空比=10%对应0度。占空比=50%对应180度
在这里插入图片描述

1.dma.c

编写USART3的DMA接收函数(解析见往文)

/* USER CODE BEGIN 2 */
void uart5_DMA_init(void)
{
	//DMA接收配置
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)&(UART5->DR));//设置外设地址
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_0, (uint32_t)uart5RX_buf);//设置内存地址
	LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, 6);//设置接受的数据长度
	
	//清除中断标志位
	LL_DMA_ClearFlag_TC0(DMA1);
	
	LL_DMA_EnableIT_TC(DMA1,LL_DMA_STREAM_0);
	
	LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);//使能DMA数据流
	LL_USART_EnableDMAReq_RX(UART5);//启用串口DMA接收模式
}
/* USER CODE END 2 */

这里只所以把数据长度设置为6是因为我蓝牙每个数据帧占6个字节。
分别为:起始位、数据位1、数据位2、数据位3、校验位、停止位
于是每次发送一帧数据就会进入TC中断。

2.tim.c

有一个点差点忘强调了,用cubemx使能了定时器用LL库生成工程,定时器配置函数会警告。报错语句如下。

  TIM_InitStruct.Prescaler = 84-LL_TIM_IC_FILTER_FDIV1_N2;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 5000-LL_TIM_IC_FILTER_FDIV1_N2;

原因是数据大小不匹配。Prescaler寄存器是32位的,而LL_TIM_IC_FILTER_FDIV1_N2是16位的,本生这种警告不不会导致程序错误。但问题是LL_TIM_IC_FILTER_FDIV1_N2值有错。通过查找发现LL_TIM_IC_FILTER_FDIV1_N2 = 0x10<<20;与1相差甚远,所以大家自行改回1,如下所示:

  TIM_InitStruct.Prescaler = 84-1;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 5000-1;

3.stm32f4xx_it.c

首先定义参数如下

/* USER CODE BEGIN TD */
unsigned char uart5RX_buf[6]={0};
unsigned char DMA1_TC0;
/* USER CODE END TD */

这里只开启了DMA中断,中断服务函数如下所示:

void DMA1_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */
	if(LL_DMA_IsActiveFlag_TC0(DMA1))
	{
		LL_DMA_ClearFlag_TC0(DMA1);
		LL_USART_Disable(UART5);
		DMA1_TC0 = 1;
	}
  /* USER CODE END DMA1_Stream0_IRQn 0 */

  /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */

  /* USER CODE END DMA1_Stream0_IRQn 1 */
}

进入TC中断后,先清除中断标志位、关闭UART5的使能(很重要,后面解释)、标志位置1。

4.mian.c

首先定义参数如下

  /* USER CODE BEGIN 1 */
u16 DS3115_pwm1=500,DS3115_pwm2=500,DS3115_pwm3=1010;
  /* USER CODE END 1 */

UART5_DMA初始化别忘了

  /* USER CODE BEGIN 2 */
  uart5_DMA_init();
  /* USER CODE END 2 */

while循环函数

while (1)
  {
		if(DMA1_TC0)
		{
			DMA1_TC0 = 0;
			//进行输入数据与脉宽的换算,设置的uart5RX_buf[x]=0~100对应脉宽500~2500
			DS3115_pwm1 = uart5RX_buf[1]*20+500;
			DS3115_pwm2 = uart5RX_buf[2]*20+500;
			DS3115_pwm3 = uart5RX_buf[3]*20+500;
			//设置上下限限制
			if(DS3115_pwm1>2500)
				DS3115_pwm1 = 2500;
			if(DS3115_pwm1<500)
				DS3115_pwm1 = 500;
			
			if(DS3115_pwm2>2500)
				DS3115_pwm2 = 2500;
			if(DS3115_pwm2<500)
				DS3115_pwm2 = 500;
			
			if(DS3115_pwm3>2500)
				DS3115_pwm3 = 2500;
			if(DS3115_pwm3<500)
				DS3115_pwm3 = 500;
			//打印到串口助手输出
			printf("DS3115_pwm1 = %d  ",DS3115_pwm1);
			printf("DS3115_pwm2 = %d  ",DS3115_pwm2);
			printf("DS3115_pwm3 = %d\n",DS3115_pwm3);
			//将计算好的占空比设置给各通道PWM
			LL_TIM_OC_SetCompareCH2(TIM2,DS3115_pwm1); 
			LL_TIM_OC_SetCompareCH3(TIM2,DS3115_pwm2); 
			LL_TIM_OC_SetCompareCH4(TIM2,DS3115_pwm3);
			//使能UART5和DMA数据流
			LL_USART_Enable(UART5);
			LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);//使能DMA数据流
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

当我发送一个数据到蓝牙,数据存入UART5_DR寄存器,同时DMA会将数据从UART5_DR转移到uart5RX_buf[6]数组中,6个字节的数据都转移结束进入TC中断,从而DMA1_TC0 = 1,进入if函数,将uart5RX_buf数组中的值换算成PWM设置给对应通道同时设置DMA1_TC0 = 0,这样一个控制流程就完成了,等待下次数据到来开启下一个轮回。
当DMA设置的字节个数全部传输完成,会自动失能DMA该通道数据流,所以每次需要使用LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);重新使能,才能开启下一个轮回。如果不对UART5进行同步的使能和失能处理会导致DMA“关机”错误,需要重新复位程序才能“开机”。
当串口的接收寄存器中已经接收到一帧数据,但数据寄存器中的数据没有及时被DMA转移走,而后面的数据又来了,会导致后面的数据无法存入,从而产生了上溢错误(ORE),而一旦产生上溢错误后,就无法再触发 DAM 请求,及时之后再启动 DMA 也不行,无法触发 DMA 请求就无法将数据寄存器内的数据及时转移走,如此陷入“关机”。
也就是说在if函数里,DMA是关闭的,而UART5是开启的,在这段函数运行时间内,如果继续给蓝牙发送数据,就会进入DMA“关机”,故if函数里也要把UART5关闭,这也是为什么在TC中断服务函数里失能UART5的原因。(花了大量时间才找到这个错误,忘珍惜)。

四、效果展示

手机APP《蓝牙调试器》自定义界面如图,这款APP挺好用的。
在这里插入图片描述

第五讲定时器PWM输出+DS3115舵机控制

五、工程下载

链接:https://pan.baidu.com/s/188rcXj0lLwKiDcqt22Aomg?pwd=1234
提取码:1234

尾言

时间匆忙,有错误之处还望大家指出,(可能有些错别字,不影响阅读就好)

  • 35
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
"2017 电赛 滚球控制系统源代码(pixy ds3115舵机 stm32 矩阵按键 lcd12864).zip" 是一个包含源代码的压缩文件。该文件主要用于电赛中滚球控制系统的开发。在这个项目中,使用了一些特定的硬件设备,包括pixy ds3115舵机、stm32单片机、矩阵按键和lcd12864液晶屏。 根据文件名和硬件设备的记述,可以推测这个源代码项目的目的是控制一个滚球,使其按照特定的规则进行运动。pixy ds3115舵机可能用来检测滚球的位置和方向,并通过与stm32单片机的通信,控制舵机转动来改变滚球的运动路径。矩阵按键可能用来提供用户与系统的交互界面,比如调整滚球速度、选择不同的运动模式等等。lcd12864液晶屏则可能用于显示滚球的状态、系统参数等信息。 这个源代码项目的压缩文件中应该包含了相关的源代码文件,供开发者参考和使用。通过阅读源代码,开发者可以了解整个滚球控制系统的工作原理和实现方式。源代码可能包含对硬件设备的初始化和配置、算法逻辑实现、运动控制代码等。 如果您需要使用或了解这个源代码项目,可以解压缩压缩文件,并通过合适的开发工具打开源代码文件进行阅读和使用。为了更好地理解和运行源代码,建议您先了解相关的硬件设备和编程平台(如stm32)的基本知识。 总之,这个源代码项目是为了实现一个滚球控制系统而开发的,提供了相关的源代码文件供开发者使用。希望以上解释对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值