Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Elettronica - Strani comportamenti di FreeRTOS su avr
Forum - Elettronica - Strani comportamenti di FreeRTOS su avr

Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 14:18
Giovedì, 29/06/2017
Buongiorno a tutti, mi sto cimentando nella scritturar di un termostato con GSM. L'hardware è abbastanza semplice: 1 arduino, 1 shield gsm, qualche termometro e il necessario per gestire un buon relè.

Visto che volevo sperimentare con FreeRTOS ho deciso di usare il porting per arduino. Una di scelta di cui non mi pento, anche se mi ha dato diversi grattacapi: in primis succedevano veri e propri disastri con l'allocazione dinamica della memoria! Ho quindi dovuto disabilitarla, abilitare l'uso di memoria preallocata per tutte le strutture del SO ed evitare in tutti i modi di allocare nuova memoria, visto che il risultato di tale allocazione è un terno al lotto........

Trovate la versione modificata da me di FreeRTOS qui: https://github.com/NeroReflex/ArduinoRTOS prestate particolarmente attenzione a questo file scritto da me: https://github.com/NeroReflex/ArduinoRTOS/blob/master/src/FreeRTOS_Utils.h che è una raccolta di macro.

Ho poi importato FreeRTOS nel mio progetto arduino, che si compone. per il momento, di 2 task:

Il primo è quello che legge la temperatura attraverso un termometro e decide cosa farne:

Codice sorgente - presumibilmente C++

  1. #define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 )
  2.  
  3. #ifdef SERIAL_DBG
  4.     static struct BoilerRegulatorDebugInfo debugInfo;
  5.  
  6.     aImportStaticQueue(temperatureDebug);
  7. #endif
  8.  
  9. aImportStaticSemaphore(temperaturePoll);
  10.  
  11. // ---------------------------------------------------
  12. // -------------- Static variables -------------------
  13. // ---------------------------------------------------
  14. static Logic logic;                       // boiler management logic
  15. static float oneWire_Temp = 127.5f;       // last read temperature
  16. static bool boilerOn = false;             // the boiler status
  17. TickType_t xLastWakeTime_BoilerRegulator; // used for the final delay
  18. static uint8_t deviceCount = 0;           // the number of ds18x20 devices
  19. static bool validMeasure = false;         // is the last measure valid?
  20. extern struct StatusPoll lastMeasure;
  21.  
  22. // ---------------------------------------------------
  23. // ------------- Most important Task -----------------
  24. // ---------------------------------------------------
  25. void TaskBoilerRegulator(void *pvParameters __attribute__((unused))) {
  26. #ifdef SERIAL_DBG
  27.     logic.setThresholdTemperature(DBG_TEMPERATURE);
  28.     logic.setHysteresis(DBG_HYSTERESIS);
  29. #endif
  30.  
  31.     // Set the heater pin
  32.     pinMode(BOILER_DIGITAL_PIN, OUTPUT);
  33.     digitalWrite(BOILER_DIGITAL_PIN, LOW);
  34.  
  35.     // setup thermometers
  36.     OneWire ow(ONEWIRE_DIGITAL_PIN);
  37.     DallasTemperature sensors(&ow);
  38.  
  39.     // This one is used for the good delay
  40.     xLastWakeTime_BoilerRegulator = xTaskGetTickCount();
  41.  
  42.     for (;;) {
  43.         // A few seconds delay to let other tasks run
  44.         vTaskDelayUntil(&xLastWakeTime_BoilerRegulator, pdMSTOTICKS( 5000 ));
  45.  
  46.         sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement
  47.         sensors.setResolution(DS18X20_PRECISION_BITS);
  48.  
  49.         // Get the number of devices
  50.         deviceCount = sensors.getDeviceCount();
  51.  
  52.         // Read the temperature from the OneWire bus (if any device is present)
  53.         if (deviceCount) {
  54.             // Send the command to get temperatures
  55.             sensors.requestTemperatures();
  56.  
  57.             float temp = 127.5f;
  58.  
  59.             // get the lowest temperature from the group of ds18b20
  60.             for (int i = 0; i < deviceCount; i++) {
  61.               // read the temperature
  62.               float currentTemp = sensors.getTempCByIndex(i);
  63.  
  64.               // skip useless values!
  65.               if (currentTemp < -60.0f) continue;
  66.               else if (currentTemp > +50.0f) continue;
  67.  
  68.               // try updating the best temperature with the smallest one
  69.               if (currentTemp < temp) {
  70.                 temp = currentTemp;
  71.                 validMeasure = true;
  72.               }
  73.             }
  74.  
  75.             // Update the current temperature
  76.             if (validMeasure) oneWire_Temp = temp;
  77.             validMeasure = false;
  78.  
  79.             // Update logic status
  80.             logic.notifyTemperature(oneWire_Temp);
  81.  
  82.             // Edit pin value only if needed
  83.             if ((bool)logic.heat() != boilerOn) {
  84.                 // Update the logic
  85.                 boilerOn = (logic.heat() == 0x01) ? true : false;
  86.  
  87.                 // New mode for the boiler
  88.                 digitalWrite(BOILER_DIGITAL_PIN, (boilerOn != false) ? HIGH : LOW);
  89.             }
  90.         }
  91.  
  92.         // update the temperature for the GSM Shield task
  93.         if (xSemaphoreTake(aGetStaticSemaphoreName(temperaturePoll), 3)) {
  94.           lastMeasure.boilerOn = boilerOn;
  95.           lastMeasure.temperatureValue = oneWire_Temp;
  96.         }
  97.  
  98. #ifdef SERIAL_DBG
  99.         // Build the debug info structure
  100.         debugInfo.count = (uint8_t)deviceCount;
  101.         debugInfo.value = oneWire_Temp;
  102.         debugInfo.enabled = boilerOn;
  103.  
  104.         // Attempt to send debug informations to the debug task
  105.         xQueueSend(aGetStaticQueueName(temperatureDebug), (const void *)&debugInfo, 8);
  106. #endif
  107.     }
  108. }



