OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3294|回复: 5

同一个定时器,两路通道输出PWM,另外两路做输入捕获可以吗?

[复制链接]

20

主题

99

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2020-6-21
在线时间
70 小时
发表于 2020-8-14 10:10:04 | 显示全部楼层 |阅读模式
我要控制11个无刷电机,需要11路PWM波占空比调速。无刷电机自己带了反馈线,会反馈信号。所以我又需要11路输入捕获。

我配置了一下定时器2做测试。 CH1 CH2输入捕获,CH3 CH4输出占空比可调的方波。
结果测试出来当CH1 (PA0)连上CH3的时候,CH2也同时有值...

我怀疑是我的配置出现了问题。有没有大哥,给小弟解惑一下。
下面贴代码,主要代码如下:
  1. /********************************************************
  2. *@函数名称:TIM2_Cap_Init()
  3. *@功能说明:定时器2初始化
  4. *@引脚说明:PA0 CH1 输入捕获
  5. *                                                PA1        CH2 输入捕获
  6. *                                                PA2        CH3 输出PWM
  7. *                                                PA3        CH4 输出PWM
  8. *@输入参数:u16 _arr 重装载值 u16 _psc 预分频值 CCRx初始值为0
  9. *@返回参数:无
  10. */
  11. void TIM2_Cap_Init(u16 _arr,u16 _psc)
  12. {         
  13.         GPIO_InitTypeDef                                         GPIO_InitStructure;
  14.         TIM_TimeBaseInitTypeDef          TIM_TimeBaseStructure;
  15.         NVIC_InitTypeDef                                         NVIC_InitStructure;
  16.         TIM_ICInitTypeDef                                  TIM2_ICInitStructure;
  17.         TIM_OCInitTypeDef                                  TIM_OCInitStructure;
  18.        
  19.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);        //使能TIM2时钟
  20.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟

  21.         /***********************PA0~PA3四个通道配置*********************/                                                
  22.         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1;  //PA0 PA1 输入捕获
  23.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
  24.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  25.         GPIO_ResetBits(GPIOA,GPIO_Pin_0);
  26.         GPIO_ResetBits(GPIOA,GPIO_Pin_1);
  27.        
  28.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_2|GPIO_Pin_3; //PA2 PA3
  29.         GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF_PP;  //复用推挽输出
  30.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  31.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  32.        

  33.         /***************************************************************/

  34.         /**************************初始化定时器2 TIM2        ******************/
  35.         TIM_TimeBaseStructure.TIM_Period                                 = _arr; //设定计数器自动重装值
  36.         TIM_TimeBaseStructure.TIM_Prescaler                 = _psc;         //预分频器   
  37.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
  38.         TIM_TimeBaseStructure.TIM_CounterMode         = TIM_CounterMode_Up;  //TIM向上计数模式
  39.         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  40.   
  41.         //初始化TIM2输入捕获参数
  42.         TIM2_ICInitStructure.TIM_Channel                         = TIM_Channel_1; //CC1S=03         选择输入端 IC1映射到TI3上
  43.   TIM2_ICInitStructure.TIM_ICPolarity         = TIM_ICPolarity_Rising;        //上升沿捕获
  44.   TIM2_ICInitStructure.TIM_ICSelection         = TIM_ICSelection_DirectTI; //映射到TI1上
  45.   TIM2_ICInitStructure.TIM_ICPrescaler         = TIM_ICPSC_DIV1;         //配置输入分频,不分频
  46.   TIM2_ICInitStructure.TIM_ICFilter                 = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  47.   TIM_ICInit(TIM2, &TIM2_ICInitStructure);
  48.        
  49.         TIM2_ICInitStructure.TIM_Channel                         = TIM_Channel_2; //CC1S=03         选择输入端 IC1映射到TI3上
  50.   TIM2_ICInitStructure.TIM_ICPolarity         = TIM_ICPolarity_Rising;        //上升沿捕获
  51.   TIM2_ICInitStructure.TIM_ICSelection         = TIM_ICSelection_DirectTI; //映射到TI1上
  52.   TIM2_ICInitStructure.TIM_ICPrescaler         = TIM_ICPSC_DIV1;         //配置输入分频,不分频
  53.   TIM2_ICInitStructure.TIM_ICFilter                 = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  54.   TIM_ICInit(TIM2, &TIM2_ICInitStructure);
  55.        
  56.        
  57.         //初始化TIM2PWM输出参数
  58.         TIM_OCInitStructure.TIM_OCMode                         = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  59.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  60.         TIM_OCInitStructure.TIM_Pulse                         = (_arr/2); //设置待装入捕获比较寄存器的脉冲值
  61.         TIM_OCInitStructure.TIM_OCPolarity         = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  62.         TIM_OC3Init(TIM2, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx       
  63.         TIM_OC4Init(TIM2, &TIM_OCInitStructure);
  64.        
  65.         //中断分组初始化
  66.         NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
  67.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
  68.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
  69.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  70.         NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  71.        
  72.         TIM_ITConfig(TIM2,TIM_IT_CC1,ENABLE);//允许CC1IE捕获中断       
  73.         TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);//允许CC2IE捕获中断       
  74.        
  75.         TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH3预装载使能         
  76.         TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);  //CH4预装载使能       
  77.         TIM_ARRPreloadConfig(TIM2, ENABLE); //使能TIMx在ARR上的预装载寄存器
  78.   
  79.         TIM_Cmd(TIM2,ENABLE );         //使能定时器2
  80. }
