一、PWM概念
1.PWM:脉冲宽度调制定时器
2.脉冲:方波信号,高低电平的变化产生方波信号
3.周期:高低电平变化所需要的时间,单位:ms
4.频率:周期和频率之间是倒数关系,1S钟可以产生的多少个方波信号,单位:HZ
5.占空比:高电平占整个周期的百分比
二、实验实现方法
1,实验板:stm32mp157a-fsmp1a
2,PWM实验对象:蜂鸣器、风扇马达
3,通过3个开关控制3个器件的通断
4,由原理图可以看出,三个器件与SOC引脚对应关系为:
蜂鸣器—>TIM4_CH1—>PB6
风扇—>TIM1_CH1—>PE9
马达—>TIM16_CH1—>PF6
5,只要给这三个引脚传递相应的PWM信号,即可启动三个器件
三、框图分析
由框图可知
1.分析RCC章节:相关控制器组使能 (GPIO*和TIM*)
2.分析GPIO章节:给对应引脚设置复用模式/复用功能
3.分析TIM4章节:产生特定的方波
四、产生方波原理
TIM产生方波原理:
1,分频器(PSC)先将时钟源的频率进行分频(eg:209MHZ—>1MHZ)
2.当定时器启动之后,自动重载计数器中的值,会自动加载到递减计数器中
3.递减计数器在CK_CNT时钟驱动下进行工作
4.每来一个时钟周期,递减计数器中的值减1
5.如果减到和比较/捕获寄存器中的值相等之后,电平发生翻转,这样就可产生PWM方波信号
五、代码实现
pwm.h
#ifndef __PWM_H__
#define __PWM_H__
#include "stm32mp1xx_rcc.h"
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_tim.h"
#include "stm32mp1xx_exti.h"
#include "stm32mp1xx_gic.h"
void pwm_init();
void pwm_close();
void pwm_open();
void motor_init();
void motor_close();
void fan_init();
void fan_close();
void fan_open();
void motor_open();
void gpio_init();
void pf9_gicd_init();
void pf9_dicc_init();
void pf9_exti_init();
#endif
pwm.c
#include "pwm.h"
void pwm_init()
{
//使能GPIOB,tim4
RCC->MP_AHB4ENSETR |= (0x1<<1);
RCC->MP_APB1ENSETR |= (0X1<<2);
//配置GPIOB
GPIOB->MODER &= (~(0X3 <<12));
GPIOB->MODER |= (0x1 <<13);
GPIOB->AFRL &= (~(0XF <<24));
GPIOB->AFRL |= (0x1 <<25);
//配置TIM4
TIM4->CR1 |= (0X1 <<7);
TIM4->CR1 &= (~(0X3 <<5));
TIM4->CR1 |= (0X1 <<0);
TIM4->CR1 |= (0X1 <<4);
TIM4->CCMR1 &= (~(0X1 <<16));
TIM4->CCMR1 &= (~(0X7 <<4));
TIM4->CCMR1 |= (0X3 <<5);
TIM4->CCMR1 |= (0X1 <<3);
TIM4->CCMR1 &= (~(0X3 <<0));
TIM4->CCER &= (~(0X1 <<1));
TIM4->CCER &= (~(0X1 <<3));
TIM4->PSC = 208;
TIM4->ARR &= (~(0XFFFF));
TIM4->ARR |= (0X3E8);
TIM4->CCR1 &= (~(0XFFFF));
TIM4->CCR1 |=(0X12C);
}
void pwm_open()
{
TIM4->CCER |= (0X1<<0);
}
void pwm_close()
{
TIM4->CCER &= (~(0X1));
}
void motor_init()
{
//使能GPIOF,tim16
RCC->MP_AHB4ENSETR |= (0x1<<5);
RCC->MP_APB2ENSETR |= (0X1<<3);
//配置GPIOF
GPIOF->MODER &= (~(0X3 <<12));
GPIOF->MODER |= (0x1 <<13);
GPIOF->AFRL &= (~(0XF <<24));
GPIOF->AFRL |= (0x1 <<24);
//配置TIM16
//1.设置分频
//2.PWM分波
//3.分波比
TIM16->PSC = 208;
TIM16->ARR &= (~(0XFFFF));
TIM16->ARR |= (0X3E8);
TIM16->CCR1 &= (~(0XFFFF));
TIM16->CCR1 |=(0X12C);
//4.设置为PWM1模式
TIM16->CCMR1 &= (~(0X1 <<16));
TIM16->CCMR1 &= (~(0X7 <<4));
TIM16->CCMR1 |= (0X3 <<5);
//5.预加载
TIM16->CCMR1 |= (0X1 <<3);
//6.输出模式
TIM16->CCMR1 &= (~(0X3 <<0));
//7.输出极高电平
//8.比较捕获寄存器输出使能
TIM16->CCER &= (~(0X1 <<1));
TIM16->CCER &= (~(0X1 <<3));
//9.自动重载寄存器预加载使能
//10.采用边沿对期
//11.采用递减计数
//12.设置计数器使能
TIM16->CR1 |= (0X1 <<7);
TIM16->CR1 |= (0X1 <<0);
TIM16->BDTR |= (0X1 <<15);
}
void motor_open()
{
TIM16->CCER |= (0X1<<0);
}
void motor_close()
{
TIM16->CCER &= (~(0X1<<0));
}
void fan_open()
{
TIM1->CCER |= (0X1<<0);
}
void fan_init()
{
//使能GPIOE,tim1
RCC->MP_AHB4ENSETR |= (0x1<<4);
RCC->MP_APB2ENSETR |= (0X1<<0);
//配置GPIOE9
GPIOE->MODER &= (~(0X3 <<18));
GPIOE->MODER |= (0x1 <<19);
GPIOE->AFRH &= (~(0XF <<4));
GPIOE->AFRH |= (0x1 <<4);
//配置TIM1
//1.设置分频
//2.PWM分波
//3.分波比
TIM1->PSC = 208;
TIM1->ARR = 1000;
TIM1->CCR1 = 600;
//4.设置为PWM1模式
TIM1->CCMR1 &= (~(0X1 <<16));
TIM1->CCMR1 &= (~(0X7 <<4));
TIM1->CCMR1 |= (0X3 <<5);
//5.预加载
TIM1->CCMR1 |= (0X1 <<3);
//6.输出模式
TIM1->CCMR1 &= (~(0X3 <<0));
//7.输出极高电平
//8.比较捕获寄存器输出使能
TIM1->CCER &= (~(0X1 <<1));
TIM1->CCER |= (0X1 <<3);
//9.自动重载寄存器预加载使能
//10.采用边沿对期
//11.采用递减计数
//12.设置计数器使能
TIM1->BDTR |= (0X1 <<15);
TIM1->CR1 |= (0X1 <<7);
TIM1->CR1 &= (~(0X3 <<5));
TIM1->CR1 |= (0X1 <<4);
TIM1->CR1 |= (0X1 <<0);
}
void fan_close()
{
TIM1->CCER &= (~(0X1<<0));
}
void gpio_init()
{
RCC->MP_AHB4ENSETR |= (0X1 <<5);
GPIOF->MODER &= (~(0X3 <<18));
GPIOF->MODER &= (~(0X3 <<16));
GPIOF->MODER &= (~(0X3 <<14));
}
//初始化EITI层
void pf9_exti_init()
{
/****设置EXTI系列寄存器*****/
//1.中断寄存器与GPIO连接
EXTI->EXTICR3 &= (~(0xff << 8));
EXTI->EXTICR3 |= (0X05 << 8);
EXTI->EXTICR3 &= (~(0xff << 0));
EXTI->EXTICR3 |= (0X05 << 0);
EXTI->EXTICR2 &= (~(0xff << 24));
EXTI->EXTICR2 |= (0X05 << 24);
//2.检测事件为下降沿
EXTI->FTSR1 |= (0X1 <<7);
EXTI->FTSR1 |= (0X1 <<8);
EXTI->FTSR1 |= (0X1 <<9);
//3.设置中断屏蔽寄存器为不屏蔽
EXTI->C1IMR1 |= (0X1 <<7);
EXTI->C1IMR1 |= (0X1 <<8);
EXTI->C1IMR1 |= (0X1 <<9);
}
//初始化GICD层
void pf9_gicd_init()
{
/*****设置GICD系列寄存器*****/
//使能GICD(全局)
GICD->CTRL |= (0X1 <<0);
//中断设置使能(对应中断号)
GICD->ISENABLER[3] |= (0X1 <<3);
GICD->ISENABLER[3] |= (0X1 <<1);
GICD->ISENABLER[3] |= (0X1 <<2);
//中断优先级设置
GICD->IPRIORITYR[24] &= (~(0X1F <<11));
GICD->IPRIORITYR[24] |= (0X1 <<11);
GICD->IPRIORITYR[24] &= (~(0X1F <<19));
GICD->IPRIORITYR[24] |= (0X1 <<19);
GICD->IPRIORITYR[24] &= (~(0X1F <<27));
GICD->IPRIORITYR[24] |= (0X1 <<27);
//选择CPU,0号,设置中断目标分配
GICD->ITARGETSR[24] &= (~(0X3 <<8));
GICD->ITARGETSR[24] |= (0X1 <<8);
GICD->ITARGETSR[24] &= (~(0X3 <<16));
GICD->ITARGETSR[24] |= (0X1 <<16);
GICD->ITARGETSR[24] &= (~(0X3 <<24));
GICD->ITARGETSR[24] |= (0X1 <<24);
}
//初始化GICC层
void pf9_dicc_init()
{
/*****设置GICC系列寄存器*****/
//使能GICC
GICC->CTRL |= (0X1 <<0);
//输入中断优先级
GICC->PMR &= (~(0X1F <<3));
GICC->PMR |= (0XF <<3);
}
main.c
#include "./include/pwm.h"
extern void printf(const char *fmt, ...);
void delay_ms(unsigned int ms)
{
int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 1800; j++)
;
}
int main()
{
gpio_init();
pwm_init();
motor_init();
fan_init();
pf9_gicd_init();
pf9_dicc_init();
pf9_exti_init();
while (1)
{
}
return 0;
}
do_irq.c(处理中断信号)
#include "pwm.h"
extern void printf(const char *fmt, ...);
extern void delay_ms(int ms);
void do_irq(void)
{
// 1. 获取中断号 GICC_IAR[9:0]
unsigned int irq_num;
irq_num = GICC->IAR & 0x3FF;
// 2. 进入到中断处理程序
switch (irq_num)
{
case 97: // KEY2
delay_ms(500);
if(TIM4->CCER & (0x1))
{
pwm_close();
}else
{
pwm_open();
}
printf("key2######\n");
//清除EXTI层挂起标志位
EXTI->FPR1 |= (0x1 <<7);
EXTI->FPR1 |= (0x1 <<8);
EXTI->FPR1 |= (0x1 <<9);
//清除GICD层挂起标志位
GICD->ICPENDR[3] |= (0X1 <<3);
GICD->ICPENDR[3] |= (0X1 <<1);
GICD->ICPENDR[3] |= (0X1 <<2);
break;
case 98: // KEY3
delay_ms(500);
if(TIM16->CCER & (0x1))
{
motor_close();
}else
{
motor_open();
}
printf("key3######\n");
//清除EXTI层挂起标志位
EXTI->FPR1 |= (0x1 <<7);
EXTI->FPR1 |= (0x1 <<8);
EXTI->FPR1 |= (0x1 <<9);
//清除GICD层挂起标志位
GICD->ICPENDR[3] |= (0X1 <<3);
GICD->ICPENDR[3] |= (0X1 <<1);
GICD->ICPENDR[3] |= (0X1 <<2);
break;
case 99: // KEY1
delay_ms(500);
if(TIM1->CCER & (0x1))
{
fan_close();
}else
{
fan_open();
}
printf("key1######\n");
//清除EXTI层挂起标志位
EXTI->FPR1 |= (0x1 <<7);
EXTI->FPR1 |= (0x1 <<8);
EXTI->FPR1 |= (0x1 <<9);
//清除GICD层挂起标志位
GICD->ICPENDR[3] |= (0X1 <<3);
GICD->ICPENDR[3] |= (0X1 <<1);
GICD->ICPENDR[3] |= (0X1 <<2);
break;
}
// 6. 清除GICC层的中断号 GICC_EOIR
GICC->EOIR = irq_num;
}
六、实现现象