Programming multicorecore for Raspberry-Pico

Hi,
I am using Raspberry-Pico with IDE-Arduino, using the double core of this microcontroleur.
My intent is to perform fast calculations in core0, and to print some of the results (Serial+LCD) at regular 'slow rate' in core1.
Programming is simple with IDE-Arduino, using :

  • for core0 : void setup() and void loop()
  • for core1 : void setup1() and void loop1()
    However, for some reasons, the LCD stops working after a few seconds.
    I suspect the link between core0 and core1 not so simple ... even if my intent is just to collect and print with core1 some values calculated with core0. Is there a subject around FIFO ?

Show your sketch and show or tell what is connected.

Are you sure that Serial + LCD slows down your calculations ? I doubt that.

There should only be a single task that uses the LCD and there should be queues from other tasks to that single "LCD" task or use a mutex for the display.
If your LCD is on the I2C bus and there are other devices, such as sensors, then you should avoid that multiple tasks can use the I2C bus at the same time.

Have you programmed before in a preemptive multitasking system ?

I think that with rtos, there is not much difference between multiple tasks on a single core or with using more cores.

I assume that you have a 3.3V LCD display.

[ADDED] I see that we asked already about the 5V/3.3V display in your previous topic.

thank you Koepel for your feedback.
I join hereafter my sketch.
The microcontroleur is installed on a machine I want to control with two tensions (the 2 outputs of the microcontroleur). The inputs are the following : a force sensor (HX711@80hz), data from two rotary encoders, three Interrupts and one analog tension.
In parallel I need to visualize some of the data, in order to drive properly the machine. This is the aim of the LCD.
In term of speed, all the input data need to be processed in the range of a few milliseconds only. However, the HX711 takes roughly 11ms (80Hz) to run. This is the maximum time I can admit for one loop. Therefore, I cannot integrate in that loop the LCD (2004), the function of which lasting roughly 80ms.
This is the reason why I organisez my scketch like that.
However, I am clearly not an expert in such technology, and my knowledges are really limited => I appreciate your help !
thank you

//                                       for raspberryPi-Pico
//                               basé sur IP_0001,0002,0005,0010 + 0013
//                 info pgm IP_0016 = 0015 - SD               + 0014 avec multicore

#include "header.h"
#include "variables.h"

void setup() {
  Serial.begin(115200);
  scale.begin(HX_DT, HX_SCK);                   // mesure capteur HX711
  lcd.begin();                                  // préparation écriture sur LCD
  lcd.backlight();
  pinMode(Pin_BP1, INPUT_PULLUP);
  pinMode(Pin_BP2, INPUT_PULLUP);
  pinMode(Pin_encoder1_CLK, INPUT);
  pinMode(Pin_encoder1_DT, INPUT);
  pinMode(Pin_encoder2_CLK, INPUT);
  pinMode(Pin_encoder2_DT, INPUT);
  pinMode(Pin_Frein, INPUT);
  pinMode(Pin_RotGene, INPUT);
  pinMode(Pin_RotRoue, INPUT);
  pinMode(Pin_RotPedal, INPUT);
  pinMode(Pin_UGene, OUTPUT);
  pinMode(Pin_UMotor, OUTPUT);
  for (i = 0; i < 100; i++)  {  // init des tables pour le calcul de la moyenne glissante
    TabFrein[i] = 3200;
    TabGene[i] = 2000;
    TabRoue[i] = 2000;
  }
  EtatPedal = digitalRead(Pin_RotPedal);
  attachInterrupt(Pin_RotGene, RotationGene, FALLING);
  attachInterrupt(Pin_RotRoue, RotationRoue, FALLING);
  attachInterrupt(Pin_RotPedal, RotationPedal, FALLING);
}

void RotationGene() {                       // repérage de l'instant StepGene
  StepGene = millis();
  chgmtGene = true;
}
void RotationRoue() {                       // repérage de l'instant StepRoue
  StepRoue = millis();
  chgmtRoue = true;
}
void RotationPedal() {                      // repérage de l'instant Pedal
  indPedal = 0;
  chgmtPedal = true;
}

