CH552的T2自动重载中断周期受代码影响

你好,

我在调试代码时发现有一处T2自动重载中断比理论值长5%。经反复测试,似乎是中断里的代码会影响T2的周期。我写了一个测试代码,可在多片CH552上复现此问题。暂时代码用Arduino框架执行。

void Timer2Interrupt(void) __interrupt (INT_NO_TMR2)
{
  timer2IntrHandler();//debugwire related
}

void timer2IntrHandler() {
  //
  if (TF2) {
    TF2 = 0;

    P1_4 = 0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
        P1_4=0;
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
//    __asm__("nop");
    
    P1_4 = 1;

  }

}


void setup() {
  // put your setup code here, to run once:

  TR2 = 0;
  //
  RCLK = 0;
  TCLK = 0;           // clear RCLK,TCLK in T2CON
  C_T2 = 0;           // clear C_T2 in T2CON for using internal clk
  EXEN2 = 0;          // clear EXEN2 in T2CON to disable T2EX
  T2MOD |= (bT2_CLK) | bTMR_CLK; // set bT2_CLK, for fast clk.Using Fsys/4
  CP_RL2 = 0;         // clear CP_RL2 in T2CON for 16bit timer, reload mode

  RCAP2 = 65536 - 128;

  TF2 = 0;
  EXF2 = 0;



  TR2 = 1; // start timer

  TL2 = 0x00; // seem only work when timer is on?
  TH2 = 0xFF;

  ET2 = 1;
}

void loop() {
  // put your main code here, to run repeatedly:

}

16M时钟下中断应该是125K的频率,一次8us。

但是如果我写一大堆P1_4=0,中断就会变慢。

Screen Shot 2024-10-07 at 2.45.53 AM.png如果我把多余的P1_4=0换成nop,就正常许多。

Screen Shot 2024-10-07 at 2.44.49 AM.png

请问这是我对定时器理解不对还是芯片Bug?

我用SDCC构建了一个工程,单文件,仍然可以重现问题。该代码大部分时候周期为8.5us,隔几个也会出现11us。

#include "ch554.h"

SBIT(P1_4,  0x90, 4);

void Timer2Interrupt(void) __interrupt (INT_NO_TMR2)  __naked
{
   
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    __asm__("push   acc");
    timer2IntrHandler();//debugwire related
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");
    __asm__("pop    acc");


    __asm__("mov  r0, SP");
    __asm__("dec  r0");
    __asm__("push   acc");
    __asm__("pop    acc");
    __asm__("push   acc");
    __asm__("pop    acc");

    __asm__("reti");

}

void timer2IntrHandler() {
  if (TF2) {
    TF2 = 0;

    P1_4 = 0;

    // P1_4 = 0;

    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");
    __asm__("nop\nnop\n");

    P1_4 = 1;
  }
}
void main(void){
    // set internal clock
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    CLOCK_CFG = CLOCK_CFG & ~MASK_SYS_CK_SEL | 0x05; // 16MHz
    SAFE_MOD = 0x00;

    for (int i=0;i<5000;i++){
        __asm__("nop");
    }

    //delayMicroseconds(5000); // needed to stablize internal RC

    EA = 1; // millis and delay needs interrupt

    T2MOD |= bTMR_CLK; // set bTMR_CLK
    TR2 = 0;

    RCLK = 0;
    TCLK = 0;           // clear RCLK,TCLK in T2CON
    C_T2 = 0;           // clear C_T2 in T2CON for using internal clk
    EXEN2 = 0;          // clear EXEN2 in T2CON to disable T2EX
    T2MOD |= (bT2_CLK); // set bT2_CLK, for fast clk.Using Fsys/1
    CP_RL2 = 0;         // clear CP_RL2 in T2CON for 16bit timer, reload mode
    RCAP2 = 65535 - 128 + 1;


    TF2 = 0;
    EXF2 = 0;

    TR2 = 1; // start timer

    TL2 = 0x00; // seem only work when timer is on?
    TH2 = 0xFF;

    ET2 = 1;

    while(1){
       
    }
   

}

Timer2Interrupt 里插入的汇编应该不会有任何影响。但是注释掉

__asm__("mov  r0, SP");
__asm__("dec  r0");

或者是对ACC的出入栈,就会让周期恢复正常。


附上代码,makefile和所有编译出来的文件。压缩包里的hex文件在Proteus里用AT89C52仿真,12M时钟下周期是稳定的128us。

icon_rar.gifch552_test_t2_sdcc.zip



实际代码测试,如果Timer2中断调用其他函数,那编译器默认会把所有R0~R7在中断中压栈,很容易触发此问题,但是把被调用函数改为inline,让编译器少压6~8个寄存器,问题就能得到极大改善。


yes saving all regs on irq entry is a problem with SDCC. Keil does a much better job on that. Have you tried to change the register bank?


@usbman I just elimated all function call in ISR in this case to avoid some register saving.

But it the additional Push/Pop should not affect T2 timer, and in test, it affects in an unstable way.


我又仔细看了看,问题有可能是中断执行时间超过了定时器周期,我这边再做一下测试。

usbman 在这个帖子/bbs/thread-124719-1.html里提到了 中断跳转的时间。加上变量保护,128时钟确实有可能会超。


测试结果,确实是我的中断执行时间太长了。我测试往里面加nop。一旦超过界限,中断就无法在T2溢出时执行。当T2多转一圈时会有一些空闲,也就是不稳定的原因。


1728494851885416.png

1728494851103098.png

#include "ch554.h"

SBIT(P1_4,  0x90, 4);
SBIT(P1_1,  0x90, 1);

void Timer2Interrupt(void) __interrupt (INT_NO_TMR2)  __naked
{
  //12 clk to enter interrupt
    P1_4 = 0;
    TF2 = 0;
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");  //10clk
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n"); __asm__("nop\nnop\n");
   
    __asm__("nop\nnop\n"); __asm__("nop\nnop\n");

    __asm__("nop\nnop\n");

    P1_4 = 1;
    __asm__("reti");
    //reti 4~5 clk
}

void main(void){
    // set internal clock
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xAA;
    CLOCK_CFG = CLOCK_CFG & ~MASK_SYS_CK_SEL | 0x05; // 16MHz
    SAFE_MOD = 0x00;

    for (int i=0;i<5000;i++){
        __asm__("nop");
    }

    //delayMicroseconds(5000); // needed to stablize internal RC

    EA = 1; // millis and delay needs interrupt

    T2MOD |= bTMR_CLK; // set bTMR_CLK
    TR2 = 0;

    RCLK = 0;
    TCLK = 0;           // clear RCLK,TCLK in T2CON
    C_T2 = 0;           // clear C_T2 in T2CON for using internal clk
    EXEN2 = 0;          // clear EXEN2 in T2CON to disable T2EX
    T2MOD |= (bT2_CLK); // set bT2_CLK, for fast clk.Using Fsys/1
    CP_RL2 = 0;         // clear CP_RL2 in T2CON for 16bit timer, reload mode
    RCAP2 = 65535 - 128 + 1;


    TF2 = 0;
    EXF2 = 0;

    TR2 = 1; // start timer

    TL2 = 0x00; // seem only work when timer is on?
    TH2 = 0xFF;

    ET2 = 1;

    while(1){
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
      P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;P1_1=0;P1_1=1;
    }
   

}



只有登录才能回复,可以选择微信账号登录