这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【CH582M】CH582M的定时器(7)捕捉功能实践

共1条 1/1 1 跳转至

【CH582M】CH582M的定时器(7)捕捉功能实践

专家
2026-09-08 17:49:33     打赏

在简单地学习了定时器的捕捉功能后,进入实践环节。首先学习边沿对边沿方式,以做简单的固定占空比模式进行测试。按照以下方案编程:

1、定时器2负责产生固定占空比的PWM,由PB11输出。无中断处理。

2、定时器1负责捕捉,设置为捕捉模式,捕捉任何边沿,由PB10输入。有中断处理。

3、短接PB10和PB11,并接入逻辑分析仪,捕捉PWM信号,作为第三方验证。

为此,编写以下程序:

 #include "CH58x_common.h"
#define PWM_BUF_SIZE 100
#define FREQ_SYS    60000000
#define PWM_CYCLE   (60000)      // 1Ms 周期 (1kHz)
// LED 接在 PB4
#define LED_PIN     GPIO_Pin_4
/* 捕获缓冲区 */
__attribute__((aligned(4))) uint32_t CapBuf[PWM_BUF_SIZE];
// 占空比数组: 5%, 10%, 15%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%
uint32_t PwmBuf[PWM_BUF_SIZE] = {3000, 6000, 9000, 12000, 18000, 24000, 30000, 36000, 42000, 48000, 54000, 1000};
// 捕获标志
volatile uint8_t capFlag = 0;
volatile uint8_t  capLen = 0;
volatile int32_t capH = 0, capL = -1;
volatile uint8_t  capReady = 0;
volatile uint8_t  firstSkip = 1;   // 丢弃启动伪数据
volatile uint8_t  lastHigh = 0;    // 处理超时拆段累加
volatile int8_t status = -1;       // 捕捉状态
int main(void) {
    SetSysClock(CLK_SOURCE_PLL_60MHz);   // 主频 60MHz
    /* 串口1打印:PA9-TX  PA8-RX */
    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
    GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA);
    /* ========== 1. TMR2 输出 PWM,PB11 ========== */
    PRINT("TMR2...\n");
    GPIOB_ResetBits(GPIO_Pin_11);
    GPIOB_ModeCfg(GPIO_Pin_11, GPIO_ModeOut_PP_5mA);
    GPIOPinRemap(ENABLE, RB_PIN_TMR2);          // PB11 复用为 TMR2
    TMR2_PWMInit(High_Level, PWM_Times_1);      // 默认高电平有效,数据不重复
    TMR2_PWMCycleCfg(200);                      // 设置周期2.5μs
    TMR2_PWMActDataWidth(60);                   // 设置高电平宽度(占空比)1μs
    //TMR2_DMACfg(ENABLE, (uint16_t)(uint32_t)&PwmBuf[0],  (uint16_t)(uint32_t)&PwmBuf[10], Mode_LOOP);
    TMR2_Enable();
    //=======================================TMR1
    PRINT("TMR1...\n");
    // 配置TMR1为捕捉功能,捕捉TMR2输出的PWM信号(PB11),需要短接 PB10 和 PB11
    GPIOB_ModeCfg(GPIO_Pin_10, GPIO_ModeIN_PU); // 捕获输入(外部接线:PB11 -> PB10)
    GPIOPinRemap(ENABLE, RB_PIN_TMR1);          // PB10 复用为 TMR1
    TMR1_CapInit(Edge_To_Edge);                 // 任意沿触发捕获
    TMR1_CAPTimeoutCfg(0xFFFFFF);               // 捕获超时时间(26位计数器)
    // RB_TMR_IE_DATA_ACT : 数据激活(捕捉模式指每次捕捉到新数据,PWM 模式指数值触发导致有效电平结束)中断使能位
    // RB_TMR_IE_CYC_END : 周期结束(捕捉模式指超时,PWM 模式和定时模式指周期结束)中断使能位: 1:使能中断;0:禁止中断。
    TMR1_ITCfg(ENABLE, RB_TMR_IE_DATA_ACT);     // 捕获到数据产生中断
    PFIC_EnableIRQ(TMR1_IRQn);
    PRINT("Loop...\n");
    while(1)  {
   
        if(capReady)    {
           
            uint32_t h, l;
            PFIC_DisableIRQ(TMR1_IRQn);
            h = capH; l = capL;
            // 主循环读取 capH/capL 后,需要手动将 capReady 置0,否则会重复处理同一组数据。
            capReady = 0;
            status = 0;
            PFIC_EnableIRQ(TMR1_IRQn);
            PRINT("high = %d, low = %d , period = %d , duty = %d%%\n", h, l, (h + l), h * 100 / (h + l));
            //PRINT("high = %d us, low = %d us, period = %d us, duty = %d%%\n", h / 60, l / 60, (h + l) / 60, h * 100 / (h + l));
        }
    }
}
/* TMR1 捕获中断服务函数 */
// 若使用蓝牙协议栈,建议将 TMR1_IRQn 优先级设为高于蓝牙中断,防止响应延迟导致FIFO溢出(RB_TMR_IF_FIFO_OV)。
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void TMR1_IRQHandler(void) {
    // GOPIO口翻转
    GPIOB_InverseBits(GPIO_Pin_4);    
    // 判断中断是否由"数据有效"事件触发(即捕捉到一个新的边沿)
    if(TMR1_GetITFlag(RB_TMR_IF_DATA_ACT)) {
        // 直接从 R32_TMR1_FIFO 读取32位捕捉数据(无需调用 TMR1_CAPGetData(),二者等效)
        // 代码没有循环读取 R8_TMR1_FIFO_COUNT,这意味着默认FIFO中每次只有一个数据(通常由捕捉触发频率决定)
        uint32_t d = R32_TMR1_FIFO;
        TMR1_ClearITFlag(RB_TMR_IF_DATA_ACT);
        // 定时器使能后,第一次捕捉到的计数值不代表真实的边沿间隔,它只是从0计数到第一个边沿的时间(相当于从启动到第一个边沿的偏移量)。
        // 如果保留这个值,会导致高/低电平测量出现一个莫名其妙的初始值。
        // 全局变量 firstSkip 初始为 1。
        // 第一次进入中断时,直接丢弃当前数据,将 firstSkip 置 0 并返回。
        // 第二次及以后的捕捉数据才是有效的边沿间隔值。
        if(firstSkip) {
            firstSkip = 0;
            return;
        }  // 丢弃第一个伪数据
        // 判断 bit25 的值(1为高电平,0为低电平)
        if(d & 0x02000000)                        // bit25=1:高电平宽度
        {
            // d & 0x01FFFFFF:提取低25位,即为实际的计数值(单位 = 系统时钟周期)
            // 变量 lastHigh 用于记录上一次测量完成的电平类型(1=高,0=低)
            if(lastHigh) capH += d & 0x01FFFFFF;  // 超时拆成多段,累加
            else         capH  = d & 0x01FFFFFF;
            // 高电平状态值
            lastHigh = 1;                          
            status = 0;                           // 允许采集下降沿
        }
        else                                      // 低电平宽度
        {
            if (status == -1) return;             // 保证先从采集到上升沿开始
            if(!lastHigh) capL += d & 0x01FFFFFF;
            else          capL  = d & 0x01FFFFFF;
            // 低电平状态值
            lastHigh = 0;
            capReady = 1;                         // 凑齐 高+低 = 一个完整周期       
        }
    }
    // RB_TMR_IF_CYC_END : 周期结束(捕捉模式指超时,PWM 模式和定时模式指周期结束,计数模式指计数溢出)标志位
    // ? 超时中断:重置状态机,准备重新同步
    if(TMR1_GetITFlag(RB_TMR_IF_CYC_END)) {
        TMR1_ClearITFlag(RB_TMR_IF_CYC_END);
       
        // 重置状态机
        status = -1;        // 强制从下一个上升沿重新开始
        firstSkip = 1;      // 重置伪数据丢弃标志(可选)
        capH = 0;           // 清空累计值
        capL = 0;           // 清空累计值
        capReady = 0;       // 清空就绪标志
    }        
    // GOPIO口翻转
    //GPIOB_InverseBits(GPIO_Pin_4);    
}

