浅谈STM32单片机学习---PWM输出

发布者:幸福家庭最新更新时间:2021-03-03 来源: eefocus关键字:STM32  单片机  PWM输出 手机看文章 扫描二维码
随时随地手机看文章

首先熟悉一下定时器的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的红外遥控重点解析
下一篇:基于ZigBee和STM32的室内智能照明系统的设计

推荐阅读最新更新时间:2024-11-09 08:37

STM32/GD32芯片信息
因为需要自动适配芯片进行系统配置,所以我们有必要通过读取一些系统寄存器来获取必要信息。 我们的代码需要兼容STM32F1/GD32F1/STM32F0/STM32F4 代码如下: #ifdef STM32F0XX void* p = (void*)0x1FFFF7AC; #else void* p = (void*)0x1FFFF7E8; #endif memcpy(ID, p, ArrayLength(ID)); CPUID = SCB- CPUID; uint MCUID = DBGMCU- IDCODE; // MCU编码。低字设备版本,高字子版本 RevID = MC
[单片机]
51单片机 模块化编程
了解模块化编程 1.首先了解一下传统方式编程和模块化编程 传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路 模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include XXX.h 文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等 2. 模块化编程注意事项 .c文件:函数、变量的定义 .h文件:可被外部调用的函数、变量的声明 任何自定义的变量、函数在调用前必须有定义或声明(同一个.c) 使用到的自定义函数的.
[单片机]
51<font color='red'>单片机</font> 模块化编程
51单片机实验——跑马灯实验
1.题目要求: 编写跑马灯程序,用P0演示跑马灯的效果,效果自定。 2.KEIL代码 #include reg52.h #include intrins.h typedef unsigned int u16; //对数据类型进行声明定义 typedef unsigned char u8; void Delay100ms() //100ms { unsigned char i, j, k; _nop_();_nop_(); i = 5;j = 52;k = 195; do{do{while (--k);} while (--j);} while (--i); } void main(
[单片机]
51<font color='red'>单片机</font>实验——跑马灯实验
51单片机的控制语句
Cx51流程控制共有3种基本结构:顺序结构、选择结构以及循环结构。 一、顺序结构 顺序结构是最基本、最简单的编程结构,程序按先后顺序执行指令代码。 下图是8051单片机的P0口和P1口上分别连接了8个LED,请分别用P0和P1口显示加法125+34和减法176-98的运算结果 #include int main(void) { unsigned char a=125,b=34,c=176,d=98; P1=a+b; //加法运算结果送P1端口,P1=159=1001 1111B P0=c-d; //减法运算结果送P0端口,P0=78=0100 1110B while(1); //循环等待,防止主程序退出后单片
[单片机]
51<font color='red'>单片机</font>的控制语句
基于AT89C52单片机的智能环保小车电路模块设计
  随着电子产品的快速发展,“智能工具”越来越普及化。智能清洁工具也为人们的生活带来了很大的便利。传统的清洁工具功能相对比较单一,只能吸尘,或只能拖地,且基本是手动的,使用起来比较费力。本文设计的多功能智能环保小车可分为以下几个部分:无线遥感模块、避障模块、寻光模块、红外对射模块、稳压模块、降压模块、电机驱动模块。它可实现洒水、拖地、风干、自动避障等多种功能,并且比较节能。   电机驱动模块   采用步进电机控制悬挂物体的准确运动,步进电机不需要使用传感器就能精确定位,但其驱动能有限,故不适合驱动小车。因此,可采用低内阻大电流的四直流电机,其速度相应较好,可以为小车的行走及其他功能更好地提供动力。利用四直流电机模块驱动小车可以
[单片机]
基于AT89C52<font color='red'>单片机</font>的智能环保小车电路模块设计
STM32单片机为什么要中断
STM32中断主题: 1什么是中断 暂停原先的程序或事情,执行另外一些程序或事情,执行完成后返回原来的程序。 2为什么要中断 因为另一些程序或事情比你原先正在做的事情要重要,或者这些突发事情你是无法控制它的来临的。 3 中断的分类 按不同方法进行分类 3.1 内部中断,外部中断 (向量表中灰色为内部) 3.2 可设置中断,固化中断 3.3 中断向量表 (cl级别的芯片有10个内部 ,0~67个外部,一些没用到,其中通用化 中断编号0~17,个性化中断编号18~67) 3.4 优先级别 (由主优先级和从优先级组成,主从优先级可以通过4个位进行设置) 4 中断的组成 具体中断的名称 中断的地址 用来保存一条跳转指令,跳到哪里去
[单片机]
基于AT89S52单片机的太阳能参数测试仪设计
引 言 为了确保太阳能发电系统能够正常的工作,需要对太阳能发电系统的各项环境参数进行测量,从而有效地控制其运行。本文介绍了一种基于单片机的太阳能参数测试仪,提供了3种参数的测量功能和通信接口,以及2种供电方式,既可作为手持设备使用,又能安装在发电系统中,具有较高的实用价值。 1 硬件电路设计 1.1 总体结构 该测试仪以AT89S52单片机为核心,外接温湿度传感器SHTll、照度传感器TSL2561、四位共阴数码管、RS485总线通信接口以及显示切换按键。单片机上电工作后,对当前温度、湿度、光强度进行实时测量,通过按键切换将测得的3种参数通过LED数码管进行轮流显示;此外,还可以通过RS485总线与PC机进行通信,将参数值传送
[单片机]
基于AT89S52<font color='red'>单片机</font>的太阳能参数测试仪设计
【51单片机】 ULN2003模块 驱动步进电机(5线)正反转 代码以及接线图
步进电机有四相(A-B-C-D) 正向转动思路为A→B→C→D 反向转动思路为D→C→B→A 通过给每一相轮流供电,实现电机轴的360度转动 、、以下代码可直接复制使用: #include reg52.h #define uchar unsigned char //宏定义 把unsigned char简写为uchar #define uint unsigned int // 把unsigned int 简写为uint uint i,j; //定义全局变量,执行电机for循环转动时间需要用到 sbit A1 = P1^0; //定义给步进电机四相连接的IO口 sbit B1 = P1^1;
[单片机]
【51<font color='red'>单片机</font>】 ULN2003模块 驱动步进电机(5线)正反转 代码以及接线图
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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