STM32单片机--PWM输出

发布者:快乐阳光最新更新时间:2020-02-15 来源: eefocus关键字:STM32  单片机  PWM输出 手机看文章 扫描二维码
随时随地手机看文章

采用定时器2的通道2,使PA1输出频率1K,占空比40的PWM波形,用PA8随意延时取反led灯,指示程序运行


上午花了半天时间熟悉了stm32的PWM模块。中午利用午饭时间把PWM功能调试成功。当然,很简单的东西,也许很多前辈估计都不屑一顾的东西。


今天最大的感叹就是网络资源实在是个巨大的宝库,真的很庆幸,在这个复杂的社会环境里,在一个到处充斥着私心、私利的时代,各个网站,各个论坛上的众多网友都时刻保持着开源的氛围。学习一定要和他人交流,而网络提供了这么一个极好的平台。


废话少说,言归正传。

实现功能:采用定时器2的通道2,使PA1输出频率1K,占空比40的PWM波形,用PA8随意延时取反led灯,指示程序运行。


首先熟悉一下定时器的PWM相关部分。看图最明白

其实PWM就是定时器的一个比较功能而已。

CNT里的值不断++,一旦加到与CCRX寄存器值相等,那么就产生相应的动作。这点和AVR单片机很类似。既然这样,我们要产生需要的PWM信号,就需要设定PWM的频率和PWM的占空比。


首先说频率的确定。由于通用定时器的时钟来源是PCLK1,而我又喜欢用固件库的默认设置,那么定时器的时钟频率就这样来确定了,如下:

AHB(72MHz)→APB1分频器(默认2)→APB1时钟信号(36MHz)→倍频器(*2倍)→通用定时器时钟信号(72MHz)。

这里为什么是这样,在RCC模块学习记录里有详细记载,不多说。

因此图中的CK_PSC就是72MHz了。

下面的资料也是网上一搜一大把,我就罗列了:

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

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

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


由此看来,模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。我用的是模式一,因此后面的设定都是按照模式一来设定的。


PWM的周期是就是由定时器的自动重装值和CNT计数频率决定的。而CNT的计数时钟是CK_PSC经分频器PSC得到,因此CNT的时钟就是CK_PSC/分频系数。这个分频系数在TIM_TimeBaseStructure.TIM_Prescaler确定。成都网站设计我设置的值是72,因此CNT的计数频率也就是CK_CNT的频率为1MHz。


下一步就是确定定时器自动重装值。因为CNT每自加到ARR寄存器的值时就会自动清零,当然前提是设定为为向上计数模式,而就是根据这个溢出事件来改变PWM的周期。所以PWM信号的频率由ARR的值来确定。我设置的值是1000-1,即TIM_TimeBaseStructure.TIM_Period = 1000-1;因此PWM的周期是1MHz/1000=1KHz。


接下来就要确定PWM的占空比了。因为CNT在自加到ARR值的过程中会不断和CRRX的值相比较,一旦二者相等就产生匹配事件,但要注意CNT不会理会这件事,它会继续++直到等于ARR。而CRRX的值我设定为400-1,那么占空比就随之确定为40%。


好了,下面就是库函数的配置了。


TIMER输出PWM实现步骤

1.设置RCC时钟;

2.设置GPIO;

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

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


首先是main函数和全局变量申明,很简单,不作说明


GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TimOCInitStructure;

int main(void)
{
      

rcc_cfg();
      gpio_cfg();
      tim2_cfg();
      pwm_cfg();
// 
  while (1)
  {
   
    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
 
 delay();


    GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
 
    delay();
  }
}


下面是IO口的配置:

void gpio_cfg()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}


此处要注意的是PWM输出口要配置为复用推挽输出,原因我也不知道,反正照搬就是了。

下面是TIM配置函数,注释很清楚了,不作说明:

void tim2_cfg()
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

TIM_DeInit(TIM2);
  TIM_InternalClockConfig(TIM2);
  //预分频系数为72,这样计数器时钟为72MHz/72 = 1MHz
  TIM_TimeBaseStructure.TIM_Prescaler = 72;
  //设置时钟分割
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  //设置计数器模式为向上计数模式
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  //设置计数溢出大小,每计1000个数就产生一个更新事件
  TIM_TimeBaseStructure.TIM_Period = 1000-1;
  //将配置应用到TIM2中
  TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

