打印
[学习笔记]

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

[复制链接]
4869|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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]; //输入通道配置,总共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[0].channel = PWM_CH_0; //信号捕获通道
    inputChConfig[0].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig[0].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig[0].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig[0].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig[0].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig[0].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig[0].interruptEn = ENABLE; //该通道输入捕获中断使能
               
    inputChConfig[1].channel = PWM_CH_1; //信号捕获通道
    inputChConfig[1].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig[1].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig[1].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig[1].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig[1].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig[1].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig[1].interruptEn = ENABLE; //输入捕获中断使能
               
    inputChConfig[2].channel = PWM_CH_2; //信号捕获通道
    inputChConfig[2].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig[2].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig[2].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig[2].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig[2].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig[2].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig[2].interruptEn = ENABLE; //输入捕获中断使能
               
    inputChConfig[3].channel = PWM_CH_6; //信号捕获通道
    inputChConfig[3].mode = PWM_INPUTCAP_SINGLE_EDGE; //输入单通道捕获模式
    inputChConfig[3].detectType = PWM_BOTH_EDGE_DETECT;//上升沿和下降沿捕获
    inputChConfig[3].onceMode = PWM_INPUTCAP_CONTINUOUS;//连续捕获
    inputChConfig[3].filterEn = DISABLE;//使能输入滤波,CH0-CH3才有该功能
    inputChConfig[3].filterValue = 0; //filterValue值范围0-31,输入信号将被延迟filterValue*4个总线时钟
    inputChConfig[3].eventPsc = PWM_EVENT_PSC_1; //输入事件与通道中断的对应关系。PWM_EVENT_PSC_1表示1次输入事件产生一次通道中断,PWM_EVENT_PSC_4表示4次输入事件产生一次通道中断。
    inputChConfig[3].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]; //定义结构体数组,分别存放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[index].pinlevel== 0)  //根据之前的电平状态,判断该溢出时间加在那个状态,避免溢出中断时电平发生跳变,导致时间计算错误
              {
                   PWM_Capture[index].low_over_cnt++;
              }
              else
              {
                    PWM_Capture[index].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[pwm_ch].captureValue = CountValue;
               
           if(gpio_edge==0) //下降沿,读出高电平时间
           {
                  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;
                  PWM_Capture[pwm_ch].pinlevel = 0;
           }
           else//上升沿,读出低电平时间
           {
                  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;
                  PWM_Capture[pwm_ch].pinlevel =1;
            }
                                
            //频率和占空比计算
           //PWM时钟24MHZ,2分频
            PWM_Capture[pwm_ch].freq  = 24000000/2/(PWM_Capture[pwm_ch].Low_time + PWM_Capture[pwm_ch].high_time);
            PWM_Capture[pwm_ch].duty = (PWM_Capture[pwm_ch].high_time)*100/(PWM_Capture[pwm_ch].Low_time + PWM_Capture[pwm_ch].high_time);  
            PWM_Capture[pwm_ch].low_over_cnt =0;
            PWM_Capture[pwm_ch].high_over_cnt = 0;
            PWM_Capture[pwm_ch].captureValue_last = PWM_Capture[pwm_ch].captureValue;
     }

}        



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



使用特权

评论回复
沙发
flytianya2010| | 2021-10-28 17:18 | 只看该作者
学习啦!感谢楼主分享。

使用特权

评论回复
板凳
flytianya2010| | 2021-10-28 17:19 | 只看该作者
感谢楼主分享。学习了。

使用特权

评论回复
地板
1983万年青| | 2021-10-29 10:11 | 只看该作者
楼主,没有看的中断当中的标志位清除指令诶?
在压缩包里面才有吗?

使用特权

评论回复
5
wangjj19950516|  楼主 | 2021-10-29 11:41 | 只看该作者
1983万年青 发表于 2021-10-29 10:11
楼主,没有看的中断当中的标志位清除指令诶?
在压缩包里面才有吗?

中断标志位在驱动库的中断里清掉了,所以回调函数里不用再清了

使用特权

评论回复
6
yinwuqing110| | 2021-10-30 09:55 | 只看该作者
感谢楼主分享,PWM应用还是比较多的

使用特权

评论回复
7
1983万年青| | 2021-11-2 10:18 | 只看该作者
本帖最后由 1983万年青 于 2021-11-2 10:21 编辑

如果读脉冲周期的话,是不是前后两次通道数据之差就可以了?

使用特权

评论回复
8
wangjj19950516|  楼主 | 2021-11-2 18:03 | 只看该作者
1983万年青 发表于 2021-11-2 10:18
如果读脉冲周期的话,是不是前后两次通道数据之差就可以了?

这里配置的上升沿和下降沿都捕获,
如果想只读脉冲周期,可以设置为单上升沿捕获或单下降沿捕获,这样前后两次捕获的间隔就是周期,不过需要考虑溢出的情况哦

使用特权

评论回复
9
chenjun89| | 2021-11-2 19:33 | 只看该作者
学习了,谢谢楼主分享。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

86

帖子

2

粉丝