历史上的今天
返回首页

历史上的今天

今天是:2025年06月16日(星期一)

正在发生

2020年06月16日 | STM32学习笔记(5):通用定时器PWM输出

2020-06-16 来源:eefocus

1.TIMER输出PWM基本概念

脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。一般用来控制步进电机的速度等等。


STM32的定时器除了TIM6和TIM7之外,其他的定时器都可以用来产生PWM输出,其中高级定时器TIM1和TIM8可以同时产生7路的PWM输出,而通用定时器也能同时产生4路的PWM输出。


1.1PWM输出模式

STM32的PWM输出有两种模式,模式1和模式2,由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。模式1和模式2的区别如下:

110:PWM模式1-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。

111:PWM模式2-在向上计数时,一旦TIMx_CNTTIMx_CCR1时通道1为有效电平,否则为无效电平。

由此看来,模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。

而从计数模式上来看,PWM也和TIMx在作定时器时一样,也有向上计数模式、向下计数模式和中心对齐模式,关于3种模式的具体资料,可以查看《STM32参考手册》的“14.3.9 PWM模式”一节,在此就不详细赘述了。


1.2PWM输出管脚

PWM的输出管脚是确定好的,具体的引脚功能可以查看《STM32参考手册》的“8.3.7定时器复用功能重映射”一节。在此需要强调的是,不同的TIMx有分配不同的引脚,但是考虑到管脚复用功能,STM32提出了一个重映像的概念,就是说通过设置某一些相关的寄存器,来使得在其他非原始指定的管脚上也能输出PWM。但是这些重映像的管脚也是由参考手册给出的。比如说TIM3的第2个通道,在没有重映像的时候,指定的管脚是PA.7,如果设置部分重映像之后,TIM3_CH2的输出就被映射到PB.5上了,如果设置了完全重映像的话,TIM3_CH2的输出就被映射到PC.7上了。


1.3PWM输出信号

PWM输出的是一个方波信号,信号的频率是由TIMx的时钟频率和TIMx_ARR预分频器所决定的,具体设置方法在前面一个学习笔记中有详细的交代。而输出信号的占空比则是由TIMx_CRRx寄存器确定的。其公式为“占空比=(TIMx_CRRx/TIMx_ARR)*100%”,因此,可以通过向CRR中填入适当的数来输出自己所需的频率和占空比的方波信号。


2.TIMER输出PWM实现步骤

1.设置RCC时钟;

2.设置GPIO时钟;

3.设置TIMx定时器的相关寄存器;

4.设置TIMx定时器的PWM相关寄存器。


第1步设置RCC时钟已经在前文中给出了详细的代码,在此就不再多说了。需要注意的是通用定时器TIMx是由APB1提供时钟,而GPIO则是由APB2提供时钟。注意,如果需要对PWM的输出进行重映像的话,还需要开启引脚复用时钟AFIO。


第2步设置GPIO时钟时,GPIO模式应该设置为复用推挽输出GPIO_Mode_AF_PP,如果需要引脚重映像的话,则需要用GPIO_PinRemapConfig()函数进行设置。


第3步设置TIMx定时器的相关寄存器时,和前一篇学习笔记一样,设置好相关的TIMx的时钟和技术模式等等。具体设置参看“TIMER基本定时功能”的学习笔记。


第4步设置PWM相关寄存器,首先要设置PWM模式(默认情况下PWM是冻结的),然后设置占空比(根据前面所述公式进行计算),再设置输出比较极性:当设置为High时,输出信号不反相,当设置为Low时,输出信号反相之后再输出。最重要是是要使能TIMx的输出状态和使能TIMx的PWM输出使能。


相关设置完成之后,就可以通过TIM_Cmd()来打开TIMx定时器,从而得到PWM输出了。


3.TIMER输出PWM源代码

由于我现在手上的奋斗开发板是将PB.5接到LED上,因此需要使用TIM3的CH2通道,并且要进行引脚重映像。打开TIM3之后,PWM输出,使得LED点亮,通过改变PWM_cfg()中的占空比可以调节LED的亮度。


#include "stm32f10x_lib.h"


void RCC_cfg();

void GPIO_cfg();

void TIMER_cfg();

void PWM_cfg();

//占空比,取值范围为0-100

int dutyfactor = 50;


int main()

{

int Temp;

RCC_cfg();

GPIO_cfg();

TIMER_cfg();

PWM_cfg();


//使能TIM3计时器,开始输出PWM

TIM_Cmd(TIM3, ENABLE);


while(1);

}


