打印
[活动]

【极海G32A1465测评】7、PDU的粗浅理解

[复制链接]
742|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 sujingliang 于 2024-12-30 22:11 编辑

可编程延迟单元(PDU)是极海G32A1465开发板上一个很独特的外设,也许是见识有限,没有在其他MCU上发现有类似的单元。
在延时方面,PDU无疑是SysTick、定时器之外的又一个新的选择。

在毫秒级延时上,SysTick、定时器在准确性上都有不错的表现,SysTick有配置简便易于理解的优势,定时器不占优势。
在微秒级延时上,SysTick不被推荐,定时器是较好的选择。

至于PDU,之前没有接触过,不过从例程上看应该可以支持微秒级延时,下面就简单分析一下。
例程位于:G32A1xxx_SDK_V1.1\Examples\G32A1465\PDU\PDU_PeriodicInterrupt

一、手册上关于PDU的描述
PDU被归为定时器

提供可控制的延迟,从内部或外部触发器,或可编程间隔刻度,到 ADC 的硬件
触发输入。PDU 可以选择性地提供脉冲输出,用作 COMP 块中的采样窗口。


触发
⚫ 最多 2 个触发输入源和一个软件触发源
⚫ 触发器输出可以独立使能或禁止
⚫ 每个预触发输出一个 16 位延迟寄存器
⚫ 可选的旁路延迟寄存器的预触发输出
⚫ 每个预触发器一个通道标志和一个序列错误标志
ADC
⚫ 8 个可配置的 PDU 通道用于 ADC 硬件触发器
⚫ 1 个 PDU 通道对应 1 个 ADC
⚫ 1 个触发输出用于 ADC 硬件触发,最多 8 个预触发输出用于每个 PDU
通道的 ADC 触发选择
操作在一次或连续模式
可选背靠背模式操作,可实现 ADC 转换完成以触发下一个 PDU 通道
脉冲输出
⚫ 8 个脉冲输出
⚫ 脉冲输出可以独立使能或禁止
⚫ 可编程脉冲宽度
DMA 支持
中断
⚫ 1 个可编程延迟中断
⚫ 1 个序列错误中断


二、从例程入手,稍作修改
只看手册云里雾里,还是从例程上分析吧


1、PDU配置参数g_pduTimerConfig
const PDU_TIMER_CONFIG_T g_pduTimerConfig =
{
    .loadValMode            = PDU_LOAD_VAL_IMMEDIATELY,
    .clkPscDiv              = PDU_CLK_PSCDIV1,
    .clkPscMultFactor       = PDU_CLK_PSCMULT_FACTOR_AS_1,
    .triggerInput           = PDU_SOFTWARE_TRIGGER,
    .intEn                  = true,
    .seqErrIntEn            = false,
    .continuousModeEn       = true,
    .dmaEn                  = false,
    .instanceBackToBackEn   = false,
};
g_pduTimerConfig 用于PDU_Init函数的参数,初始化PDU:

.loadValMode            = PDU_LOAD_VAL_IMMEDIATELY,
对应LDMODSEL寄存器

PDU_LOAD_VAL_IMMEDIATELY为0,当控制寄存器LDSUS设置为1时,内部寄存器立即从它们的缓冲区中加载值。
typedef enum
{
    /* Loaded immediately after load operation. */
    PDU_LOAD_VAL_IMMEDIATELY                       = 0U,

    /* Loaded when counter hits the modulo after load operation. */
    PDU_LOAD_VAL_AT_MODULO_COUNTER                 = 1U,

    /* Loaded when detecting an input trigger after load operation. */
    PDU_LOAD_VAL_AT_NEXT_TRIGGER                   = 2U,

    /* Loaded when counter hits the modulo or detecting an input trigger after load operation. */
    PDU_LOAD_VAL_AT_MODULO_COUNTER_OR_NEXT_TRIGGER = 3U
} PDU_LOAD_VAL_MODE_T;



.clkPscDiv              = PDU_CLK_PSCDIV1,
原例程是PDU_CLK_PSCDIV128,这个参数对应PSCDIVCFG寄存器

typedef enum
{
    PDU_CLK_PSCDIV1   = 0U, /* ( Prescaler multiplication factor ) x ( Counter divided by 1 ) */
    PDU_CLK_PSCDIV2   = 1U, /* ( Prescaler multiplication factor ) x ( Counter divided by 2 ) */
    PDU_CLK_PSCDIV4   = 2U, /* ( Prescaler multiplication factor ) x ( Counter divided by 4 ) */
    PDU_CLK_PSCDIV8   = 3U, /* ( Prescaler multiplication factor ) x ( Counter divided by 8 ) */
    PDU_CLK_PSCDIV16  = 4U, /* ( Prescaler multiplication factor ) x ( Counter divided by 16 ) */
    PDU_CLK_PSCDIV32  = 5U, /* ( Prescaler multiplication factor ) x ( Counter divided by 32 ) */
    PDU_CLK_PSCDIV64  = 6U, /* ( Prescaler multiplication factor ) x ( Counter divided by 64 ) */
    PDU_CLK_PSCDIV128 = 7U  /* ( Prescaler multiplication factor ) x ( Counter divided by 128 ) */
} PDU_CLK_PSCDIV_T;
设置为PDU_CLK_PSCDIV1,不分频。

.clkPscMultFactor       = PDU_CLK_PSCMULT_FACTOR_AS_1,
原例程是PDU_CLK_PSCMULT_FACTOR_AS_10
这个参数对应寄存器MULPSCCFG配置预分频器乘法系数


typedef enum
{
    PDU_CLK_PSCMULT_FACTOR_AS_1  = 0U, /* Multiplication factor is 1. */
    PDU_CLK_PSCMULT_FACTOR_AS_10 = 1U, /* Multiplication factor is 10. */
    PDU_CLK_PSCMULT_FACTOR_AS_20 = 2U, /* Multiplication factor is 20. */
    PDU_CLK_PSCMULT_FACTOR_AS_40 = 3U  /* Multiplication factor is 40. */
} PDU_CLK_PSCMULT_FACTOR_T;
.triggerInput           = PDU_SOFTWARE_TRIGGER,
对应SWTRGSEL寄存器

软件触发

.continuousModeEn       = true,
对应CMODEN寄存器

连续模式


2、main初始化
 PDU_Init(PDU0_INSTANCE, &g_pduTimerConfig);
   
    /* Calculate the required value of the PDU timer counter for the specified timeout */
    if (!PDU_CalculateIntTimerValue(&g_pduTimerConfig, PDU_TIMEOUT_US, &pduIntTimerValue))
    {
        goto end;
    }
   
    /* Set the period of the counter */
    PDU_ConfigPeriodCountValue(PDU0_INSTANCE, pduIntTimerValue);
   
    /* Set the delay value to schedule the PDU interrupt */
    PDU_ConfigTimerIntDelayValue(PDU0_INSTANCE, pduIntTimerValue);
   
    /* Enable PDU */
    PDU_Enable(PDU0_INSTANCE);
   
    /* Executes the command of loading values */
    PDU_LoadValuesCmd(PDU0_INSTANCE);
   
    /* Software trigger the pdu counter */
    PDU_EnableSoftTrigger(PDU0_INSTANCE);


PDU_Init(PDU0_INSTANCE, &g_pduTimerConfig);
初始化PDU的实例,配置分频、中断等参数。

if (!PDU_CalculateIntTimerValue(&g_pduTimerConfig, PDU_TIMEOUT_US, &pduIntTimerValue))
这行代码调用PDU_CalculateIntTimerValue函数来计算在给定的周期时间


PDU_ConfigPeriodCountValue(PDU0_INSTANCE, pduIntTimerValue);
使用计算得到的定时器值pduIntTimerValue来配置PDU实例的周期计数值。当PDU的计数器达到这个值时,会触发一个周期事件。
此函数将 CNTPRD=pduIntTimerValue



PDU_ConfigTimerIntDelayValue(PDU0_INSTANCE, pduIntTimerValue);
使用pduIntTimerValue来配置PDU实例的中断延迟值。
此函数将  SINTDLY=pduIntTimerValue
PDU_Enable(PDU0_INSTANCE);
启用PDU实例,使其开始运行。
此函数将 PDUEN=1


PDU_LoadValuesCmd(PDU0_INSTANCE);
执行加载值命令,将之前配置的所有参数(如周期计数值和中断延迟值)加载到PDU的硬件寄存器中。
此函数将 LDSUS=1

PDU_EnableSoftTrigger(PDU0_INSTANCE);
软件触发PDU计数器。
此函数将 SWTRGSEL=1

3、PDU_CalculateIntTimerValue函数
main中有如下调用
PDU_CalculateIntTimerValue(&g_pduTimerConfig, PDU_TIMEOUT_US, &pduIntTimerValue)

PDU_CalculateIntTimerValue用于计算周期/中断数值,计算公式:
tempIntTimerValue = (pduClkFreq * us) / (clkPscDiv * clkPscMultFactor);
这里us为PDU_TIMEOUT_US,可以参数化设置,设PDU_TIMEOUT_US=1000,即1ms
已知:pduClkFreq =48mhz,clkPscDiv =1(不分频),clkPscMultFactor=1(乘法因子为1)
tempIntTimerValue=48000000*1000/(1*1)=48000


根据寄存器SINTDLY说明

tempIntTimerValue应满足:0<tempIntTimerValue<0xFFFF


4、PDU0_IRQHandler中断处理函数
void PDU0_IRQHandler(void)
{
    if (PDU_ReadTimerIntFlag(PDU0_INSTANCE) != false)
    {
        /* Clear PDU Interrupt flag */
        PDU_ClearTimerIntFlag(PDU0_INSTANCE);
        
        /* Toggle green LED */
        //LED_Toggle(LED_GREEN);
        pdu_count++;
                                
    }
}
每1ms进入中断一次,pdu_count加1。

5、while(1)
    while (1)
    {
                        if(pdu_count>=500)
                                {
                                        pdu_CNTCUR=PDU_ReadTimerValue(PDU0_INSTANCE);
                                        printf("pdu_CNTCUR:%d\r\n",pdu_CNTCUR);
                                        printf("PDU timing 500ms.\r\n");
                                        pdu_count=0;
                                }
    }
判断pdu_count,大于等于500(500ms),打印信息:
CNTCUR(计算器当前值),500ms信息,清pdu_count为0。

6、PDU_TIMEOUT_US
#define PDU_TIMEOUT_US          (1000UL)

可以通过改变PDU_TIMEOUT_US数值,改变进入中断的时间,从字面上看可以设置到1us。



使用特权

评论回复
沙发
闪烁阴影| | 2025-1-21 10:54 | 只看该作者
这个延时是阻塞还是后台运行的

使用特权

评论回复
板凳
sujingliang|  楼主 | 2025-1-21 12:59 | 只看该作者
闪烁阴影 发表于 2025-1-21 10:54
这个延时是阻塞还是后台运行的

PDU使用了中断处理,应该是后台吧

使用特权

评论回复
地板
遗忘领域| | 2025-1-22 10:10 | 只看该作者
PDU还是第一次见

使用特权

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

本版积分规则

48

主题

96

帖子

0

粉丝