#include "log.h" #include "rtc.h" #include "ringfs.h" #include "spi_flash.h" #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "event_groups.h" #include "rtc.h" #include "settings_api.h" #include "digital_input.h" #include #include #include #undef DBG #define DBG if(1) static bool archive_state = false; static bool log_state = true; static bool log_init_f = false; static bool archive_init_f = false; struct ringfs fs_log; //struct ringfs fs_archive; struct ringfs fs_ch_arch[ARCH_CH_NUMBER]; SemaphoreHandle_t log_mutex; xQueueHandle log_queue; EventGroupHandle_t archive_event; uint16_t log_entries_capacity; uint16_t archive_entries_capacity; uint32_t archive_cnt[ARCH_CH_NUMBER] = {0}; void archive_task(void *params); void log_task(void *params); // static int op_sector_erase(struct ringfs_flash_partition *flash, int address) { (void)flash; int ret; ret = spi_flash_erase_sector(address, 0); return ret; } // static ssize_t op_program(struct ringfs_flash_partition *flash, int address, const void *data, size_t size) { (void)flash; int ret; ret = spi_flash_write(address, data, size, 0); return ret; } // static ssize_t op_read(struct ringfs_flash_partition *flash, int address, void *data, size_t size) { (void)flash; int ret; ret = spi_flash_read(address, data, size, 0); return ret; } // static struct ringfs_flash_partition ringfs_flash_log = { .sector_offset = LOG_FLASH_SECTOR_OFFSET, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }; // static struct ringfs_flash_partition ringfs_flash_archive = { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }; // static struct ringfs_flash_partition fingfs_flash_ch_arch[ARCH_CH_NUMBER] = { { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*1, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*2, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*3, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*4, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*5, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*6, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, { .sector_offset = ARCHIVE_FLASH_SECTOR_OFFSET + ARCHIVE_CHANNEL_OFFSET*7, .sector_erase = op_sector_erase, .program = op_program, .read = op_read, }, }; // void log_init(bool format) { DBG printf("[LOG] Init...\r\n"); if (!spi_flash_desc.present) return; // ---------------------------------------------------------------------- // // Журнал ringfs_flash_log.sector_size = spi_flash_desc.sector_size; ringfs_flash_log.sector_count = LOG_FLASH_SECTOR_COUNT; ringfs_init(&fs_log, &ringfs_flash_log, LOG_ENTRY_VERSION, sizeof(log_entry_t)); if (format || ringfs_scan(&fs_log) != 0) { DBG printf("FAT1 false\r\n"); ringfs_format(&fs_log); } DBG printf("FAT1 true\r\n"); // ---------------------------------------------------------------------- // // Архив. 8 буфером на каждый канал. for (uint8_t i = 0; i < ARCH_CH_NUMBER; i ++) { fingfs_flash_ch_arch[i].sector_size = spi_flash_desc.sector_size, fingfs_flash_ch_arch[i].sector_count = ARCHIVE_FLASH_SECTOR_COUNT, ringfs_init(&fs_ch_arch[i], &fingfs_flash_ch_arch[i], ARCHIV_ENTRY_VERSION + i, sizeof(archive_entry_t)); if (ringfs_scan(&fs_ch_arch[i]) != 0) { DBG printf("FAT for channel %u is false\r\n", i + 1); ringfs_format(&fs_ch_arch[i]); } DBG printf("FAT for channel %u is true\r\n", i + 1); } // ---------------------------------------------------------------------- // log_mutex = xSemaphoreCreateMutex(); log_entries_capacity = ringfs_capacity(&fs_log); archive_entries_capacity = ringfs_capacity(&fs_ch_arch[0]); // Event archive_event = xEventGroupCreate(); xTaskCreate(archive_task, "archive_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL); log_queue = xQueueCreate(10, sizeof(log_entry_t)); xTaskCreate(log_task, "log_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL); log_init_f = true; archive_init_f = true; // Таймер для ведения архива с разным периодом по разным каналам log_init_archive_tim(); } // Настройка таймера на частоту 10 Гц void log_init_archive_tim(void) { printf("Init log timer...\r\n"); crm_clocks_freq_type crm_clocks_freq_struct = {0}; crm_periph_clock_enable(CRM_TMR14_PERIPH_CLOCK, TRUE); crm_clocks_freq_get(&crm_clocks_freq_struct); tmr_base_init(TMR14, 999, (crm_clocks_freq_struct.ahb_freq / 10000) - 1); tmr_cnt_dir_set(TMR14, TMR_COUNT_UP); tmr_flag_clear(TMR14, TMR_OVF_FLAG); nvic_priority_group_config(NVIC_PRIORITY_GROUP_4); nvic_irq_enable(TMR8_TRG_HALL_TMR14_IRQn, 5, 0); tmr_counter_enable(TMR14, TRUE); tmr_interrupt_enable(TMR14, TMR_OVF_INT, TRUE); } // void TMR8_TRG_HALL_TMR14_IRQHandler(void) { if (tmr_flag_get(TMR14, TMR_OVF_FLAG) != RESET) { tmr_flag_clear(TMR14, TMR_OVF_FLAG); if (archive_state) { log_check_archive_cnt(); printf("TMR_14 irq\r\n"); } } } // int log_fetch(void *entry, entry_type_t entry_type, uint8_t ch, uint32_t timeout) { int ret; ret = xSemaphoreTake(log_mutex, (TickType_t)timeout); if (ret == pdFALSE) return ret; if (entry_type == LOG_ENTRY) ret = ringfs_fetch(&fs_log, entry); else if (entry_type == ARCHIVE_ENTRY) ret = ringfs_fetch(&fs_ch_arch[ch], entry); else ret = -1; xSemaphoreGive(log_mutex); return ret; } // int log_discard(void *entry, entry_type_t entry_type, uint8_t ch, uint32_t timeout) { int ret; ret = xSemaphoreTake(log_mutex, (TickType_t)timeout); if (ret == pdFALSE) return ret; if (entry_type == LOG_ENTRY) ret = ringfs_discard(&fs_log); else if (entry_type == ARCHIVE_ENTRY) ret = ringfs_discard(&fs_ch_arch[ch]); else ret = -1; xSemaphoreGive(log_mutex); return ret; } // int log_append(void *entry, entry_type_t entry_type, uint8_t ch) { int ret; TM_RTC_t time; common_entry_t *entry_ptr = entry; log_entry_t *log_etnry_ptr; archive_entry_t *archive_etnry_ptr; ret = xSemaphoreTake(log_mutex, portMAX_DELAY); if (ret == pdFALSE) return ret; if (entry_ptr->timestamp == 0) entry_ptr->timestamp = rtc_get_ms(); if (entry_type == LOG_ENTRY) { log_etnry_ptr = (log_entry_t*)entry; log_etnry_ptr->crc = crc_8(entry, sizeof(log_entry_t) - 1); ret = ringfs_append(&fs_log, entry); } else if (entry_type == ARCHIVE_ENTRY) { archive_etnry_ptr = (archive_entry_t*)entry; archive_etnry_ptr->crc = crc_8(entry, sizeof(archive_entry_t) - 1); ret = ringfs_append(&fs_ch_arch[ch], entry); } else ret = -1; xSemaphoreGive(log_mutex); return ret; } // uint16_t log_capacity(void) { return ringfs_count_exact(&fs_log); } // uint16_t log_arch_capacity(uint8_t ch) { return ringfs_count_exact(&fs_ch_arch[ch]); } // -------------------------------------------------------------------------- // // misc uint8_t crc_8(uint8_t *data, int length) { uint8_t crc = 0x00; uint8_t extract; uint8_t sum; for (int i = 0; i < length; i++) { extract = *data; for (uint8_t tmp = 8; tmp; tmp--) { sum = (crc ^ extract) & 0x01; crc >>= 1; if (sum) crc ^= 0x8C; extract >>= 1; } data++; } return crc; } // -------------------------------------------------------------------------- // // Tests // val - 0 - журнал // val - 1 - архив // ch - номер канала архива void log_info(uint8_t val, uint8_t ch) { if (val > 1) return; struct ringfs *fs = val == 0 ? &fs_log : &fs_ch_arch[ch]; int capacity_flash = 0; int count_flash = 0; int count_estimate = 0; capacity_flash = ringfs_capacity(fs); count_flash = ringfs_count_exact(fs); count_estimate = ringfs_count_estimate(fs); if (val == 0) { DBG printf("Log partition capacity: %u\r\n", capacity_flash); DBG printf("Count log entry: %u\r\n", count_flash); DBG printf("Estimate count: %u\r\n", count_estimate); } else { DBG printf("Archive partition capacity: %u\r\n", capacity_flash); DBG printf("Count archive entry: %u\r\n", count_flash); DBG printf("Estimate count: %u\r\n", count_estimate); } } // val - 0 - журнал // val - 1 - архив // ch - номер канала архива void log_format(uint8_t val, uint8_t ch) { if (val == 0) { DBG printf("Formating log partition...\r\n"); ringfs_format(&fs_log); } else if (val == 1) { DBG printf("Formating archive partition...\r\n"); ringfs_format(&fs_ch_arch[ch]); } } // Добавить n записей журнала int log_add_random_entry(uint8_t val, uint32_t cnt_entry, uint8_t ch) { int ret; log_entry_t log_entry = {0}; archive_entry_t archive_entry = {0}; static uint8_t log_index = 0; static uint32_t archive_index = 0; if (val == 0) { DBG printf("Appending %u archive entries\r\n", cnt_entry); for (uint32_t i = 0; i < cnt_entry; i++) { log_entry.code_type = log_index; log_entry.code_state = log_index; log_entry.channel_number = log_index; log_entry.value = (float)log_index++; ret = log_append((void*)&log_entry, LOG_ENTRY, 0); } DBG printf("Result: %u\r\n", ret); } if (val == 1) { DBG printf("Appending %u archive entries\r\n", cnt_entry); for (uint32_t i = 0; i < cnt_entry; i++) { archive_entry.input_value = archive_index++; ret = log_append((void*)&archive_entry, ARCHIVE_ENTRY, ch); } DBG printf("Result: %u\r\n", ret); } return ret; } // int log_add_entry(log_event_type_t type, log_event_state_t state, uint8_t channel_number, float value) { log_entry_t entry; if (!log_init_f) return -1; entry.timestamp = rtc_get_ms(); entry.code_type = (uint8_t)type; entry.code_state = (uint8_t)state; entry.channel_number = channel_number; entry.value = value; xQueueSend(log_queue, &entry, 0); return 0; } // void test_fetch(void) { archive_entry_t entry = {0}; log_fetch(&entry, ARCHIVE_ENTRY, 0, portMAX_DELAY); //printf("\r\n%" PRId64 " [ms]\r\n", rtc_get_ms()); printf("[entry] timestamp = % " PRId64 ", value = %u, crc = %u\r\n", entry.timestamp, entry.input_value, entry.crc); } // void log_archive_state(bool state) { archive_state = state; } // void log_log_state(bool state) { log_state = state; } void archive_task(void *params) { int ret = 0; uint32_t event = 0; archive_entry_t entry = {0}; EventBits_t bits; for (;;) { bits = xEventGroupWaitBits(archive_event, ARCH_CH_1 | ARCH_CH_2 | ARCH_CH_3 | ARCH_CH_4 | ARCH_CH_5 | ARCH_CH_6 | ARCH_CH_7 | ARCH_CH_8, pdTRUE, pdFALSE, portMAX_DELAY); for (uint32_t i = 0; i < ARCH_CH_NUMBER; i++) { if (bits & (1 << i)) { DBG printf("Archive event: %u\r\n", (1 << i)); entry.timestamp = 0; entry.input_value = di_get(i - 1); DBG printf("Append archive entry..."); ret = log_append((void*)&entry, ARCHIVE_ENTRY, i - 1); if (ret != 0) { DBG printf("FAIL\r\n"); } else { DBG printf("OK\r\n"); } } } #if 0 if ((!archive_state) || (!archive_init_f)) { vTaskDelay(1000); continue; } entry.timestamp = 0; entry.input_value = (uint8_t)di_state_bit; DBG printf("Append archive entry..."); ret = log_append((void*)&entry, ARCHIVE_ENTRY, 0); if (ret != 0) { DBG printf("FAIL\r\n"); } else { DBG printf("OK\r\n"); } vTaskDelay(settings.period_archive*1000); #endif } } // void log_task(void *params) { int ret; log_entry_t entry; for (;;) { if (xQueueReceive(log_queue, &entry, portMAX_DELAY) == pdTRUE) { DBG printf("Try append LOG entry... "); ret = log_append((void*)&entry, LOG_ENTRY, 0); DBG printf("Result: %i\r\n", ret); } } } // Вызывается в прерывании таймера с частотой 10 Гц void log_check_archive_cnt(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; for (uint8_t i = 0; i < ARCH_CH_NUMBER; i++) { if (archive_cnt[i]++ == 10*settings.period_archive[i]) { archive_cnt[i] = 0; // Номер канала с 0..7 printf("Send event: %u\r\n", 1 << i); xEventGroupSetBitsFromISR(archive_event, 1 << i, &xHigherPriorityTaskWoken); } } }