PWM参数计算
分辨率越细,分的分量越精细,越稳定,假如它为1%,则它可使得风扇以1为单位的等级调速。可以从1调到100档位,假如它为50%,那么只有两个档位。50和 100。
计算
:如果我现在想要生成一个 PWM频率为1KHZ,占空比可以为任意,PWM分辨率为1%,则PSC(分频器系数),ARR(重装值)应改为多少呢?
Reso分辨率=1%=1/100 ,又因为Reso=1/ARR+1 故而ARR=100-1=99。
PWM频率=1k=1000 = CK_PSC / (PSC+1)* (ARR + 1)
``由于CK_PSC在上篇文章中已经介绍,为72MHz。 故而
式子 CK_PSC / (PSC+1)* (ARR + 1)=PWM频率
变为 72000000 / (PSC + 1) *(99 + 1)=1000,故而PSC=720 -1
挨个配置 打通这条路即可
//通用定时器TIM2时钟开启
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//开启GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//内部时钟配置(方便)
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数的模式
TIM_TimeBaseInitStructure.TIM_Period=100 -1;//重装值ARR,根据上面公式计算
TIM_TimeBaseInitStructure.TIM_Prescaler=720 -1;//psc 根据上面公式计算
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//配置CCR
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给所有成员变量先赋个初值
//下面单独修改通用定时器才用到的成员变量
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1 ;//PWM1模式,参考pptP68,PWM1模式和PWM2模式的区别
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//CNT<CCR时,REF置有效电平,参考pptP69
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse= 50;//设置CCR 即输出比较的值 这里随便给个测试,占空比50%
TIM_OC1Init(TIM2,& TIM_OCInitStructure);
注意,上面用到了TIM_OCStructInit(&TIM_OCInitStructure);给所有成员变量赋值,这是为什么呢?
原因如下:
TIM_OCInitStructure.TIM_OCIdleState和TIM_OCInitStructure.TIM_OutputNState: Idle和里面含有N的一般都是高级定时器才用的
我们用到的都是通用定时器,但TIM_OCInitStructure成员里面有我们用不到的,例如刚才的TIM_OCInitStructure.TIM_OCIdleState
//那如果我们不给未用到的成员赋初值,那么后续就会有各种问题。
//这里可以使用TIM_OCStructInit()函数给所有成员给个默认值,然后单独再修改通用定时器模块的成员变量的值。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//观察引脚图可知,引脚定义,ppt11页可知GPIO_PA0有引脚复用的CH1
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
这里解释下为何复用推挽
,因为下图,来自片上外设
一般使用复用功能输出
引脚复用(Pin Multiplexing)是指单个物理引脚可以根据需要被不同的功能模块使用。在许多现代的微控制器中,物理引脚的功能并不是固定的,而是可以通过软件配置进行更改,这样可以使同一个引脚实现多种功能,这就是引脚复用。这种设计可以有效地节省物理引脚,提高了系统的灵活性。
重定义功能(Alternate Function)则是指在引脚复用功能中,每个物理引脚可以被分配多种不同的功能,这些功能通常是由于同一个引脚在不同的模式下所承担的功能不同而产生的。例如,同一个物理引脚在GPIO模式下可以用作普通的数字输入/输出引脚,而在UART模式下可以用作串行通信的引脚。在这种情况下,这个物理引脚就具有了两种不同的重定义功能。
什么时候开启AFIO时钟?
STM32 中的大部分 GPIO 都有复用功能,所以对于有复用功能的 I/O 引脚,还要开启其复用功能时钟。如 GPIO 的 pin4 可以用作 ADC1 的输入引脚,当我们把它作为 ADC1 使用时,需要开启 ADC1 的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
另外, STM32 的所有 GPIO 都引入到 EXTI 外部中断线上,使得所有的 GPIO 都能作为外部中断的输入源。所以如果把 GPIO 用作 EXTI 外部中断时,还需要开启 AFIO 时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
那么,问题来了!
AFIO 时钟什么时候需要开启?
我们从《STM32中文参考手册_V10》中找到:对寄存器 AFIO_EVCR(事件控制寄存器)、AFIO_MAPR(重映射) 和 AFIO_EXTICRX (外部中断)进行读写操作前,应当首先打开 AFIO 的时钟(设置 APB2 外设时钟使能寄存器 RCC_APB2ENR)。
也就是说:当你需要配置 AFIO 这些寄存器的时候,就需要把 RCC_APB2ENR 寄存器的 AFIO 位置‘1’打开 AFIO 时钟。
跟 AFIO 相关的寄存器有:
事件控制寄存器(AFIO_EVCR)
复用重映射和调试I/O 配置寄存器(AFIO_MAPR)
外部中断配置寄存器1(AFIO_EXTICR1)
外部中断配置寄存器2(AFIO_EXTICR2)
外部中断配置寄存器3(AFIO_EXTICR3)
外部中断配置寄存器4(AFIO_EXTICR4)
假如此时PA0引脚
被占用,我又想使用PA0
的端口处的复用功能的CH1,此时可以看到PA15的重映射定义处有该引脚,此时便可以重映射到该引脚,``但需注意,PA15主功能是JTDI,未加粗,它是调试引脚。故而,需要配置下才能使用。如何配置呢?
1、开启AFIO时钟
//开启AFIO重映射的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
2、配置重映射
由上图可知下图及其解释。
上图在参考手册如下图片的位置可找到。
找到库函数的GPIO的 GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
函数来配置重映射。
其中函数的第一个参数uint32_t GPIO_Remap有三个类型:PartialRemap1_TIM2 //即部分映射 1
PartialRemap2_TIM2 //部分映射2 FullRemap_TIM2//全映射
对应上面的图
这里选择部分映射1
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2 , ENABLE);
如果被映射到的端口的主功能不是调试端口,则配置这两句就可以完成任务了。但如果被配置的端口主功能是调试端口,则需要把调试端口配置为普通的GPIO端口。
如PA0重映射到PA15时,由于PA15引脚主功能是调试端口(如下图)
配置其为普通GPIO
仍使用该函数
GPIO_PinRemapConfig();
第一个参数有三种:
谨慎使用第三个 GPIO_Remap_SwJ_Disable
,它会把调试端口都
变为普通端口,这样的话,程序就没法烧录到板子上了。
这里,让PA15的JTAG失能,而保证调试用的Sw-Dp继续使能即可。让JTAG不发挥作用即可。
配置如下:
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
综合上面的:如果被映射到的端口的主功能不是调试端口,则配置这两句就可以完成任务了
//开启AFIO重映射的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2 , ENABLE);
如果被映射到的是调试端口:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2 , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//这行代码具体看被映射到的端口是哪个调试端口,来具体分析哪个使能哪个使能。
最后一定要开启定时器
TIM_Cmd(TIM2,ENABLE);//开启定时器
如何更改占空比的值? 即CCR
使用TIM_SetCompare1
函数即可
PWM.c
```c
#include "stm32f10x.h" // Device header
/**
* @brief PWM初始化
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//开启GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//内部时钟配置
TIM_InternalClockConfig(TIM2);
//如果使用了重映射,即PA0->PA15,则需要加这三行
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2 , ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//这行代码具体看被映射到的
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数的模式
TIM_TimeBaseInitStructure.TIM_Period=100 -1;//重装值ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=7200 -1;//psc
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//配置CCR
TIM_OCInitTypeDef TIM_OCInitStructure;
//TIM_OCInitStructure.TIM_OCIdleState和TIM_OCInitStructure.TIM_OutputNState: Idle和里面含有N的一般都是高级定时器才用的
//我们用到的都是通用定时器,但TIM_OCInitStructure成员里面有我们用不到的,例如刚才的TIM_OCInitStructure.TIM_OCIdleState
//那如果我们不给未用到的成员赋初值,那么后续就会有各种问题。
//这里可以使用TIM_OCStructInit()函数给所有成员给个默认值,然后单独再修改通用定时器模块的成员变量的值。
TIM_OCStructInit(&TIM_OCInitStructure);//给所有成员变量先赋个初值
//下面单独修改通用定时器才用到的成员变量
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1 ;//PWM1模式,参考pptP68,PWM1模式和PWM2模式的区别
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//CNT<CCR时,REF置有效电平,参考pptP69
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse= 0;//设置CCR 即输出比较的值 这里随便给个测试,后面通过函数TIM_SetCompare1单独更改
TIM_OC1Init(TIM2,& TIM_OCInitStructure);
//GPIO配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出,为何用复用推挽?因为ppt20页码,
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//观察引脚图可知,引脚定义,ppt11页可知GPIO_PA0有引脚复用的CH1
//假如开启了重映射,PA0->PA15,则需要将上面的引脚0,改为15,即
//GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
/**
* @brief 设置占空比
* @param Compare:CCR,比较/捕获 占空比
* @retval 无
*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
main .c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "PWM.h"
#include "Delay.h"
uint8_t i;
int main()
{
OLED_Init();
PWM_Init();
while(1)
{
for(i=0;i<=100;i++)//逐渐变亮
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=0;i<=100;i++)
{
PWM_SetCompare1(100-i);//逐渐变灭
Delay_ms(10);
}
}
}
原理
由于舵机在-90°时高电平在一个周期20ms内维持0.5ms,则占空比 = 0.5/20 =0.025=2.5%
而占空比Duty又 =CCR / (ARR+1) 同理有下
:
占空比(0.5ms时) 即2.5%=0.025
时:则 CCR如果取 500时 则 (ARR+1)就应该取 20k
占空比(1ms时) 即5%=0.05
时:则 CCR如果取 1000时 则 (ARR+1)仍取 20k
占空比(1.5ms时) 即7.5%=0.075
时:则 CCR如果取 1500时 则 (ARR+1)仍取 20k
占空比 (2ms时)即 10%=0.1时
:则CCR取2000 时, 则 (ARR+1)仍取 20k
此时为了好计算以及高电平时间与CCR一一对应才取得ARR=20K即 0.5ms ->CCR设置500 , 1ms->CCR设置为1000,1.5ms->CCR设置为1500,2ms->CCR设置为2000
。
已知条件: Ferq=1/20ms(一个周期) =50Hz ; CK_PSC=72Mhz ; ARR+1=20k
这时将已知条件
带入 第一个公式求频率的公式:Ferq=CK_PSC/ (PSC+1 ) * (ARR+1)可得
求得:PSC=72-1
还可采用其他CCR,ARR,PSC值搭配(但都没上面的方便)
例如:
由50Hz计算 ARR和PSC,这里Reso分辨率用0.1,此时ARR仍为1000-1=99,计算PSC:
CK_PSC =72000000 =50Hz *(psc+1) * (ARR+1)
psc+1=7200/5 ;故而 PSC= 1440 -1
则ARR+1 =1000,仍按照上面计算占空比:CRR=25 ,ARR+1=1000 ,此时占空比为0.025,高电平时间为0.5ms 这样有个对应关系:
没上面的看着简洁明了。上面CCR=500 ->0.5ms,这里CCR=25 ->0.5。
代码配置(按照最上面的)这两行就可以这样写:
TIM_TimeBaseInitStructure.TIM_Period=72 -1;//重装值ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=20000-1;//psc 20k
文章浏览阅读355次。javaScript的Array.sort()方法被用来就地数组元素进行排序,并返回排序后的数组。在这里,我们将使用冒泡排序(简单排序技术)对数组元素进行升序排序。错误的输出:[100、12、23、25、31、75、81]正确的输出:[12、23、25、31、75、81、100]示例:本示例使用compare函数对数组元素进行升序排序。示例:本示例使用compare函数对数组元素进行降序排序。输入:[12,25,31,23,75,81,100]示例:本示例以字符串格式对数组元素进行排序。_js 0-100升序排序
文章浏览阅读1.5k次。windows进程监控批处理,检测进程不在,则直接启动进程_批处理】如何启动进程
文章浏览阅读499次。通过域生成的目录,需要单独只存储目录,想把目录内容转为普通带格式文本。_word域转换为静态文本
文章浏览阅读506次。发送纯文本文件非常简单,基本上拷贝别人的代码做下业务处理即可但是发送不是纯文本的邮件时有点不同,为了简单介绍,就一步一步来其实就是发送的内容需要构建一个消息体然后设置消息提的内容,内容就是HTML代码,这样接收者看到的就是带有样式的邮件内容你也可以增加附件,增加背景音乐等我直接贴代码:packagecom.mail;importjava.util.Date;importjava.util.P..._java 发送邮件 字体红色
文章浏览阅读44次。对于一个网页设计师来说,大部分时间都花在不同的软件环境下的测试,因此,参考手册对他们有很大的帮助。在这篇文章中,我提出了34个重要的备忘单,帮助开发人员在开发的时候遇到问题能够及时解决WordPress1. Liquidicity – WordPress help sheet2. WP-The Loop3. WordPress Theme Development Check L...
文章浏览阅读523次。你可以让计算属性返回一个函数,然后在模板中作为方法调用这个函数并传入需要的参数。_vue 计算属性传参
文章浏览阅读1.2k次。一、概述 ISP(Image Signal Processor), 即图像信号处理, 主要作用是对前端图像传感器输出的信号做后期处理, 依赖于 ISP 才能在不同的光学条件下都能较好的还原现场细节。Cmos YUV sensor 的 ISP 处理流程如图所示:景物通过 Lens 生成的光学图像投射到 sensor 表面上, 经过光电转换为模拟电信号, 消噪声后经过 A/D 转换后变..._isp流程
文章浏览阅读693次。第1章 系统简介 概述 随着水路运输业的快速发展,现代港口规模和吞吐量不断增长,港口各种装卸设备数量不断增加,如何高效可靠使用这些装卸设备是各港口单位关心的问题。港口装卸设备中主要的斗轮堆取料机又称悬臂式堆取料机,是散货堆场作业的核心设备。它是堆取料合一的机械,即是一种挖取和堆存煤炭、矿石、砂石等松散物料的高效率机械。它不仅适用于电厂,而且在码头、港口也很适用,大多数的转运煤及松散物料..._港口取料机防碰撞方法
文章浏览阅读566次,点赞11次,收藏9次。在下面的示例中,上下文管理器提供了数据库连接,并且还构建了事务内部的操作。Python DBAPI 的默认行为包括事务始终在进行中;当连接范围被释放时,会发出 ROLLBACK 来结束事务。当我们想要提交数据时,我们通常需要调用Connection.commit()方法,可以隐式开启一个事务。并且执行正常后,会自动提交。如果有异常发送,则会回滚。此处我们以pymysql为mysql的数据库驱动。_sqlalchamy with
文章浏览阅读3.6k次。怎么解决 boot/efi 没有分配空间的问题呢。某些机器上,特别是有双硬盘的电脑,在装 Linux 双系统时,经常会提示 boot/efi 没有分配空间。有时候,明明已经分配了,还会提示没有分配,如何解决呢。1、启动机器时,按下 F12,进入 BIOS有的品牌的机器,可能是按F2、DEL、ESC等。2、进入BIOS设置界面后,按左右方向键←→,选择【启动】3、再按上下方向键↑↓,选择【Boot ..._安装linux系统提示boot:
文章浏览阅读2.4k次。背景相信很多公司都是基于Hive做的数仓,从而对外提供数据服务。这里只讨论离线数仓,做数仓必然离不开对大量数据的ETL处理工作。目前的ETL种类繁多,可选择的工具也有很多,比如使用Sqoop, DataX做离线的T+1数据同步, Spark或者Flink做T+0的实时数据同步等。目前有很多公司业务是T+1的,每天需要同步昨天的业务库(MySQL、mongodb等)的数据到Hive数据仓库中,..._数据向数仓同步数据需要注意的
文章浏览阅读1.2k次。人生苦短,我用 python!我一直坚持使用 python3,因为它代表了python的未来。虽然向后兼容是它的硬伤,但是这个局面迟早会改变的, 而且python3的未来需要每个人的帮助和支持。 目前市面上的教程书籍,网上的手册大部分基本都是2.x系列的,专门基于3.x系列的书籍少的可怜。最近看到一本《Python Cookbook》3rd Edition,完全基于python3,写的也很不错。 ..._pythoncookbook第三版电子书