CH582M可以工作在60MHz下,其系统时钟为16.67ns.为了验证可以捕捉的PWM信号,做了一下几种组合(前一个数据为PWM的周期脉冲数,后一个数据为高电平持续的脉冲数,这里的脉冲指的是系统时钟脉冲)。

一、周期为150,高电平为60

    TMR2_PWMCycleCfg(150);                      // 设置周期2.5μs

    TMR2_PWMActDataWidth(60);                   // 设置高电平宽度1μs

逻辑分析仪获得的波形:

image.png

串口输出信息:

image.png

得出的占空比虽然是对的,但测量出的计数值是错误的,被测成为高电平持续120个脉冲,周期为300个脉冲。

二、周期为200,高电平为60

    TMR2_PWMCycleCfg(200);                      // 设置周期3.3μs

    TMR2_PWMActDataWidth(60);                   // 设置高电平宽度1μs

image.png

image.png

这个结果是正常的。

三、周期为180,高电平为60

    TMR2_PWMCycleCfg(180);                      // 设置周期3μs

    TMR2_PWMActDataWidth(60);                   // 设置高电平宽度1μs


image.png

image.png

测量结果不稳定,大部分是错的。

实际上,周期低于200的,高电平低于60的模式,都是不正常,甚至于根本就没法完成测量,但实际输出都是正常的。测量中,通过测量PB4,获取到的TMR1的中断处理周期在800ns左右。

image.png

结论:

根据上面的实验,最终得出以下结论:如果PWM的周期低于200个脉冲(约 3.3 μs),高电平持续时间低于60(1  μs)的,CH582M是无法准确测量的。

另外,以DMA方式也做了一些实验,包括DMA方式产生不同占空比混杂的PWM输出,及DMA方式捕捉,均没有成功,哪怕周期和占空比是否符合可测量的范围。

还有是PWM周期特别长,以至于即使产生了测量溢出的PWM信号,我没有做深入测试,主要是这个周期高平持续时间最短的需要能达到2.8s左右,很少有这么长的PWM波。

 





关键词: CH582M     TMR     捕捉    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]