void loop() {

  tloop = micros();
  Frein = analogRead(Pin_Frein);                // FREIN [2200 - 3260]
  effortPedal = scale.read();                   // note: tps d'exécution de la commande ~11ms !!!
  effortPedal = effortPedal / 1000 + 250;

  // CALCUL DE DureeGene, MoyGene et vitGene
  if (chgmtGene == true) {                      //si interrupt a mis à jour StepGene depuis la derniere boucle:
    chgmtGene = false;
    DureeGene = min(2000, StepGene - prevGene);
    prevGene = StepGene;
    // calcul MoyGene glissante
    oldvalue = TabGene[indGene];
    TabGene[indGene] = DureeGene;
    SomGene = SomGene - oldvalue + DureeGene;
    MoyGene = SomGene / 10;
    indGene++;
    indGene = indGene % 10;
    indPedal++;                 // incrément indPedal de 0 à 12 (0 déterminé par l'interrupt Pedal)
    indPedal = indPedal % 12;
  }
  if ((millis() - prevGene) > 2000)  {          // Si pas d'impulsion Gene depuis plus de 2000ms, alors DG et MG à 2000
    DureeGene = 2000;
    // calcul MoyGene glissante
    oldvalue = TabGene[indGene];
    TabGene[indGene] = DureeGene;
    SomGene = SomGene - oldvalue + DureeGene;
    MoyGene = SomGene / 10;
    indGene++;
    indGene = indGene % 10;
  }
  vitGene = 38.7 / MoyGene;                     // vitesse de rot géné en tr/sec

  // CALCUL DureeRoue, MoyRoue et vitRoue
  if (chgmtRoue == true) {                      //si interrupt a mis à jour StepRoue depuis la derniere boucle:
    chgmtRoue = false;
    DureeRoue = min(2000, StepRoue - prevRoue);
    prevRoue = StepRoue;
    // calcul MoyRoue glissante
    oldvalue = TabRoue[indRoue];
    TabRoue[indRoue] = DureeRoue;
    SomRoue = SomRoue - oldvalue + DureeRoue;
    MoyRoue = SomRoue / 6;
    indRoue++;
    indRoue = indRoue % 6;
  }
  if ((millis() - prevRoue) > 2000)  {          // Si pas d'impulsion Roue depuis plus de 2000ms, alors DR et MR à 2000
    DureeRoue = 2000;
    // calcul MoyRoue glissante
    oldvalue = TabRoue[indRoue];
    TabRoue[indRoue] = DureeRoue;
    SomRoue = SomRoue - oldvalue + DureeRoue;
    MoyRoue = SomRoue / 6;
    indRoue++;
    indRoue = indRoue % 6;
  }
  vitRoue = 957.0 / MoyRoue;                    // vitesse d"avancement vélo (km/h)
  if (vitRoue < 0.5) vitRoue = 0;

  // CALCUL DE Assit (Encoder1)
  BPstate1 = digitalRead(Pin_BP1);
  prevstate1 = state1;
  state1 = digitalRead(Pin_encoder1_CLK);
  if ((state1 == !prevstate1) && ((micros() - tEncod1) > DTEncod))  { // premiere bascule de 'pin_encoder1_CLK' depuis DTEncod
    tEncod1 = micros();
    if (state1 == LOW)  {
      if (digitalRead(Pin_encoder1_DT) == HIGH)  {
        Assist += 0.5;
        Assist = min(Assist, encodSup);
      }
      else  {
        Assist -= 0.5;
        Assist = max(Assist, encodInf);
      }
    }
  }

  // CALCUL DE Dev (Encoder2)
  BPstate2 = digitalRead(Pin_BP2);
  prevstate2 = state2;
  state2 = digitalRead(Pin_encoder2_CLK);
  if ((state2 == !prevstate2) && ((micros() - tEncod2) > DTEncod))  { // premiere bascule de 'pin_encoder2_CLK' depuis DTEncod
    tEncod2 = micros();
    if (state2 == LOW)  {
      if (digitalRead(Pin_encoder2_DT) == HIGH)  {
        Dev += 0.5;
        Dev = min(Dev, encodSup);
      }
      else  {
        Dev -= 0.5;
        Dev = max(Dev, encodInf);
      }
    }
  }

  if (Frein < 3050)  {      // ************************ F R E I N   A C T I F *********************
    UMotor = map(Frein, 3050, 2350, 77.0, 0.0);          // 0,95V à 0V
    UMotor = constrain(UMotor, 0, 77);                  // 0,95V à 0V
    UGene = 77.0;                           // PAS SUR !!!!!!!!!!!!!!!!!!!!
  }

  else {                    // ********************* F R E I N   P A S   A C T I F *****************

    // CALCUL DE UMotor
    if (vitGene < 1. / 16.)
      UMotor = 77.0;
    else  {
      CoefCinem = 5 * (5 * vitGene * Dev - vitRoue);                      // COEFS A REGLER
      UMotorprev = UMotor;
      UMotor = 57 + CoefCinem;                                            // COEFS A REGLER
      varminMotor = UMotorprev - 0.1 * (micros() - TpsUmot) / 1000.0;     // COEFS A REGLER
      varmaxMotor = UMotorprev + 0.1 * (micros() - TpsUmot) / 1000.0;     // COEFS A REGLER
      UMotor = constrain(UMotor, varminMotor, varmaxMotor);
      UMotor = constrain(UMotor, 77, 255);
    }

    // CALCUL DE UGene
    vitGeneTheo = 0.2 / Dev * vitRoue;
    ForceGene = 5.0 * (vitGene - vitGeneTheo) * (10 - Assist);            // COEFS A REGLER
    ForceGene = 77.0 - ForceGene;
    // limitation de variation de ForceGene avec le tps
    //varForceGene = 0.1 * (micros() - TpsUGene) / 1000.0;                // COEFS A REGLER
    //ForceGene = constrain(ForceGene, ForceGene - varForceGene, ForceGene + varForceGene);
    //EffetPedal = (1 + cos(6.28 * indPedal / 12)) / 2;   // variation en cos de la force de pédalage [0-1]
    //EffetPedal = EffetPedal * 0.2 * (77.0 - ForceGene);                 // COEFS A REGLER
    //ForceGene = ForceGene - EffetPedal;                 // prise en compte de l'EffetPedal
    UGene = constrain(ForceGene, 0, 77);

  }   // frein non actif

  analogWrite(Pin_UMotor, UMotor);          // écriture de la tension moteur sur le port de sortie
  analogWrite(Pin_UGene, UGene);            // écriture de la tension géné sur le port de sortie
  batterie = analogRead(Pin_Batterie) / 94.8;
  DTboucle = micros() - tloop;

}  // FIN LOOP

