STM32呼吸灯的PWM原理与代码实现

发布者:码农创想家最新更新时间:2023-01-06 来源: zhihu关键字:STM32吗  呼吸灯  PWM 手机看文章 扫描二维码
随时随地手机看文章

用定时器生成PWM波

PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改pwm的阈值,就弄出了呼吸的效果。

这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。


void TIM3_IRQHandler(void)

{

TIM_ClearITPendingBit(TIM3,TIM_IT_Update);

if(counter==255)

counter = 0;

else

counter +=1;

if(mode == 0){

if(counter < pwm)

GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);

else

GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);

}

if(mode == 1)

{

if(counter < pwm)

GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);

else

GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);

}

if(mode ==2){

if(counter < pwm)

GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);

else

GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);

}

}

程序流程

开启外设时钟(GPIO和TIM)


void RCC_Configuration(void)

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE);

}


配置GPIO

配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)


void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure

TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断

TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3

TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志

TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断

TIM_Cmd(TIM3,ENABLE); // 使能TIM3

}


配置中断优先级


void nvic() //配置中断优先级

{

NVIC_InitTypeDef NVIC_InitStructure; // // 命名一优先级变量

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能

NVIC_Init(&NVIC_InitStructure); // 初始化

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}


写中断服务函数

代码实现

为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源。


#include "stm32f10x.h"

#include "math.h"

#include "stdio.h"


u8 counter=0;

int pwm=100;

int flag=0;

int mode =0;

int velocity =0;

int turning=1;


void RCC_Configuration(void); //时钟初始化,开启外设时钟

void GPIO_Configuration(void); //IO口初始化,配置其功能

void tim3(void); //定时器tim4初始化配置

void tim4(void); //定时器tim4初始化配置

void nvic(void); //中断优先级等配置

void exti(void); //外部中断配置

void delay_nus(u32); //72M时钟下,约延时us

void delay_nms(u32); //72M时钟下,约延时ms

void breathing(int velocity){

switch(velocity){

case 0:

if(flag)

pwm +=1;

if(pwm>240) flag=0;

if(flag == 0){

pwm -=1;

if(pwm<10) flag=1;

}

break;

case 1:

if(flag)

pwm +=2;

if(pwm>240) flag=0;

if(flag == 0){

pwm -=2;

if(pwm<10) flag=1;

}

break;

case 2:

if(flag)

pwm +=3;

if(pwm>240) flag=0;

if(flag == 0){

pwm -=3;

if(pwm<10) flag=1;

}

break;

}

}



void assert_failed(uint8_t* file, uint32_t line)

{

printf("Wrong parameters value: file %s on line %drn", file, line);

while(1);

}


void TIM4_IRQHandler(void) //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比

{

u8 key_in1=0x01,key_in2=0x01;

TIM_ClearITPendingBit(TIM4,TIM_IT_Update);// 清空TIM4溢出中断响应函数标志位

key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12); // 读PC12的状态

key_in2= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);// 读PC13的状态

if(key_in1 && key_in2) turning =1;

breathing(velocity);

if(key_in1==0 && turning){

turning =0;

velocity = (velocity + 1) % 3;

}//调速度

if(key_in2==0 && turning){

turning =0;

mode = (mode + 1) % 3;

}//调颜色

}



void TIM3_IRQHandler(void) // //TIM3的溢出更新中断响应函数,产生pwm波

{

TIM_ClearITPendingBit(TIM3,TIM_IT_Update); // // 清空TIM3溢出中断响应函数标志位

if(counter==255) //counter 从0到255累加循环计数,每进一次中断,counter加一

counter = 0;

else

counter +=1;

if(mode == 0){

if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低

GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平

else

GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); // 将PC14 PC15置为低电平

}

if(mode == 1)

{

if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低

GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平

else

GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); // 将PC14 PC15置为低电平

}

if(mode ==2){

if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低

GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平

else

GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平

}

}



int main(void)

{

RCC_Configuration();

GPIO_Configuration();

tim4();

tim3();

nvic();

while(1)

{

}

}


void delay_nus(u32 n) //72M时钟下,约延时us

{

u8 i;

while(n--)

{

i=7;

while(i--);

}

}



void delay_nms(u32 n) //72M时钟下,约延时ms

{

while(n--)

delay_nus(1000);

}



void RCC_Configuration(void) //使用任何一个外设时,务必开启其相应的时钟

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE); //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册

}



void GPIO_Configuration(void) //使用某io口输入输出时,请务必对其初始化配置

{

GPIO_InitTypeDef GPIO_InitStructure; //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure

//typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; } GPIO_InitTypeDef;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13; //配置被选中的管脚,|表示同时被选中

GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC的相应IO口为上述配置,用于按键检测

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //配置IO口工作模式为 推挽输出(有较强的输出能力)

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; //配置被选的管脚,|表示同时被选中

GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA的相应IO口为上述配置

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口

}



void tim4() //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure

TIM_TimeBaseStructure. TIM_Period =9999; // 配置计数阈值为9999,超过时,自动清零,并触发中断

TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数

TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); // 初始化tim4

TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志

TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 使能TIM4的溢出更新中断

TIM_Cmd(TIM4,ENABLE); // 使能TIM4

}



void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure

TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断

TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3

TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志

TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断

TIM_Cmd(TIM3,ENABLE); // 使能TIM3

}



void nvic() //配置中断优先级

{

NVIC_InitTypeDef NVIC_InitStructure; // // 命名一优先级变量

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级

NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能

NVIC_Init(&NVIC_InitStructure); // 初始化

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}


关键字:STM32吗  呼吸灯  PWM 引用地址:STM32呼吸灯的PWM原理与代码实现

上一篇:STM32等单片机程序加密的方法
下一篇:入手STM32单片机的知识点总结

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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