void RCC_cfg()

{

//定义错误状态变量

ErrorStatus HSEStartUpStatus;


//将RCC寄存器重新设置为默认值

RCC_DeInit();


//打开外部高速时钟晶振

RCC_HSEConfig(RCC_HSE_ON);


//等待外部高速时钟晶振工作

HSEStartUpStatus = RCC_WaitForHSEStartUp();

if(HSEStartUpStatus == SUCCESS)

{

//设置AHB时钟(HCLK)为系统时钟

RCC_HCLKConfig(RCC_SYSCLK_Div1);


//设置高速AHB时钟(APB2)为HCLK时钟

RCC_PCLK2Config(RCC_HCLK_Div1);


//设置低速AHB时钟(APB1)为HCLK的2分频

RCC_PCLK1Config(RCC_HCLK_Div2);


//设置FLASH代码延时

FLASH_SetLatency(FLASH_Latency_2);


//使能预取指缓存

FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);


//设置PLL时钟,为HSE的9倍频8MHz * 9 = 72MHz

RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);


//使能PLL

RCC_PLLCmd(ENABLE);


//等待PLL准备就绪

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);


//设置PLL为系统时钟源

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);


//判断PLL是否是系统时钟

while(RCC_GetSYSCLKSource() != 0x08);

}


//开启TIM3的时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

//开启GPIOB的时钟和复用功能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);


}


void GPIO_cfg()

{

GPIO_InitTypeDef GPIO_InitStructure;



//部分映射,将TIM3_CH2映射到PB5

// GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);


//选择引脚5

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

//输出频率最大50MHz

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

//复用推挽输出

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;


GPIO_Init(GPIOB,&GPIO_InitStructure);

}


void TIMER_cfg()

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;


//重新将Timer设置为缺省值

TIM_DeInit(TIM3);

//采用内部时钟给TIM3提供时钟源

TIM_InternalClockConfig(TIM3);

//预分频系数为0,即不进行预分频,此时TIMER的频率为72MHz

TIM_TimeBaseStructure.TIM_Prescaler = 0;

//设置时钟分割

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

//设置计数器模式为向上计数模式

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

//设置计数溢出大小,每计7200个数就产生一个更新事件,即PWM的输出频率为10kHz

TIM_TimeBaseStructure.TIM_Period = 7200 - 1;

//将配置应用到TIM3中

TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

}


void PWM_cfg()

{

TIM_OCInitTypeDef TimOCInitStructure;

//设置缺省值

TIM_OCStructInit(&TimOCInitStructure);

//PWM模式1输出

TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

//设置占空比,占空比=(CCRx/ARR)*100%或(TIM_Pulse/TIM_Period)*100%

TimOCInitStructure.TIM_Pulse = dutyfactor * 7200 / 100;

//TIM输出比较极性高

TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

//使能输出状态

TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

//TIM3的CH2输出

TIM_OC2Init(TIM3, &TimOCInitStructure);

//设置TIM3的PWM输出为使能

TIM_CtrlPWMOutputs(TIM3,ENABLE);

}

推荐阅读

史海拾趣

CML Microcircuits公司的发展小趣事

随着技术的不断进步,CML Microcircuits公司始终保持对创新的追求。在模拟、数字和混合信号集成电路领域,CML不断推出具有创新性的产品,满足市场的多样化需求。通过与全球顶级客户的紧密合作,CML深入了解市场趋势,并将其转化为具有竞争力的产品。这些努力使CML在行业中逐渐建立了技术创新的引领者地位。

Gigarams Semiconductor Device Corp公司的发展小趣事
这可能是由于定位力量不够或初始力矩过大导致的。解决方案包括调整定位参数、优化启动策略或增加缓冲装置以减少振动。
正泰公司的发展小趣事

正泰公司始建于1984年,初期以低压电器业务为主。随着公司业务的不断拓展,正泰逐渐将业务范围扩展至智慧能源系统领域。在这一过程中,公司深入践行“产业化、科技化、国际化、数字化、平台化”战略举措,形成了“绿色能源、智能电气、智慧低碳”三大板块。正泰的低压电器产品长期保持国内领先地位,品牌认知度和客户粘性极强,为公司后续的发展奠定了坚实的基础。

Cypress(赛普拉斯)公司的发展小趣事

