搜索
bottom↓
回复: 100

STM32 F103基础学习笔记05(定时器 和 PWM)_2014_5_2

  [复制链接]

出0入0汤圆

发表于 2014-5-2 16:17:13 | 显示全部楼层 |阅读模式
本帖最后由 oldbeginner 于 2014-5-2 20:42 编辑

********************
定时器基础概念
********************


笔记04 中只是实现了一个简单的普通定时器功能,产生一个外部中断。

首先,定时器时钟来源有8个,这里暂时只使用内部时钟,其它几个后面再理解。



********************************
再解释一下 定时器 时钟频率 http://zhlyz2003.blog.163.com/blog/static/168414962013317112951774/

定时器的时钟不是直接来自APB1或APB2,而是来自于输入为APB1或APB2的一个倍频器。

下面以定时器2~7的时钟说明这个倍频器的作用:当APB1的预分频系数为1时,这个倍频器不起作用,定时器的时钟频率等于APB1的频率;当APB1的预分频系数为其它数值(即预分频系数为2、4、8或16)时,这个倍频器起作用,定时器的时钟频率等于APB1的频率两倍。

假定AHB=36MHz,因为APB1允许的最大频率为36MHz,所以APB1的预分频系数可以取任意数值;当预分频系数=1时,APB1=36MHz,TIM2~7的时钟频率=36MHz(倍频器不起作用);当预分频系数=2时,APB1=18MHz,在倍频器的作用下,TIM2~7的时钟频率=36MHz。

有人会问,既然需要TIM2~7的时钟频率=36MHz,为什么不直接取APB1的预分频系数=1?答案是:APB1不但要为TIM2~7提供时钟,而且还要为其它外设提供时钟;设置这个倍频器可以在保证其它外设使用较低时钟频率时,TIM2~7仍能得到较高的时钟频率。

再举个例子:当AHB=72MHz时,APB1的预分频系数必须大于2,因为APB1的最大频率只能为36MHz。如果APB1的预分频系数=2,则因为这个倍频器,TIM2~7仍然能够得到72MHz的时钟频率。能够使用更高的时钟频率,无疑提高了定时器的分辨率,这也正是设计这个倍频器的初衷。

*********************************

计数方式                                                              

计数方式有三种,向上计数 和向下 类似,

比方说,你学会了STM32,率先进入中国梦后,决定每周吃一次包.Zi.,那么就可以使用向上计数,



TIM_Period(即是TIMx_ARR寄存器的值) 的大小实际上表示的是需要经 过TIM_Period 次计数后才会发生一次更新或中断。

用正规的解释如下,



向下计数也很好理解,就是向下计到 0 ,才重新开始向下计数。

双向计数稍微绕一些,



它的触发点 分别是 arr-1 和 1,并不是 arr 和 0。

中央对齐模式(向上/向下计数)是计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

********************

有了上面的概念,可以利用定时器 实现外部中断,但是要实现 PWM 还是不够的。

找不到下面引用的链接了,

TIMERS的简单应用-周期定时                                         

  1. 例程
  2. 定时器初始化
  3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIMx, ENABLE);           //使能TIM3的设备时钟
  4. TIM_TimeBaseStructure.TIM_Prescaler = 15;                                            //预分频器初始化
  5. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    //计数模式选择
  6. TIM_TimeBaseStructure.TIM_Period = 4500;                                            //计数周期
  7. TIM_TimeBaseStructure.TIM_ClockDivision = 0;                                       
  8. TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  9. TIM_TimeBaseInit(TIMx,&TIM_TimeBaseStructure);


  10. 事件中断使能
  11. TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);                                      //使能更新事件中断


  12. 开启定时器计数
  13. TIM_Cmd(TIMx, ENABLE);                                                                       //开启定时器计数


  14. 中断服务程序
  15. void TIMx_IRQHandler(void){                                    //中断函数
  16.   if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET)  {                     //判断发生中断的信息
  17.     TIM_ClearITPendingBit(TIMx, TIM_IT_Update);                                  // 清除中断标志
  18.     /* User Program*/
  19.    }
  20. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2014-5-2 18:34:11 | 显示全部楼层
*********************
众想 STM32 的PWM 输出例子

*********************


http://www.tudou.com/programs/view/6lgXPMTI5FA/

首先是 PWM 理论基础,





只是简单说明了一下 PWM 是 基于 面积相等 的原则下 推导的:

PWM 是一种对模拟信号电平进行数字编码的方法。
通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。
PWM 信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。

**********************

因为 锻炼 能增强 食欲,为了多吃,你决定 实施 多吃包.Zi. 跑步 计划,例如



跑步越多,吃得越多;如果用点亮 LED 来理解,就是 LED 越亮。

实际就是,



STM32 的PWM是TIMx_ARR寄存器确定频率(周期)、由TIMx_CCRx寄存器确定占空比的信号

ARR: auto reload register
CRR: capture / compare register

可以参考
http://blog.sina.com.cn/s/blog_67d246db01010f1h.html

下面是一个PWM模式1的例子。当TIMx_CNT<TIMx_CCRx时PWM信号参考OCxREF为高,否则为低。如果TIMx_CCRx中的比较值大于自动重装载值(TIMx_ARR),则OCxREF保持为’1’。如果比较值为0,则OCxREF保持为’0’。 下图为TIMx_ARR=8时边沿对齐的PWM波形实例。




现在看 众想 STM32 视频13 的代码

  1. int main(void)
  2. {
  3.    u8 led_fx=1;
  4.    u16 led_dt=0;

  5.    RCC_Configuration();        //系统时钟初始化
  6.    GPIO_Configuration();//端口初始化
  7.    TIM3_Configuration();//定时器和pwm配置
  8.    while(1)
  9.    {
  10.             delay_ms(10);
  11.          if(led_fx==1)
  12.          {
  13.                  led_dt++;
  14.          }
  15.          else
  16.          {
  17.            led_dt--;
  18.          }

  19.          if(led_dt>300)  led_fx=0;
  20.             if(led_dt==0)         led_fx=1;

  21.          TIM_SetCompare2(TIM3,led_dt);
  22.    }       
  23. }
复制代码


TIM_SetCompare2(TIM3,led_dt) 就是 设置 CCR 的大小,例子中利用了变量 led_dt,通过 led_dt 变大 和 变小 来控制 LED 亮度。

例子中 AAR 的大小 为 899。

看看定时器 和 PWM 配置 (一起)的代码

  1. void TIM3_Configuration(void)
  2. {
  3.         TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
  4.         TIM_OCInitTypeDef TIM_OCInitStructure;

  5.         GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);

  6.         //定时器初始化
  7.         TIM_TimeBaseStruct.TIM_Period=899;//初值
  8.         TIM_TimeBaseStruct.TIM_Prescaler=0;//预分频
  9.         TIM_TimeBaseStruct.TIM_ClockDivision=0;
  10.         TIM_TimeBaseStruct.TIM_CounterMode=TIM_CounterMode_Up;//向上

  11.         TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStruct);
  12.        

  13.         //pwm 初始化
  14.         TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
  15.         TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
  16.         TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;

  17.         TIM_OC2Init(TIM3,&TIM_OCInitStructure);

  18.         TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);

  19.         TIM_Cmd(TIM3,ENABLE);
  20.                  
  21. }
复制代码


然后,PWM 输出 使用的 GPIO 配置 是 复用 推挽输出,

还利用了 TIM3 的部分映射功能,是 通道2 和 PB5 一致,

        GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);

**********************************

内容还是比较多的。
因为 众想STM32 的 引脚 PB5 连接 LED,LED 的另一端 是 3.3v 电压,所以输出低电平 LED 亮。

通过        TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; 输出低电平。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-2 19:16:19 | 显示全部楼层
很强大,收藏慢慢享用谢谢!

出0入0汤圆

发表于 2014-5-4 00:23:57 | 显示全部楼层
LZ在TIM_TimeBaseInitTypeDef配置的时候注意下TIM_Period 和TIM_Prescaler的值。我参考其他地方写的,真实Period应该是(TIM_Period + 1),而Prescaler也应该为( TIM_Prescaler + 1)。如果这个观点无误的话LZ的第一段代码可能有误。

出0入0汤圆

 楼主| 发表于 2014-5-6 19:50:29 | 显示全部楼层
tianyiran02 发表于 2014-5-4 00:23
LZ在TIM_TimeBaseInitTypeDef配置的时候注意下TIM_Period 和TIM_Prescaler的值。我参考其他地方写的,真实P ...

谢谢错误提示。我也发现了,因为暂时不影响主线学习,还没打算勘误。

出0入0汤圆

 楼主| 发表于 2014-5-6 21:31:16 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-5-6 22:24 编辑
oldbeginner 发表于 2014-5-2 18:34
*********************
众想 STM32 的PWM 输出例子


*************************
野火 的 4路 PWM输出 例子

*************************


代码下载,http://cache.amobbs.com/bbs_uplo ... ev_691956GOE4P5.rar
对应教程,http://cache.amobbs.com/bbs_uplo ... ev_692002IAMTQZ.pdf

野火的例子,输出 4路 不同占空比 的方波,并使用 keil 模拟仿真(对我很新鲜)。

PWM 背景并没有介绍,直奔主题。





过程也很清晰,不过代码 看起来有些复杂。为了理解代码,先做一下准备,理解上面步骤的重点是什么。



上图是前4步,配置相应定时器,对应代码如下,使能并没有图中示意。

  1.   /* Time base configuration */                 
  2.   TIM_TimeBaseStructure.TIM_Period = 999;       //当定时器从0计数到999,即为1000次,为一个定时周期
  3.   TIM_TimeBaseStructure.TIM_Prescaler = 0;            //设置预分频:不预分频,即为36MHz
  4.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;        //设置时钟分频系数:不分频
  5.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式

  6.   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
复制代码


然后就是 学习的重点, PWM 配置特有,



原来也很简单,对应代码如下,

  1.   /* PWM1 Mode configuration: Channel1 */
  2.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;            //配置为PWM模式1
  3.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;       
  4.   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;           //设置跳变值,当计数器计数到这个值时,电平发生跳变
  5.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //当定时器计数值小于CCR1_Val时为高电平

  6.   TIM_OC1Init(TIM3, &TIM_OCInitStructure);         //使能通道1

  7.   TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
复制代码


因为有4路(channel),上面只配置了1路,如何确定是第几路?还需要眼力好一些,



知道了如何配置通道1,配置通道2 也 手到擒来 ,

  1.   /* PWM1 Mode configuration: Channel2 */
  2.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  3.   TIM_OCInitStructure.TIM_Pulse = CCR2_Val;          //设置通道2的电平跳变值,输出另外一个占空比的PWM

  4.   TIM_OC2Init(TIM3, &TIM_OCInitStructure);          //使能通道2

  5.   TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
