標籤: ESP8266

ESP8266 跟時間賽跑 中斷篇

No Comments

繼前篇文章的跟時間賽跑,聚焦在 gpio 本身的響應時間,及 cycle count。本文要再初賽一次才能晉級,將聚焦在中斷。
本文將摸索 gpio 中斷及 timer 中斷。中斷的種類就不展開了也不洗殘廢澡了更省了貼圖了,就直接講結果。因此建議先看過前篇的初賽再看本篇多少有點啣接性。

參考資料

GPIO 中斷

#include <ESP8266WiFi.h>

// @160MHz
// use pin4 for delay, pin12 for signaling(output) xor gpio interrupt(input), pin13 for indicating int occurred.
// stop software/hardware wdt at these tests. note that wdt-reset after 71 seconds but enough time for probing.
// 2 of wemos d1 mini are parallel connected. one to test the other.
// macro switches: 1. tester or testee. 2. arduino or api in testee. 3. printout or gpio13 indication in testee.
//    4. tester gpio latency/behaviors.
// note: if multiple gpios are interrupts, use GPIO_REG_READ(GPIO_STATUS_ADDRESS) to check respective pin triggered or not.


IRAM_ATTR volatile unsigned &PIN_OUT_SET = *((volatile unsigned*)0x60000304);
IRAM_ATTR volatile unsigned &PIN_OUT_CLEAR = *((volatile unsigned*)0x60000308);
IRAM_ATTR volatile unsigned &HardwareWDT = *((volatile unsigned*)0x60000900);

#define PIN4                0x10
#define PIN12               0x1000
#define PIN13               0x2000
#define DELAY_75ns          (PIN_OUT_SET=PIN4)
#define digitalWrite12HIGH  (PIN_OUT_SET=PIN12)
#define digitalWrite12LOW   (PIN_OUT_CLEAR=PIN12)
#define digitalWrite13HIGH  (PIN_OUT_SET=PIN13)
#define digitalWrite13LOW   (PIN_OUT_CLEAR=PIN13)
#define disableHardwareWDT  (HardwareWDT&=~1)
#define enableHardwareWDT   (HardwareWDT|=1)

IRAM_ATTR static uint32_t _getCycleCount() __attribute__((always_inline));
IRAM_ATTR static inline uint32_t _getCycleCount(){
    uint32_t ccount;
    __asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
    return ccount;
}


#if 0 // 1 tester or 0 testee, tester only by pin12 signaling.

    #define GPIO_LATENCY 0 // in order to probe the latency between the signal source and the int triggered.


void setup() {
    pinMode(1, INPUT); // disable tx
    pinMode(3, INPUT); // disable rx

    pinMode(4, OUTPUT);
    pinMode(12, OUTPUT);
    pinMode(13, INPUT);
}

IRAM_ATTR void loop() {
    static unsigned t1=1;
    if (t1){
        WiFi.mode(WIFI_OFF);
        WiFi.forceSleepBegin(-1);
        t1=0;
        system_soft_wdt_feed();
        system_soft_wdt_stop();
        disableHardwareWDT;

        delay(7500);

    }

    while (1){
        #if GPIO_LATENCY
            delayMicroseconds(random(300)+300); // from 300us to 600us
            digitalWrite12LOW;

            static unsigned k=0;
            if (++k>20) k=0;
            for (int i=0; i<k; i++) DELAY_75ns; // from 75ns to 1.5us
            delayMicroseconds(300); // at least 300us
            digitalWrite12HIGH;
        #else // GPIO_LATENCY
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 900
    
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 1350


            // ----------- this block of code for expanding for fit arduino-style testee
            #if 0
            DELAY_75ns; // 1650
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 1650, 1650+1425=3075
            #endif
            // ----------- this block of code for expanding for fit arduino-style testee


            //digitalWrite(12, LOW);
            digitalWrite12LOW; // 1425ns
    
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 900
    
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 1350


            // ----------- this block of code for expanding for fit arduino-style testee
            #if 0
            DELAY_75ns; // 1650
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns;
            DELAY_75ns; // 1650, 1650+1425=3075
            #endif
            // ----------- this block of code for expanding for fit arduino-style testee


            //digitalWrite(12, HIGH);
            digitalWrite12HIGH; // 1425ns
        #endif // GPIO_LATENCY
    }
}


#else // testee, pin12 for gpio interrupt, pin13 shows the responses.


IRAM_ATTR volatile unsigned old_cnt=_getCycleCount(), cur_cnt=0, min_cnt=-1, max_cnt=0;

    #define PRINTOUT            0
    #define ARDUINO_STYLE       0
    #define API_STYLE           !ARDUINO_STYLE


#if ARDUINO_STYLE

IRAM_ATTR void arduino_isr(){
    #if !PRINTOUT // gpio13 indication.
        IRAM_ATTR volatile static unsigned u=0;
        digitalWrite(13, u^=1);
    #else // print out
        cur_cnt=_getCycleCount();
        unsigned tmp=cur_cnt-old_cnt;
        if (tmp<min_cnt) min_cnt=tmp;
        else if (tmp>max_cnt) max_cnt=tmp;
        old_cnt=cur_cnt;
    #endif // print or not
}

#else // API_STYLE