void setup1() {}

void loop1() {                         // FONCTION POUR IMPRESSION LCD ET PORT SERIE AVEC CORE1

  char buffer[120];
  //sprintf( buffer, "Millis,%6i,EffPed,%6i,Ass,%3.1f,Dev,%3.1f,DG,%3i,MoyG,%3i,MoyR,%3i,CoefC,%5.2f,FG,%3i,UM,%3i,UG,%3i", millis(), effortPedal, Assist, Dev, DureeGene, MoyGene, MoyRoue, CoefCinem, ForceGene, UMotor, UGene);
  sprintf( buffer, "DT,%6i,DG,%4i,StepG,%4i,prevG,%4i,oldV,%4i,SomG,%4i,MoyG,%4i", DTboucle, DureeGene, StepGene, prevGene, oldvalue, SomGene, MoyGene);
  Serial.println( buffer);

  chgmtGene = false;
  DureeGene = min(2000, StepGene - prevGene);
  prevGene = StepGene;
  // calcul MoyGene glissante
  oldvalue = TabGene[indGene];
  TabGene[indGene] = DureeGene;
  SomGene = SomGene - oldvalue + DureeGene;
  MoyGene = SomGene / 10;
  indGene = indGene++ % 10;
  indPedal = indPedal++ % 12;

  if (millis() - t2 > 250) {              // AFFICHAGE SUR LCD TOUTES LES 250MS
    lcd.setCursor(0, 0);
    char buffer[20];
    sprintf( buffer, "Assis= %3.1f Dev= %3.1f ", Assist, Dev);
    lcd.print( buffer);
    lcd.setCursor(0, 2);
    sprintf( buffer, " %3.1f Km/h   %3.1f t/s", vitRoue, vitGene);
    lcd.print( buffer);
    lcd.setCursor(0, 3);
    sprintf( buffer, " Bat=%4.1fV  F=%5i", batterie, effortPedal);
    lcd.print( buffer);
    t2 = millis();
  }
}

as far as the LCD with 3.3V/5V is concerned:
the supply tension VCC of my LCD is indeed 5V.
However, both SDA and SCL are 3.3V input => Works pretty well ...

Now I understand, you don't use the Mbed/rtos, but just the extra setup1() and loop1().
Both cores share the memory, the FIFO seems to be a buffer that can be used with special functions. As far as I can tell, you don't use those functions. You just use variables in memory (that both cores can access).
The compiler options might keep data in registers and not always use the memory location.
Programming for a preemptive multitasking system is not the same as a normal Arduino sketch.

If your rename loop1(), and call that from loop(), then at least you can test if the code runs on a single core.

Have your removed the I2C pullup resistors from the display ? The Raspberry Pi Pico does not have 5V tolerant pins.

Both cores with both setup() functions start at the same time, and after the setup() they run the loop(). That means that a "Serial" or "lcd" function in the second core can be run before the "Serial.begin()" and "lcd.begin()" has finished in the first core.

I did not perform that exercice in my troubleshooting, thank you Koepel for the idea. As a result, using one single core (even if the data are disturbed by the new inexpected time for one loop) does not finally solve the concern. Frustrating when compared to any examples of sketches for LCD !
However, I realized that the trouble comes from the link between the Serial and the LCD library (LCD_I2C used, but also other libraries were used with no more success ...) : removing the Serial port from the sketch, and keeping the LCD working in the second Core works perfectly !
As a final configuration of sketch, I will not use the Serial port. Therefore, I can now progress in my project.
However, it can be interesting to understand the reason of that Serial/LCD concern ?
Any idea ?
thank you.

Sorry, I have no clue, not at all :grimacing:
The "Serial" is the serial-over-usb I assume ? Maybe there is a bug somewhere that creates a conflict with the I2C bus. Maybe you have a combination of things that no one else has tried yet. It could also be your sketch.
Your sketch does not tell much, there is even a website for that: https://snippets-r-us.com/.
Some on this forum say: "The problem is in the part that you are not showing".

You could try another serial port with a usb-to-serial module to the computer. Or a software I2C library that only uses digitalWrite() and digitalRead(). There are many things that you can test, but that takes time and a bug in a library is hard to find.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.