复制代码


如法炮制 通道3 和 通道4,

  1.   /* PWM1 Mode configuration: Channel3 */
  2.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  3.   TIM_OCInitStructure.TIM_Pulse = CCR3_Val;        //设置通道3的电平跳变值,输出另外一个占空比的PWM

  4.   TIM_OC3Init(TIM3, &TIM_OCInitStructure);         //使能通道3

  5.   TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

  6.   /* PWM1 Mode configuration: Channel4 */
  7.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  8.   TIM_OCInitStructure.TIM_Pulse = CCR4_Val;        //设置通道4的电平跳变值,输出另外一个占空比的PWM

  9.   TIM_OC4Init(TIM3, &TIM_OCInitStructure);        //使能通道4

  10.   TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

  11.   TIM_ARRPreloadConfig(TIM3, ENABLE);                         // 使能TIM3重载寄存器ARR
复制代码


最后,使能定时器

  1.   /* TIM3 enable counter */
  2.   TIM_Cmd(TIM3, ENABLE);                   //使能定时器3       
复制代码


************************************

理解了核心内容,主函数等配套 就不再理解了,

直接看疗效,



是怎样达到上面的效果呢?

结束前复习一下,


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-6 21:47:10 | 显示全部楼层
谢谢楼主这么形象的讲解,

出0入0汤圆

发表于 2014-5-6 22:13:49 | 显示全部楼层
动画效果是如何做出来的?很想知道,谢谢

出0入0汤圆

发表于 2014-5-7 05:07:37 来自手机 | 显示全部楼层
楼主快点更新啊…等不急了…能不能把高级定时器仔细讲解一下啊…

出0入0汤圆

发表于 2014-5-7 06:20:50 | 显示全部楼层
很不错,楼主辛苦了。

出0入0汤圆

 楼主| 发表于 2014-5-7 20:39:13 | 显示全部楼层
oldbeginner 发表于 2014-5-6 21:31
*************************
野火 的 4路 PWM输出 例子

************************
众想 PWM 和 野火 PWM 的区别

************************


众想的 PWM 占空比 是 动态的,可以制造类似的呼吸灯



野火的 PWM 占空比 是固定的



*********************
区别在于,众想 STM32 使用了 TIM_SetCompare2 函数(通道2) 给 跳变电平赋值, 而 野火 STM32 使用的是 TIM_OCInitStructure.TIM_Pulse = CRR 赋值。

显然,要让占空比 变化,应该使用 TIM_SetComparex 函数(x表示相应通道,例如2)。



一个呼吸灯的设置步骤 重点,本质和 众想 STM32 类似,

http://www.cnblogs.com/2cats/p/3579523.html

  • 时钟(RCC_APB1PeriphClockCmd,RCC_APB2PeriphClockCmd)
    • GPIO|TIMx:RCC_APB2Periph_GPIOA,RCC_APB1Periph_TIMx        
  • GPIO(GPIO_Init)
    • 推挽复用:GPIO_Mode_AF_PP
  • TIM(TIM_TimeBaseInit)->决定频率
    • 重装值:TIM_Period
    • 分频:TIM_Prescaler(当为0时表示不分频所以要减1)
    • 计数模式:TIM_CounterMode_Up/Down...
  • PWM(TIM_OCxInit(&TIM_OCInitTypeDef))         //红色x代表通道,0-4
    • PWM模式1/2:TIM_OCMode=TIM_OCMode_PWM1/2   //可导致波形反转:模式1:【计数器<比较器】->有效电平 | 模式2:【计数器>比较器】->有效电平
    • 正/负逻辑: TIM_OCPolarity = TIM_OCPolarity_High;//可导致波形反转 :此处设置有效电平为高
    • 输出使能: TIM_OutputState = TIM_OutputState_Enable;
  • 使能
    • TIM_Cmd(TIMx, ENABLE);
  • 设置/修改 占空比(TIM_SetComparex)
    • 占空比 = Compare/TIM_Period


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-7 21:05:36 | 显示全部楼层
对PWM的理解又加深了一点,谢谢楼主。

出0入0汤圆

 楼主| 发表于 2014-5-9 08:43:07 | 显示全部楼层
oldbeginner 发表于 2014-5-7 20:39
************************
众想 PWM 和 野火 PWM 的区别


******************
安富莱的 TFT 屏幕背光 变亮变暗 的例子

******************


看了几个开发板 的例子,感觉 安富莱 和 野火 的风格 有点像

程序下载
http://bbs.armfly.com/job.php?action=download&aid=10

先看一下只会捣乱的原理图,



还没学习 TFT 连线,我是不懂,如果卡在这里很不值,因为只是为了学习PWM 的用法,

其实非常简单,该 TFT 的背光 只是由 一个引脚(PB1) 控制,和 LED 等价。

控制屏幕的背光 和 控制 一个LED 一样简单。

显然,使用众想 STM32 中出现 的 TIM_SetComparex 函数是 可以的,
不过,安富莱的写法不太一样,还是 使用的 TIM_Pulse 成员变量。

先看一下主函数的循环体,

  1. /* 进入主程序循环体 */
  2.         while (1)
  3.         {
  4.                 CPU_IDLE();                /* 这个宏在bsp_timer.h 中定义,目前定义为空。用户可以修改这个宏实现CPU休眠和喂狗 */

  5.                 ucKeyCode = bsp_GetKey();         /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  6.                 if (ucKeyCode != KEY_NONE)
  7.                 {
  8.                         /* 有键按下 */
  9.                         switch (ucKeyCode)          
  10.                         {
  11.                                 case KEY_DOWN_JOY_UP:                /* 摇杆UP键按下 */
  12.                                         ucBright += BRIGHT_STEP;
  13.                                         if (ucBright > BRIGHT_MAX)
  14.                                         {
  15.                                                 ucBright = BRIGHT_MAX;
  16.                                         }
  17.                                         SetBackLight(ucBright);          /* 设置背光亮度 */
  18.                                         printf("设置LCD背景光,当前亮度 = % d \r\n", ucBright);
  19.                                         break;

  20.                                 case KEY_DOWN_JOY_DOWN:                /* 摇杆DOWN键按下 */
  21.                                         if (ucBright < BRIGHT_STEP)
  22.                                         {
  23.                                                 ucBright = 0;
  24.                                         }
  25.                                         else
  26.                                         {
  27.                                                 ucBright -= BRIGHT_STEP;
  28.                                         }
  29.                                          SetBackLight(ucBright);           /* 设置背光亮度 */
  30.                                         printf("设置LCD背景光,当前亮度 = % d \r\n", ucBright);
  31.                                         break;

  32.                                 default:
  33.                                         break;
  34.                         }
  35.                 }
  36.         }
复制代码


也有点复杂,不过知道 是通过 摇杆 来控制 背光亮暗的,核心就是 SetBackLight(ucBright); 函数

  1. static void SetBackLight(uint8_t _bright)
  2. {
  3.         /*
  4.                 背光口线是 PB1, 复用功能选择 TIM3_CH4
  5.        
  6.                 当关闭背光时,将CPU IO设置为浮动输入模式(推荐设置为推挽输出,并驱动到低电平)
  7.                 将TIM3关闭以节约功耗
  8.         */

  9.         GPIO_InitTypeDef GPIO_InitStructure;
  10.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  11.         TIM_OCInitTypeDef  TIM_OCInitStructure;

  12.         /* 第1步:打开GPIOB RCC_APB2Periph_AFIO 的时钟        */
  13.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

  14.         if (_bright == 0)
  15.         {
  16.                 /* 配置背光GPIO为输入模式 */
  17.                 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  18.                 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  19.                 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  20.                 GPIO_Init(GPIOB, &GPIO_InitStructure);

  21.                 /* 关闭TIM3 */
  22.                 TIM_Cmd(TIM3, DISABLE);
  23.                 return;
  24.         }
  25.         else if (_bright == BRIGHT_MAX)        /* 最大亮度 */
  26.         {
  27.                 /* 配置背光GPIO为推挽输出模式 */
  28.                 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  29.                 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  30.                 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  31.                 GPIO_Init(GPIOB, &GPIO_InitStructure);

  32.                 GPIO_SetBits(GPIOB, GPIO_Pin_1);

  33.                 /* 关闭TIM3 */
  34.                 TIM_Cmd(TIM3, DISABLE);
  35.                 return;
  36.         }

  37.         /* 配置背光GPIO为复用推挽输出模式 */
  38.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  39.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  40.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  41.         GPIO_Init(GPIOB, &GPIO_InitStructure);

  42.         /* 使能TIM3的时钟 */
  43.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  44.         /*
  45.                 TIM3 配置: 产生1路PWM信号;
  46.                 TIM3CLK = 72 MHz, Prescaler = 0(不分频), TIM3 counter clock = 72 MHz
  47.                 计算公式:
  48.                 PWM输出频率 = TIM3 counter clock /(ARR + 1)

  49.                 我们期望设置为100Hz

  50.                 如果不对TIM3CLK预分频,那么不可能得到100Hz低频。
  51.                 我们设置分频比 = 1000, 那么  TIM3 counter clock = 72KHz
  52.                 TIM_Period = 720 - 1;
  53.                 频率下不来。
  54.          */
  55.         TIM_TimeBaseStructure.TIM_Period = 720 - 1;        /* TIM_Period = TIM3 ARR Register */
  56.         TIM_TimeBaseStructure.TIM_Prescaler = 0;
  57.         TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  58.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  59.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  60.         /* PWM1 Mode configuration: Channel1 */
  61.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  62.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  63.         /*
  64.                 _bright = 1 时, TIM_Pulse = 1
  65.                 _bright = 255 时, TIM_Pulse = TIM_Period
  66.         */
  67.         TIM_OCInitStructure.TIM_Pulse = (TIM_TimeBaseStructure.TIM_Period * _bright) / BRIGHT_MAX;        /* 改变占空比 */

  68.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  69.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);
  70.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

  71.         TIM_ARRPreloadConfig(TIM3, ENABLE);

  72.         /* TIM3 enable counter */
  73.         TIM_Cmd(TIM3, ENABLE);
  74. }
复制代码


代码很多,不过结构比较清晰,注释也到位,降低了理解难度。

代码暂时不用全部理解,核心在后面,

        /* PWM1 Mode configuration: Channel1 */
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        /*
                _bright = 1 时, TIM_Pulse = 1
                _bright = 255 时, TIM_Pulse = TIM_Period
        */
        TIM_OCInitStructure.TIM_Pulse = (TIM_TimeBaseStructure.TIM_Period * _bright) / BRIGHT_MAX;        /* 改变占空比 */