E il thread di debug, che riceve le informazioni di debug dal task principale e le manda via seriale al pc:
Codice sorgente - presumibilmente C++

  1. #define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 )
  2.  
  3. #ifdef SERIAL_DBG
  4.  
  5. aImportStaticQueue(temperatureDebug)
  6.  
  7. void TaskDebug(void *pvParameters __attribute__((unused))) {
  8.     // Setup the hardware serial port
  9.     Serial.begin(115200);
  10.  
  11.     // Wait for the serial port to be ready
  12.     while (!Serial);
  13.  
  14.     // Hello, my dear :)
  15.     Serial.println("========================================");
  16.     Serial.println("Starting Debug (over serial 115200 baud)");
  17.     Serial.println("========================================");
  18.  
  19.     for (;;) {
  20.         // Get boiler regulator debug info
  21.         struct BoilerRegulatorDebugInfo boilerRegulatorInfo;
  22.         if(xQueueReceive(aGetStaticQueueName(temperatureDebug), (void*)&boilerRegulatorInfo, 3))
  23.         {
  24.             Serial.println();
  25.             Serial.println("========================================");
  26.  
  27.             if (boilerRegulatorInfo.count > 0) {
  28.                 Serial.print("OneWire thermometers: ");
  29.                 Serial.println(boilerRegulatorInfo.count);
  30.                 Serial.print("OneWire temperature: ");
  31.                 Serial.print(boilerRegulatorInfo.value);
  32.                 Serial.println("° C");
  33.             } else {
  34.                 Serial.println("No thermometers");
  35.             }
  36.  
  37.             if (boilerRegulatorInfo.enabled) Serial.println("Boiler ON");
  38.             else Serial.println("Boiler OFF");
  39.  
  40.             Serial.println("========================================");
  41.         }
  42.  
  43.  
  44.     }
  45. }
  46.  
  47. #endif



Il task di debug ha priorità 1, il task principale 3 (la massima) significa che il task d debug può eseguirso SOLO mentre il task principale è in attesa di essere risvegliato (attraverso la syscall vTaskDelayUntil()).

Ora... Tutto ciò, funziona in maniera impeccabile. Non riscontro alcun problema.
Per capire il motivo della mia domandaa devo dirvi che il task che gestisce il modulo gsm è un task a se, che ho già preparato (almeno per quanto riguarda la struttura):
Codice sorgente - presumibilmente C++

  1. #define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 )
  2.  
  3. aImportStaticSemaphore(temperaturePoll);
  4.  
  5. // ---------------------------------------------------
  6. // -------------- Static variables -------------------
  7. // ---------------------------------------------------
  8. struct StatusPoll lastMeasure;
  9. TickType_t xLastWakeTime_MobileManagement;
  10.  
  11. void TaskMobileManagement(void *pvParameters) {
  12.     //SIM900 sim900;
  13.  
  14.     xLastWakeTime_MobileManagement = xTaskGetTickCount();
  15.  
  16.     for (;;) {
  17.         // A few seconds delay to let other tasks run
  18.         vTaskDelayUntil(&xLastWakeTime_MobileManagement, pdMSTOTICKS( 2000 ));
  19.  
  20.         /*SIM900DateTime currentTime;
  21.  
  22.         if (sim900.getTime(currentTime)) {
  23.  
  24.         }*/
  25.  
  26.     }
  27. }



In questo codice ho disabilitato tutti i riferimenti alla classe SIM900, quindi non sarà posizionato sullo stack alcun elemento, mantenendo molto piccole le risorse utilizzate dal singolo task (per fare delle prove).

