STM32的定时器还有一个模式叫做输出比较翻转模式。这种模式,顾名思义,可以翻转电平,但是条件是:当计数值达到比较值时,才会在对应的通道引脚翻转原先的电平。利用这个特点,我们可以在引脚上生成PWM波。

下面就讲讲如何利用这个“翻转”这个特点,来输出PWM波。还是基于我自己的规工程。

1、工程的修改

1)这里用到了定时器,所以需要将stm32f10x_tim.h添加到STM32F10x_StdPeriod_Driver工程组中。

2)打开stm32f0x_conf.h文件,将其中原先被屏蔽的语句:#include "stm32f10x_tim.h"的注释去掉。

3)新建OCToggle.c与OCToggle.h两个文件,分别保存在BSP文件夹里下的src与inc中,然后在将OCToggle.c添加到BSP工程组。

2、OCToggle.c与OCToggle.h文件程序的编写

首先是引脚的初始化。我使用TIM2,所以需要初始化TIM2对应的引脚PA0、PA1、PA2、PA3这四个引脚将它们配置成复用推挽输出,代码如下:

/*************************************************************

Function : OCToggle_GPIO_Init

Description: 输出比较翻转模式下定时器对应通道引脚初始化

Input : none

return : none

*************************************************************/

static void OCToggle_GPIO_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//定时器各通道引脚配置成复用推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

}

接下去当然是要配置定时器了。它的代码如下:

/*************************************************************

Function : OCToggle_TIM2_Init

Description: 输出比较翻转模式定时器2初始化

Input : none

return : none

*************************************************************/

static void OCToggle_TIM2_Init(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//初始化TIM2时钟

/* -------------------------------------------------------

TIM_OCMode_Toggle模式计数值等于比较值翻转电平

在PA0引脚输出频率为72M/CCR1_Val/2=732Hz,占空比为50%的PWM波

在PA1引脚输出频率为72M/CCR2_Val/2=1099Hz,占空比为50%的PWM波

在PA2引脚输出频率为72M/CCR3_Val/2=2197Hz,占空比为50%的PWM波

在PA3引脚输出频率为72M/CCR4_Val/2=4395Hz,占空比为50%的PWM波

---------------------------------------------------------*/

TIM_TimeBaseStructure.TIM_Period = 65535;//定时器计数周期

TIM_TimeBaseStructure.TIM_Prescaler = 1 - 1;//预分频

TIM_TimeBaseStructure.TIM_ClockDivision = 1 - 1;//时钟不分频

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//增计数

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//初始化定时器

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//输出比较主动模式

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能

TIM_OCInitStructure.TIM_Pulse = CCR1_Val;//设置比较值(跳变值)

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高电平

TIM_OC1Init(TIM2, &TIM_OCInitStructure);//初始化输出比较寄存器

TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);//关闭预转载

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能

TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//设置比较值(跳变值)

TIM_OC2Init(TIM2, &TIM_OCInitStructure);//初始化输出比较寄存器

TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能

TIM_OCInitStructure.TIM_Pulse = CCR3_Val;//设置比较值(跳变值)

TIM_OC3Init(TIM2, &TIM_OCInitStructure);//初始化输出比较寄存器

TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能

TIM_OCInitStructure.TIM_Pulse = CCR4_Val;//设置比较值(跳变值)

TIM_OC4Init(TIM2, &TIM_OCInitStructure);//初始化输出比较寄存器

TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Disable);

TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);//清除中断标志

TIM_Cmd(TIM2, ENABLE);//打开定时器2

}

不对定时器做任何分频,并且让它满计数,即计数周期为65535。接下去再设置个通道的的工作模式为输出比较翻转模式,设置通道1的比较值为CCR1_Val、CCR3_Val、CCR3_Val、CCR4_Val,它们的值在OCToggle.h中定义。然后打开个通道的输出比较事件的中断。最后在打开定时器。这样的话,定时器这段就配置完成了。

既然打开了中断,则需要配置下中断,设置TIM2的中断优先级为1,代码如下:

/*************************************************************

Function : OCToggle_Int_Init

Description: 输出比较翻转模式中断初始化

Input : none

return : none

*************************************************************/

static void OCToggle_Int_Init(void)

