前言:
此刻兄弟们对“定时器的代码”都比较着重,看官们都想要知道一些“定时器的代码”的相关资讯。那么小编也在网摘上搜集了一些关于“定时器的代码””的相关资讯,希望你们能喜欢,同学们快快来学习一下吧!事情回顾
话说上周调试控制器,当把导通角调整到170°以上时,由可控硅控制的白炽灯产生随机闪烁了故障现象。
过零信号的外部中断启动导通角延时的定时器中断,进入定时器中断之后立即停止定时器,并使能PWM输出,产生触发脉冲,同时再次启动定时器,以控制触发脉冲的个数,确保只触发导通一个半波;
经过一番分析,发现当导通角比较大时,CPUTimer0的周期寄存器PRDH:PRD设置的值比较小,在进入定时器中断程序并调用停止定时器的代码之前,CPUTimer的TIMH:TIM寄存器就减到了0,又自动加载RPDH:RPD寄存器加载数值到TIMH:TIM寄存器,继续递减计数,紧接着计数又减到了0;
此时CPU还在响应上一次的定时中断并进入了中断程序,但依然没执行到停止定时器的代码;
这种情况下,PIEIFR1的INTx7被置1,紧接着再次启动定时器以控制解发脉冲个数时,即使定时器中断标志位TF通过置1来清0,但是当把PIEACK置1时,定时器又立即产生了中断。
从而触发脉冲串迅速又在定时器中断程序中被关闭,甚至用示波器都观测不到触发脉冲串的踪迹。
当时的做法是在停止和启动定时器时,通过下述语句将PIEIFR1的INTx7清0。
PieCtrlRegs.PIEIFR1.bit.INTx7 = 0;
简单测了一段时间,没有再碰到闪烁的情况,于是一身轻松回了家。
一波三折
本周,客户接入变压器测试,发现检测不到闪络。
今天,我用下载工具调试代码,发现CPU运行一段时间之后就无法再进入ADC中断。
而ADC在初始化时被配置成通过EPWM3触发自动采样,将几路ADC组成一个系列,系列采样完之后,就进入中断,计算用于在定时器中断中判断闪络的二次电流,二次电压的平均值,最大、最小值。
memset((void *)&g_Adc_stRegs, 0, sizeof(g_Adc_stRegs)); InitAdc(); SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; ADC_cal(); SysCtrlRegs.HISPCP.all = 3; // HSPCLK = SYSCLKOUT / ADC_MODCLK = 25.0MHz EDIS; AdcRegs.ADCTRL1.bit.SUSMOD = 3; // 非挂起模式 AdcRegs.ADCTRL1.bit.ACQ_PS = 1; // 采样窗口: 4倍ADC时钟 AdcRegs.ADCTRL1.bit.CPS = 1; // AD时钟: 2分频高速时钟= 25.0MHz / 2 = 12.5MHz AdcRegs.ADCTRL1.bit.CONT_RUN = 0; // 采用启/停模式 AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // 级联模式 AdcRegs.ADCTRL1.bit.SEQ_OVRD = 0; // AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x05; // 数据采集通道数: 6通道 AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA0(U1X) as 1st SEQ1 conv AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; // Setup ADCINA0(U2X) as 1st SEQ1 conv AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x2; // Setup ADCINA0(I1X) as 1st SEQ1 conv AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x3; // Setup ADCINA0(I2X) as 1st SEQ1 conv AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x5; // Setup ADCINA0(TMP) as 1st SEQ1 conv AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x7; // Setup ADCINA0(YK ) as 1st SEQ1 conv AdcRegs.ADCTRL3.bit.ADCBGRFDN = 3; AdcRegs.ADCTRL3.bit.ADCPWDN = 1; AdcRegs.ADCTRL3.bit.ADCCLKPS= 1; // ADCLK = HSPCLK / 2 * (CPS + 1) = 12.50MHz AdcRegs.ADCTRL3.bit.SMODE_SEL = 0; // 采样方式选择: 顺序采样 AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1=1; AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; EPwm3Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group EPwm3Regs.ETSEL.bit.SOCASEL= 4; // Select SOC form CPMA on upcount EPwm3Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event EPwm3Regs.CMPA.half.CMPA = (ADC_SAMPLE_PERIOD_CNT / 2); // Set compare A value EPwm3Regs.TBPRD = (ADC_SAMPLE_PERIOD_CNT - 1); // Set period for EPwm3 EPwm3Regs.TBCTL.bit.CTRMODE = 0;
在运行期间,ADC的配置没有被修改过,那怎么就进不了ADC中断呢?
而如果不启动设备,则ADC中断始终都能进入,而一旦启动设备,使能过零信号中断,并调用了CPUTIMER启动或者停止代码,则过不了多久,在中断程序中翻转的IO口就测试不到电平变化,也就是CPU不再进入ADC中断。
逐一注释CPUTIMER启动或者停止的指令,最终发现执行将PIEIFR1的INTx7清0的操作导致了这一问题,也就是:
PieCtrlRegs.PIEIFR1.bit.INTx7 =0
根据STM32处理器的开发经验,如果是通过某一个位的置位操作来清空某一位的标志位。
那这个操作就是原子位操作,可以不考虑资源互斥访问的问题。
但是PIEIFR1的INTx7的标志位的清零却是直接赋值为0。
这可能不是原子操作!
有可能做了两步操作:
第一步,将PIEIFR1的数值读到Register
第二步,与操作之后再写回到PIEIFR1寄存器。
在作这一连贯动作时,硬件模块也在操作这个寄存器。
比如该死的ADC也产生了中断,在CPU完成第一步,将要执行第二步时,将PIEFR1的INTx4置1。
当CPU把Register存储的数值写回PEIFR1寄存器之后,其中硬件置位的INTx4标志位被覆盖成0。
没有INTx4标志位,即使ADC的INT_SEQ1中断标志位置位,CPU也无法进入ADC中断。
不能进入ADC中断,INT_SEQ1不能被清零,INTx4标志位不能再次被置位,从而CPU再也不能进入ADC中断,除非手动将INT_SEQ1标志位清零。
在CPUTIMER启动或者停止代码中删去了将PIEIFR1的INTx7清0的指令,长时间测试都没有再碰到不进入ADC中断的情况。
一筹莫展
删去了将PIEIFR1的INTx7清0的指令,导通角超过170°之后,又出现了灯泡随机闪烁的故障。
翻遍了TMS320F28335的规格书和示例代码,没有找到PIEIFR1寄存器的原子位操作。
如果定时器的TMRH:TMR寄存器减到0之后,定时器能自动停止下来,闪烁的问题也能解决,但是,经过确认,free设置为0,stop设置成1或者0,定时器自动停止仅在程序仿真,CPU断点暂停时有效。
峰回路转
我苦苦思索...
作为有几十年工作经验的优秀IT工程师,岂能败于小小的标志位,
我灵光一闪,有了办法,
在启动定时器的代码中,当设置了PRDH:PRD寄存器,并通过置位TRB标志位将数值立即从影响寄存器加载到真实寄存器之后,把PRDH:PRD寄存器再次赋值位比较大的数值,比如65535,代码如下:
#define SPARK_TIMER_ENABLE() do{\ PieCtrlRegs.PIEIER1.bit.INTx7 = 1;\ CpuTimer0Regs.TPR.all = 74;\ CpuTimer0Regs.TPRH.all = 0;\ CpuTimer0Regs.TCR.bit.TRB = 1;\ CpuTimer0Regs.TCR.bit.SOFT = 1;\ CpuTimer0Regs.TCR.bit.FREE = 0;\ CpuTimer0Regs.TCR.bit.TIF = 1;\ CpuTimer0Regs.TCR.bit.TIE = 1;\ CpuTimer0Regs.PRD.all = SPARK_TIMER_INIT_COUNTER;\ CpuTimer0Regs.TCR.bit.TSS = 0;\}while(0)
“CpuTimer0Regs.PRD.all = SPARK_TIMER_INIT_COUNTER”这一条语句拯救了我。
它使得TIMH:TIM减到0,产生第一次中断后,TIMH:TIM从PRDH:PRD寄存器中加载了一个比较大的数值,从而在进入中断停止定时器之前,TIMH来不及再次减到0,再次产生中断。
改完之后测试,闪烁故障不再出现,ADC中断也能始终进入。
这真是“山重水复疑无路,柳暗花明又一村"。
标签: #定时器的代码