本帖最后由 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。
|