Cypress在USB技术领域取得了显著的成就。自1996年开始深耕USB产品以来,Cypress逐渐成为了USB领域内的领先者。公司推出的EZ-PD系列产品是业界第一个支持USB PD 3.0供电规范的解决方案,赢得了市场的广泛认可。此外,Cypress还不断推出新的USB产品和技术,推动了USB技术的不断发展和创新。

博流(Bouffalo Lab)公司的发展小趣事

博流公司的卓越表现引起了业界的广泛关注。2019年,公司荣获了Red Herring Top 100 Asia大奖,这是对其在AIoT/边缘计算SOC芯片研发领域的快速成长和技术实力的高度认可。此外,博流还获得了多项国内外专利和荣誉,证明了公司在行业内的领先地位。

DESOUTTER公司的发展小趣事

随着全球经济的不断发展,Desoutter公司开始实施国际化战略,积极开拓国际市场。公司通过与全球知名企业的合作,如大众集团、捷豹路虎集团等,成功进入了欧洲、北美等地区的市场。同时,Desoutter公司还在全球范围内建立了多个生产基地和研发中心,以更好地满足不同地区客户的需求。

问答坊 | AI 解惑

1394接口在PC市场萎缩 转攻消费市场

据In-Stat报道,1394在PC和外设市场正在受到来自USB的强烈挤压,但有迹象表明,在消费电子(CE)市场正出现新的生机。该高科技市场调查公司称,数字电视、有线电视机顶盒以及DVD录像机都在推动着1394a在消费电子市场的增长。另外,高清音视频网络联盟 ...…

查看全部问答>

我的电脑

我的电脑 [ 本帖最后由 geyin 于 2009-6-13 21:48 编辑 ]…

查看全部问答>

测量电源纹波[转帖]

测量电源纹波本身有一定技巧性。图1给出了一个不当使用示波器测量电源纹波的实例。在这个例子中出现了几个错误,首先是使用了接地线很长的示波器探针;其二是让由探针和接地线形成的回路靠近功率变压器和开关元件;最后是允许在示波器探针和输出电 ...…

查看全部问答>

基于ARM 和DSP的地震加速度信号处理系统设计

  1 系统构成及工作原理     地震加速度计由传感探头、光电转换及信号处理系统构成.传感探头由采用基于3x3耦合的光纤M—z干涉仪和相关机械部分组成.如图1所示,干涉仪的输入端是一只2x2耦合器,输出端是一只3x3耦合器 ...…

查看全部问答>

WINCE 6.0下如何安装驱动

有个触摸屏装的WINCE 6.0,想增加一个串口,使用了USB转串口的转换器,但是驱动不知道怎么装上去,转换器的驱动只有一个DLL文件和一个INF文件,请问如何安装?…

查看全部问答>

智能手机蓝牙虚拟串口开发?

智能手机中没有提供串口通信服务,因此需要先在智能手机上安装一个虚拟串口软件,请问各位请没有做过相关工作的,这个软件是自己开发还是有可以免费使用的呀?…

查看全部问答>

CCS中main函数输入参数int argc,char *argv[]; 的设置

有一个关于CCS的使用问题:在CCS中怎么设置C语言main函数的输入参数int argc,char *argv[];   麻烦知道的大虾提示下! 谢谢…

查看全部问答>

TI/M4芯片时钟怎么设置啊?

ROM_SysCtlClockSet(SYSCTL_SYSDIV_4| SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); 我觉得太慢,改成ROM_SysCtlClockSet(SYSCTL_SYSDIV_64| SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); 第一次下载进去了,第二次怎么 ...…

查看全部问答>

【深度评测STM32 Nucleo】+st-link utility工具试用

本帖最后由 fyaocn 于 2014-12-15 11:11 编辑 进一步的测试中,从网上搜到了很多适用的工具,其中最傻瓜的就是这个ST-LINK GUI Utility了。把程序下载的板子,可以直接查看到内存的数据,更牛的是可以直接修改,包括运行的中间数据。 STM提供了 ...…

查看全部问答>

[转载]DM642 DSP内存管理的一点经验

可恶的DSP又吞没了我的假期。先整理个经验,碰到类似问题可以获得些启发的: 俺的程序是QC_LDPC,传说中最占内存的信道编码,所以程序和内存的矛盾是这个程序的主要矛盾。 1. 本想在一个函数里建立一个很大的数组,函数运行完会自动删掉,但那是没 ...…

查看全部问答>