wangjj19950516 发表于 2021-10-27 16:16

AC7801实现一个PWM同时捕获多通道信号

本帖最后由 wangjj19950516 于 2021-10-27 16:44 编辑

最近有较多客户提出需要捕获多路PWM信号,ATC芯片的PWDT是专门做捕获功能,但每个PWDT只能捕获一个通道,对于多通道的需求,资源不够用。PWM模块也可以做输入捕获功能,且PWM有多个通道。一般常规的使用都是用一个PWM模块做一路信号的捕获,难免有些浪费资源,这里向大家介绍一下PWM同时捕获多通道的做法。
一、PWM工作模式
       AC7801的PWM模块包含很多功能,输出比较,输入捕获,双边沿捕获,组合输出模式,正交解码等。

可以看到,有输入捕获和双边沿输出捕获模式可以用来做外部信号的捕获。
1.输入捕获:单通道捕获,每个通道独立作为捕获通道,可设置捕获边沿。当通道输入出现选定的边沿时,PWM计数器的当前值会被捕获到PWM_CHnV寄存器中。同时,通道捕获中断标志位被置1,如果使能了通道中断,则可以产生中断。

如上图所示,设置上升沿捕获,当输入信号的上升沿来到时,把当前计数器的值2和8存入CHnV寄存器,相邻两次的数值差对应的时间即为波形的周期。
如果设置上升沿和下降沿都发生捕获,则可以分别得到高电平和低电平时间,从而可以计算波形的频率和占空比。
2.双边沿捕获:信号只能输入到通道CH0,CH2,CH4,但实际在芯片内部信号分两路走,CHn和CHn+1都有输入,可以设置CHn和CHn+1的捕获边沿。但不能同时使能CHn和CHn+1的中断,只能使能一个,即使设置的边沿不同,在一个周期内只能产生一个中断。
当输入信号出现CHn选择的边沿时,PWM计数器的当前值会存入CHnV寄存器,当出现CHn+1选择的边沿时,计数器的当前值会存入CHn+1V寄存器。