其中,#define BRIGHT_MAX                255                /* 最大亮度 */

_bright 就是传入的参数,这个数不断变化,从而 TIM_Pulse 相应变化,实现 占空比 的改变,同时 PWM 重新配置一次,(TIM_SetCompare 只改变CRR 大小,不需要重新配置)。

因为  SetBackLight 这个函数在进入 循环体之前已经被调用了一次,所以 PWM 配置已经完成,所以采用 TIM_SetCompare 也可以,只不过 在最亮时 和 最暗时,没能关闭 TIM3。关闭TIM3 的目的是 省功耗,最暗时可以省功耗,最亮时不知道能节约多少?

之所以 能在 最暗时 和最亮时,关闭 TIM3 ,这时 PWM也被关闭,原因是 占空比 0 和 占空比 100% 可以分别用 悬空输入 和 推挽输出(高电平)来代替,没有必要再PWM了。

所以主函数循环体,可以更改如下,
        /* 进入主程序循环体 */

  1.         /* 进入主程序循环体 */
  2.         while (1)
  3.         {
  4.                 CPU_IDLE();                /* 这个宏在bsp_timer.h 中定义,目前定义为空。用户可以修改这个宏实现CPU休眠和喂狗 */

  5.                 ucKeyCode = bsp_GetKey();         /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  6.                 if (ucKeyCode != KEY_NONE)
  7.                 {
  8.                         /* 有键按下 */
  9.                         switch (ucKeyCode)          
  10.                         {
  11.                                 case KEY_DOWN_JOY_UP:                /* 摇杆UP键按下 */
  12.                                         ucBright += BRIGHT_STEP;
  13.                                         if (ucBright > BRIGHT_MAX)
  14.                                         {
  15.                                                 ucBright = BRIGHT_MAX;
  16.                                         }
  17.                                         TIM_SetCompare4(TIM3,ucBright*719/BRIGHT_MAX);          /* 设置背光亮度 */
  18.                                         printf("设置LCD背景光,当前亮度 = % d \r\n", ucBright);
  19.                                         break;

  20.                                 case KEY_DOWN_JOY_DOWN:                /* 摇杆DOWN键按下 */
  21.                                         if (ucBright < BRIGHT_STEP)
  22.                                         {
  23.                                                 ucBright = 0;
  24.                                         }
  25.                                         else
  26.                                         {
  27.                                                 ucBright -= BRIGHT_STEP;
  28.                                         }
  29.                                         TIM_SetCompare4(TIM3,ucBright*719/BRIGHT_MAX);           /* 设置背光亮度 */
  30.                                         printf("设置LCD背景光,当前亮度 = % d \r\n", ucBright);
  31.                                         break;

  32.                                 default:
  33.                                         break;
  34.                         }
  35.                 }
  36.         }
复制代码


把 SetBackLight(ucBright);
改为了 TIM_SetCompare4(TIM3,ucBright*719/BRIGHT_MAX);

缺点是 占空比为 0 或 100% 时,不能关闭 TIM3。优点是,不需要每次改变亮度都配置 PWM。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-9 10:52:24 | 显示全部楼层
好东西。谢谢分享

出0入0汤圆

 楼主| 发表于 2014-5-15 10:08:39 | 显示全部楼层
oldbeginner 发表于 2014-5-9 08:43
******************
安富莱的 TFT 屏幕背光 变亮变暗 的例子

********************
梦断 定时器 高级功能

********************


定时器 的 高级功能在 《STM32中文参考手册》有介绍,使用的是 火星语,并且 寄存器 方式,刚入门 就要学习 这些高级 功能 是 自我毁灭,不仅学不会,还打击热情和信心。
http://blog.sina.com.cn/s/blog_79fbaced010160v9.html
看STM32定时器一个多星期,真是乱啊。

一方面因为 这些 高级功能的配套 学习资料太少,另外感觉 stm32 定时器 设计(包括库函数)也不合理,人为复杂化,用 51 做得频率计 波形发生器 直流电机PWM控制 等等,代码仿真 例子非常多,而 功能更强大的stm32这样的例子居然少之又少,看来 出现 因为功能过于强大复杂,简单的应用都变得遥不可及。

首先,常见的开发板 都没有 定时器 高级功能 全面的例子,9成 都是 用 PWM 方式;而 toggle inactive active oneulse 等功能基本没有介绍,然后 开发板的提高篇 和 高级篇 其实 是用更 高级的外设(知识不见得高级),而不是 更深入和全面的内容。

唯一比较详细 的 定时器 高级 功能 是 官方的 例子,



遗憾的是 开发板都只选用了其中 两三个例子,安富莱好一些,把 官方代码 (v3.4库)整理成工程并编译了一下,不过 和它自己开发板的引脚并不一致,也不能 直接套用。
刚才 搜了一下,好像 是 只有百为 stm32 可以直接兼容官方例程,没有用过,也不知道是不是还是 v2.0的库。
http://www.amobbs.com/thread-5513825-1-1.html

******************************

打算先继续跟着其它开发板,进入其它主题,定时器 高级功能 逐渐在后面补充,应该是以 官方例程为主的理解。

******************************
野火 的呼吸灯

******************************


野火最新的程序,

也是 PWM 模式,不过 占空比 是变化的,而且利用了定时器。

  1. /* LED亮度等级 PWM表 */
  2. uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,
  3.         107,143,191,255,255,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};
  4. uint16_t CCR1_Val=50;
复制代码


然后,见过好几次的PWM配置

  1.   /* PWM模式配置 */
  2.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                                            //配置为PWM模式1

  3.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;        //使能输出
  4.   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;                                                                                                          //设置初始PWM脉冲宽度为0       
  5.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;            //当定时器计数值小于CCR1_Val时为低电平

  6.   TIM_OC3Init(TIM3, &TIM_OCInitStructure);                                                                                 //使能通道3
  7.        
复制代码


与其它开发板 不一致的,在于 中断函数利用了寄存器赋值 TIM3->CCR3 = indexWave[pwm_index],其实也可以用 SetCompare3 函数代替。
  1. /* 呼吸灯中断服务函数 */
  2. void TIM3_IRQHandler(void)
  3. {       
  4.         static uint8_t pwm_index = 0;                        //用于PWM查表
  5.         static uint8_t period_cnt = 0;                //用于计算周期数
  6.         uint16_t capture = 0;

  7.        
  8.         if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)        //TIM_IT_Update
  9.         {                       
  10.                         period_cnt++;
  11.                         if(period_cnt >= 10)                                                                                //若输出的周期数大于10,输出下一种脉冲宽的PWM波
  12.                         {
  13.                                
  14.                                 TIM3->CCR3 = indexWave[pwm_index];        //根据PWM表修改定时器的比较寄存器值
  15.                                 pwm_index++;                                                                                                //标志PWM表的下一个元素
  16.                        
  17.                                 if( pwm_index >=  40)                                                                //若PWM脉冲表已经输出完成一遍,重置PWM查表标志
  18.                                 {
  19.                                         pwm_index=0;                                                               
  20.                                 }
  21.                                
  22.                                 period_cnt=0;                                                                                                //重置周期计数标志
  23.                 }
  24.                 TIM_ClearITPendingBit (TIM3, TIM_IT_Update);        //必须要清除中断标志位
  25.                
  26.         }
  27. }
复制代码


代码写得也很清晰。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-5-17 08:05:58 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-5-17 08:14 编辑
oldbeginner 发表于 2014-5-15 10:08
********************
梦断 定时器 高级功能


*******************
官方例程 的理解

*******************


官方例程总体来说 格式 八股,读起来没有热情,有点像 早期 “留言请按1,。。。2,。。。。3,。。。。。4,。。。。。。。。#” 的感觉。

从适合入门角度看,官方例程比 淘宝上 开发板配套的例程 要差,不过也有优点:同一个素材,相关内容多。比如定时器,很多开发板只是配套了 两三个例程,就没下文了,不能够实现深入理解。

所以,要深入理解 TIM,官方配套例程 TIM 这一块 还是值得看的(其它模块还不清楚是否 也需要看 官方例程,毕竟 开发板配套的例程有学习优势)。

**************************
学习 工具: 安富莱 v2 + 8通道 逻辑分析仪。

安富莱配套了 v3.4 例程,而且工程文件已经配置好了,可以直接编译,但是 无法利用 MDK 仿真(还没找到 具体原因)。

***************
TIM_OCInactive

***************


TIM_OCxxxx,output compare 输出对比模式, Inactive,无效模式,字面很好理解。但是 好像翻译 是  输出比较非主动模式,感觉翻译得不好,没有无效(失效)具体。

Example Description                          

This example shows how to configure the TIM peripheral in Output Compare Inactive mode with the corresponding Interrupt requests for each channel.

输出四路,引脚是
PC.06
PC.07
PC.08
PC.09

输出什么呢?                                       

The TIM2 CCR1 register value is equal to 1000: TIM2_CC1 delay = CCR1_Val/TIM2 counter clock = 1000 ms so the PC.06 is reset after a delay equal to 1000 ms.

The TIM2 CCR2 register value is equal to 500: TIM2_CC2 delay = CCR2_Val/TIM2 counter clock = 500 ms so the PC.07 is reset after a delay equal to 500 ms.

The TIM2 CCR3 register value is equal to 250: TIM2_CC3 delay = CCR3_Val/TIM2 counter clock = 250 ms so the PC.08 is reset after a delay equal to 250 ms.

The TIM2 CCR4 register value is equal to 125: TIM2_CC4 delay = CCR4_Val/TIM2 counter clock = 125 ms so the PC.09 is reset after a delay equal to 125 ms.


到底是什么?                                            




上图 通道1 (PC7)默认是高电平,应该是 硬件问题,安富莱v2 有个开关switch,刚开始 off 状态,四个引脚都应该是低电平(PC7 有问题,高电平)。

然后,switch 拨到 on,四个引脚 上升沿,然后各自持续一段时间,再回到低电平状态。

首先,配置定时器2

  1.   /* Time base configuration */
  2.   TIM_TimeBaseStructure.TIM_Period = 65535;
  3.   TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  4.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  5.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  6.   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
复制代码


只看通道1的 代码就可以了 ,

  1.   /* Output Compare Active Mode configuration: Channel1 */
  2.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive;
  3.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  4.   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  5.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  6.   TIM_OC1Init(TIM2, &TIM_OCInitStructure);

  7.   TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
复制代码


其中,
uint16_t CCR1_Val = 1000;
uint16_t CCR2_Val = 500;
uint16_t CCR3_Val = 250;
uint16_t CCR4_Val = 125;

看到 CCRx_Val 基本上 就能理解 输出图了,

