(stm32f103学习总结)—stm32 PMW输出实验

时间:2024-02-23 11:54:12

一.PWM简介

  PWM是 Pulse Width Modulation 的缩写,中文意思就是脉冲宽度调 制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控 制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成 为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信, 功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些 音频放大器,因此学习PWM具有十分重要的现实意义。 其实我们也可以这样理解,PWM是一种对模拟信号电平进行数字编码 的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个 具体模拟信号的电平进行编码。PWM 信号仍然是数字的,因为在给定的 任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压 或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去 的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被 断开的时候。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。

 

 

二.STM32F1 PWM介绍

  STM32F1除了基本定时器TIM6和TIM7,其他定时器都可以产生PWM输出 。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出 。而通用定时器也能同时产生多达 4路的 PWM 输出,这些在定时器中断 章节中已经介绍过。 PWM的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号 ,信号频率是由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定。

 

 

  PWM输出比较模式总共有8种,具体由寄存器 CCMRx 的位 OCxM[2:0] 配置。我们这里只讲解最常用的两种PWM输出模式:PWM1和PWM2,其他几 种模式可以参考《STM32F10x中文参考手册》13、14、15定时器章节。

  PWM1和PWM2这两种模式用法差不多,区别之处就是输出电平的极性不 同。

   PWM模式根据计数器CNT计数方式,可分为边沿对齐模式和中心对齐模 式。

(1)PWM边沿对齐模式

  当 TIMx_CR1 寄存器中的 DIR 位为低时执行递增计数,计数器CNT从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0 开始 计数并生成计数器上溢事件。 以 PWM 模式 1 为例。只要TIMx_CNT < TIMx_CCRx, PWM 参考信号 OCxREF 便为有效的高电平,否则为无效的低电平。如果 TIMx_CCRx 中 的比较值小于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。 如果比较值为 0, 则 OCxREF 保持为“ 0”。

   当 TIMx_CR1 寄存器中的 DIR 位为高时执行递减计数,计数器CNT从 自动重载值(TIMx_ARR 寄存器的内容)递减计数到0,然后重新从 TIMx_ARR值开始计数并生成计数器下溢事件。 以 PWM 模式 1 为例。只要TIMx_CNT >TIMx_CCRx, PWM 参考信号 OCxREF 便为无效的低电平,否则为有效的高电平。如果 TIMx_CCRx 中 的比较值大于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。 此模式下不能产生0%的PWM波形。

(2)PWM中心对齐模式

  在中心对齐模式下,计数器 CNT 是工作做递增/递减模式下。开始的 时候, 计数器CNT 从 0 开始计数到自动重载值减 1(ARR-1),生成计数 器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事 件。之后从 0 开始重新计数。

  我们以ARR=8,CCRx=4为例进行介绍。第一阶段计数器CNT工作在递增 计数方式,从0开始计数,当TIMx_CNT < TIMx_CCRx时,PWM 参考信号 OCxREF为高电平,当TIMx_CNT >= TIMx_CCRx时,PWM 参考信号 OCxREF 为低电平。第二阶段计数器CNT工作在递减计数方式,从ARR开始递减计 数,当TIMx_CNT > TIMx_CCRx时,PWM 参考信号 OCxREF为低电平,当 TIMx_CNT <= TIMx_CCRx时,PWM 参考信号 OCxREF为高电平。

 

三.PWM输出配置步骤

(1)使能定时器及端口时钟,并设置引脚复用器映射 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE); 可选的参数在 stm32f10x_gpio.h 都已经列出来非常详细

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出

(2)初始化定时器参数,包含自动重装值,分频系数,计数方式等

void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

(3)初始化PWM输出参数,包含PWM模式、输出极性,使能等

void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

(4)开启定时器

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); TIM_Cmd(TIM3,ENABLE); //开启定时器

(5)修改TIMx_CCRx的值控制占空比

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint32_t Compare1);