//禁止ARR预装载缓冲器
  TIM_ARRPreloadConfig(TIM2, DISABLE);
 
  TIM_Cmd(TIM2, ENABLE);  //使能TIMx外设
}


接下来是关键的PWM的配置函数:

void pwm_cfg()

{

//设置缺省值

TIM_OCStructInit(&TimOCInitStructure);

//PWM模式1输出

TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

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

TimOCInitStructure.TIM_Pulse = 400-1;

//TIM输出比较极性高

TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

//使能输出状态

TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

//TIM2的CH2输出

TIM_OC2Init(TIM2, &TimOCInitStructure);

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

TIM_CtrlPWMOutputs(TIM2,ENABLE);

}


stm32固件库的输出比较单元结构体与定时器的时基单元是分开定义的,而PWM模式只是输出比较结构体成员TimOCInitStructure.TIM_OCMode的一个取值,当把此结构体填充完后,还要映射到某个定时器,用TIM_OCXInit函数实现,我用了一个X,说明不止一个这样的函数,事实上,stm32的通用定时器都有四个通道,每个通道对应一个初始化函数,这里真够纠结的!最后还要使能该定时器的PWM输出功能,TIM_CtrlPWMOutputs(TIM2,ENABLE)函数要注意,是outputs而不是output,说明TIM2不止一个通道嘛!够复杂,够繁琐的!

下面是输出比较单元的结构体原型:

typedef struct
{
  uint16_t TIM_OCMode;       

uint16_t TIM_OutputState;  

uint16_t TIM_OutputNState; 

uint16_t TIM_Pulse;        

uint16_t TIM_OCPolarity;   

uint16_t TIM_OCNPolarity;  

uint16_t TIM_OCIdleState;  

uint16_t TIM_OCNIdleState; 
} TIM_OCInitTypeDef;


其中没有加色的成员是高级定时器才有的,通用定时器就不用管了。


这里还有个TimOCInitStructure.TIM_OCPolarity 成员需要注意,它有什么作用呢?在网上查的资料,如下图:

前面说到pwm有pwm1和pwm2两种模式,这两种模式只能控制到OCXREF为止,TIM_OCPolarity 能控制OC1是直接等于OCXREF,还是取反极性!OC1才是最终的PWM信号。


这里有个小插曲,我用示波器去测量PWM信号,发现信号居然是双极性的,然后改变TIM_OCPolarity ,再测,还是双极性,只是倒了个跟头。还真以为stm32单片机能输出两极性的PWM,后面把示波器改为直流档(之前用的是交流档),波形才从零电位一下纵向移上去。以后要注意!

关键字:STM32  单片机  PWM输出 引用地址:STM32单片机--PWM输出

上一篇:基于STM32的数字PDA系统软件系统设计
下一篇:单片机STM32时钟图文理解

推荐阅读最新更新时间:2024-11-12 18:49

