espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.75k stars 7.43k forks source link

just need little guidance related to timer and multitasking. #775

Closed Akskaypohankar closed 6 years ago

Akskaypohankar commented 7 years ago

----------------------------- Remove above -----------------------------

Hardware:

Board: ?ESP32 things sparkfun Core Installation/update date: 11/jul/2017 IDE name: Arduino IDE Flash Frequency: 80Mhz Upload Speed: 115200.

Description:

HI, this is what I need to do, I need a countdown timer running in the background along with the main loop. a max7219 with 4 digs 7 seg display is used to show the countdown of time left in seconds. some 4X3 keyboard related task needs to be performed while the timer is running in the background and the time should be displayed on the 7 seg display.

I used following code but it worked perfectly until I added keyboard related programmes. now timer starts counts dawn from 100 to 98 and then suddenly stops but the main loop of the keyboard is working what am I doing wrong?

Sketch:

#include <Keypad.h>
#include "LedControl.h"
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

volatile uint32_t isrCounter = 0;
int timelimit =100;
//volatile uint32_t lastIsrAt = 0;
boolean once=1;
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);// Increment the counter and set the time of ISR
  isrCounter++;
  timelimit--;
  update7seg();
//  lastIsrAt = millis();
  portEXIT_CRITICAL_ISR(&timerMux);
  // Give a semaphore that we can check in the loop
  xSemaphoreGiveFromISR(timerSemaphore, NULL);
  // It is safe to use digitalRead/Write here if you want to toggle an output
}

LedControl lc = LedControl(23, 18, 19, 1);

long secMillis = 0; // store last time for second add
char password[4]={'2', '5', '8','0'};
int currentLength = 0;
int i = 0;
char entered[4];
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {5, 15, 2, 0}; //connect to the row pinouts of the keypadbyte
byte colPins[COLS] = {4, 17, 16}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void startT()
{
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
}
void update7seg()
{
  if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) {
   // int isrCount = 0;
    portENTER_CRITICAL(&timerMux);
    //isrCount = isrCounter;
    Serial.print("onTimer no. ");
    Serial.print(timelimit);
    int TH = timelimit / 1000 % 10;
    int HU = timelimit / 100 % 10;
    int TE = timelimit / 10 % 10;
    int UN = timelimit % 10;
    lc.setDigit(0, 3, UN, false);
    lc.setDigit(0, 2, TE, false);
    lc.setDigit(0, 1, HU, false);
    lc.setDigit(0, 0, TH, false);
    Serial.print(" at ");
//    Serial.print(isrTime);
    Serial.println(" ms");
    if (timelimit == 0) {
      // If timer is still running
      if (timer) {
        // Stop and free timer
        timelimit = 0;
        timerEnd(timer);
        timer = NULL;
      }
    }
  }
}

void setup() {
  Serial.begin(115200);
  timerSemaphore = xSemaphoreCreateBinary();
  timer = timerBegin(0, 80, true);
  lc.shutdown(0, false);
  lc.setIntensity(0, 8);
  lc.clearDisplay(0);
}

void loop() {
  if (once)
  {startT();
  once=0;}
  Serial.print("Enter Code: ");
      while (currentLength < 4) {
        char key2 = keypad.getKey(); if (key2 != NO_KEY)
        { Serial.print(key2);
          entered[currentLength] = key2; currentLength++;
          delay(100); 
        }
      } 
      if (currentLength == 4) {
        if (entered[0] == password[0] && entered[1] == password[1] && entered[2] == password[2] && entered[3] == password[3])
        {
          Serial.println("  Correct Password  ");
          currentLength = 0; 
         Serial.print("Game 1 cleare   ");
         delay(1000) ;
        } 
        else
        {Serial.println("Wrong Password   ");
          Serial.print(entered[0]);
          Serial.print(entered[1]);
          Serial.print(entered[2]);
          Serial.println(entered[3]);
          currentLength = 0;
          delay(1000);
        }
      }
    }

### Debug Messages:

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
ets Jun  8 2016 00:22:57

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0010,len:4
load:0x3fff0014,len:572
load:0x40078000,len:0
load:0x40078000,len:9880
entry 0x400789d8
Enter Code: 
onTimer no. 98 at  ms **// timer will not update after this.//** 
2586Wrong Password   
2586
Enter Code: 5694Wrong Password   
5694
Enter Code: 2580  Correct Password  
Game 1 cleare   Enter Code: 

Enable Core debug level: Debug on tools menu of Arduino IDE, then put the serial output here

beegee-tokyo commented 7 years ago

Not sure but what I see in your code inside update7seg() is that you call portENTER_CRITICAL(&timerMux), but you never call portEXIT_CRITICAL(&timerMux). That might block future updates.

Additional info: Remark in portmacro.h:

Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vTaskEnterCritical, meaning
that either function can be called both from ISR as well as task context. This is not standard FreeRTOS 
behaviour; please keep this in mind if you need any compatibility with other FreeRTOS implementations.

You call portENTER_CRITICAL_ISR in the timer ISR routine and then portENTER_CRITICAL again in update7seg(). That is double call of the same according to portmacro.h

koskee commented 6 years ago

I'm by no means an expert, so others can feel free to correct me if I'm wrong, but a few things strike me as potential issues :

  1. entering critical section requires you to exit the critical section (as quickly as possible) - as mentioned above
  2. ISRs should be as short as possible - calling update7seg() from the ISR will likely take too much time. Since you are already using some RTOS features, I think that implementing update7seg() as a task with a higher priority than your loop might be a better approach. It would have to be written in an infinite loop, and use the already existing semaphore take to cause it to go into the blocked state and wait until the ISR gives the semaphore before it can continue, which would essentially synchronize the ISR with the update7seg() function, while still allowing the ISR to return as quickly as possible. Multiple tasks will also likely speed up the application because then both cores of the CPU would run in parallel, ie. You'd be doing 2 things at once.
everslick commented 6 years ago

This issue is closed, because it looks as if it is not a bug or problem with the ESP32 Arduino core or its support libraries. For general API usage questions or help on specific coding challenges, please visit the arduino-esp32 Gitter channel. If you feel this issue was closed in error, reopen it and comment, why you think this is a bug in the Arduino-Core.