通道1 的高电平 持续了 1000 ms ,然后低电平;
通道2 的高电平 持续了   500 ms ,然后低电平;
。。。

配置好通道后,


  1.   /* TIM IT enable */
  2.   TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);

  3.   /* Set PC.06, PC.07, PC.08 and PC.09 pins */
  4.   GPIO_SetBits(GPIOC, GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9);

  5.   /* TIM2 enable counter */
  6.   TIM_Cmd(TIM2, ENABLE);

  7.   while (1)
  8.   {}
复制代码


再看中断函数,


  1. void TIM2_IRQHandler(void)
  2. {
  3.   if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.   {
  5.     /* Clear TIM2 Capture Compare1 interrupt pending bit*/
  6.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

  7.     /* PC.06 turn-off after 1000 ms */
  8.     GPIO_ResetBits(GPIOC, GPIO_Pin_6);
  9.   }
  10.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  11.   {
  12.     /* Clear TIM2 Capture Compare2 interrupt pending bit*/
  13.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);

  14.     /* PC.07 turn-off after 500 ms */
  15.     GPIO_ResetBits(GPIOC, GPIO_Pin_7);
  16.   }
  17.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  18.   {
  19.     /* Clear TIM2 Capture Compare3 interrupt pending bit*/
  20.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);

  21.     /* PC.08 turn-off after 250 ms */
  22.     GPIO_ResetBits(GPIOC, GPIO_Pin_8);
  23.   }
  24.   else
  25.   {
  26.     /* Clear TIM2 Capture Compare4 interrupt pending bit*/
  27.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);

  28.     /* PC.09 turn-off after 125 ms */
  29.     GPIO_ResetBits(GPIOC, GPIO_Pin_9);
  30.   }
  31. }
复制代码


把 PC6 ~ PC9 引脚 高电平,所以 switch 一接通,4个引脚会出现上升沿,然后持续 CCRx_Val 毫秒,再到 低电平。




如果把 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
改为
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

输出没有变化,
http://bbs.21ic.com/icview-285762-1-1.html

TIM_OCMode_Inactive:输出比较非主动模式 (匹配时设置输出引脚为无效电平,当计数值为比较/捕获寄存器值相同时,强制输出为低电平)。

当 Inactive 时,TIM_OCPolarity 从实验上看没有影响。

看来迷惑是普遍的,不止我有。

***********STM32 应该重新 找些 专业的程序员 把 库函数 做一下,尤其 TIM 这一块************************

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-5-17 09:01:33 | 显示全部楼层
oldbeginner 发表于 2014-5-17 08:05
*******************
官方例程 的理解

****************
TIM_OCMode_Active

****************


Example Description               
This example shows how to configure the TIM peripheral to generate four different signals with four different delays.

引脚

PA.06 (TIM3_CH1)
PA.07 (TIM3_CH2)
PB.00 (TIM3_CH3)
PB.01 (TIM3_CH4)

相应参数


The TIM3 CCR1 register value is equal to 1000: TIM3_CH1 delay = CCR1_Val/TIM3 counter clock = 1000 ms so the TIM3 Channel 1 generates a signal with a delay equal to 1000 ms.

The TIM3 CCR2 register value is equal to 500: TIM3_CH2 delay = CCR2_Val/TIM3 counter clock = 500 ms so the TIM3 Channel 2 generates a signal with a delay equal to 500 ms.

The TIM3 CCR3 register value is equal to 250: TIM3_CH3 delay = CCR3_Val/TIM3 counter clock = 250 ms so the TIM3 Channel 3 generates a signal with a delay equal to 250 ms.

The TIM3 CCR4 register value is equal to 125: TIM3_CH4 delay = CCR4_Val/TIM3 counter clock = 125 ms so the TIM3 Channel 4 generates a signal with a delay equal to 125 ms.


这次图像不怎么好了,应该是硬件问题




通道4没信号, 通道3 好像 抖动的感觉。

*************************

看来是硬件兼容的问题,把 引脚改成 和 Inactive 模式一致(TIM2),(官方例程 TIM 模块  每个例子 都更改 引脚,没有必要,而且加大了学习难度,如果出现引脚硬件故障则手足无措)
PC.06
PC.07
PC.08
PC.09

然后,结果



PC7 是老问题,默认是 高电平。

首先,配置定时器

  1.   /*Compute the prescaler value */
  2.   PrescalerValue = (uint16_t) (SystemCoreClock / 2000) - 1;
  3.   /* Time base configuration */
  4.   TIM_TimeBaseStructure.TIM_Period = 65535;
  5.   TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  6.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  7.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  8.   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
复制代码


然后,通道配置


  1.   /* Output Compare Active Mode configuration: Channel1 */
  2.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Active;
  3.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  4.   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  5.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  6.   TIM_OC1Init(TIM2, &TIM_OCInitStructure);

  7.   TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
复制代码


其中,

uint16_t CCR1_Val = 1000;
uint16_t CCR2_Val = 500;
uint16_t CCR3_Val = 250;
uint16_t CCR4_Val = 125;

通道配置好以后,


  1.   TIM_ARRPreloadConfig(TIM2, ENABLE);

  2.   /* TIM3 enable counter */
  3.   TIM_Cmd(TIM2, ENABLE);

  4.   while (1)
  5.   {}
复制代码


没有中断函数

从图上看, CCRx_Val 好像 没有什么作用。

然后,如果把   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
改成
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

输出也没有改变,

所以当 Inactive 时,TIM_OCPolarity 从实验上看没有影响。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-18 00:09:36 | 显示全部楼层
楼主不容易啊,整理的这么好哈~~~谢谢啦

出0入0汤圆

发表于 2014-5-18 10:09:38 | 显示全部楼层
楼主不容易啊,整理的这么好哈~~~谢谢啦=====+++++1!

出0入0汤圆

发表于 2014-5-18 21:27:39 | 显示全部楼层
期待输入捕获。

出0入0汤圆

发表于 2014-5-22 12:29:14 | 显示全部楼层
楼主很用心,值得学习

出0入0汤圆

发表于 2014-5-22 12:45:42 | 显示全部楼层
这些图片是用什么软件做的,有点意思

出0入0汤圆

发表于 2014-5-22 13:11:06 | 显示全部楼层
mark,,相当不错,慢慢看,

出0入0汤圆

 楼主| 发表于 2014-5-24 16:25:35 | 显示全部楼层
oldbeginner 发表于 2014-5-17 09:01
****************
TIM_OCMode_Active

*****************
TIM Time Base example

*****************


一个被 很多开发板 无视的例子,但这个例子 能学到很多 定时器 用法。

Example Description
This example shows how to configure the TIM peripheral in Output Compare Timing mode with the corresponding Interrupt requests for each channel in order to generate 4 different time bases.

信号如下:
The TIM2 CC1 register value is equal to 40961, CC1 update rate = TIM2 counter clock / CCR1_Val = 146.48 Hz, so the TIM2 Channel 1 generates an interrupt each 6.8ms

The TIM2 CC2 register is equal to 27309, CC2 update rate = TIM2 counter clock / CCR2_Val = 219.7 Hz so the TIM2 Channel 2 generates an interrupt each 4.55ms

The TIM2 CC3 register is equal to 13654, CC3 update rate = TIM2 counter clock / CCR3_Val = 439.4Hz so the TIM2 Channel 3 generates an interrupt each 2.27ms

The TIM2 CC4 register is equal to 6826, CC4 update rate = TIM2 counter clock / CCR4_Val = 878.9 Hz so the TIM2 Channel 4 generates an interrupt each 1.13ms.

就是下面图片所示的信号
  1. __IO uint16_t CCR1_Val = 40961;
  2. __IO uint16_t CCR2_Val = 27309;
  3. __IO uint16_t CCR3_Val = 13654;
  4. __IO uint16_t CCR4_Val = 6826;
复制代码




这个程序和上面的程序不同之处, 不仅 是 通过中断 调用了 TIM_SetCompare1 函数,而且调用的参数 使用了 捕获方式,
    capture = TIM_GetCapture1(TIM2);
    TIM_SetCompare1(TIM2, capture + CCR1_Val);


******************************************
先看一下程序,只看主干,详细要看源代码

首先,
  1.   /* Time base configuration */
  2.   TIM_TimeBaseStructure.TIM_Period = 65535;
  3.   TIM_TimeBaseStructure.TIM_Prescaler = 0;
  4.   TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  5.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  6.   TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
复制代码


然后,
  1.   /* Output Compare Timing Mode configuration: Channel1 */
  2.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
  3.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  4.   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  5.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  6.   TIM_OC1Init(TIM2, &TIM_OCInitStructure);
  7.   TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
复制代码


然后,
  1.   /* Output Compare Timing Mode configuration: Channel2 */
  2.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  3.   TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

  4.   TIM_OC2Init(TIM2, &TIM_OCInitStructure);

  5.   TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);
复制代码


同理 通道3,4

最后,使能 进入 大循环,
  1.   /* TIM IT enable */
  2.   TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);

  3.   /* TIM2 enable counter */
  4.   TIM_Cmd(TIM2, ENABLE);

  5.   while (1);
复制代码


上面的 内容 和 前面的练习类似,难度不大,

学习重点在于中断 函数,

  1. void TIM2_IRQHandler(void)
  2. {
  3.   if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.   {
  5.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

  6.     /* Pin PC.06 toggling with frequency = 73.24 Hz */
  7.     GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_6)));
  8.     capture = TIM_GetCapture1(TIM2);
  9.     TIM_SetCompare1(TIM2, capture + CCR1_Val);
  10.   }
  11.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  12.   {

  13.     此处省略
  14.   }
  15.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  16.   {

  17.     此处省略
  18.   }
  19.   else
  20.   {
  21.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);

  22.     此处省略
  23.   }
  24. }
复制代码


核心 是理解

    capture = TIM_GetCapture1(TIM2);
    TIM_SetCompare1(TIM2, capture + CCR1_Val);


查找 GetCapture 的用法
uint16_t TIM_GetCapture1  ( TIM_TypeDef *  TIMx )  

Gets the TIMx Input Capture 1 value.

Parameters:
TIMx,: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral.  

Return values:
Capture Compare 1 Register value.  


返回 CRR 值,这个值比较熟悉了。

然后为什么 返回的CRR 还要 + CCR1_Val,然后作为新的 CRR 再赋值呢?

网上搜一下,
http://www.360doc.com/content/11/0917/23/7736891_149118340.shtml
这里的解释是:设置TIMx捕获比较1寄存器值然后动态修改其CCR的值 使整个程序一直进行下去。

不详细,还得继续找答案,
http://www.eefocus.com/bbs/article_244_520168.html
这个就详细点了,

