Где-то была тема про ведение отладочных логов. Я вот для себя сваял
макросы. Очень удобно получилось. Делюсь: #define _C_LOG_RECORD(name, rec_p, ...) do { uint16_t i = name##_top; \
if ( rec_p != NULL ) memcpy(&name[i], rec_p, sizeof(name[0])); \
if ( ++i >= (sizeof(name)/sizeof(name[0]) ) i = 0; \
name##_top = i; } while(0)
#define _C_LOG_TOP_NAME(name, ...) name##_top
/** @brief Declare logging variables - array and top index */
#define C_LOG_DECL(name, type, element_cnt) uint16_t name##_top; type name[element_cnt]
/** @brief Get top record pointer */
#define C_LOG_REC_P(name) (&name[_C_LOG_TOP_NAME(name, 0)])
/** @brief Record log: C_LOG_RECORD(name) - shift top index, C_LOG_RECORD(name, rec_p) - record data from rec_p and shift top index */
#define C_LOG_RECORD(...) _C_LOG_RECORD(__VA_ARGS__, NULL, 0)
/** @brief Record triggered log (top index != 0 condition) */
#define C_LOG_TRIGGER(...) if ( _C_LOG_TOP_NAME(__VA_ARGS__, 0) ) _C_LOG_RECORD(__VA_ARGS__, NULL, 0)
// Usage example
typedef struct _log_t { uint8_t event, param; } log_t;
C_LOG_DECL(event_log, log_t, 100);
void t function1(uint8_t event, uint8_t param)
{
log_t *log_ptr = C_LOG_REC_P(event_log);
log_ptr->event = event;
log_ptr->param = param;
C_LOG_RECORD(event_log);
...
}
void function2(uint8_t event, uint8_t param)
{
log_t log_record;
log_record.event = event;
log_record.param = param;
C_LOG_TRIGGER(event_log, &log_record);
...
}
Для записи лога есть 2 макроса - C_LOG_RECORD и C_LOG_TRIGGER. Первый просто делает запись в голову лога и сдвигает указатель, второй - делает запись только если указатель != 0 (т.е. если уже были записи в лог).
Так-же оба макроса можно использовать 2 способами:
- получить указатель на голову лога (C_LOG_REC_P), заполнить все поля и затем передвинуть указатель (C_LOG_RECORD / C_LOG_TRIGGER с 1-м параметром)
- завести внешнюю переменную, заполнить ее и потом записать в лог (C_LOG_RECORD / C_LOG_TRIGGER с 2-мя параметрами)