[学习笔记] AC7801实现一个PWM同时捕获多通道信号

[复制链接]
5477|8
 楼主| wangjj19950516 发表于 2021-10-27 16:16 | 显示全部楼层 |阅读模式
本帖最后由 wangjj19950516 于 2021-10-27 16:44 编辑

最近有较多客户提出需要捕获多路PWM信号,ATC芯片的PWDT是专门做捕获功能,但每个PWDT只能捕获一个通道,对于多通道的需求,资源不够用。PWM模块也可以做输入捕获功能,且PWM有多个通道。一般常规的使用都是用一个PWM模块做一路信号的捕获,难免有些浪费资源,这里向大家介绍一下PWM同时捕获多通道的做法。
一、PWM工作模式
       AC7801的PWM模块包含很多功能,输出比较,输入捕获,双边沿捕获,组合输出模式,正交解码等。
1.png 2.png
可以看到,有输入捕获和双边沿输出捕获模式可以用来做外部信号的捕获。
1.输入捕获:单通道捕获,每个通道独立作为捕获通道,可设置捕获边沿。当通道输入出现选定的边沿时,PWM计数器的当前值会被捕获到PWM_CHnV寄存器中。同时,通道捕获中断标志位被置1,如果使能了通道中断,则可以产生中断。
3.JPG
如上图所示,设置上升沿捕获,当输入信号的上升沿来到时,把当前计数器的值2和8存入CHnV寄存器,相邻两次的数值差对应的时间即为波形的周期。
如果设置上升沿和下降沿都发生捕获,则可以分别得到高电平和低电平时间,从而可以计算波形的频率和占空比。
2.双边沿捕获:信号只能输入到通道CH0,CH2,CH4,但实际在芯片内部信号分两路走,CHn和CHn+1都有输入,可以设置CHn和CHn+1的捕获边沿。但不能同时使能CHn和CHn+1的中断,只能使能一个,即使设置的边沿不同,在一个周期内只能产生一个中断。
当输入信号出现CHn选择的边沿时,PWM计数器的当前值会存入CHnV寄存器,当出现CHn+1选择的边沿时,计数器的当前值会存入CHn+1V寄存器。
4.JPG
二、PWM多通道捕获
这里使用输入捕获(单通道)模式,捕获4路外部信号,每路信号都计算出频率和占空比。
使用PB5->PWM1_CH0,PB4->PWM1_CH1,PB8->PWM1_CH2,PC9->PWM1_CH6,这四个通道
1.首先配置GPIO功能,
  1. void GPIO_PWM_Init(void)
  2. {
  3.          GPIO_SetFunc(GPIOB, GPIO_PIN5, GPIO_FUN1);//PWM1_CH0
  4.          GPIO_SetFunc(GPIOB, GPIO_PIN4, GPIO_FUN1);//PWM1_CH1
  5.          GPIO_SetFunc(GPIOB, GPIO_PIN8, GPIO_FUN1);//PWM1_CH2
  6.          GPIO_SetFunc(GPIOC, GPIO_PIN9, GPIO_FUN1);//PWM1_CH6
  7. }