设置TIM2捕获比较寄存器1 的值  ,
//将计数值加上翻转的脉冲值写入输出比较寄存器中,以保证下一个TIM事
   //将TIM1_CCR1的值增加2000,使得下一个TIM事件也需要2000个脉冲,
//另一种方式是清零脉冲计数器

基本可以理解了,因为 脉冲计数器(CNT),没有清零,一致再累加,为了 每计够 CCR 次时能翻转,所以 将CCR 不断 累加。感觉没有 使用 清零脉冲计数器 的方案好。

怎样清零,找到 另一种方式是清零脉冲计数器 //TIM_SetCounter(TIM2,0x0000);

先修改一下中断函数,
  1. void TIM2_IRQHandler(void)
  2. {
  3.   if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.   {
  5.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);

  6.     /* Pin PC.06 toggling with frequency = 73.24 Hz */
  7.     GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_6)));

  8.                 TIM_SetCounter(TIM2,0x0000);
  9.   }
  10.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  11.   {
  12.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);

  13.     /* Pin PC.07 toggling with frequency = 109.8 Hz */
  14.     GPIO_WriteBit(GPIOC, GPIO_Pin_7, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_7)));

  15.                 TIM_SetCounter(TIM2,0x0000);
  16.   }
  17.   else if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  18.   {
  19.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);

  20.     /* Pin PC.08 toggling with frequency = 219.7 Hz */
  21.     GPIO_WriteBit(GPIOC, GPIO_Pin_8, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8)));

  22.                 TIM_SetCounter(TIM2,0x0000);
  23.   }
  24.   else
  25.   {
  26.     TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);

  27.     /* Pin PC.09 toggling with frequency = 439.4 Hz */
  28.     GPIO_WriteBit(GPIOC, GPIO_Pin_9, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9)));
  29.     capture = TIM_GetCapture4(TIM2);
  30.     TIM_SetCompare4(TIM2, capture + CCR4_Val);
  31.   }
  32. }
复制代码


前三个通道使用 SetCounter 函数,


只有第三个通道有输出,

如果全部都使用 SetCounter 函数,



只有第四个通道有输出,

这不是偶然的,因为 第四个通道 的CCR 值最小,其它三个通道还没来得及翻转,就因为第四个通道翻转并重新把脉冲计数器 置0 ,所以其它三个通道永远无法翻转。
多个通道,而且CCR 不一致时,不要用SetCounter 。

因为CCR最大值 是 65535,所以会溢出。capture + CCR1_Val 不停进行下去,会溢出,目前看来没影响的,感觉应该类似环形队列,CCR 和 CNT 能绕回来 再从0 又开始增加,不停循环。(CCR溢出,刚开始迷惑了好久。)

看来 CCR 溢出,会破坏定时器,是个流言!


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-5-25 15:04:31 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-5-25 15:08 编辑
oldbeginner 发表于 2014-5-24 16:25
*****************
TIM Time Base example


***********************
那这里为什么写65535呢?

***********************


网上一个有问无答的帖子:
http://forum.eepw.com.cn/thread/226670/1

在 time_base 案例中,定义了下面

TIM_TimeBaseStructure.TIM_Period = 65535;   

65535 是什么? 是 0xFFFF,加 1 即溢出。 为什么要这样,网上 的答案是 必须的 (我的某一个已经记不起的老师 最喜欢这样解释)。http://blog.csdn.net/dainifeixiang/article/details/5494355

我感觉是: 计数器(cnt)每次加 1 即溢出,引发 中断,调用中断函数,即 cnt 每次加1后,进入中断函数判断 CCR 是否和 cnt 一致。

在time_base 中,
__IO uint16_t CCR1_Val = 40961;
__IO uint16_t CCR2_Val = 27309;
__IO uint16_t CCR3_Val = 13654;
__IO uint16_t CCR4_Val = 6826;

它们的最大公约数 是 1(估计),所以 cnt 每次加1 中断。

如果改成
__IO uint16_t CCR1_Val = 40000;
__IO uint16_t CCR2_Val = 20000;
__IO uint16_t CCR3_Val = 15000;
__IO uint16_t CCR4_Val = 5000;

最大公约数 是 5000, 让 TIM_Period = 65536-5000= 60536; 是否可以呢? 试一下就知道了。



理论上可以,实际上 效果不行,可能是这样计时本身就有误差。

CCR改一下
__IO uint16_t CCR1_Val = 39999;
__IO uint16_t CCR2_Val = 19999;
__IO uint16_t CCR3_Val = 14900;
__IO uint16_t CCR4_Val = 4999;



还是有问题,
再改一下
TIM_Period = 65536-500= 65036



好多了。同时这样也说明了,每次中断 计数的时间不是非常准确的(不知道确切误差),误差是通过分摊(多次中断)而减小的。

不难想象 ,如果
TIM_Period = 65536-50= 65486  会更好。



**************************************

不用 65535,而用  65536 - 最大公约数,作为 中断计数因子,可以减少 cpu 负担(中断消耗cpu资源),但是误差 可能会偏大(具体多少,需要实际测量)。

总的来说,相比 PWM输出,time_base 这种方式没有实用价值(易出错,同时cpu 负担大),应该加入 不推荐使用 之列。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-5-26 13:35:51 | 显示全部楼层
这种好贴必须顶,楼主真是个用心之人啊!

出0入0汤圆

发表于 2014-6-2 16:42:19 | 显示全部楼层
感谢整理,认真学习

出0入0汤圆

发表于 2014-6-3 23:40:12 | 显示全部楼层
非常感谢,好帖

出0入0汤圆

发表于 2014-7-18 09:27:36 | 显示全部楼层
好帖必顶

出0入0汤圆

发表于 2014-7-24 16:19:24 | 显示全部楼层
我必须说,你太牛了,这么好的资料,我一定要顶

出0入0汤圆

发表于 2014-8-1 10:45:34 | 显示全部楼层
GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_6)));
    capture = TIM_GetCapture1(TIM2);
    TIM_SetCompare1(TIM2, capture + CCR1_Val);

  GPIO_WriteBit(GPIOC, GPIO_Pin_8, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8)));

                TIM_SetCounter(TIM2,0x0000);

这两段代码实现不了上面说的吧,我在KEIL仿真了一下,结果不是楼主说的这样

当然我没有用示波器测试,不知道硬件输出什么样子;不过根据推理应该是这样的:
不论是将计数器清零还是用 TIM_SetCompare4(TIM2, capture + CCR4_Val);从新设置 TIMx->CCR1 ,效果
最终都是计数值没有达到设定值(从新设置 TIMx->CCR1问题更严重),那么此时即便人为在中断中翻转此管脚电平
但是,硬件会马上自动更改成当初设定的状态  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
所以不会像楼主说的那样是一个那么好的方波

出0入0汤圆

发表于 2014-8-1 10:56:09 | 显示全部楼层
不错,笔记制作的很好

出0入0汤圆

发表于 2014-9-25 20:43:33 | 显示全部楼层
mark,好东西,以后好好深入研究!

出0入0汤圆

发表于 2014-9-25 20:50:00 | 显示全部楼层
LZ好有心,谢谢分享@@@

出0入0汤圆

发表于 2014-9-25 20:56:24 | 显示全部楼层
这真是 定时器超精解  谢谢

出0入0汤圆

发表于 2014-9-25 23:20:11 | 显示全部楼层
要是教程都做成这样,什么都很容易学会了。

出0入0汤圆

发表于 2014-9-28 18:02:44 | 显示全部楼层
看过之后感觉非常之好。。谢谢

出0入4汤圆

发表于 2014-10-5 21:30:12 | 显示全部楼层
值得好好学习下。

出0入8汤圆

发表于 2014-10-10 14:57:03 | 显示全部楼层
总结的太好了~

出0入0汤圆

发表于 2014-10-10 23:30:24 | 显示全部楼层
mark。。。。。。。。。。。。

出0入102汤圆

发表于 2014-10-11 09:09:08 | 显示全部楼层
加上很多GIF,生动有趣。

出0入95汤圆

发表于 2014-10-11 10:07:50 | 显示全部楼层
很不错,参考一下

出0入0汤圆

发表于 2014-10-11 15:26:56 | 显示全部楼层
很好,很强大

出0入0汤圆

发表于 2014-10-12 08:35:30 | 显示全部楼层
很好                                 

出0入0汤圆

发表于 2014-10-18 11:47:01 | 显示全部楼层
不错的文章,收藏了。

出0入0汤圆

发表于 2014-10-19 15:50:12 | 显示全部楼层
有点意思,呵呵

出0入0汤圆

发表于 2014-10-19 15:50:44 | 显示全部楼层
有点意思,呵呵!

出0入0汤圆

发表于 2014-10-19 15:51:16 | 显示全部楼层
有点意思,还比较详细。呵呵!

出0入0汤圆

发表于 2014-10-20 11:37:59 | 显示全部楼层
MARK

出0入0汤圆

发表于 2014-11-4 10:12:22 | 显示全部楼层
楼主用心良苦 我要吃透他

出0入0汤圆

发表于 2014-11-5 03:17:03 | 显示全部楼层
好好好!给楼主赞一个,非常好,非常适合入门的人看。

出0入0汤圆

 楼主| 发表于 2014-11-18 19:36:29 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-11-18 19:39 编辑
oldbeginner 发表于 2014-5-25 15:04
***********************
那这里为什么写65535呢?


**********************
ST 厂家TIM例程大阅兵 ——  先分分类

**********************





ST厂家有很多例程,而且没有分类,首先就是对这些例程进行分类。
题外话,看STM32开发板资料时绝对没有想到定时器有这么复杂,而且有这么多应用。(所有开发板关于定时器的例程加在一起也没有ST厂家的详细)。



学STM32的过程,就是配置结构体的过程,所以利用结构体来分类是比较自然的。

TIM中使用最多的结构体就是 BASE


不过,可惜的是,只使用BASE结构体,只能得到类似51那种定时中断效果,而在TIM 例程中没有这么简单的例子。所以BASE 结构体,不能用来分类。

可以分类的是 OC 和 IC,不过OC 分量绝对足,占据了内容50%多,难度的90%多。



OC的复杂之处,除了六种模式外(以及模式对应的参数),

还可以和定时器组合,



然后,可以用来分类的结构体就是 IC



先说结果,后面边学习边理解为什么这样分类和具体操作。



分成三类,


大块头,也是核心和重点和难点,(BASE+OC),还可以再细分,例如 是否含 Automatic Output enable, Break, dead time and lock configuration。


然后,比较简单的IC


最后,把 IC 和 BASE+OC 合在一起。



分好类之后,就可以具体分析了,而且感觉有思路了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-11-18 20:26:04 | 显示全部楼层
oldbeginner 发表于 2014-11-18 19:36
**********************
ST 厂家TIM例程大阅兵 ——  先分分类