复制代码
  1. void TIM2_IRQHandler(void)
  2. {
  3.         if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.         {
  5.                 CapFlag.TIM2_CH1++;
  6.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);       
  7.         }
  8.        
  9.         if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  10.         {
  11.                 CapFlag.TIM2_CH2++;
  12.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);       
  13.         }       
  14. }
复制代码
  1. void TIM6_Int_Init(u16 arr,u16 psc)
  2. {
  3.   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  4.         NVIC_InitTypeDef NVIC_InitStructure;

  5.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //时钟使能

  6.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到5000为500ms
  7.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
  8.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  9.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  10.         TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  11.         TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
  12.         NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM3中断
  13.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
  14.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
  15.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  16.         NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

  17.         TIM_Cmd(TIM6, ENABLE);  //使能TIMx外设                                                 
  18. }
复制代码
  1. void TIM6_IRQHandler(void)   //TIM3中断
  2. {
  3.         if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
  4.                 {
  5.                         TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源
  6.                         printf("\r\nTIM2_CH1_PA0:%d\r\n",CapFlag.TIM2_CH1);
  7.                         printf("TIM2_CH2_PA1:%d\r\n",CapFlag.TIM2_CH2);
  8.                         CapFlag.TIM2_CH1=0;
  9.                         CapFlag.TIM2_CH2=0;
  10.                 }
  11. }
复制代码


其中定时器6是普通定时器,我配置成每秒在串口打印一下TIM2_CHx的值
TIM2定时器配置成我说的模式。


下面贴一下整个代码
TIM2.zip (4.48 MB, 下载次数: 14)
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

17

主题

175

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1016
金钱
1016
注册时间
2014-4-7
在线时间
99 小时
发表于 2020-8-14 15:20:43 | 显示全部楼层
打印不要在中断中进行,会出现奇怪的问题
在你没有做出成绩之前,这个世界不会在乎你的自尊。
回复 支持 反对

使用道具 举报

20

主题

99

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2020-6-21
在线时间
70 小时
 楼主| 发表于 2020-8-14 16:17:58 | 显示全部楼层
一个定时器同时输出pwm波和输入捕获是可以的。而且两路pwm波占空比可以不一样,不过频率是一样的。而且pwm的频率和输入捕获的最大频率也没关系。
我定时器开的10hz 去捕获另外一个单片机发出的1khz的波,结果显示出来的值时960多,这个误差我可以接受了。



主要问题出在这里
  1. void TIM2_IRQHandler(void)
  2. {
  3.         if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.         {
  5.                 CapFlag.TIM2_CH1++;
  6.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);      
  7.         }
  8.       
  9.         if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  10.         {
  11.                 CapFlag.TIM2_CH2++;
  12.                 TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);      
  13.         }      
  14. }
复制代码
这个现象我从理论上说不通,我测试时候发现。

我的本意是,四路通道触发TIM2_IRQ的中断,进到服务函数里面。

然后根据TIM_GETITStatus来判断到底是哪个通道..
但是...好像这四个通道误触发率有点大..明明没连接它自己就能进中断。不知道是硬件问题还是我程序写的有问题。




比如,我把PA1引脚的配置注释掉,并且把 下面这段代码注释掉,它还是能进到  PA1的计数值 CapFlag.TIM2CH2++这句话..
  1. TIM2_ICInitStructure.TIM_Channel                         = TIM_Channel_2; //CC1S=03         选择输入端 IC1映射到TI3上
  2.   TIM2_ICInitStructure.TIM_ICPolarity         = TIM_ICPolarity_Rising;        //上升沿捕获
  3.   TIM2_ICInitStructure.TIM_ICSelection         = TIM_ICSelection_DirectTI; //映射到TI1上
  4.   TIM2_ICInitStructure.TIM_ICPrescaler         = TIM_ICPSC_DIV1;         //配置输入分频,不分频
  5.   TIM2_ICInitStructure.TIM_ICFilter                 = 0x0F;//IC1F=0000 配置输入滤波器 不滤波
  6.   TIM_ICInit(TIM2, &TIM2_ICInitStructure);