(6)使能TIMx在CCRx上的预装载寄存器 使能输出比较预装载库函数是:

void  TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

第一个参数用于选择定时器,第二个参数用于选择使能还是失能输出比较预装载寄存器,可选择为TIM_OCPreload_Enable、TIM_OCPreload_Disable。

(7)使能 TIMx 在 ARR 上的预装载寄存器允许位 使能 TIMx 在 ARR 上的预装载寄存器允许位库函数是:

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);  //第一个参数用于选择定时器,第二个参数用于选择使能还是失能。

  高级定时器要想输出PWM波形,必须要设置一个 MOE 位(TIMx_BDTR 的第 15 位),以使能主输出,否则不会输出 PWM。库函数设置的函数为:

   void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

 

pwm.h
1
#ifndef _pwm_H 2 #define _pwm_H 3 4 #include "system.h" 5 6 void TIM3_CH1_PWM_Init(u16 per,u16 psc); 7 8 #endif

 问题一:19行问什么要AFIO使能   27行改变管脚映射怎么回事

  27行改变管脚映射是对应第三步而言的,就是将定时器输出的管脚TIM_CH1即PA6管脚映射到PC6管脚输出(这里以TIM_CH1为例)

  而管脚映射需要用到AFIO这个外设里面寄存器的配置才能实现

 

 问题二:24行为什么GPIOC使用复用推挽输出,为什么使用复用功能

  复用功能是接受其他外设所传递的数据(这里是复用的定时器结构图右下角所输出的数据)然后通过管脚输出

 

 

 

   总结:所比较的数据从TIM3_CH1口出来,经过AFIO外设内寄存器的配置将TIM3_CH1管脚即PA6管脚所映射到的PC6管脚上,经过PC6管脚的复用功能输出的模式将TIM3_CH1的数据输出

 

四、PWM代码

pwm.c
1
#include "pwm.h" 2 3 /******************************************************************************* 4 * 函 数 名 : TIM3_CH1_PWM_Init 5 * 函数功能 : TIM3通道1 PWM初始化函数 6 * 输 入 : per:重装载值 7 psc:分频系数 8 * 输 出 : 无 9 *******************************************************************************/ 10 void TIM3_CH1_PWM_Init(u16 per,u16 psc) 11 { 12 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; 13 TIM_OCInitTypeDef TIM_OCInitStructure; 14 GPIO_InitTypeDef GPIO_InitStructure; 15 16 /* 开启时钟 */ 17 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); 18 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); 19 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO使能 20 21 /* 配置GPIO的模式和IO口 */ 22 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6; 23 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 24 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出 25 GPIO_Init(GPIOC,&GPIO_InitStructure); 26 27 GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射 28 29 TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值 30 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数 31 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 32 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式 33 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); 34 35 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; 36 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; 37 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; 38 TIM_OC1Init(TIM3,&TIM_OCInitStructure); //输出比较通道1初始化 39 40 TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIMx在 CCR1 上的预装载寄存器 41 TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器 42 43 TIM_Cmd(TIM3,ENABLE); //使能定时器 44 45 }

 

 

 main.c
1
#include "system.h" 2 #include "SysTick.h" 3 #include "led.h" 4 #include "pwm.h" 5 6 int main() 7 { 8 u16 i=0; 9 u8 fx=0; 10 SysTick_Init(72); 11 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组 12 LED_Init(); 13 TIM3_CH1_PWM_Init(500,72-1); //频率是2Kh 14 15 while(1) 16 { 17 18 if(fx==0) 19 { 20 i++; 21 if(i==300) 22 { 23 fx=1; 24 } 25 } 26 else 27 { 28 i--; 29 if(i==0) 30 { 31 fx=0; 32 } 33 } 34 TIM_SetCompare1(TIM3,i); //i值最大可以取499,因为ARR最大值是499. 35 delay_ms(10); 36 } 37 }