{

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//中断优先级为1

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

还要编写一个总函数:OCToggle_Init()函数将上面的初始化相关代码都在这个函数中调用,代码如下:

/*************************************************************

Function : OCToggle_Init

Description: 输出比较翻转模式初始化

Input : none

return : none

*************************************************************/

void OCToggle_Init(void)

{

OCToggle_GPIO_Init();

OCToggle_TIM2_Init();

OCToggle_Int_Init();

}

接下去OCToggle.h的代码:

#ifndef __OCTOGGLE_H__

#define __OCTOGGLE_H__

#include "stm32f10x.h"

#define CCR1_Val 49152

#define CCR2_Val 32768

#define CCR3_Val 16384

#define CCR4_Val 8192

void OCToggle_Init(void);

#endif

这里之所以将CCR1_Val的值在这个h文件中宏定义,而不在c文件中直接定义成变量,原因是为了在其他文件中调用方便,如果定义成变量的话,在其他文件还要用extern关键字来声明,比较麻烦!

3、stm32f10x_it.c文件的修改

TIM2的中断服务函数的程序如下:

/*************************************************************

Function : TIM2_IRQHandler

Description: 定时器2中断服务程序

Input : none

return : none

*************************************************************/

void TIM2_IRQHandler(void)

{

static u16 capture = 0;

if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//通道1检测到比较事件

{

TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);//清除标志位

capture = TIM_GetCapture1(TIM2);

TIM_SetCompare1(TIM2, capture + CCR1_Val);//重新设置比较值

}

if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//通道1检测到比较事件

{

TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);//清除标志位

capture = TIM_GetCapture2(TIM2);

TIM_SetCompare2(TIM2, capture + CCR2_Val);//重新设置比较值

}

if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)//通道1检测到比较事件

{

TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);//清除标志位

capture = TIM_GetCapture3(TIM2);

TIM_SetCompare3(TIM2, capture + CCR3_Val);//重新设置比较值

}

if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)//通道1检测到比较事件

{

TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);//清除标志位

capture = TIM_GetCapture4(TIM2);

TIM_SetCompare4(TIM2, capture + CCR4_Val);//重新设置比较值

}

}

在这个中断服务程序中,每当检测到输出比较时事件时,通道对应的引脚就会自动翻转电平,然后我们在中断中,重新设置它的比较值,在原来的计数值的基础上再增加与计数值相同的计数值作为下一次的比较值。这样的话,就会形成占空比为50%的PWM波。以通道1为例解释下。通道1原来设置的计数值为49152,当定时器的计数值达到这个数时,就翻转通道1对应的输出引脚PA0翻转电平,然后在设置新的计数值为49152+49152,因为定时器只有16位,会溢出,所以下一个比较值为32768,这样的话,如果49152计数值时间内引脚输出高电平的话,下一个49152计数值时间内就会输出低电平,形成频率为72M/49152/2=732Hz,占空比为50%的PWM波。同样的,通道2会输出频率为1099Hz,占空比为50%的PWM波;通道3输出频率为2197Hz,占空比为50%的PWM波;通道4输出频率为4395Hz,占空比为50%的PWM波。

4、main函数的编写

main函数很简单,只是调用一些初始化函数:

/*************************************************************

Function : main

Description: main入口

Input : none

return : none

*************************************************************/

int main(void)

{

BSP_Init();

OCToggle_Init();

PRINTF("\nmain() is running!\r\n");

while(1)

{

LED1_Toggle();

Delay_ms(1000);

}

}

5、测试

用示波器的探头分别连接引脚PA0、PA1、PA2、PA3。可以看到下面的现象:

连接PA0可以检测到频率为732Hz,占空比为50%的PWM波。如下图所示:

连接PA1可以检测到频率为1099Hz,占空比为50%的PWM波。如下图所示:

连接PA2可以检测到频率为2197Hz,占空比为50%的PWM波。如下图所示:

连接PA3可以检测到频率为4395Hz,占空比为50%的PWM波。如下图所示:

本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