复制代码

只要这句话是打开的..它就可以进去让计数值增加. 在我没有给PA1端口连任何输入的情况下..
  1. TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);//允许CC2IE捕获中断
复制代码


它就还能进去。


最后的解决方法是把IRQ函数里的代码改成这样子。
  1. void TIM2_IRQHandler(void)
  2. {
  3.         if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
  4.         {
  5.                 TIM_ClearITPendingBit(TIM2,TIM_IT_CC1);
  6.                 a1++;                       
  7.         }       
  8.         if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  9.         {
  10.                 TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);
  11.                 a2++;
  12.         }
  13.         TIM_ClearITPendingBit(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4);       
  14. }
复制代码
嗯。。就是在IRQ函数最后再清一遍标志位。

可是理论上说不通啊..
我根本就没有给CH2通道输入方波啊,我给的是CH1通道输入方波,结果CH2通道自己就把TIMx->SR寄存器的标志位置1了..
搞的我要在每次进中断的时候把所有的标志清一遍。。




这样子我们会发现PA0输入捕获的时候,明明PA1没有连杜邦线,但是PA1也有计数值增加...
说实话挺扯淡的...

还有..一点..貌似这两段代码,注释不注释,当PA0,PA1有上升沿的时候都能进中断..
  1. GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
  2.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0 输入  
  3.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  4.         GPIO_ResetBits(GPIOA,GPIO_Pin_0);                                                 //PA0 下拉
  5.        
  6. //        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;  //PA0 清除之前设置          /***************************************************************/       
  7. //        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //PA0 输入                                  /***********************很怪很怪,反而PA1不设置抓的准*********************/       
  8. //        GPIO_Init(GPIOA, &GPIO_InitStructure);                                                                                                                /********************************************/       
  9. //        GPIO_ResetBits(GPIOA,GPIO_Pin_1);                                                 //PA0 下拉                       
复制代码



不像是PWM波配置的代码,一注释掉就不会输出了..



太玄学了...
TIM_GetITStatus(TIMx, TIM_IT_CCx)
这个标志


下面贴完整的工程文件..
TIM2两路捕获两路输出.zip (4.52 MB, 下载次数: 4)
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165536
金钱
165536
注册时间
2010-12-1
在线时间
2117 小时
发表于 2020-8-15 02:05:21 | 显示全部楼层
可以的,不过不能清CNT寄存器,就可以。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

20

主题

99

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2020-6-21
在线时间
70 小时
 楼主| 发表于 2020-8-15 11:25:29 | 显示全部楼层
正点原子 发表于 2020-8-15 02:05
可以的,不过不能清CNT寄存器,就可以。

原子哥,我现在的问题是当我同用一个定时器的不同通道捕获的时候,其他通道会误触发。

举个例子,比如配置CH1 CH2为输入捕获。当我用TIM5CH2捕获,TIM5CH1会误触发。而且概率还挺高的..
Annotation 2020-08-15 111837.png

我给TIM5CH2一个1Khz的信号,然后有时候TIM5CH1会误触发。

这个情况是不是没办法解决..

在TIM5_IRQHandler中
  1. void TIM5_IRQHandler(void)
  2. {
  3.         if (<b>TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET</b>)
  4.         {

  5.                 CapFlag.TIM5_CH1++;
  6.                 TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_CC3|TIM_IT_CC4);
  7.         }       
  8.         if (TIM_GetITStatus(TIM5, TIM_IT_CC2) == SET)
  9.         {

  10.                 CapFlag.TIM5_CH2++;
  11.                 TIM_ClearITPendingBit(TIM5,TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4);
  12.         }
  13.         TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4);
  14. }
复制代码


(TIM_GetITStatus(TIM5, TIM_IT_CC1) !=RESET 和 (TIM_GetITStatus(TIM5, TIM_IT_CC1)==SET
我把原来的!=RESET 改成了 == SET后,好像误触发几率变得小了,没长时间试,也不知道是不是偶发性事件..
回复 支持 反对

使用道具 举报

20

主题

99

帖子

0

精华

初级会员

Rank: 2

积分
178
金钱
178
注册时间
2020-6-21
在线时间
70 小时
 楼主| 发表于 2020-8-15 11:30:18 | 显示全部楼层
目前我尝试出来减少误触发概率的办法是两个..
第一个办法:在中断服务函数最后加一句
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4);

虽然我没有打开CC3,CC4,但是加上去可以明显减少误触发的概率...

第二个:把TIM_GetITStatus(TIM5, TIM_IT_CC1) !=RESET改成 (TIM_GetITStatus(TIM5, TIM_IT_CC1)==SET
这个也可以减少误触发的概率..

不过不知道原理是什么...感觉挺玄学的..
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-9 17:48

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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