2,配置PWM
  1. void PWM_Capture_Init(void)
  2. {
  3.     PWM_DeInit(PWM1); //去初始化PWM1
  4.     PWM_inputChConfigType inputChConfig[4]; //输入通道配置,总共4个通道
  5.     PWM_inputCaptureConfigType inputCapconfig; //输入捕获结构体
  6.     PWM_ConfigType config; //PWM模块结构体

  7.     //结构体数据清零
  8.     memset(&inputChConfig, 0, sizeof(inputChConfig));
  9.     memset(&inputCapconfig, 0, sizeof(inputCapconfig));
  10.     memset(&config, 0, sizeof(config));
  11.     //输入通道结构体配置,如果有多个输入通道,更改结构体数组,在后面添加相关配置即可。
  12.     /*
  13.     初始化PWM为捕获模式,捕获模式可以设上升/下降沿
  14.     */

  15.     inputChConfig[0].channel = PWM_CH_0; //信号捕获通道
  16.     inputChConfig[0].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
  17.     inputChConfig[0].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
  18.     inputChConfig[0].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
  19.     inputChConfig[0].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
  20.     inputChConfig[0].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
  21.     inputChConfig[0].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
  22.     inputChConfig[0].interruptEn = ENABLE; //该通道输入捕获中断使能
  23.                
  24.     inputChConfig[1].channel = PWM_CH_1; //信号捕获通道
  25.     inputChConfig[1].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
  26.     inputChConfig[1].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
  27.     inputChConfig[1].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
  28.     inputChConfig[1].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
  29.     inputChConfig[1].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
  30.     inputChConfig[1].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
  31.     inputChConfig[1].interruptEn = ENABLE; //输入捕获中断使能
  32.                
  33.     inputChConfig[2].channel = PWM_CH_2; //信号捕获通道
  34.     inputChConfig[2].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
  35.     inputChConfig[2].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
  36.     inputChConfig[2].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
  37.     inputChConfig[2].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
  38.     inputChConfig[2].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
  39.     inputChConfig[2].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
  40.     inputChConfig[2].interruptEn = ENABLE; //输入捕获中断使能
  41.                
  42.     inputChConfig[3].channel = PWM_CH_6; //信号捕获通道
  43.     inputChConfig[3].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
  44.     inputChConfig[3].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
  45.     inputChConfig[3].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
  46.     inputChConfig[3].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
  47.     inputChConfig[3].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
  48.     inputChConfig[3].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
  49.     inputChConfig[3].interruptEn = ENABLE; //输入捕获中断使能
  50.    
  51.     //输入捕获配置
  52.     inputCapconfig.channelNum = 4;  //输入捕获通道数目
  53.     inputCapconfig.channelConfig = inputChConfig;//输入通道配置变量地址赋值。
  54.    
  55.     //PWM模块配置
  56.     config.mode = PWM_MODE_INPUT_CAPTURE;//PWM模块配置为输入捕获模式
  57.     config.initModeStruct = &inputCapconfig;//输入捕获配置变量赋值
  58.     config.clkSource = PWM_CLK_SOURCE_APB; //PWM时钟源配置
  59.     /*
  60.     设置捕获时钟频率:
  61.     可捕获的最小频率(前提是时钟源为PWM_CLK_SOURCE_APB):
  62.     min_clk = SYS_CLK/(PWM_CAPTURE_PRESCALER+1)/65536 = 24000000/2/65536 = 183HZ
  63.     */
  64.     config.clkPsc = 1;//PWM时钟源分频,2分频
  65.     config.initValue = 0;//计数器初始寄存器值
  66.     config.maxValue = 65535; //PWM计数器最大值
  67.     config.overflowInterrupEn = ENABLE;//计数器溢出中断使能位
  68.     config.cntOverflowFreq = 0;//CNTOF中断产生的频率与计数器频率的关系(0-127), 0表示每次计数器溢出都产生溢出中断,1表示间隔1次,2表示间隔2次,以此内推。
  69.    
  70.     config.interruptEn = ENABLE; //PWM中断使能
  71.     config.callBack = PWM_Capture_CallBack; //PWM中断回调

  72.     PWM_Init(PWM1, &config); //配置初始化生效

  73.     NVIC_SetPriority(PWM1_IRQn, 0); //设置PWM模块中断的优先级
  74. }