Il main è così, notate il commento posto proprio per evitare l'esecuzione dell'ultimo task:
Codice sorgente - presumibilmente Elettronica

  1. // --------------------------------------------------
  2. // --------------------- Queues ---------------------
  3. // --------------------------------------------------
  4. aDefineStaticQueue(temperatureDebug, sizeof(struct BoilerRegulatorDebugInfo), 1);
  5.  
  6. // --------------------------------------------------
  7. // ------------------- Semaphores -------------------
  8. // --------------------------------------------------
  9. aDefineStaticSemaphore(temperaturePoll);
  10.  
  11. // --------------------------------------------------
  12. // --------------------- Tasks ----------------------
  13. // --------------------------------------------------
  14. aDefineStaticTask(MobileCommunication, (configMINIMAL_STACK_SIZE + 25));
  15. aDefineStaticTask(BoilerRegulator, (configMINIMAL_STACK_SIZE + 125));
  16. #ifdef SERIAL_DBG
  17.   aDefineStaticTask(Debug, (configMINIMAL_STACK_SIZE + 25));
  18. #endif
  19.  
  20. // --------------------------------------------------
  21. // -------------------- FreeRTOS --------------------
  22. // --------------------------------------------------
  23. void setup() {
  24.     // Initialize Semaphores
  25.     aInitStaticMutexSemaphore(temperaturePoll);
  26.  
  27.     // Initialize Queue
  28.     aInitStaticQueue(temperatureDebug, sizeof(struct BoilerRegulatorDebugInfo), 1);
  29.  
  30.     // Prepare tasks
  31.     aCreateTask(BoilerRegulator, TaskBoilerRegulator, NULL, (configMAX_PRIORITIES - 1));
  32.     //aCreateTask(MobileCommunication, TaskBoilerRegulator, NULL, (configMAX_PRIORITIES - 2));
  33. #ifdef SERIAL_DBG
  34.     aCreateTask(Debug, TaskDebug, NULL, (configMAX_PRIORITIES - 3));
  35. #endif
  36.  
  37.     // Start the real time scheduler.
  38.     vTaskStartScheduler();
  39.  
  40.     // Probably we've failed trying to initialise heap for the scheduler. Let someone know.
  41.     vApplicationMallocFailedHook();
  42. }



Fin qui tutto bene. Una analisi dello schema di esecuzione rivela che:
vTaskStartScheduler() fa eseguire il processo principale perchè ha priorità maggiore, e nessun altro processo potrà eseguirsi finchè questo non si metterà in pausa. Quindi inizializza tutto ciò di cui ha bisogno al suo funzionamento e si mette in pausa appena entrato nel for.

Viene eseguito il processo secondario (di debug) che inizializza la seriale, stampa che il debug su schermo è iniziato e poco altro, visto che la coda è ancora vuota!

Viene risvegliato il processo principale, esegue una misura e la inserisce nella coda delle misure di debug. Il processo non può essere interrotto perché ha la priorità maggiore e nessun altro processo ha la stessa priorità, quindi la prima interruzione la farà al prossimo tentativo di lettura della temperatura (il for riparte).

A questo punto parte il processo di debug. Ha molto tempo per esegursi (circa 5 secondi) in questo lasso di tempo può SOLO trovare una misura sulla coda, stamparla a video e ciclare poi senza fare nulla, finchè il controllo non ritornerà al primo processo.

Il risultato è questo:
Codice sorgente - presumibilmente Delphi

  1. ========================================
  2. Starting Debug (over serial 115200 baud)
  3. ========================================
  4.  
  5. ========================================
  6. OneWire thermometers: 2
  7. OneWire temperature: 27.00° C
  8. Boiler ON  <<<====== Ho impostato per motivi di debug la temperatura minima a 30.0° C ecco perchè è accesa
  9. ========================================
  10.  
  11. ========================================
  12. OneWire thermometers: 2
  13. OneWire temperature: 27.00° C
  14. Boiler ON
  15. ========================================
  16.  
  17. ========================================
  18. OneWire thermometers: 2
  19. OneWire temperature: 27.00° C
  20. Boiler ON
  21. ========================================
  22.  
  23. ========================================
  24. OneWire thermometers: 2
  25. OneWire temperature: 27.00° C
  26. Boiler ON
  27. ========================================
  28.  
  29. ========================================
  30. OneWire thermometers: 2
  31. OneWire temperature: 27.00° C
  32. Boiler ON
  33. ========================================
  34.  
  35. Poi metto un dito sui termometri e l temperatura si alza
  36.  
  37. ========================================
  38. OneWire thermometers: 2
  39. OneWire temperature: 27.50° C
  40. Boiler ON
  41. ========================================
  42.  
  43. ========================================
  44. OneWire thermometers: 2
  45. OneWire temperature: 27.75° C
  46. Boiler ON
  47. ========================================
  48.  
  49. ========================================
  50. OneWire thermometers: 2
  51. OneWire temperature: 28.05° C
  52. Boiler ON
  53. ========================================
  54.  
  55. ECCETERA....
  56.  
  57. ========================================
  58. OneWire thermometers: 2
  59. OneWire temperature: 31.14° C
  60. Boiler OFF <<<=== L'isteresi è di un grado, quindi a un grado sopra il 30 si spegne
  61. ========================================