********************
BASE+OC 的理解 —— 第一部分

********************
识别方法:


先看例程,按照字母排序(含TIM_BDTRInitStructure结构体的放在后面);

*******************
7PWM_Output
*******************
产生七路PWM,就是没有8路。

只看相关代码
  /* Time Base configuration */
  TIM_TimeBaseStructure.TIM_Prescaler = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;


  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  /* Channel 1, 2,3 and 4 Configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
  TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

  TIM_OC1Init(TIM1, &TIM_OCInitStructure);

  TIM_OCInitStructure.TIM_Pulse = Channel2Pulse;
  TIM_OC2Init(TIM1, &TIM_OCInitStructure);

  TIM_OCInitStructure.TIM_Pulse = Channel3Pulse;
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

  TIM_OCInitStructure.TIM_Pulse = Channel4Pulse;
  TIM_OC4Init(TIM1, &TIM_OCInitStructure);

  /* TIM1 counter enable */
  TIM_Cmd(TIM1, ENABLE);

  /* TIM1 Main Output Enable */
  TIM_CtrlPWMOutputs(TIM1, ENABLE);


看起来,例程的重点在于教 怎样根据频率计算相应的 TIM_Pulse ,我这里的目的不是这样,是为了认识 BASE+OC 。
有兴趣的看,
   The objective is to generate 7 PWM signal at 17.57 KHz:
     - TIM1_Period = (SystemCoreClock / 17570) - 1
   The channel 1 and channel 1N duty cycle is set to 50%
   The channel 2 and channel 2N duty cycle is set to 37.5%
   The channel 3 and channel 3N duty cycle is set to 25%
   The channel 4 duty cycle is set to 12.5%
   The Timer pulse is calculated as follows:
     - ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100

  /* Compute the value to be set in ARR regiter to generate signal frequency at 17.57 Khz */
  TimerPeriod = (SystemCoreClock / 17570 ) - 1;
  /* Compute CCR1 value to generate a duty cycle at 50% for channel 1 and 1N */
  Channel1Pulse = (uint16_t) (((uint32_t) 5 * (TimerPeriod - 1)) / 10);
  /* Compute CCR2 value to generate a duty cycle at 37.5%  for channel 2 and 2N */
  Channel2Pulse = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
  /* Compute CCR3 value to generate a duty cycle at 25%  for channel 3 and 3N */
  Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);
  /* Compute CCR4 value to generate a duty cycle at 12.5%  for channel 4 */
  Channel4Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod- 1)) / 1000);


*************************
Cascade_Synchro —— 初识 主从定时器
*************************

想不到定时器也学习人类,团结在以。。。。为核心。。。。

先看核心代码
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* Master Configuration in PWM1 Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 64;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM2, &TIM_OCInitStructure);

  /* Select the Master Slave Mode */
  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);

  /* Master Mode selection */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

  /* Slaves Configuration: PWM1 Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 1;

  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);

  /* Slave Mode selection: TIM3 */
  TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);
  TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);

  /* Select the Master Slave Mode */
  TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

  /* Master Mode selection: TIM3 */
  TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

  /* Slave Mode selection: TIM4 */
  TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Gated);
  TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);
  
  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);
  TIM_Cmd(TIM2, ENABLE);
  TIM_Cmd(TIM4, ENABLE);

这个例程还比较绕,定时器2是定时器3的主人,定时器3是定时器4的主人,好熟悉的场景。

这里面涉及到了主从设置,不过感觉蛮简单的,暂时略过。实现的功能
       The Master Timer TIM2 is running at TIM2 frequency :
       TIM2 frequency = (TIM2 counter clock)/ (TIM2 period + 1) = 281.250 KHz
       and the duty cycle = TIM2_CCR1/(TIM2_ARR + 1) = 25%.

       The TIM3 is running:
       - At (TIM2 frequency)/ (TIM3 period + 1) = 70.312 KHz and a duty cycle
         equal to TIM3_CCR1/(TIM3_ARR + 1) = 25%

        The TIM4 is running:
      - At (TIM3 frequency)/ (TIM4 period + 1) = 17.578 KHz and a duty cycle
        equal to TIM4_CCR1/(TIM4_ARR + 1) = 25%


********************
DMA
********************
。。。。
TIM_TimeBaseStructure.TIM_RepetitionCounter = 2;
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  /* Channel 3 Configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
  TIM_OCInitStructure.TIM_Pulse = SRC_Buffer[0];
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

  /* TIM1 Update DMA Request enable */
  TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);

  /* TIM1 counter enable */
  TIM_Cmd(TIM1, ENABLE);

  /* Main Output Enable */
  TIM_CtrlPWMOutputs(TIM1, ENABLE);

看看如何理解:
  The objective is to configure TIM1 channel 3 to generate complementary PWM
  signal with a frequency equal to 17.57 KHz:
     - TIM1_Period = (SystemCoreClock / 17570) - 1
  and a variable duty cycle that is changed by the DMA after a specific number of
  Update DMA request.

The number of this repetitive requests is defined by the TIM1 Repetion counter,
  each 3 Update Requests, the TIM1 Channel 3 Duty Cycle changes to the next new
  value defined by the SRC_Buffer .
  -----------------------------------------------------------------------------*/
  /* Compute the value to be set in ARR regiter to generate signal frequency at 17.57 Khz */
  TimerPeriod = (SystemCoreClock / 17570 ) - 1;
  /* Compute CCR1 value to generate a duty cycle at 50% */
  SRC_Buffer[0] = (uint16_t) (((uint32_t) 5 * (TimerPeriod - 1)) / 10);
  /* Compute CCR1 value to generate a duty cycle at 37.5% */
  SRC_Buffer[1] = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
  /* Compute CCR1 value to generate a duty cycle at 25% */
  SRC_Buffer[2] = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);

通过 TIM1 Repetion counter 来更改 占空比,第一看到,目前的任务是先浏览所有例程,这个例程值得回头再理解。


*********************
OCActive 和 OCInActive ——好像没有多大意思
*********************

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  /* Output Compare Active Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Active;  //更改一下,就是 OCInActive
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Active Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

  TIM_OC2Init(TIM3, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Active Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

  TIM_OC3Init(TIM3, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Active Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;

  TIM_OC4Init(TIM3, &TIM_OCInitStructure);

  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);

  TIM_ARRPreloadConfig(TIM3, ENABLE);

不知道,会应用到什么场合
    The objective is to get TIM3 counter clock at 1 KHz:
     - Prescaler = (TIM3CLK / TIM3 counter clock) - 1
    And generate 4 signals with 4 different delays:
    TIM3_CH1 delay = CCR1_Val/TIM3 counter clock = 1000 ms
    TIM3_CH2 delay = CCR2_Val/TIM3 counter clock = 500 ms
    TIM3_CH3 delay = CCR3_Val/TIM3 counter clock = 250 ms
    TIM3_CH4 delay = CCR4_Val/TIM3 counter clock = 125 ms

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-11-18 21:04:57 | 显示全部楼层
oldbeginner 发表于 2014-11-18 20:26
********************
BASE+OC 的理解 —— 第一部分

*******************
BASE+OC 的理解 —— 第二部分
*******************

一旦有了固定模式,脑力劳动也变成了体力劳动,
****************
OCToggle —— 类似OCActive用法、

****************

感觉 OCToggle  OCActive OCInActive 都是打酱油的,猪脚120%绝对是 PWM。
当然,还是要看一下代码:

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  /* Output Compare Toggle Mode configuration: Channel1 */
  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_Low;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

  TIM_OC2Init(TIM3, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

  TIM_OC3Init(TIM3, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;

  TIM_OC4Init(TIM3, &TIM_OCInitStructure);

  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);

  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);

看看功能,
    TIM3 Configuration: Output Compare Toggle Mode:
    TIM3CLK = SystemCoreClock / 2,
    The objective is to get TIM3 counter clock at 12 MHz:
     - Prescaler = (TIM3CLK / TIM3 counter clock) - 1
    CC1 update rate = TIM3 counter clock / CCR1_Val = 366.2 Hz
    CC2 update rate = TIM3 counter clock / CCR2_Val = 732.4 Hz
    CC3 update rate = TIM3 counter clock / CCR3_Val = 1464.8 Hz
    CC4 update rate = TIM3 counter clock / CCR4_Val = 2929.6 Hz


****************
Parallel_Synchro —— 又是主从关系

****************
上次的主从关系是 班长,组长和小兵;
这次的主从关系是,老板 和两个小兵。

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_TimeBaseStructure.TIM_Period = 9;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  TIM_TimeBaseStructure.TIM_Period = 4;
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* Master Configuration in PWM1 Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 64;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM2, &TIM_OCInitStructure);

  /* Select the Master Slave Mode */
  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);

  /* Master Mode selection */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

  /* Slaves Configuration: PWM1 Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 3;

  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);

  /* Slave Mode selection: TIM3 */
  TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);
  TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);
  
  /* Slave Mode selection: TIM4 */
  TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Gated);
  TIM_SelectInputTrigger(TIM4, TIM_TS_ITR1);
  
  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);
  TIM_Cmd(TIM2, ENABLE);
  TIM_Cmd(TIM4, ENABLE);

配置步骤很清楚,看看功能,
  /* Timers synchronisation in parallel mode ----------------------------
     1/TIM2 is configured as Master Timer:
     - PWM Mode is used
     - The TIM2 Update event is used as Trigger Output  
     2/TIM3 and TIM4 are slaves for TIM2,
     - PWM Mode is used
     - The ITR1(TIM2) is used as input trigger for both slaves
     - Gated mode is used, so starts and stops of slaves counters
      are controlled by the Master trigger output signal(update event).

     The TIMxCLK is fixed to 72 MHz, the TIM2 counter clock is 72 MHz.
     The Master Timer TIM2 is running at 281.250 KHz and the duty cycle
     is equal to 25%
     The TIM3 is running:
     - At (TIM2 frequency)/ (TIM3 period + 1) = 28.125 KHz and a duty cycle
     equal to TIM3_CCR1/(TIM3_ARR + 1) = 30%
     The TIM4 is running:
     - At (TIM2 frequency)/ (TIM4 period + 1) = 56.250 KHz and a duty cycle
     equal to TIM4_CCR1/(TIM4_ARR + 1) = 60%

感觉主从关系,应该是用在 好几个STM32 互相联系通信,用来协调动作的。不过没有看到过这方面的实际应用。


******************
PWM_Output —— 哈哈,老面孔

******************
这个例程,就是野火 开发板上采用的例程,说明这个例程还是蛮经典的。
再复习一下,

  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR2_Val;

  TIM_OC2Init(TIM3, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR3_Val;

  TIM_OC3Init(TIM3, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

  /* PWM1 Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR4_Val;

  TIM_OC4Init(TIM3, &TIM_OCInitStructure);

  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

  TIM_ARRPreloadConfig(TIM3, ENABLE);

  /* TIM3 enable counter */
  TIM_Cmd(TIM3, ENABLE);