定时器翻转io口的好处_STM32 定时器输出比较翻转模式相关推荐

  1. dma和通道的区别_STM32 定时器触发 ADC 多通道采集,DMA搬运至内存

    引言 ADC 的功能是将模拟信号采样得到数字信号,而有些时候,我们需要使用到定时采样,比如在计算一个采集的波形的频率的时候,我们需要精确的知道采样频率,也就是 1 s 内采集的点数,这个时候,就需要使 ...

  2. 国产单片机IO口模拟IrDA1.0协议

    单片机IO口模拟IrDA1.0协议 IrDA1.0协议是一种利用红外通信的无线传输协议,可以很好的解决一些便携式设备与主机之间通信的问题,具有携带方便,低功耗,成本低,传输可靠等特点,缺点是传输距离较 ...

  3. 51单片机——IO口

    IO口分类 此单片机有39个IO口,P0到P3每个8个口,P4有7个口 一般用P1,P2,P3,P4口与外部模块通信,这4个口是准双向口,具备弱上拉电阻的:P0口重新上电后是开漏输出,若总线扩展用,不 ...

  4. 5个IO口扫描25个按键的解决方法(转帖)

    转帖来源:http://www.ouravr.com/bbs/bbs_content_all.jsp?bbs_sn=854599 在做项目(工程)的时候,我们经常要用到比较多的按键,而且IO资源紧张, ...

  5. 5个IO口实现25个按键的扫描,他做到了!堪称一绝!

    排版整理:晓宇 转自:芯片之家 在做项目(工程)的时候,我们经常要用到比较多的按键,而且IO资源紧张,于是我们就想方设法地在别的模块中节省IO口,好不容易挤出一两个IO口,却发现仍然不够用,实在没办法 ...

  6. 推挽输出和开漏输出_关于51单片机IO口的输出模式结构

    单片机种类繁多今天主要谈谈51单片机,在stm32单片机中IO(GPIO)输出模式有很多种.如 模拟输入 .浮空输入.下拉输入.上拉输入.开漏输出.推挽输出等很多种,不同的输入输出有不同的功能,实现不 ...

  7. 模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

    在使用模拟IIC的时候,观看别人的程序的时候发现了程序之间的一些不一样的地方 ----------------------------------代码1------------------------ ...

  8. 1.2基本IO口控制

    1.2.1从最基础的点LED灯开始 单片机通过寻址找到 IO口 怎么(编程)找到IO口呢:ANSI C sfr"指令": 用来直接描述硬件地址,先理解成"一组IO口&qu ...

  9. 单片机IO口应用-独立按键控制LED以及控制LED灯闪烁

    目录 P1=0xdf: sbit LED=P1^5; 输出控制 P1并行口第5位引脚IO置为1 P1并行口第5位引脚IO置为0 输入检测 延时函数 控制LED灯闪烁 方法一 方法二 方法三 P1=0x ...

最新文章

  1. Android实例-手机震动(XE8+小米2)
  2. 利用jquery的qrcode.js插件生成二维码的两种方式的使用
  3. java从入门到精通_Java入门到精通、学习路线、就业方向、薪资及前景分析(上篇)...
  4. java 数组处理_JAVA操作数组
  5. planning algorithms chapter 2
  6. java设计模式观察者模式吗_Java设计模式之观察者模式原理与用法详解
  7. T-SQL select语句连接两个表
  8. h5-localStorage实现缓存ajax请求数据
  9. [转载]QMessageBox 用法_vortex_新浪博客
  10. 无线接口配置 DHCP配置
  11. 最大似然参数估计的求解
  12. spark保存数据到hbase_Spark读取Hbase中的数据
  13. EasyUI 中自定义组件 icon 图标
  14. 晕晕沉沉的一天,ISAPI_Rewrite 2.9破解版竟然是假的
  15. 基于SSM实现微博系统
  16. 阿兹后来明白了,其实别人没有我们想象中的那么关注我们
  17. markdown之表格的使用
  18. 课后作业——Day6
  19. python中出现IndentationError:unindent does not match any outer indentation level是什么问题?
  20. 导出excel,后台执行,前台无反应

热门文章

  1. HDU_1711 Number Sequence(KMP)
  2. gnome-mplayer 挂载 srt字幕 乱码
  3. 华为机试HJ8:合并表记录
  4. persistence java_Java数据持久层 - Persistence Manager Factory
  5. 服务器连接池怎么配置文件,服务器连接池怎么配置
  6. php 用户中心 框架,OpenCenter —— PHP 通用用户中心框架【国人开发,代码托管在 Git@OSC】...
  7. java实现功能6_Java 6
  8. 深入理解JVM(第二版读书笔记)
  9. 21天Jmeter打卡合集之从入门到精通
  10. 3分钟通过日志定位bug,这个技能测试人必须会