本帖最后由 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, 下载次数: 31)
|