IRAM_ATTR void api_isr(){ // assume only 1 gpio int set, the gpio12
    #if !PRINTOUT // gpio13 indication.
        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, GPIO_REG_READ(GPIO_STATUS_ADDRESS)); // clear all gpios int flags
    
        IRAM_ATTR volatile static unsigned u=0;
        if (u^=1) digitalWrite13HIGH;
        else digitalWrite13LOW;
    #else // print out
        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, GPIO_REG_READ(GPIO_STATUS_ADDRESS)); // clear all gpios int flags
        cur_cnt=_getCycleCount();
        unsigned tmp=cur_cnt-old_cnt;
        if (tmp<min_cnt) min_cnt=tmp;
        else if (tmp>max_cnt) max_cnt=tmp;
        old_cnt=cur_cnt;
    #endif // print or not
}

#endif // styles


void setup() {
    pinMode(4, INPUT);
    pinMode(13, OUTPUT);
}

IRAM_ATTR void loop() {
    static unsigned t1=1;
    if (t1){
        WiFi.mode(WIFI_OFF);
        WiFi.forceSleepBegin(-1);
        t1=0;
        delay(3000);
        system_soft_wdt_feed();
        system_soft_wdt_stop();
        disableHardwareWDT;

        #if ARDUINO_STYLE // arduino style
                attachInterrupt(digitalPinToInterrupt(12), arduino_isr, CHANGE); // both edges
        #else // API_STYLE
                PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); // set as gpio
                GPIO_DIS_OUTPUT(GPIO_ID_PIN(12)); // set gpio12 as input
                ETS_GPIO_INTR_DISABLE(); // disable all gpios interrupts
                ETS_GPIO_INTR_ATTACH(api_isr, NULL); // set gpio12 isr
                gpio_pin_intr_state_set(GPIO_ID_PIN(12), GPIO_PIN_INTR_ANYEDGE); // both edges
                ETS_GPIO_INTR_ENABLE(); // enable gpio ints
        #endif // styles
    }

    #if PRINTOUT
        IRAM_ATTR static unsigned t=1;
        while (t){
            if (max_cnt && ++t>=18){ // about 100us if no any ints
                ETS_GPIO_INTR_DISABLE();
                Serial.begin(115200);
                delay(6000);
                Serial.printf("%u,%u\r\n", min_cnt, max_cnt);
                delay(300);
                Serial.end();
                //old_cnt=_getCycleCount(), cur_cnt=0, min_cnt=-1, max_cnt=0;
                //delay(200);
                t=0;
                //ETS_GPIO_INTR_ENABLE();
            }
        }
    #endif
}


#endif // 1 tester or 0 testee.
  • 上面這份程式碼大概就可測出想要的數據了,透過 comment/uncomment 一些註解及開關一些 switches,建議全盤了解這一點點的程式碼便可知如何測/測了哪些東西,及了解中斷的用法。
  • 其使用了兩張 wemos d1 mini,the same pin to the same pin 對接,由其中一張發訊號讓另一張觸發中斷,並在 isr 中由另一支 pin toggle 電平來反映 timing。有可能無法啟動,則反覆按 reset button,若兩張的 pin12 & pin13 初始態都是 low 則表示成功啟動。
  • 有些狀況並未測試到,例如 level trigger,及多支 gpios 中斷,及同時讓多支 gpios 中斷等。這些也都是 timing critical 而該測,但或許直接由現有的結論推論即可因為,當前測果並不是很令筆者滿意 XD。
  • 函式 cycle-count 仍會飄,所以關於 cycle count,未來可能還需再好好地正確地測出結果。
  • 因 timing 的量測是使用解析度不高的 LA/80MHz,所以此本身就是一個不確定因素;但該指出的是,CPU 結構下的中斷機制,依筆者不負責任的認知(臆測),可能是每一個指令週期(數個不等的時脈週期)之後會去檢查中斷,故須多花費一個時脈週期,斥或,每個指令週期中就包含了中斷檢查,即與管線同等地位,斥或使用一個專用的指令週期去檢查中斷。該強調的是 CPU 的中斷仍等同於取樣機制,故來源訊號的頻率大小,不絕對與中斷反應時間成反比更何況是 LA 的量測結果。但所描述的數據仍是具參考價值的。
  • 該提出的是,筆者仍納悶為何中斷會花這麼多的時脈週期,約 (900ns-150ns-75ns)/6.25ns=108 clock cycles。
  • 結論:
  • 使用 arduino 中斷,訊號來源是 75ns,則中斷響應時間是 3us。其中,首次的延遲時間是 2.85us,故預估淨響應時間是 (2850-150-75=2625ns),當 isr 中僅用於計算時便可參考。訊號源改成 900ns,有同樣結果。
  • 使用 arduino 中斷,唯有將訊號來源調高成(至少) 3075ns,則中斷響應時間才會跟來源同步,即 71 秒內來源與觸發總數相同。
  • 使用 api 中斷,訊號來源是 75ns,則中斷響應時間是 900ns。其中,首次的延遲時間是 1.15us,故預估淨響應時間是 (1150-150-75=925ns),當 isr 中僅用於計算時便可參考。訊號源改成 225ns,有同樣結果。但當訊號源改成 300ns,雖有同樣結果但卻會定時穿插誤失,即有規律地某次響應遠多於 900ns。
  • 使用 api 中斷,唯有將訊號來源調高成(至少) 1425ns,則中斷響應時間才會跟來源同步,即 1425ns。(註:響應者,少了一對 high/low,原因未知,要不就是再加 75ns)

Categories: Arduino

Tags: ,

PHP Code Snippets Powered By : XYZScripts.com