复习一下功能:
TIM3 Configuration: generate 4 PWM signals with 4 different duty cycles:
    TIM3 Channel1 duty cycle = (TIM3_CCR1/ TIM3_ARR)* 100 = 50%
    TIM3 Channel2 duty cycle = (TIM3_CCR2/ TIM3_ARR)* 100 = 37.5%
    TIM3 Channel3 duty cycle = (TIM3_CCR3/ TIM3_ARR)* 100 = 25%
    TIM3 Channel4 duty cycle = (TIM3_CCR4/ TIM3_ARR)* 100 = 12.5%

再复习一下仿真,



*****************
TIM10_PWMOutput —— TIM 当中最简单的例程

*****************
这个应该是最简单,看代码就知道了,
TIM_TimeBaseInit(TIM10, &TIM_TimeBaseStructure);

  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = CCR1Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM10, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM10, TIM_OCPreload_Enable);

  TIM_ARRPreloadConfig(TIM10, ENABLE);

  /* TIM10 enable counter */
  TIM_Cmd(TIM10, ENABLE);


不过,很多人没见过定时器10,因为This example runs only on STM32F10x XL-Density Devices.
把定时器10 改成 1.。。8都可以用的。


********************
TimeBase
********************
这个例程之前分析过,功能类似PWM,因为是利用中断,所以耗费大量CPU资源,效果还没有 PWM效果好,是不推荐的 应用。

********************
至此,不含TIM_BDTRInitStructure 结构体的 OC+BASE 应用就浏览了一遍,其中还有很多细节需要分享,例如
所有例程中
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
没有出现过其他数值,因为这个参数和IC有关,可惜的是,IC的例程中也是=0。

另外  TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable); 改为 Enable 有什么区别?

主从关系,占空比计算 等都有待细化。

不过,要把 IC 先浏览后再说。

出0入0汤圆

发表于 2014-11-18 21:14:29 | 显示全部楼层
哇 。非常不错啊。。感谢。。。

出0入0汤圆

 楼主| 发表于 2014-11-18 21:25:49 | 显示全部楼层
oldbeginner 发表于 2014-11-18 21:04
*******************
BASE+OC 的理解 —— 第二部分
*******************

***********************
IC 的理解 —— 制作频率计

***********************
感觉IC 非常的朴素,BASE都不需要,代码也是非常简短(在STM32中显得异类)。



***************
InputCapture —— 需要中断的帮助
***************

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0x0;

  TIM_ICInit(TIM3, &TIM_ICInitStructure);
  
  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);

  /* Enable the CC2 Interrupt Request */
  TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);

看上面的代码,识别模式很简单, TIM_ICInit
看看功能:
  /* TIM3 configuration: Input Capture mode ---------------------
     The external signal is connected to TIM3 CH2 pin (PA.07)  
     The Rising edge is used as active edge,
     The TIM3 CCR2 is used to compute the frequency value
  ------------------------------------------------------------ */
需要中断的帮助,

void TIM3_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET)
  {
    /* Clear TIM3 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
    if(CaptureNumber == 0)
    {
      /* Get the Input Capture value */
      IC3ReadValue1 = TIM_GetCapture2(TIM3);
      CaptureNumber = 1;
    }
    else if(CaptureNumber == 1)
    {
      /* Get the Input Capture value */
      IC3ReadValue2 = TIM_GetCapture2(TIM3);
      
      /* Capture computation */
      if (IC3ReadValue2 > IC3ReadValue1)
      {
        Capture = (IC3ReadValue2 - IC3ReadValue1);
      }
      else
      {
        Capture = ((0xFFFF - IC3ReadValue1) + IC3ReadValue2);
      }
      /* Frequency computation */
      TIM3Freq = (uint32_t) SystemCoreClock / Capture;
      CaptureNumber = 0;
    }
  }
}
怎么计算就不分析了,重点是 理解 TIM_ICInit + 中断函数 可以计算输入频率。


***************
PWM_Input —— 也是计算频率
***************

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0x0;

  TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);

  /* Select the TIM3 Input Trigger: TI2FP2 */
  TIM_SelectInputTrigger(TIM3, TIM_TS_TI2FP2);

  /* Select the slave Mode: Reset Mode */
  TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);

  /* Enable the Master/Slave Mode */
  TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);

  /* Enable the CC2 Interrupt Request */
  TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);

功能和上面一个一样,
  /* TIM3 configuration: PWM Input mode ------------------------
     The external signal is connected to TIM3 CH2 pin (PA.01),
     The Rising edge is used as active edge,
     The TIM3 CCR2 is used to compute the frequency value
     The TIM3 CCR1 is used to compute the duty cycle value
  ------------------------------------------------------------ */
多了一个主从关系设置,另外还要有中断进行计算,
void TIM3_IRQHandler(void)
{
  /* Clear TIM3 Capture compare interrupt pending bit */
  TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);

  /* Get the Input Capture value */
  IC2Value = TIM_GetCapture2(TIM3);

  if (IC2Value != 0)
  {
    /* Duty cycle computation */
    DutyCycle = (TIM_GetCapture1(TIM3) * 100) / IC2Value;

    /* Frequency computation */
    Frequency = SystemCoreClock / IC2Value;
  }
  else
  {
    DutyCycle = 0;
    Frequency = 0;
  }
}

IC 应用还是比较简单的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2014-11-18 21:48:46 | 显示全部楼层
oldbeginner 发表于 2014-11-18 21:25
***********************
IC 的理解 —— 制作频率计

*****************
BASE+IC+OC —— 全家福

*****************
不是说吉祥混沌。


****************
ExtTrigger_Synchro —— 殖民地控制关系写照

****************
感觉有了模式,代码可以一目十行了,难道这就是人们所说的chunk?

  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  TIM_TimeBaseStructure.TIM_Period = 73;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

  TIM_TimeBaseStructure.TIM_Period = 73;
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* Master Configuration in Toggle Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 64;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM1, &TIM_OCInitStructure);

  /* TIM1 Input Capture Configuration */
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0;

  TIM_ICInit(TIM1, &TIM_ICInitStructure);

  /* TIM1 Input trigger configuration: External Trigger connected to TI2 */
  TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2);
  TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Gated);

  /* Select the Master Slave Mode */
  TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);

  /* Master Mode selection: TIM1 */
  TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Enable);

  /* Slaves Configuration: Toggle Mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);


  /* Slave Mode selection: TIM3 */
  TIM_SelectInputTrigger(TIM3, TIM_TS_ITR0);
  TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);

  /* Select the Master Slave Mode */
  TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

  /* Master Mode selection: TIM3 */
  TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Enable);

  /* Slave Mode selection: TIM4 */
  TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);
  TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Gated);
  
  /* TIM1 Main Output Enable */
  TIM_CtrlPWMOutputs(TIM1, ENABLE);

  /* TIM enable counter */
  TIM_Cmd(TIM1, ENABLE);
  TIM_Cmd(TIM3, ENABLE);
  TIM_Cmd(TIM4, ENABLE);

虽然,已经不再怕这么多代码了,但是功能还是一眼看不出来,
看看功能介绍:
/* Timers synchronisation in cascade mode with an external trigger -----
    1/TIM1 is configured as Master Timer:
     - Toggle Mode is used
     - The TIM1 Enable event is used as Trigger Output

    2/TIM1 is configured as Slave Timer for an external Trigger connected
     to TIM1 TI2 pin (TIM1 CH2 configured as input pin):
     - The TIM1 TI2FP2 is used as Trigger Input
     - Rising edge is used to start and stop the TIM1: Gated Mode.

    3/TIM3 is slave for TIM1 and Master for TIM4,
     - Toggle Mode is used
     - The ITR1(TIM1) is used as input trigger
     - Gated mode is used, so start and stop of slave counter
       are controlled by the Master trigger output signal(TIM1 enable event).
     - The TIM3 enable event is used as Trigger Output.

    4/TIM4 is slave for TIM3,
     - Toggle Mode is used
     - The ITR2(TIM3) is used as input trigger
     - Gated mode is used, so start and stop of slave counter
       are controlled by the Master trigger output signal(TIM3 enable event).

虽然很多内容,又是主从关系又是输入输出的,这样理解,TIM1是殖民地的国外,一方面是TIM3 TIM4 的主人,另一方面是 外部输入 TIM1 CH2输入的奴隶。

这样相当于整个节奏是被外部输入控制的。


******************
OnePulse ——

******************
先看代码
  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  /* TIM4 PWM2 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 16383;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM4, &TIM_OCInitStructure);

  /* TIM4 configuration in Input Capture Mode */

  TIM_ICStructInit(&TIM_ICInitStructure);

  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0;

  TIM_ICInit(TIM4, &TIM_ICInitStructure);

  /* One Pulse Mode selection */
  TIM_SelectOnePulseMode(TIM4, TIM_OPMode_Single);

  /* Input Trigger selection */
  TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);

  /* Slave Mode selection: Trigger Mode */
  TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Trigger);


代码基本横扫,但是功能还是不能一下理解,
需要看看帮助,
  /* TIM4 configuration: One Pulse mode ------------------------
     The external signal is connected to TIM4_CH2 pin (PB.07),
     The Rising edge is used as active edge,
     The One Pulse signal is output on TIM4_CH1 pin (PB.06)
     The TIM_Pulse defines the delay value
     The (TIM_Period -  TIM_Pulse) defines the One Pulse value.

     The TIM_Pulse defines the delay value, the delay value is fixed
     to 682.6 us:
     delay =  CCR1/TIM4 counter clock = 682.6 us.
     The (TIM_Period - TIM_Pulse) defines the One Pulse value,
     the pulse value is fixed to 2.048 ms:
     One Pulse value = (TIM_Period - TIM_Pulse) / TIM4 counter clock = 2.048 ms.

外部输入一个上跳变,定时器输出一个2.048ms的脉冲。

这样,除了电机控制(死区)外的例程就浏览完毕了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-11-18 22:07:09 | 显示全部楼层
学习学习啦!

出0入0汤圆

发表于 2014-11-19 14:09:21 | 显示全部楼层
mark 后 再mark

出0入0汤圆

发表于 2014-11-19 15:24:06 | 显示全部楼层
这个不错,很详细

出0入0汤圆

 楼主| 发表于 2014-11-19 19:56:20 | 显示全部楼层
本帖最后由 oldbeginner 于 2014-11-19 20:54 编辑
oldbeginner 发表于 2014-11-18 21:48
*****************
BASE+IC+OC —— 全家福


********************
TIM 细节 ——  由例程而想到的
********************


****************************
TIM_ClockDivision 一个放错地方的参数
****************************
所有的例程中,涉及到TIM_ClockDivision ,无一例外

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

再来看看五兄弟:
typedef struct
{
u16 TIM_Period;
u16 TIM_Prescaler;
u8 TIM_ClockDivision;
u16 TIM_CounterMode;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;

TIM_ClockDivision 是一个麻烦,它让使用的人感到疑惑,感到编程控制力的削弱,因为它被放错地方了。
TIM_ClockDivision 可能就是一个 真的 丑的小鸭。


**************
TIM_RepetitionCounter —— TIM_ClockDivision 的难兄难弟
**************
TIM_RepetitionCounter 只使用了一次,不过不一般,就是DMA哪个例程。

只看核心:
  /* Channel 3 Configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
。。。
  TIM_OCInitStructure.TIM_Pulse = SRC_Buffer[0];
。。。
  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

  /* TIM1 Update DMA Request enable */
  TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);

