PDB编程实践
PDB周期定时中断
说了这么多天书,还是来实践一下才是正道。首先我们做点简单的事情,让PDB先运行起来,不触发任何东西,就自己生成定时中断好了。打开例程“LPLD_PdbPeriodicInt”,这个例程实现的功能和PIT的功能类似,我们将在周期产生的中断函数中输出信息。具体先看初始化函数pdb_init()的代码:
1 | pdb_init_struct.PDB_CounterPeriodMs = 1000; //计数器溢出周期1000毫秒 | 2 | pdb_init_struct.PDB_TriggerInputSourceSel = TRIGGER_SOFTWARE; //触发源为软件触发 | 3 | pdb_init_struct.PDB_ContinuousModeEnable = TRUE; //连续工作模式 | 4 | pdb_init_struct.PDB_DelayMs = 200; //中断延时时间200毫秒 | 5 | pdb_init_struct.PDB_IntEnable = TRUE; //使能延时中断 | 6 | pdb_init_struct.PDB_Isr = pdb_isr; //中断函数设置 | 7 | LPLD_PDB_Init(pdb_init_struct); | 8 | LPLD_PDB_EnableIrq(); | 9 | LPLD_PDB_SoftwareTrigger(); |
Line 1:配置PDB计数器的溢出周期为1000毫秒,库函数会根据我们设置的时间,自动计算出MOD寄存器、MULT因子、PRESCALER分频的值。
Line 2:利用成员变量PDB_TriggerInputSourceSel配置PDB的触发源为软件触发,TRIGGER_SOFTWARE。
Line 3:配置PDB的工作模式为连续工作,即图中CONT位的值为1,PDB计数器会在等于MOD的值后立即复位并继续计数。
Line 4:配置中断延时时间为200毫秒,库函数会根据这个时间自动计算出“PDBIDLY”寄存器的值。
Line 5~6:使能PDB中断,配置中断函数。
Line 7:初始化PDB。
Line 8:使能中断请求。
Line 9:软件触发PDB开始工作。这个函数的作用就是让图中红框部分产生一个软件触发信号给“Control Logic”。 配置完PDB后,PDB的延时中断功能就会立即开始工作,不过程序会运行以下这句代码:
1 | printf("Waiting for 200ms, and then:\r\n"); |
等待200ms后,第一次中断会发生,再等待1000ms后,第二次中断会发生。以后每个1000ms会发送一次中断。也就是说,初始化完成后,程序首先会延时200ms,才会触发第一次中断。这就是初始化时设置PDB_DelayMs为200ms的效果了。如果你不明白为什么,可以回头去看看第一幅PDB模块图。
PDB触发ADC采集与前面几个例程不同,这是一个综合例程,我们要配合使用ADC+PDB+PIT这个3个模块来实现自动定时采集功能。打开例程“LPLD_PdbAnalogSample”,看main()函数中的初始化代码,首先初始化ADC的相关设置:
1 | adc0_init_struct.ADC_Adcx = ADC0; //选择ADC0 | 2 | adc0_init_struct.ADC_BitMode = SE_12BIT; //配置转换精度 | 3 | adc0_init_struct.ADC_CalEnable = TRUE; //使能初始化自动校准 | 4 | adc0_init_struct.ADC_HwTrgCfg = HW_TRGA; //配置为硬件触发转换 | 5 | adc0_init_struct.ADC_Isr = adc0_isr; //设置ADC中断函数 | 6 | LPLD_ADC_Init(adc0_init_struct); | 7 | LPLD_ADC_Chn_Enable(ADC0, DAD1); | 8 | LPLD_ADC_EnableConversion(ADC0, DAD1, 0, TRUE); | 9 | LPLD_ADC_EnableIrq(adc0_init_struct); |
Line 4:前面几行没有要说的,主要是这里配置成员变量ADC_HwTrgCfg使ADC0为硬件触发转换模式。
Line 5:配置ADC中断函数,由于我们没有使用DMA功能,因此需要在中断里来获取转换结果。
Line 8:调用LPLD_ADC_EnableConversion()函数使能转换,该函数的前两个参数分别设置模块号和通道号,第3个参数设置转换通道为A组,第4个参数为使能转换结束中断。
Line 9:使能中断请求。
接下来初始化PDB的相关设置:
1 | pdb_init_struct.PDB_CounterPeriodMs = 1000; //PDB计数器周期设置 | 2 | pdb_init_struct.PDB_LoadModeSel = LOADMODE_0; //加载模式设置 | 3 | pdb_init_struct.PDB_ContinuousModeEnable = FALSE; //禁用连续工作模式 | 4 | pdb_init_struct.PDB_TriggerInputSourceSel = TRIGGER_PIT0; //配置触发源为PIT0 | 5 | LPLD_PDB_Init(pdb_init_struct); | 6 | LPLD_PDB_AdcTriggerCfg(ADC0, PRETRIG_EN_A, 0); |
Line 2:这是第1个例程没有配置过的参数,配置PDB_LoadModeSel加载模式,这里选择模式LOADMODE_0。加载模式指的是PDB寄存器中如CNT、MOD、DLY寄存器的加载模式,具体描述请参见PDB模块在线函数手册。(点击此处)
Line 3:禁用连续工作模式。
Line 4:配置PDB输入触发源为TRIGGER_PIT0,即PIT0模块。
Line 6:PDB对ADC的触发配置,调用函数LPLD_PDB_AdcTriggerCfg(),配置预触发ADC0模块,预触发A通道使能,预触发延时为0。 最后配置PDB的触发源,PIT模块:
1 | pit0_init_struct.PIT_Pitx = PIT0; //选择PIT0 | 2 | pit0_init_struct.PIT_PeriodMs = 500; //PIT0计数周期500ms | 3 | LPLD_PIT_Init(pit0_init_struct); |
Line 2:配置PIT0技术周期为500毫秒,即每过500毫秒触发一次PDB。那么它和PDB的1000ms计数周期什么关系呢,这里的意思是没500ms触发一次PDB开始工作,这是PDB开始计数,其实PDB根本计不到1000ms就会被下一次PIT0触发。
Line 3:直接初始化PIT0,不用设置中断函数,也不用使能中断请求,因为PIT只需要产生触发信号,不用产生中断。
全部初始化完毕,整个工程才真正开始运作,首先PIT会触发PDB开始工作,PDB收到触发输入后经过0秒延迟预触发ADC0模块的A组转换通道,A组通道被触发后开始AD转换工作,转换完成后触发中断运行函数adc0_isr(),看其代码:
1 | void adc0_isr() | 2 | { | 3 | int16 result; | 4 | uint8 ab = LPLD_ADC_GetSC1nCOCO(ADC0); | 5 | result = LPLD_ADC_GetResult(ADC0, ab); | 6 | printf("ADC0_R[%d]=%d\r\n", ab, result); | 7 | } |
Line 4:首先调用LPLD_ADC_GetSC1nCOCO()函数查看到底是ADC0的A组还是B组产生的中断,因为ADCx的A、B组通道都进入同一个中断,因此必须用此函数判断是哪组产生的中断。
Line 5:调用LPLD_ADC_GetResult()函数获取ADC0模块A组通道的转换结果,该函数和ADC的LPLD_ADC_Get()函数都是获取转换结果,但是前者不会等待转换结束,且必须指定A、B组通道,后者会等待转换结束,且只获取A组结果(因为只有A组能软件触发转换)。
未交待的后话本节课只介绍了PDB的定时中断和ADC触发例程,其实PDB还可以触发DAC模块,大家可以在了解OSKinetis V3库函数的基础上自行学习使用。
而且关于触发ADC的转换,更加效率的方式是配合DMA的使用。这样的话,就不需要ADC产生中断来获取转换结果了,可以直接通过DMA的ADC请求源来不依靠CPU获取转换结果。具体例程可以参考“LPLD_DmaPdbAnalogSample”。
更加牛叉的方法是利用ADC的乒乓模式配合PDB的back to back功能,来完成4个ADC通道的自动连续采集,实际案例请参考“LPLD_DmaPdbADCx4”。
|