3.中断处理函数
  1. PWM_CAPTURE_T PWM_Capture[4]; //定义结构体数组,分别存放4路信号的数据

  2. void PWM_Capture_CallBack(void *device, uint32_t wpara, uint32_t lpara)
  3. {
  4.       static uint8_t pwm_ch;
  5.       uint8_t gpio_edge =0;  //边沿,1--上升沿  0--下降沿
  6.       uint16_t CountValue =0;
  7.      //PWM计数器溢出
  8.     if (wpara & PWM_INIT_CNTOF_Msk)
  9.     {
  10.          for(uint8_t index =0; index < 4;index++)  //计算每个通道的电平溢出次数
  11.          {
  12.               if(PWM_Capture[index].pinlevel== 0)  //根据之前的电平状态,判断该溢出时间加在那个状态,避免溢出中断时电平发生跳变,导致时间计算错误
  13.               {
  14.                    PWM_Capture[index].low_over_cnt++;
  15.               }
  16.               else
  17.               {
  18.                     PWM_Capture[index].high_over_cnt ++;
  19.               }
  20.          }
  21.     }
  22.         
  23.    //通道中断
  24.    if(lpara !=0)
  25.    {
  26.          if (lpara & PWM_STR_CH0SF_Msk)  //CH0中断
  27.          {               
  28.               pwm_ch = 0;
  29.               if(GPIO_GetPinLevel(GPIOB, GPIO_PIN5)== GPIO_LEVEL_LOW)
  30.               {
  31.                    gpio_edge = 0;
  32.               }
  33.               else
  34.               {
  35.                    gpio_edge = 1;
  36.               }
  37.               CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_0); //读取通道0的捕获数据
  38.          }
  39.          else if(lpara & PWM_STR_CH1SF_Msk)  //CH1中断
  40.          {
  41.                pwm_ch =1;
  42.                if(GPIO_GetPinLevel(GPIOB, GPIO_PIN4)== GPIO_LEVEL_LOW)
  43.                {
  44.                     gpio_edge = 0;
  45.                }
  46.                else
  47.                {
  48.                     gpio_edge = 1;
  49.                }
  50.                CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_1);  //读取通道1的捕获数据
  51.           }               
  52.           else if(lpara & PWM_STR_CH2SF_Msk)  //CH2中断
  53.           {
  54.                 pwm_ch =2;
  55.                 if(GPIO_GetPinLevel(GPIOB, GPIO_PIN8)== GPIO_LEVEL_LOW)
  56.                 {
  57.                       gpio_edge = 0;
  58.                 }
  59.                 else
  60.                 {
  61.                       gpio_edge = 1;
  62.                 }
  63.                 CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_2);  //读取通道2 的捕获数据
  64.            }
  65.            else if(lpara & PWM_STR_CH6SF_Msk)  //CH6中断
  66.            {
  67.                  pwm_ch =3;
  68.                  if(GPIO_GetPinLevel(GPIOC, GPIO_PIN9)== GPIO_LEVEL_LOW)
  69.                  {
  70.                        gpio_edge = 0;
  71.                  }
  72.                  else
  73.                  {
  74.                        gpio_edge = 1;
  75.                  }
  76.                  CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_6); //读取通道6的捕获数据
  77.            }
  78.            else{}
  79.                                 
  80.            PWM_Capture[pwm_ch].captureValue = CountValue;
  81.                
  82.            if(gpio_edge==0) //下降沿,读出高电平时间
  83.            {
  84.                   PWM_Capture[pwm_ch].high_time = (uint32_t)(PWM_Capture[pwm_ch].high_over_cnt)*65536 +  PWM_Capture[pwm_ch].captureValue - PWM_Capture[pwm_ch].captureValue_last;
  85.                   PWM_Capture[pwm_ch].pinlevel = 0;
  86.            }
  87.            else//上升沿,读出低电平时间
  88.            {
  89.                   PWM_Capture[pwm_ch].Low_time = (uint32_t)PWM_Capture[pwm_ch].low_over_cnt * 65536 + PWM_Capture[pwm_ch].captureValue - PWM_Capture[pwm_ch].captureValue_last;
  90.                   PWM_Capture[pwm_ch].pinlevel =1;
  91.             }
  92.                                 
  93.             //频率和占空比计算
  94.            //PWM时钟24MHZ,2分频
  95.             PWM_Capture[pwm_ch].freq  = 24000000/2/(PWM_Capture[pwm_ch].Low_time + PWM_Capture[pwm_ch].high_time);
  96.             PWM_Capture[pwm_ch].duty = (PWM_Capture[pwm_ch].high_time)*100/(PWM_Capture[pwm_ch].Low_time + PWM_Capture[pwm_ch].high_time);  
  97.             PWM_Capture[pwm_ch].low_over_cnt =0;
  98.             PWM_Capture[pwm_ch].high_over_cnt = 0;
  99.             PWM_Capture[pwm_ch].captureValue_last = PWM_Capture[pwm_ch].captureValue;
  100.      }

  101. }        



三、附件
至此,便可以同时捕获4路信号,互不影响,实测每个通道都能正确计算出频率和占空比。
附件为测试工程,有需要可以参考
pwm_多通道捕获.rar (20.87 KB, 下载次数: 31)



flytianya2010 发表于 2021-10-28 17:18 来自手机 | 显示全部楼层
学习啦!感谢楼主分享。
flytianya2010 发表于 2021-10-28 17:19 来自手机 | 显示全部楼层
感谢楼主分享。学习了。
1983万年青 发表于 2021-10-29 10:11 | 显示全部楼层
楼主,没有看的中断当中的标志位清除指令诶?
在压缩包里面才有吗?
 楼主| wangjj19950516 发表于 2021-10-29 11:41 | 显示全部楼层
1983万年青 发表于 2021-10-29 10:11
楼主,没有看的中断当中的标志位清除指令诶?
在压缩包里面才有吗?

中断标志位在驱动库的中断里清掉了,所以回调函数里不用再清了
yinwuqing110 发表于 2021-10-30 09:55 | 显示全部楼层
感谢楼主分享,PWM应用还是比较多的
1983万年青 发表于 2021-11-2 10:18 | 显示全部楼层
本帖最后由 1983万年青 于 2021-11-2 10:21 编辑

如果读脉冲周期的话,是不是前后两次通道数据之差就可以了?
 楼主| wangjj19950516 发表于 2021-11-2 18:03 | 显示全部楼层
1983万年青 发表于 2021-11-2 10:18
如果读脉冲周期的话,是不是前后两次通道数据之差就可以了?

这里配置的上升沿和下降沿都捕获,
如果想只读脉冲周期,可以设置为单上升沿捕获或单下降沿捕获,这样前后两次捕获的间隔就是周期,不过需要考虑溢出的情况哦
chenjun89 发表于 2021-11-2 19:33 来自手机 | 显示全部楼层
学习了,谢谢楼主分享。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

86

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部