TIM_DMA_Update
TIM更新DMA源

当 TIM1 更新 DMA源时,会发出DMA申请。

同样看DMA配置
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM1_CCR3_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SRC_Buffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = 3;
。。。。
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
。。。。

  DMA_Init(DMA1_Channel5, &DMA_InitStructure);

  /* DMA1 Channel5 enable */
  DMA_Cmd(DMA1_Channel5, ENABLE);


应该这样理解


重复计数器在下述任一条件成立时递减: ● 向上计数模式下每次计数器溢出时,
更新事件(UEV)是只能在重复计数达到0的时候产生。

each 3 Update Requests, the TIM1 Channel 3 Duty Cycle changes to the next new
  value defined by the SRC_Buffer .

天啊!直接用分频不是更简单吗?难道又是一只丑小鸭。

***************************
TIM_OC1PreloadConfig —— 什么时候不需要CRR
***************************

用法:例:
/* Enables the TIM2 Preload on CC1 Register */
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

TIM_OCPreload_Enable
TIMx在CCR1上的预装载寄存器使能

TIM_OCPreload_Disable
TIMx在CCR1上的预装载寄存器失能

在例程中,ENABLE 和 DISABLE 都有使用,那么为什么?

现在流行统计,出现 DISABLE的情况是: Active InActive 和 Toggle 都是使用的 DISABLE,这点可以理解


CCR 寄存器主要针对 对PWM的占空比的,

再来看看到代码,
在TIM_OC1Init中,有
  /* Set the Capture Compare Register value */
  TIMx->CCR4 = TIM_OCInitStruct->TIM_Pulse;

在TIM_OC1PreloadConfig中,有
  /* Enable or Disable the Output Compare Preload feature */
  tmpccmr2 |= (uint16_t)(TIM_OCPreload << 8);

所以,如果要是设定PWM的占空比有效,就必须设定 ENABLE。

所以可以这样认为,
DISABLE : Active InActive Toggle 其它
ENABLE : PWM 而且设定占空比

之所以 ENABLE 出现的频率非常高,是因为设定占空比的PWM 使用率非常高。


******************
什么时候使用 TIM_CtrlPWMOutputs —— 开发板上没有看到过的函数
******************
查看一下函数
    /* Enable the TIM Main Output */
    TIMx->BDTR |= TIM_BDTR_MOE;

互补信号OCx和OCxN通过下列控制位的组合进行控制:TIMx_CCER寄存器的CCxE和CCxNE位,TIMx_BDTR和TIMx_CR2寄存器中的MOE、OISx、OISxN、OSSI和OSSR位

例程(电机控制例程除外)中出现 TIM_CtrlPWMOutputs 有三处,其中有两处都有 TIM1_OCInitStructure.TIM1_OutputNState = TIM1_OutputNState_Enable; 另外一处感觉多余。不管怎样,这个函数多写了,不会出错。

如果 使用了 PWM的互补功能(或更复杂的功能),开启 TIM_CtrlPWMOutputs。


**************
感觉,OC 配置太多,为了简单和熟记,不妨忘记 active inactive toggle 等功能,只保留PWM 功能,这样 用法就非常固定,也容易记忆。

PWM1和 PWM2只是电平相反,记忆的时候和在一起,不用区分。

这样 OC 只有一个功能,即输出PWM,而且TIM_OC1PreloadConfig 一定要设置为ENABLE,这样才可以设定占空比;如果使用了 互补信号(或者自己感觉复杂的PWM功能),那么就还要用 TIM_CtrlPWMOutputs ,即使用不上这个函数(多余),也不会有坏处。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2014-11-20 13:58:08 | 显示全部楼层
有意思,先收藏再看看呵呵

出0入0汤圆

发表于 2014-11-20 14:32:45 | 显示全部楼层
楼主图文并茂,实在是很用心,值得学习

出0入16汤圆

发表于 2014-12-10 15:21:23 | 显示全部楼层
所见到的的最深刻的PWM 帖子

出0入0汤圆

发表于 2014-12-10 15:54:52 | 显示全部楼层
非常有用,学习下,已标记。

出0入0汤圆

发表于 2015-4-22 21:04:27 | 显示全部楼层
好牛啊              

出0入0汤圆

发表于 2015-4-23 08:00:33 | 显示全部楼层
这么好的学习帖,果断支持一下。

出0入0汤圆

发表于 2015-7-20 14:44:04 | 显示全部楼层
好有趣的样子

出0入0汤圆

发表于 2015-7-20 14:47:17 | 显示全部楼层
有意思,

出0入0汤圆

发表于 2015-8-10 20:20:46 | 显示全部楼层
MARk一下

出0入0汤圆

发表于 2015-8-10 21:47:49 | 显示全部楼层
呼吸灯的创意不错,最近刚好要做一个类似效果的工具,学习了。

出0入0汤圆

发表于 2015-8-13 14:43:08 | 显示全部楼层
mark……

出0入0汤圆

发表于 2015-8-15 11:35:09 | 显示全部楼层
mark..........

出0入0汤圆

发表于 2015-8-15 12:22:03 | 显示全部楼层
楼主有心了,谢谢楼主

出0入0汤圆

发表于 2015-9-10 13:08:27 | 显示全部楼层
好东西,stm 定时器

出0入0汤圆

发表于 2015-9-18 15:35:26 | 显示全部楼层
楼主,霸气

出0入0汤圆

发表于 2015-9-21 11:20:46 | 显示全部楼层
好东西!楼主有心了!

出0入0汤圆

发表于 2015-11-24 09:52:47 | 显示全部楼层
好东西,stm 定时器,谢谢楼主

出0入53汤圆

发表于 2016-2-3 20:56:06 | 显示全部楼层
LZ分析的很透彻

出0入53汤圆

发表于 2016-2-3 20:57:07 | 显示全部楼层
请问lz为什么 我的 TIM3 OC3 PB0无法输出脉冲
  1. {  
  2.         GPIO_InitTypeDef GPIO_InitStructure;
  3.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  4.         TIM_OCInitTypeDef  TIM_OCInitStructure;

  5.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能

  8.         GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); //Timer3全映射 GPIOC-> 6,7,8,9                                                                             //用于TIM3的CH2输出的PWM通过该LED显示

  9.         //设置该引脚为复用输出功能,输出TIM3 CH1 CH2 CH3 CH4 的PWM脉冲波形
  10.         GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0; //初始化GPIO
  11.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  12.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  13.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  14.         GPIO_ResetBits(GPIOB,GPIO_Pin_0);//默认电机使能端状态:不使能
  15.        
  16.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  17.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  这里是72分频,那么时钟频率就是1M
  18.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  19.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  20.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  21.          
  22.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  23.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  24.         TIM_OCInitStructure.TIM_Pulse = 50; //设置待装入捕获比较寄存器的脉冲值
  25.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  26.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  27.        
  28.         TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器

  29.         TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
  30.         TIM_CtrlPWMOutputs(TIM3, ENABLE);//TIM8->BDTR|=1<<15;
  31.        
  32.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
  33.          
  34. }
复制代码

出0入0汤圆

发表于 2016-2-4 01:46:54 | 显示全部楼层
谢谢分享!!!

出0入0汤圆

发表于 2016-2-5 22:27:59 | 显示全部楼层
6666,,这教程写得真详细

出0入0汤圆

发表于 2016-2-15 08:48:52 | 显示全部楼层
这教程才叫教程,浅显易懂,受教不少。

出0入0汤圆

发表于 2016-2-15 11:19:59 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2016-3-10 23:01:04 | 显示全部楼层
生动形象,好帖。话说今天才好好地把时钟这块重新看了下。。。

出0入0汤圆

发表于 2016-3-10 23:47:11 | 显示全部楼层
mark!终于要用单片机了,学起来!

出0入0汤圆

发表于 2016-3-11 00:09:27 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2016-3-11 16:01:15 | 显示全部楼层
留爪,谢谢

出0入0汤圆

发表于 2017-5-11 15:59:48 | 显示全部楼层
没有最好,只有更好,原来觉得开发板市场已经被开发殆尽了,学习资料也足够完美了,原子版,野火版,各个大神版,看来还可以有更大的大神版着实给力。

出0入0汤圆

发表于 2017-6-9 00:30:58 来自手机 | 显示全部楼层
好NB的帖子才发现 佩服楼主 感谢楼主

出0入0汤圆

发表于 2017-6-10 16:47:53 | 显示全部楼层
楼主有才,写得很好

出0入0汤圆

发表于 2017-6-10 16:55:30 | 显示全部楼层
非常不错,佩服楼主的学习精神~~~~~~~~~~~~~~~~

出5入8汤圆

发表于 2017-9-26 16:38:22 | 显示全部楼层
谢谢楼主,正好需要好好学习一下定时器。

出0入0汤圆

发表于 2017-9-26 17:13:21 | 显示全部楼层
MARK一下。多谢楼主。

出20入0汤圆

发表于 2017-9-26 17:51:47 | 显示全部楼层
非常感谢楼主,好生动活泼

出0入0汤圆

发表于 2018-2-22 12:02:46 | 显示全部楼层
强悍的好贴!谢谢分享.

出0入0汤圆

发表于 2018-2-22 14:32:48 | 显示全部楼层
很好的东东。

出0入13汤圆

发表于 2018-2-23 16:44:40 | 显示全部楼层
好贴!谢谢分享.

出0入0汤圆

发表于 2018-2-24 11:11:49 来自手机 | 显示全部楼层
学习了,谢谢分享

出0入4汤圆

发表于 2018-2-24 11:38:06 | 显示全部楼层
教材这样好生动!好评!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 05:15

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表