E fin qui tutto perfetto! Sono molto soddisfatto del risultato..... MA appena decommento la linea di creazione del task per il GSM succede un DISASTRO!

Codice sorgente - presumibilmente Elettronica

  1. ========================================
  2. Starting Debug (over serial 115200 baud)
  3. ========================================
  4.  
  5. ========================================
  6. No thermometers
  7. Boiler OFF
  8. ========================================
  9.  
  10. ========================================
  11. No thermometers
  12. Boiler OFF
  13. ========================================
  14.  
  15. ========================================
  16. OneWire thermometers: 2
  17. OneWire temperature: 26.19° C
  18. Boiler ON
  19. ========================================
  20.  
  21. ========================================
  22. OneWire thermometers: 2
  23. OneWire temperature: 26.19° C
  24. Boiler ON
  25. ========================================
  26.  
  27. ========================================
  28. OneWire thermometers: 2
  29. OneWire temperature: 26.19° C
  30. Boiler ON
  31. ========================================



E qui si blocca TUTTO (presumibilmente)! Vedo il led del TX seriale che si accende, ma sul mio schermo non appare nient'altro.
Inoltre il led rimane acceso anche quando la temperatura supera 31° C. ma tutto ciò non dovrebbe assolutamente succedere!

Qualcuno sarebbe così gentile da darmi una mano a risolvere questo mistero?

PM Quote
Avatar
pierotofy (Admin)
Guru^2


Messaggi: 6230
Iscritto: 04/12/2003

Segnala al moderatore
Postato alle 18:10
Giovedì, 29/06/2017
Bel progetto!

Ho letto il post, ho guardato il codice, la mia prima impressione e' che probabilmente questo problema deriva dal cambio tra allocazione dinamica e statica.

Codice sorgente - presumibilmente Elettronica

  1. #define configSUPPORT_DYNAMIC_ALLOCATION    0
  2. #define configSUPPORT_STATIC_ALLOCATION     1



Forse non hai calcolato le dimensioni corrette per lo stack del modulo GPS?

Codice sorgente - presumibilmente Plain Text

  1. aDefineStaticTask(MobileCommunication, (configMINIMAL_STACK_SIZE + 25));



E' giusto 25?


Il mio blog: https://piero.dev
PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 19:37
Giovedì, 29/06/2017
Il minimo + 25 è anche troppo per un task che usa pochissimo stack. Il problema lo ho trovato per sbaglio....

Quando creo il secondo task passo il puntatore alla funzione sbagliata. Quindi creavo due task uguali con priorità diverse e succedeva un casino.

Adesso che ho risolto e ho inserito diverso codice ho problemi di stack perché ho quasi finito la RAM :'( 98.8% used!!!

Spero non mi tocchi cambiare micro

PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 21:16
Giovedì, 29/06/2017
Finita la RAM. Sembra che sopra al 97% FreeRTOS non parta nemmeno più.

Devo cambiare micro..... Probabilmente comprerò un arduino Mega 2550 per non perdere tutto il lavoro fatto.

PM Quote
Avatar
pierotofy (Admin)
Guru^2


Messaggi: 6230
Iscritto: 04/12/2003

Segnala al moderatore
Postato alle 23:26
Giovedì, 29/06/2017
:k:


Il mio blog: https://piero.dev
PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 3:41
Venerdì, 30/06/2017
Pubblicherò il progetto su github, ma non escludo la possibilità di creare un articolo relativo alla mia esperienza con freertos e Arduino e di postarlo nel sito. O magari una guida specifica per freertos....

Allo stato attuale sono in grado di interagire con il modulo GSM, sapere se è bloccato da un pin o puk, oppure pronto per le comunicazioni e gestire il modulo RTC incluso nella shield e descritto nel protocollo AT.

Sono senza RAM e credo che la mia esperienza con Arduino uno finirà qui, tuttavia sono riuscito a ottimizzare di "molto" l'utilizzo della memoria RAM, a un punto tale che ora lo stack è sufficiente per tutti e tre i processi.

Vedremo se con Arduino mega riuscirò ad usare anche un display LCD. Sarebbe il top.


PM Quote