STM32开发笔记32:STM32F4定时器驱动程序的移植
单片机型号:STM32F401 本文讲述移植STM32CubeMX生成的驱动程序到自己的项目中,以定时器为例。为什么要写这篇文章呢?是因为定时器移植这件事情耗费了我整整2天的时间,只因为一个特殊的地方没有注意到。把整个移植过程用文字记录下来,以利于自身其它程序的移植,并对本文的读者提供一些技术上的参考。 1、如果仅完成计时功能,在STM32CubeMX中设置比较简单,如下图所示,只需指定其时钟源即可。 2、对时钟进行进一步设置,这个在先前的日志中已经描述过,将分频系数与MCU的主频系数设置成一致,例如我现在MCU的主频是72M,则进行72分频即可,这样单位计数周期是1us,按照下图所示,我在Count
[单片机]
<font color='red'>STM32</font>开发笔记32:STM32F4定时器驱动程序的移植
STM32 IAP/BOOT,三步速成
Stm32的IAP现在已经是非常简单了,下面就教大家三步学成: 前提条件:已经有Flash读写函数,这个函数好弄,很多stm32例程里面都有 第一步(对于Boot程序):跳转到指定Flash位置函数,如下: void Jump_To_Application(const u32 startAddr) { Jump_To_App = (pFunction)(*(vu32*) (startAddr + 4)); MSR_MSP(*(vu32*) startAddr); Jump_To_App(); } 第二步(对于APP):在keil工程配置里设置程序起始地址和APP程序大小,如图: 第三步(对于APP):如果APP中涉及
[单片机]
学好单片机能找什么工作?单片机好学吗?
随着电子集成技术的不但地发展,很多电子产品的设计都使用集成化的芯片,其中单片机在现在电子产品设计中,扮演者越来越重要的角色。 集成电路让产品的电路设计越来越简单化,但对嵌入式单片机程序编程水平依赖是越来越高。 小到儿童玩具,大到飞机导弹,太多智能电子产品都离不开单片机的开发,电子烟,按摩椅,智能红绿灯,耳机,手表,音箱…… 还有自动化产品的设计,产品生产治具的设计,智能机器人等,凡事需要智能控制的地方,都需要单片机开发,而且随着不同行业的融合,单片机开发的市场需求是越来越大。 1.学好单片机能找什么工作? 单片机主要应用在电子产品的设计上,所以学好单片机,主要是应用在电子产品的开发,主要的职位是”嵌入式/单片机开发工程师”。
[单片机]
STM32 PB3或者PB4不能正常使用的讲解
最近用STM32F103T8做项目,发现PB3和PB4这两个IO不可控,一直是高电平; 原因是PB3和PB4在系统复位时候,分别默认为SYS_JIDO和SYS_HJTRST; 所以需要通过用户自行禁止其功能; 也就是想要正常使用PB3和PB4的主功能的时候。 在初始化IO时候,增加代码如下:(这里使用J-Link的SWD模式烧录程序) //打开时钟函数 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE); //打开GPIO口时钟,先打开复用才能修改复用功能 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTA
[单片机]
<font color='red'>STM32</font> PB3或者PB4不能正常使用的讲解
STM32学习记录15 ucosii消息队列
现在还不清楚消息邮箱和消息队列的区别,貌似消息队列是消息邮箱的集合?不太清楚。 环境:STM32F107VC 72M主频 1:主函数: int main(void) { BspInit();//初始化硬件并使能 SysTick 定时器 OSInit(); //必须调用,建立两个任务 空闲任务和统计任务 OSTaskCreate(TaskStart,(void *)0,&start_task_stk , START_TASK_PRIO); //建立任务 OSStart();//将控制权交给ucos,开始运行多任务 return 0; }: 2:先看一下初始化硬件和Systick函数 //初
[单片机]
平凡单片机教学 第八讲 单片机指令(二)
数据传递类指令 (3)以直接地址为目的操作数的指令MOV direct,A 例: MOV 20H,A MOV direct,Rn MOV 20H,R1MOV direct1,direct2 MOV 20H,30HMOV direct,@Ri MOV 20H,@R1MOV direct,#data MOV 20H,#34H (4)以间接地址为目的操作数的指令MOV @Ri,A 例:MOV @R0,A MOV @Ri,direct MOV @R1,20HMOV @Ri,#data MOV @R0,#34H (5)十六位数的传递指令MOV DPTR,#data16 8051是一种8位机,这是唯一的一条16位立即数传递指令,其功能是
[单片机]
平凡<font color='red'>单片机</font>教学 第八讲 <font color='red'>单片机</font>指令(二)
STM32串口的理解
串口设置的一般步骤可以总结为如下几个步骤: 1) 串口时钟使能,GPIO 时钟使能 2) 串口复位 3) GPIO 端口模式设置 4) 串口参数初始化 5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤) 6) 使能串口 7) 编写中断处理函数 淡蓝色是以串口1举例,深蓝色是以结构体函数,可拿结构体复制到Keil5中,右键 仔细看结构体里要求的模式等 1.串口时钟使能: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1); 2.串口复位: void USART_DeInit(USART_TypeDef* USARTx);//串口复位 假设,复位串口 1: U
[单片机]
STM32的三种延时方法的代码实现_纯软件延时, 系统定时器延时, 定时器延时
/* 外部调用: delay_init(72); //系统主频, 单位为MHZ, 仅对 SYSTICK_DELAY 有效 delay_ms(1000); */ #include stm32f10x.h //#define SOFT_DELAY //纯软件延时 //#define SYSTICK_DELAY //系统定时器延时 #define TIMER_DELAY //定时器延时 #ifdef TIMER_DELAY #define TIMER_DELAY TIM4 #define TIMER_DELAY_PERIOD 1000 #de
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved