[活动] 【极海G32A1465测评】7、PDU的粗浅理解

[复制链接]
2581|3
 楼主| sujingliang 发表于 2024-12-30 21:51 | 显示全部楼层 |阅读模式
本帖最后由 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被归为定时器
1.png
提供可控制的延迟,从内部或外部触发器,或可编程间隔刻度,到 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
  1. const PDU_TIMER_CONFIG_T g_pduTimerConfig =
  2. {
  3.     .loadValMode            = PDU_LOAD_VAL_IMMEDIATELY,
  4.     .clkPscDiv              = PDU_CLK_PSCDIV1,
  5.     .clkPscMultFactor       = PDU_CLK_PSCMULT_FACTOR_AS_1,
  6.     .triggerInput           = PDU_SOFTWARE_TRIGGER,
  7.     .intEn                  = true,
  8.     .seqErrIntEn            = false,
  9.     .continuousModeEn       = true,
  10.     .dmaEn                  = false,
  11.     .instanceBackToBackEn   = false,
  12. };
g_pduTimerConfig 用于PDU_Init函数的参数,初始化PDU:

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

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

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

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



.clkPscDiv              = PDU_CLK_PSCDIV1,
原例程是PDU_CLK_PSCDIV128,这个参数对应PSCDIVCFG寄存器
3.png
  1. typedef enum
  2. {
  3.     PDU_CLK_PSCDIV1   = 0U, /* ( Prescaler multiplication factor ) x ( Counter divided by 1 ) */
  4.     PDU_CLK_PSCDIV2   = 1U, /* ( Prescaler multiplication factor ) x ( Counter divided by 2 ) */
  5.     PDU_CLK_PSCDIV4   = 2U, /* ( Prescaler multiplication factor ) x ( Counter divided by 4 ) */
  6.     PDU_CLK_PSCDIV8   = 3U, /* ( Prescaler multiplication factor ) x ( Counter divided by 8 ) */
  7.     PDU_CLK_PSCDIV16  = 4U, /* ( Prescaler multiplication factor ) x ( Counter divided by 16 ) */
  8.     PDU_CLK_PSCDIV32  = 5U, /* ( Prescaler multiplication factor ) x ( Counter divided by 32 ) */
  9.     PDU_CLK_PSCDIV64  = 6U, /* ( Prescaler multiplication factor ) x ( Counter divided by 64 ) */
  10.     PDU_CLK_PSCDIV128 = 7U  /* ( Prescaler multiplication factor ) x ( Counter divided by 128 ) */
  11. } PDU_CLK_PSCDIV_T;
设置为PDU_CLK_PSCDIV1,不分频。

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

4.png
  1. typedef enum
  2. {
  3.     PDU_CLK_PSCMULT_FACTOR_AS_1  = 0U, /* Multiplication factor is 1. */
  4.     PDU_CLK_PSCMULT_FACTOR_AS_10 = 1U, /* Multiplication factor is 10. */
  5.     PDU_CLK_PSCMULT_FACTOR_AS_20 = 2U, /* Multiplication factor is 20. */
  6.     PDU_CLK_PSCMULT_FACTOR_AS_40 = 3U  /* Multiplication factor is 40. */
  7. } PDU_CLK_PSCMULT_FACTOR_T;
.triggerInput           = PDU_SOFTWARE_TRIGGER,
对应SWTRGSEL寄存器
5.png
软件触发

.continuousModeEn       = true,
对应CMODEN寄存器
6.png
连续模式


2、main初始化
  1. PDU_Init(PDU0_INSTANCE, &g_pduTimerConfig);
  2.    
  3.     /* Calculate the required value of the PDU timer counter for the specified timeout */
  4.     if (!PDU_CalculateIntTimerValue(&g_pduTimerConfig, PDU_TIMEOUT_US, &pduIntTimerValue))
  5.     {
  6.         goto end;
  7.     }
  8.    
  9.     /* Set the period of the counter */
  10.     PDU_ConfigPeriodCountValue(PDU0_INSTANCE, pduIntTimerValue);
  11.    
  12.     /* Set the delay value to schedule the PDU interrupt */
  13.     PDU_ConfigTimerIntDelayValue(PDU0_INSTANCE, pduIntTimerValue);
  14.    
  15.     /* Enable PDU */
  16.     PDU_Enable(PDU0_INSTANCE);
  17.    
  18.     /* Executes the command of loading values */
  19.     PDU_LoadValuesCmd(PDU0_INSTANCE);
  20.    
  21.     /* Software trigger the pdu counter */
  22.     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
9.png


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

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说明
7.png
tempIntTimerValue应满足:0<tempIntTimerValue<0xFFFF


4、PDU0_IRQHandler中断处理函数
  1. void PDU0_IRQHandler(void)
  2. {
  3.     if (PDU_ReadTimerIntFlag(PDU0_INSTANCE) != false)
  4.     {
  5.         /* Clear PDU Interrupt flag */
  6.         PDU_ClearTimerIntFlag(PDU0_INSTANCE);
  7.         
  8.         /* Toggle green LED */
  9.         //LED_Toggle(LED_GREEN);
  10.         pdu_count++;
  11.                                 
  12.     }
  13. }
每1ms进入中断一次,pdu_count加1。

5、while(1)
  1.     while (1)
  2.     {
  3.                         if(pdu_count>=500)
  4.                                 {
  5.                                         pdu_CNTCUR=PDU_ReadTimerValue(PDU0_INSTANCE);
  6.                                         printf("pdu_CNTCUR:%d\r\n",pdu_CNTCUR);
  7.                                         printf("PDU timing 500ms.\r\n");
  8.                                         pdu_count=0;
  9.                                 }
  10.     }
判断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还是第一次见
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

147

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部