二、PWM多通道捕获
这里使用输入捕获(单通道)模式,捕获4路外部信号,每路信号都计算出频率和占空比。
使用PB5->PWM1_CH0,PB4->PWM1_CH1,PB8->PWM1_CH2,PC9->PWM1_CH6,这四个通道
1.首先配置GPIO功能,
void GPIO_PWM_Init(void)
{
         GPIO_SetFunc(GPIOB, GPIO_PIN5, GPIO_FUN1);//PWM1_CH0
         GPIO_SetFunc(GPIOB, GPIO_PIN4, GPIO_FUN1);//PWM1_CH1
         GPIO_SetFunc(GPIOB, GPIO_PIN8, GPIO_FUN1);//PWM1_CH2
         GPIO_SetFunc(GPIOC, GPIO_PIN9, GPIO_FUN1);//PWM1_CH6
}2,配置PWM
void PWM_Capture_Init(void)
{
    PWM_DeInit(PWM1); //去初始化PWM1
    PWM_inputChConfigType inputChConfig; //输入通道配置,总共4个通道
    PWM_inputCaptureConfigType inputCapconfig; //输入捕获结构体
    PWM_ConfigType config; //PWM模块结构体

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

    inputChConfig.channel = PWM_CH_0; //信号捕获通道
    inputChConfig.mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig.detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig.onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig.filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig.filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig.eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig.interruptEn = ENABLE; //该通道输入捕获中断使能
               
    inputChConfig.channel = PWM_CH_1; //信号捕获通道
    inputChConfig.mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig.detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig.onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig.filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig.filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig.eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig.interruptEn = ENABLE; //输入捕获中断使能
               
    inputChConfig.channel = PWM_CH_2; //信号捕获通道
    inputChConfig.mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig.detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig.onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig.filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig.filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig.eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig.interruptEn = ENABLE; //输入捕获中断使能
               
    inputChConfig.channel = PWM_CH_6; //信号捕获通道
    inputChConfig.mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig.detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig.onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig.filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig.filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig.eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig.interruptEn = ENABLE; //输入捕获中断使能
   
    //输入捕获配置
    inputCapconfig.channelNum = 4;//输入捕获通道数目
    inputCapconfig.channelConfig = inputChConfig;//输入通道配置变量地址赋值。
   
    //PWM模块配置
    config.mode = PWM_MODE_INPUT_CAPTURE;//PWM模块配置为输入捕获模式
    config.initModeStruct = &inputCapconfig;//输入捕获配置变量赋值
    config.clkSource = PWM_CLK_SOURCE_APB; //PWM时钟源配置
    /*
    设置捕获时钟频率:
    可捕获的最小频率(前提是时钟源为PWM_CLK_SOURCE_APB):
    min_clk = SYS_CLK/(PWM_CAPTURE_PRESCALER+1)/65536 = 24000000/2/65536 = 183HZ
    */
    config.clkPsc = 1;//PWM时钟源分频,2分频
    config.initValue = 0;//计数器初始寄存器值
    config.maxValue = 65535; //PWM计数器最大值
    config.overflowInterrupEn = ENABLE;//计数器溢出中断使能位
    config.cntOverflowFreq = 0;//CNTOF中断产生的频率与计数器频率的关系(0-127), 0表示每次计数器溢出都产生溢出中断,1表示间隔1次,2表示间隔2次,以此内推。
   
    config.interruptEn = ENABLE; //PWM中断使能
    config.callBack = PWM_Capture_CallBack; //PWM中断回调

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

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

void PWM_Capture_CallBack(void *device, uint32_t wpara, uint32_t lpara)
{
      static uint8_t pwm_ch;
      uint8_t gpio_edge =0;//边沿,1--上升沿0--下降沿
      uint16_t CountValue =0;
   //PWM计数器溢出
    if (wpara & PWM_INIT_CNTOF_Msk)
    {
         for(uint8_t index =0; index < 4;index++)//计算每个通道的电平溢出次数
         {
            if(PWM_Capture.pinlevel== 0)//根据之前的电平状态,判断该溢出时间加在那个状态,避免溢出中断时电平发生跳变,导致时间计算错误
            {
                   PWM_Capture.low_over_cnt++;
            }
            else
            {
                  PWM_Capture.high_over_cnt ++;
            }
         }
    }
      
   //通道中断
   if(lpara !=0)
   {
         if (lpara & PWM_STR_CH0SF_Msk)//CH0中断
         {               
            pwm_ch = 0;
            if(GPIO_GetPinLevel(GPIOB, GPIO_PIN5)== GPIO_LEVEL_LOW)
            {
                   gpio_edge = 0;
            }
            else
            {
                   gpio_edge = 1;
            }
            CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_0); //读取通道0的捕获数据
         }
         else if(lpara & PWM_STR_CH1SF_Msk)//CH1中断
         {
               pwm_ch =1;
               if(GPIO_GetPinLevel(GPIOB, GPIO_PIN4)== GPIO_LEVEL_LOW)
               {
                  gpio_edge = 0;
               }
               else
               {
                  gpio_edge = 1;
               }
               CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_1);//读取通道1的捕获数据
          }               
          else if(lpara & PWM_STR_CH2SF_Msk)//CH2中断
          {
                pwm_ch =2;
                if(GPIO_GetPinLevel(GPIOB, GPIO_PIN8)== GPIO_LEVEL_LOW)
                {
                      gpio_edge = 0;
                }
                else
                {
                      gpio_edge = 1;
                }
                CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_2);//读取通道2 的捕获数据
         }
         else if(lpara & PWM_STR_CH6SF_Msk)//CH6中断
         {
               pwm_ch =3;
               if(GPIO_GetPinLevel(GPIOC, GPIO_PIN9)== GPIO_LEVEL_LOW)
               {
                     gpio_edge = 0;
               }
               else
               {
                     gpio_edge = 1;
               }
               CountValue = PWM_GetChannelCountValue(PWM1, PWM_CH_6); //读取通道6的捕获数据
         }
         else{}
                              
         PWM_Capture.captureValue = CountValue;
               
         if(gpio_edge==0) //下降沿,读出高电平时间
         {
                  PWM_Capture.high_time = (uint32_t)(PWM_Capture.high_over_cnt)*65536 +PWM_Capture.captureValue - PWM_Capture.captureValue_last;
                  PWM_Capture.pinlevel = 0;
         }
         else//上升沿,读出低电平时间
         {
                  PWM_Capture.Low_time = (uint32_t)PWM_Capture.low_over_cnt * 65536 + PWM_Capture.captureValue - PWM_Capture.captureValue_last;
                  PWM_Capture.pinlevel =1;
            }
                              
            //频率和占空比计算
         //PWM时钟24MHZ,2分频
            PWM_Capture.freq= 24000000/2/(PWM_Capture.Low_time + PWM_Capture.high_time);
            PWM_Capture.duty = (PWM_Capture.high_time)*100/(PWM_Capture.Low_time + PWM_Capture.high_time);
            PWM_Capture.low_over_cnt =0;
            PWM_Capture.high_over_cnt = 0;
            PWM_Capture.captureValue_last = PWM_Capture.captureValue;
   }

}      


三、附件
至此,便可以同时捕获4路信号,互不影响,实测每个通道都能正确计算出频率和占空比。
附件为测试工程,有需要可以参考




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

学习了,谢谢楼主分享。
页: [1]
查看完整版本: AC7801实现一个PWM同时捕获多通道信号