Mechanizm testowałem w mikrokontrolerze STM32H725 z systemem FreeRtos.
Do zapisu danych wykorzystam zewnętrzną pamięć flash.
W celu wykonania tych operacji należy przygotować kilka rzeczy.
Na samym początku tworzymy tablicę przechowującą wiadomości jakie chcemy aby zostały przesłane
- static const char* const STRING_PL[] = {
- "",
- "Uruchomienie aplikacji Kontrolera",
- "Ustawiono czas",
- "Wejście do ustawień w aplikacji",
- "Wyjście z ustawień w aplikacji",
- "Zmiana stanu wejscia: Kn=1; We=%u; Fn=%u; St=%u",
- "Zmiana stanu wyjscia: Kn=1; Wy=%u; Fn=%u; St=%u",
- "Alarm! Kn=1",
- "Pozar! Kn=1",
- "Awaria zasilania!",
- "Logowanie administratora systemu.",
- "Wylogowanie administratora systemu.",
- "Wprowadzono zmiane loginu i hasla do WWW",
- "Wprowadzono modyfykacje parametrow sieciowych kontrolera",
- "Wprowadzono nową wartość licencji",
//...
- ""
- };
Mając powyższą tablicę w pamięci nie musimy zapisywać pełnych ciągów tylko przypisujemy indeks do wiadomości, wraz z parametrami jakie chcemy umieścić w wiadomości.
Kolejnym elementem jest przygotowanie typu wyliczeniowego, który będzie się odnosił do odpowiedniego ciągu znaków:
- typedef enum {
- LOG_JEZYK_STRING_ID_FIRST = 0,
- // identyfikatory dla wartości tekstowych dla parametru "zdarzenie - treść":
- LOG_JEZYK_STRING_ID_ZDARZENIE_APPLICATION_START,
- LOG_JEZYK_STRING_ID_ZDARZENIE_CLOCK_SET,
- LOG_JEZYK_STRING_ID_ZDARZENIE_SETTINGS_ENTER,
- LOG_JEZYK_STRING_ID_ZDARZENIE_SETTINGS_EXIT,
- LOG_JEZYK_STRING_ID_ZDARZENIE_INPUT_STATE,
- LOG_JEZYK_STRING_ID_ZDARZENIE_OUTPUT_STATE,
- LOG_JEZYK_STRING_ID_ZDARZENIE_ALARM,
- LOG_JEZYK_STRING_ID_ZDARZENIE_POZAR,
- LOG_JEZYK_STRING_ID_ZDARZENIE_ZASILANIE,
- LOG_JEZYK_STRING_ID_ZDARZENIE_ADMIN_LOGIN,
- LOG_JEZYK_STRING_ID_ZDARZENIE_ADMIN_LOGOUT,
- LOG_JEZYK_STRING_ID_ZMIANA_LOGINU_I_HASLA_WWW,
- LOG_JEZYK_STRING_ID_MODYFIKACJA_PARAMETROW_SIECIOWYCH,
- LOG_JEZYK_STRING_ID_WPROWADZONO_NOWA_LICENCJE,
- LOG_JEZYK_STRING_ID_LAST
- } Log_StringId_Enum;
Teraz przygotowuję strukturę przechowującą dane jakie będą zapisane w pamięci:
- typedef enum {
- LOG_DEBUG = 1,
- LOG_SYSTEM = 2,
- LOG_OSTRZEZ = 3
- } LogType_Enum;
- typedef struct {
- LogType_Enum Typ;
- LogTime_t Czas;
- Log_StringId_Enum KodZdarzenia;
- uint8_t NumerWejscia;
- uint8_t NumerWyjscia;
- uint8_t FunkcjaWeWy;
- uint8_t StanWeWy;
- } LogMsg_t;
W programie mam jeden wątek, który zajmuje się tylko obsługą pamięci flash. Podłączone jest kilka kości flash, komunikujących się przez jeden interfejs SPI. Pozwala to na uniknięcie ewentualnych problemów z nakładaniem się zapisów/odczytów z różnych wątków.
Gdy pojawi się konieczność zapisania jakichś danych do logów, to wywołuje odpowiednią funkcję zapisującą informację do bufora kołowego, która uruchamia wątek zapisu danych do flash:
- void LOG_ZapisZmianaStanuWejscia(const uint8_t numerWejscia, const uint8_t Funckja, const uint8_t Stan)
- {
- /* "Zmiana stanu wejscia: Kn=1; We=%u; Fn=%u; St=%u", */
- LogMsg_t log;
- Log_ClearLogTypedef(&log);
- RTC_TimeTypeDef rtc_time;
- RTC_DateTypeDef rtc_date;
- HAL_RTC_GetTime(&hrtc, &rtc_time, RTC_FORMAT_BIN);
- HAL_RTC_GetDate(&hrtc, &rtc_date, RTC_FORMAT_BIN);
- log.Typ = LOG_SYSTEM;
- log.Czas.Godzina = rtc_time.Hours;
- log.Czas.Minuta = rtc_time.Minutes;
- log.Czas.Sekunda = rtc_time.Seconds;
- log.Czas.Dzien = rtc_date.Date;
- log.Czas.Miesiac = rtc_date.Month;
- log.Czas.Rok = rtc_date.Year;
- log.KodZdarzenia = LOG_JEZYK_STRING_ID_ZDARZENIE_INPUT_STATE;
- log.FunkcjaWeWy = Funckja;
- log.NumerWejscia = numerWejscia;
- log.NumerWyjscia = 0;
- log.StanWeWy = Stan;
- LogCircBuffer_PutDataToBuff(&log);
- }
- int8_t LogCircBuffer_PutDataToBuff(LogMsg_t *logPtr)
- {
- if(LogCircBuff.lock != 0) { return -1; }
- uint8_t head_temp = LogCircBuff.head + 1;
- if (head_temp == Log_CIRC_BUFFER_SIZE ){ head_temp = 0; }
- if (head_temp == LogCircBuff.tail) { return -1; }
- LogCircBuff.lock = 1;
- LogStruct_Array[head_temp].Typ = zdarzeniaPtr->Typ;
- LogStruct_Array[head_temp].Czas.Dzien = zdarzeniaPtr->Czas.Dzien;
- LogStruct_Array[head_temp].Czas.Godzina = zdarzeniaPtr->Czas.Godzina;
- LogStruct_Array[head_temp].Czas.Miesiac = zdarzeniaPtr->Czas.Miesiac;
- LogStruct_Array[head_temp].Czas.Minuta = zdarzeniaPtr->Czas.Minuta;
- LogStruct_Array[head_temp].Czas.Rok = zdarzeniaPtr->Czas.Rok;
- LogStruct_Array[head_temp].Czas.Sekunda = zdarzeniaPtr->Czas.Sekunda;
- LogStruct_Array[head_temp].FunkcjaWeWy = zdarzeniaPtr->FunkcjaWeWy;
- LogStruct_Array[head_temp].KodZdarzenia = zdarzeniaPtr->KodZdarzenia;
- LogStruct_Array[head_temp].NumerWejscia = zdarzeniaPtr->NumerWejscia;
- LogStruct_Array[head_temp].NumerWyjscia = zdarzeniaPtr->NumerWyjscia;
- LogStruct_Array[head_temp].StanWeWy = zdarzeniaPtr->StanWeWy;
- ZdarzeniaCircBuff.head = head_temp;
- ZdarzeniaCircBuff.lock = 0;
- return 0;
- }
Po dodaniu danych do bufora wykonuje ich zapis do pamięci flash:
- uint8_t Log_SaveDataInMemmory(LogMsg_t* logPtr)
- {
- uint8_t buffer[LOG_FLASH_SIZE] = {0x00};
- uint8_t opStatus = 0xFF;
- LogMsg_t log;
- Log_ClearData(&log);
- log.Typ = logPtr->Typ;
- log.Czas.Godzina = logPtr->Czas.Godzina;
- log.Czas.Minuta = logPtr->Czas.Minuta;
- log.Czas.Sekunda = logPtr->Czas.Sekunda;
- log.Czas.Dzien = logPtr->Czas.Dzien;
- log.Czas.Miesiac = logPtr->Czas.Miesiac;
- log.Czas.Rok = logPtr->Czas.Rok;
- log.KodZdarzenia = log.KodZdarzenia
- log.FunkcjaWeWy = log.FunkcjaWeWy;
- log.NumerWejscia = log.NumerWejscia;
- log.NumerWyjscia = log.NumerWyjscia;
- log.StanWeWy = log.StanWeWy;
- Log_ConvertIntoBuffer(&buffer[0], &log);
- opStatus = Log_SaveLogInFlash(&buffer[0], &LogFlashPointerts);
- return opStatus;
- }
Następnie odczytuje według pozycji wskaźników ostatnie nową wiadomość do przesłania.
Po odczycie wykonuje jej konwersje do postaci tekstowej:
- uint8_t PutDataIntoLogTxt(char* buffer, const unsigned char* format, LogMsg_t *logPtr)
- {
- uint8_t length = 0;
- switch(zdarzeniaPtr->KodZdarzenia)
- {
- case LOG_JEZYK_STRING_ID_FIRST:
- /* NOTHING */
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_APPLICATION_START:
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_CLOCK_SET:
- length = sprintf(buffer, (const char*)format);
- /* NOTHING */
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_SETTINGS_ENTER:
- length = sprintf(buffer, (const char*)format);
- /* NOTHING */
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_SETTINGS_EXIT:
- length = sprintf(buffer, (const char*)format);
- /* NOTHING */
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_INPUT_STATE:
- //"Zmiana stanu wejscia: Kn=1; We=%u; Fn=%u; St=%u",
- length = sprintf(buffer, (const char*)format, zdarzeniaPtr->NumerWejscia, zdarzeniaPtr->FunkcjaWeWy, zdarzeniaPtr->StanWeWy);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_OUTPUT_STATE:
- //"Zmiana stanu wyjscia: Kn=1; Wy=%u; Fn=%u; St=%u",
- length = sprintf(buffer, (const char*)format, zdarzeniaPtr->NumerWyjscia, zdarzeniaPtr->FunkcjaWeWy, zdarzeniaPtr->StanWeWy);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_ALARM:
- //"Alarm! Kn=1",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_POZAR:
- //"Pozar! Kn=1",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_ZASILANIE:
- //"Awaria zasilania!",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_ADMIN_LOGIN:
- //"Logowanie administratora systemu.",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZDARZENIE_ADMIN_LOGOUT:
- //"Wylogowanie administratora systemu.",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_ZMIANA_LOGINU_I_HASLA_WWW:
- //"Wprowadzono zmiane loginu i hasla do WWW",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_MODYFIKACJA_PARAMETROW_SIECIOWYCH:
- //"Wprowadzono modyfykacje parametrow sieciowych kontrolera",
- length = sprintf(buffer, (const char*)format);
- break;
- case LOG_JEZYK_STRING_ID_WPROWADZONO_NOWA_LICENCJE:
- //"Wprowadzono nową wartość licencji",
- length = sprintf(buffer, (const char*)format);
- break;
- default:
- break;
- }
- return length;
- }
Tak przygotowany string może już zostać przesłany, czy to przez SSL czy np. z wykorzystanie interfejsu UART.