소스 검색

Подключение ringfs.

TelenkovDmitry 10 달 전
부모
커밋
5c8e323895

+ 496 - 0
fw/modules/log/log.c

@@ -0,0 +1,496 @@
+#include "log.h"
+#include "rtc.h"
+#include "ringfs.h"
+#include "spi_flash.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+#include <string.h>
+
+
+
+#undef DBG
+#define DBG if(1)
+
+
+
+#define LOG_ENTRY_VERSION 1
+
+#define LOG_FLASH_SECTOR_OFFSET	4
+
+#define ALARM_LOG_FLASH_SECTOR_OFFSET	258
+#define SECTOR_COUNT (spi_flash_desc.sector_count/2 - LOG_FLASH_SECTOR_OFFSET)
+
+
+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 = 
+{
+	.sector_offset = LOG_FLASH_SECTOR_OFFSET,
+	.sector_erase = op_sector_erase,
+	.program = op_program,
+	.read = op_read,
+};
+
+static struct ringfs fs;
+
+static struct ringfs_flash_partition ringfs_flash2 = 
+{
+	.sector_offset = ALARM_LOG_FLASH_SECTOR_OFFSET,
+	.sector_erase = op_sector_erase,
+	.program = op_program,
+	.read = op_read,
+};
+
+static struct ringfs fs2;
+
+
+//
+void log_init(bool format) 
+{
+	DBG printf("[LOG] Init...\r\n");
+
+	if (!spi_flash_desc.present)
+		return;
+    
+	ringfs_flash.sector_size = spi_flash_desc.sector_size;
+	ringfs_flash.sector_count = SECTOR_COUNT;
+
+	ringfs_init(&fs, &ringfs_flash, LOG_ENTRY_VERSION, sizeof(log_entry_t));
+	if (format || ringfs_scan(&fs) != 0){
+		DBG printf("FAT1 false\r\n");
+		ringfs_format(&fs);
+	}
+	DBG printf("FAT1 true\r\n");
+	ringfs_flash2.sector_size = spi_flash_desc.sector_size;
+	ringfs_flash2.sector_count = SECTOR_COUNT;
+
+	ringfs_init(&fs2, &ringfs_flash2, LOG_ENTRY_VERSION, sizeof(log_entry_t));
+	if (format || ringfs_scan(&fs2) != 0){
+		DBG printf("FAT2 false\r\n");
+		ringfs_format(&fs2);
+	}
+	DBG printf("FAT2 true\r\n");
+	
+    //fLogInit = true;
+
+	//log_mutex = xSemaphoreCreateMutex();
+
+	//xTaskCreate(log_task, ( char * ) "log_task", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY, NULL);
+}
+
+
+
+
+
+#if 0
+
+char logFileBuf[FILE_BUF_MAX_LEN];
+
+char name_login[50];
+
+extern const char* logsStrShortRu[];
+
+bool flUpdateLog = false;
+static bool fLogInit = false;  // Флаг инициализации журнала
+
+#define LOG_TIME	1000*60*10
+
+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 = {
+	.sector_offset = LOG_FLASH_SECTOR_OFFSET,
+
+	.sector_erase = op_sector_erase,
+	.program = op_program,
+	.read = op_read,
+};
+
+static struct ringfs fs;
+
+static struct ringfs_flash_partition ringfs_flash2 = {
+	.sector_offset = ALARM_LOG_FLASH_SECTOR_OFFSET,
+
+	.sector_erase = op_sector_erase,
+	.program = op_program,
+	.read = op_read,
+};
+
+static struct ringfs fs2;
+
+static SemaphoreHandle_t log_mutex;
+
+
+/**
+  * @brief  Отключает журнал для безопасной перезагрузки
+  */
+bool LOG_Disable(void)
+{
+    if (fLogInit) {
+        /* Ожидаем завершения работы с журнал */
+        if ( xSemaphoreTake(log_mutex, 10000) == pdTRUE ) {
+            //fLogInit = false;
+            //xSemaphoreGive(logMutex);
+            return true;
+        }
+        else {
+           return false;
+        }
+    }
+    else {
+        return true;
+    }
+}
+
+void log_task(void* params)
+{
+	for(;;){
+		flUpdateLog = true;
+		vTaskDelay(LOG_TIME);
+		/*vTaskDelay(50);
+		log_event_data(LOG_SYSTEM_BOOT, "Администратор");
+		log_add(")215.7;215.7;220.5;000;50.1;2.30;25.0;00000001;");*/
+	}
+}
+
+void log_init(bool format) {
+	DBG printf(">>> Event log\n");
+
+	if (!spi_flash_desc.present)
+		return;
+	ringfs_flash.sector_size = spi_flash_desc.sector_size;
+	ringfs_flash.sector_count = SECTOR_COUNT;
+
+	ringfs_init(&fs, &ringfs_flash, LOG_ENTRY_VERSION, sizeof(log_entry_t));
+	if (format || ringfs_scan(&fs) != 0){
+		DBG printf("FAT1 false\r\n");
+		ringfs_format(&fs);
+	}
+	DBG printf("FAT1 true\r\n");
+	ringfs_flash2.sector_size = spi_flash_desc.sector_size;
+	ringfs_flash2.sector_count = SECTOR_COUNT;
+
+	ringfs_init(&fs2, &ringfs_flash2, LOG_ENTRY_VERSION, sizeof(log_entry_t));
+	if (format || ringfs_scan(&fs2) != 0){
+		DBG printf("FAT2 false\r\n");
+		ringfs_format(&fs2);
+	}
+	DBG printf("FAT2 true\r\n");
+	fLogInit = true;
+
+	log_mutex = xSemaphoreCreateMutex();
+
+	xTaskCreate(log_task, ( char * ) "log_task", configMINIMAL_STACK_SIZE * 2, NULL, tskIDLE_PRIORITY, NULL);
+}
+
+
+int capacity_flash = 0;
+int count_flash = 0;
+int log_test(void) {
+	int ret;
+	log_entry_t entry;
+
+	log_init(false);
+	capacity_flash = ringfs_capacity(&fs);
+	count_flash = ringfs_count_exact(&fs);
+	DBG printf("\tCapacity:   %d\n", capacity_flash);
+	DBG printf("\tCount:      %d\n", count_flash);
+
+	DBG printf("\tAppending   ");
+//	ret = log_event(LOG_SYSTEM_DEFCONFIG, 0, 0);
+	DBG printf("%s\n", ret == 0 ? "ok" : "error");
+	if (ret == 0)
+		return -1;
+
+//	ret = log_event(LOG_SYSTEM_DEFCONFIG, 0, 512);
+	entry.timestamp = 0;
+	entry.type = 0;
+	DBG printf("\tFetching    ");
+	if (log_fetch(&entry, portMAX_DELAY) == 0){
+		DBG printf("ok, time=%d, type=%d\n", entry.timestamp, entry.type);
+		log_fetch(&entry, portMAX_DELAY);
+		entry.timestamp = 0;
+			entry.type = 0;
+		log_fetch(&entry, portMAX_DELAY);
+		entry.timestamp = 0;
+					entry.type = 0;
+				log_fetch(&entry, portMAX_DELAY);
+				entry.timestamp = 0;
+							entry.type = 0;
+						log_fetch(&entry, portMAX_DELAY);
+		return 0;
+	}
+	else {
+		DBG printf("fail\n");
+		return -1;
+	}
+
+	DBG printf("\tDiscarding  ");
+	if (log_discard(&entry,portMAX_DELAY) == 0)
+		DBG printf("ok\n");
+	else {
+		DBG printf("fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int log_append(log_entry_t *entry) {
+	int ret;
+	TM_RTC_t data;
+	ret = xSemaphoreTake( log_mutex, portMAX_DELAY );
+	if (ret == pdFALSE)
+		return ret;
+	if (!entry->timestamp){
+		TM_RTC_GetDateTime(&data, TM_RTC_Format_BIN);
+		entry->timestamp = data.unix;
+	}
+	if(entry->type == LOG_VALUE)
+		ringfs_append(&fs, entry);
+	else
+		ringfs_append(&fs2, entry);
+	xSemaphoreGive(log_mutex);
+	return ret;
+}
+
+int log_fetch(log_entry_t *entry, uint32_t timeout) {
+	int ret;
+	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
+	if (ret == pdFALSE)
+		return ret;
+	if(entry->type == LOG_VALUE)
+		ret = ringfs_fetch(&fs, entry);
+	else
+		ret = ringfs_fetch(&fs2, entry);
+	xSemaphoreGive(log_mutex);
+	return ret;
+}
+
+int log_rewind(log_entry_t *entry, uint32_t timeout) {
+	int ret;
+	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
+	if (ret == pdFALSE)
+		return ret;
+	if(entry->type == LOG_VALUE)
+		ret = ringfs_rewind(&fs);
+	else
+		ret = ringfs_rewind(&fs2);
+	xSemaphoreGive(log_mutex);
+	return ret;
+}
+
+int log_discard(log_entry_t *entry, uint32_t timeout) {
+	int ret;
+	ret = xSemaphoreTake( log_mutex, (TickType_t)timeout );
+	if (ret == pdFALSE)
+		return ret;
+	if(entry->type == LOG_VALUE)
+		ret = ringfs_discard(&fs);
+	else
+		ret = ringfs_discard(&fs2);
+	xSemaphoreGive(log_mutex);
+	return ret;
+}
+
+void log_event_data(log_type_t type, char *data)
+{
+	log_entry_t entry_data;
+
+	entry_data.timestamp = 0;
+	entry_data.type = type;
+	strncpy(entry_data.data, data, 49);
+	if (fLogInit)
+		log_append(&entry_data);
+}
+
+void log_add(char *log_data)
+{
+	char buf_value[50];
+	uint8_t i, len;
+
+
+	memset(buf_value, 0, 50);
+	len = strlen(log_data);
+	if (len != UPS_DATA_STRING_SIZE) {
+		//len = UPS_DATA_STRING_SIZE;
+		return;
+	}
+
+	strncpy(buf_value, log_data, len);
+
+	DBG printf("UPS log data: %s\r\n", log_data);
+	buf_value[0] = '\"';
+
+	for(i = 0; i < len; i++)
+	{
+		if(buf_value[i] == ' ')
+			buf_value[i] = ';';
+	}
+	buf_value[len - 1] = ';';
+
+	if(fLogInit){
+		if(fs.write.slot>67)
+		{
+			log_entry_t entry_data;
+			entry_data.timestamp = 0;
+			log_event_data(LOG_VALUE, buf_value);
+		}
+		else
+		log_event_data(LOG_VALUE, buf_value);
+	}
+
+}
+
+/**
+  * @brief  Возвращает true если журнал проинициализирован
+  */
+bool LOG_IsInit()
+{
+  return fLogInit;
+}
+
+/**
+  * @brief  Возвращает общее количество страниц
+  */
+uint32_t LOG_GetPageCount(void)
+{
+  return (((ringfs_count_estimate(&fs)) / 10) + 1);
+}
+
+uint32_t LOG_GetTotalSTRCount(void)
+{
+	return ringfs_count_estimate(&fs);
+}
+
+void LOG_GetPage_tabs(char *str, uint32_t page)
+{
+	TM_RTC_t rtc_data;
+	log_entry_t entry;
+	char buf[20];
+	uint8_t i;
+	int start =LOG_GetTotalSTRCount();//(fs.write.sector*fs.slots_per_sector + fs.write.slot);
+
+	memset(buf, 0, 20);
+	for(i=0; i < 10; i++){
+		fs.cursor_position =  start - 10*(page-1) - 1 - i;
+		if(fs.cursor_position < 0)
+			break;
+		else{
+			fs.cursor.sector = (fs.read.sector + fs.cursor_position/fs.slots_per_sector)%fs.flash->sector_count;
+			fs.cursor.slot = fs.cursor_position%fs.slots_per_sector;
+		}
+		entry.type = LOG_VALUE;
+		log_fetch(&entry, portMAX_DELAY);
+		entry.data[49] = 0;
+		strncat(str, entry.data, strlen(entry.data));
+		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
+		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
+		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
+		strcat(str, buf);
+		strcat(str, "\",");
+		strcat(str, "\r\n");
+	}
+}
+
+void LOG_GetPage(char *str, uint32_t page)
+{
+	TM_RTC_t rtc_data;
+	log_entry_t entry;
+	char buf[20];
+	uint8_t i;
+	int start =LOG_GetTotalSTRCount();//(fs.write.sector*fs.slots_per_sector + fs.write.slot);
+
+	memset(buf, 0, 20);
+	for(i=0; i < 10; i++){
+		fs.cursor_position =  start - 10*(page-1) - 1 - i;
+		if(fs.cursor_position < 0)
+			break;
+		else{
+			fs.cursor.sector = (fs.read.sector + fs.cursor_position/fs.slots_per_sector)%fs.flash->sector_count;
+			fs.cursor.slot = fs.cursor_position%fs.slots_per_sector;
+		}
+		entry.type = LOG_VALUE;
+		log_fetch(&entry, portMAX_DELAY);
+		entry.data[49] = 0;
+		strncat(str, entry.data, strlen(entry.data));
+		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
+		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
+		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
+		strcat(str, buf);
+		strcat(str, "\",");
+	}
+}
+
+uint32_t LOG_GetData(int ptr, char *str, uint32_t size, bool start)
+{
+	TM_RTC_t rtc_data;
+	log_entry_t entry;
+	char buf[20];
+	uint8_t i;
+	entry.type = LOG_VALUE;
+	if(start)
+		log_rewind(&entry, portMAX_DELAY);
+
+	fs.cursor_position =  ptr/STRING_SIZE;
+	fs.cursor.sector = (fs.read.sector + fs.cursor_position/fs.slots_per_sector)%fs.flash->sector_count;
+	fs.cursor.slot = fs.cursor_position%fs.slots_per_sector;
+
+	for(i = 0; i < size/STRING_SIZE; i++)
+	{
+		entry.type = LOG_VALUE;
+		log_fetch(&entry, portMAX_DELAY);
+		entry.data[49] = 0;
+		strncat(str, &entry.data[1], (strlen(entry.data) - 1));
+		TM_RTC_GetDateTimeFromUnix(&rtc_data, entry.timestamp);
+		sprintf(buf, "%02i.%02i.%02i %02i:%02i:%02i",  rtc_data.date, rtc_data.month,
+		rtc_data.year, rtc_data.hours, rtc_data.minutes, rtc_data.seconds);
+		strcat(str, buf);
+		strcat(str, "\n");
+	}
+	return strlen(str);
+
+}
+
+
+
+#endif

+ 109 - 0
fw/modules/log/log.h

@@ -0,0 +1,109 @@
+#ifndef __LOG_H
+#define __LOG_H
+
+#include "at32f403a_407.h"
+#include <stdbool.h>
+
+
+typedef enum 
+{
+    LOG_SYSTEM_BOOT = 0,			// device booted
+    LOG_SYSTEM_DEFCONFIG,	// default config applied
+    LOG_UPDATE_SOFT,
+    LOG_TURN_ON,		//
+    LOG_PSW_CHANGE,		//
+    LOG_SETTING_SAVE,		//
+    LOG_LOGIN_TELNET,		//
+    LOG_LOGIN_SSH,
+    LOG_LOGIN,		//
+    LOG_TEST_UPS,		//
+    LOG_SHUTDOWN_UPS,
+    LOG_ALARM_VAC_LOW_OUTPUT,
+    LOG_ALARM_VAC_HIGH_OUTPUT,
+    LOG_ALARM_HIGH_TEMP,
+    LOG_ALARM_LOW_TEMP,
+    LOG_ALARM_LINE,
+    LOG_ALARM_LOW_BAT,
+    LOG_ALARM_POWER,
+    LOG_ALARM_UPS,
+    LOG_ALARM_AKB,
+    LOG_ALARM_CHANGE_AKB,
+    LOG_PHASE_FAIL,
+    LOG_EPO_FAIL,
+    LOG_VALUE,
+    LOG_NONE,
+} log_type_t;
+
+
+
+typedef __packed struct 
+{
+	uint32_t timestamp;
+	log_type_t type:8;
+	char data[50];
+    
+} log_entry_t;
+
+
+
+
+void log_init(bool format);
+
+
+
+#if 0
+
+#include "main.h"
+#include "common_config.h"
+
+#define FILE_BUF_MAX_LEN            10*STRING_SIZE_HISTORY // Размер временного буфера для отправки/копирования Лог файла
+#define FILE_BUF_MAX_LEN_LOG        10*STRING_SIZE // Размер временного буфера для отправки/копирования Лог файла
+#define STRING_SIZE                 64
+#define STRING_SIZE_HISTORY		  	100
+#define UPS_DATA_STRING_SIZE        47
+
+extern char logFileBuf[FILE_BUF_MAX_LEN];
+
+extern char name_login[50];
+
+
+#define LOG_ENTRY_VERSION 1
+
+extern void log_init(bool format);
+extern int log_test(void);
+extern int log_append(log_entry_t *entry);
+extern int log_fetch(log_entry_t *entry, uint32_t timeout);
+extern int log_rewind(log_entry_t *entry, uint32_t timeout);
+extern int log_discard(log_entry_t *entry, uint32_t timeout);
+
+void log_add(char *log_data);
+void log_event_data(log_type_t type, char *data);
+
+/**
+  * @brief  Возвращает true если журнал проинициализирован
+  */
+bool LOG_IsInit();
+
+/**
+  * @brief  Отключает журнал для безопасной перезагрузки
+  */
+bool LOG_Disable(void);
+
+/**
+  * @brief  Возвращает общее количество страниц
+  */
+uint32_t LOG_GetPageCount(void);
+uint32_t LOG_GetTotalSTRCount(void);
+void LOG_GetPage(char *str, uint32_t page);
+void LOG_GetPage_tabs(char *str, uint32_t page);
+uint32_t LOG_GetData(int ptr, char *str, uint32_t size, bool start);
+
+uint32_t History_GetPageCount(void);
+uint32_t History_GetTotalSTRCount(void);
+void History_GetPage(char *str, uint32_t page);
+uint32_t History_GetData(int ptr, char *str, uint32_t size, bool start);
+void History_GetPage_tabs(char *str, uint32_t page);
+
+#endif
+
+#endif /* __LOG_H */

+ 16 - 124
fw/modules/spi_flash/spi_flash.c

@@ -27,7 +27,7 @@
 #define SR_SRWD	(1 << 7)
 
 
-static spi_flash_desc_t spi_flash_desc;
+spi_flash_desc_t spi_flash_desc;
 
 
 //
@@ -78,7 +78,7 @@ static inline void send_addr(int addr)
 static int spi_flash_read_sfdp(int addr, void *buf, size_t len) 
 {
     uint8_t *ptr = (uint8_t*)buf;
-	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+
 	SPI_FLASH_CS_L();
 	spi_tx_rx(CMD_RDSFDP);
 	send_addr(addr);
@@ -86,7 +86,7 @@ static int spi_flash_read_sfdp(int addr, void *buf, size_t len)
 	while (len--)
         *ptr++ = spi_tx_rx(0);
 	SPI_FLASH_CS_H();
-	//xSemaphoreGive(spi_mutex);
+
 	return 0;
 }
 
@@ -95,14 +95,14 @@ ssize_t spi_flash_read(int addr, void *buf, size_t len, uint32_t timeout)
 {
     uint8_t *ptr = (uint8_t*)buf;
 	(void)timeout;
-	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+	
 	SPI_FLASH_CS_L();
 	spi_tx_rx(CMD_READ);
 	send_addr(addr);
 	while (len--)
         *ptr++ = spi_tx_rx(0);
 	SPI_FLASH_CS_H();
-	//xSemaphoreGive(spi_mutex);
+
 	return len;
 }
 
@@ -117,7 +117,7 @@ uint16_t spi_flash_pp(int addr, const void *buf, size_t len, uint32_t timeout)
 	if ((addr & 0xFF) + len > 0xFF)
 	    len = 0x100 - (addr & 0xFF);
 	ret = len;
-	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+
 	SPI_FLASH_CS_L();
 	spi_tx_rx(CMD_WREN);
 	SPI_FLASH_CS_H();
@@ -132,7 +132,7 @@ uint16_t spi_flash_pp(int addr, const void *buf, size_t len, uint32_t timeout)
 	SPI_FLASH_CS_H();
 
 	wait_write_end();
-	//xSemaphoreGive(spi_mutex);
+
 	return ret;
 }
 
@@ -153,7 +153,7 @@ ssize_t spi_flash_write(int addr, const void *buf, size_t len, uint32_t timeout)
 int spi_flash_chip_erase(uint32_t timeout) 
 {
 	(void)timeout;
-	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+
 	SPI_FLASH_CS_L();
 	spi_tx_rx(CMD_WREN);
 	SPI_FLASH_CS_H();
@@ -164,7 +164,7 @@ int spi_flash_chip_erase(uint32_t timeout)
 	SPI_FLASH_CS_H();
 
 	wait_write_end();
-	//xSemaphoreGive(spi_mutex);
+
 	return 0;
 }
 
@@ -172,7 +172,7 @@ int spi_flash_chip_erase(uint32_t timeout)
 int spi_flash_erase_sector(int addr, uint32_t timeout) 
 {
 	(void)timeout;
- 	//xSemaphoreTake(spi_mutex, portMAX_DELAY);
+
 	SPI_FLASH_CS_L();
 	spi_tx_rx(CMD_WREN);
 	SPI_FLASH_CS_H();
@@ -185,7 +185,7 @@ int spi_flash_erase_sector(int addr, uint32_t timeout)
 	SPI_FLASH_CS_H();
 
 	wait_write_end();
- 	//xSemaphoreGive(spi_mutex);
+
 	return 0;
 }
 
@@ -212,125 +212,17 @@ bool spi_flash_is_init(void) {
 //
 bool spi_flash_init(void) 
 {
-    bool ret = false;
-    uint8_t tmp[4] = {0};
-    
-    SPI_FLASH_CS_H();
-  
-    spi_flash_get_id(tmp);
-    
-    if (!(tmp[0] == 0x1F && tmp[1] == 0x86 && tmp[2] == 0x01))
-    {
-        ret = false;
-        
-        spi_flash_read_sfdp(0, tmp, 4);
-        if (!(tmp[0] == 0x53 && tmp[1] == 0x46 && tmp[2] == 0x44 && tmp[3] == 0x50))
-            ret = false;
-        else 
-            ret = true;
-    }
-    else 
-        ret = true;
-      
-    if (ret)
-    {
-        spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
-        spi_flash_desc.sector_erase_op = 32;
-        spi_flash_desc.sector_count = 512;
-        spi_flash_desc.present = true;
-        return true;
-    }
-    else    
-        return false;
-  
-#if 0  
-	uint32_t i, ptable, bitsize = 0;
-	uint8_t tmp[4];
-
-	spi_flash_desc.present = false;
-
-	// check SFDP magic
-    
-    SPI_FLASH_CS_H();
+    spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
+    spi_flash_desc.sector_erase_op = 32;
+    spi_flash_desc.sector_count = 512;
+    spi_flash_desc.present = true;
     
-	spi_flash_read_sfdp(0, tmp, 4);
-	if (!(tmp[0] == 0x53 && tmp[1] == 0x46 && 
-		tmp[2] == 0x44 && tmp[3] == 0x50))
-		return 0;
-
-	// get parameter headers count
-	spi_flash_read_sfdp(0x06, tmp, 1);
-
-	// find first jedec pheader (with ID == 0)
-	for (ptable = 0x08, i = 0; i <= tmp[0]; i++, ptable += 8) {
-		spi_flash_read_sfdp(ptable, tmp, 1);
-		if (tmp[0] == 0)
-			break;
-	}
-
-	// read ptable pointer from pheader
-	spi_flash_read_sfdp(ptable + 4, &ptable, 3);
-
-	// get flash density (size in bits)
-	if (spi_flash_read_sfdp(ptable + 4, &bitsize, 4) < 0 || !bitsize)
-		return 0;
-
-	// find smallest available sector
-	for (i = 0; i < 4; i++) {
-		tmp[0] = 0;
-		if (spi_flash_read_sfdp(ptable + 0x1C + i*2, &tmp, 2) >= 0 &&
-				tmp[0]) {
-			spi_flash_desc.sector_size = 1 << tmp[0];
-			spi_flash_desc.sector_erase_op = tmp[1];
-			spi_flash_desc.sector_count = (bitsize + 1) >> (3 + tmp[0]);
-			break;
-		}
-	}
-	if (!spi_flash_desc.sector_size)
-		return 0;
-
-	spi_flash_desc.present = true;
-#endif    
-	return 1;
-}
-
-
-/*
-bool spi_flash_init(void){
-	//spi_init_();
-	spi_flash_desc.sector_size = SPI_FLASH_SECTOR_SIZE;
-	spi_flash_desc.sector_erase_op = 32;
-	spi_flash_desc.sector_count = 512;
-	spi_flash_desc.present = true;
-
-	return 1;
+    return true;
 }
-*/
 
-/*
-void init_spi_mutex(void)
-{
-	spi_mutex = xSemaphoreCreateMutex();
-}
-*/
 
 // -------------------------------------------------------------------------- //
 
-void spi_flash_get_id(uint8_t *buf)
-{
-	SPI_FLASH_CS_L();
-	spi_tx_rx(0x9F);
-    
-    *buf++ = spi_tx_rx(0);
-    *buf++ = spi_tx_rx(0);
-    *buf++ = spi_tx_rx(0);
-    
-	SPI_FLASH_CS_H();
-}
-
-// -------------------------------------------------------------------------- //
-
-
 
 #if INCLUDE_SPI_TEST
 const uint8_t txbuf1[] = "This film came out on DVD yesterday and I rushed to buy it. \

+ 5 - 0
fw/modules/spi_flash/spi_flash.h

@@ -14,6 +14,7 @@
 
 #include "FreeRTOS.h"
 
+#define LOG_FLASH_SECTOR_OFFSET	4
 
 #define SPI_FLASH_SECTOR_SIZE		        4096
 #define SPI_FLASH_SECTORS_IN_BLOCK_NUMBER   16
@@ -48,10 +49,14 @@ unsigned int spi_flash_get_total_size(void);
 
 
 bool spi_flash_is_init(void);
+
 bool spi_flash_test(void);
 
 void spi_flash_get_id(uint8_t *buf);
 
+extern spi_flash_desc_t spi_flash_desc;
+
+
 // -------------------------------------------------------------------------- //
 
 #endif /* SPI_FLASH_H */

+ 142 - 0
fw/modules/user_ringfs/user_ringfs.c

@@ -0,0 +1,142 @@
+#include "at32f403a_407.h"
+#include "dac_transport.h"
+#include "FreeRTOS.h"
+#include "task.h"
+#include "misc.h"
+#include <stdio.h>
+#include <stdbool.h>
+
+
+#define DAC_SPI  SPI4
+
+
+// PE12 - CS_DAC1
+// PD1 - CS_DAC2
+// PD0 - CS_DAC3
+// PD3 - CS_DAC4
+void dac_gpio_init(void)
+{
+    gpio_init_type gpio_initstructure;
+    spi_init_type spi_init_struct;
+  
+    crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
+    crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
+    crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);
+    
+    gpio_pin_remap_config(SPI4_GMUX_0001, TRUE);
+  
+    // SCK
+    gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;  
+    gpio_initstructure.gpio_pull           = GPIO_PULL_DOWN;  
+    gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;  
+    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
+    gpio_initstructure.gpio_pins           = GPIO_PINS_11;
+    gpio_init(GPIOE, &gpio_initstructure);
+    
+    // MOSI
+    gpio_initstructure.gpio_pull           = GPIO_PULL_UP;  
+    gpio_initstructure.gpio_mode           = GPIO_MODE_MUX; 
+    gpio_initstructure.gpio_pins           = GPIO_PINS_14;
+    gpio_init(GPIOE, &gpio_initstructure);
+     
+    // CS 
+    gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;  
+    gpio_initstructure.gpio_pull           = GPIO_PULL_UP;  
+    gpio_initstructure.gpio_mode           = GPIO_MODE_OUTPUT;  
+    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
+    gpio_initstructure.gpio_pins           = GPIO_PINS_12;
+    gpio_init(GPIOE, &gpio_initstructure);
+    
+    gpio_initstructure.gpio_pins           = GPIO_PINS_0 | GPIO_PINS_1 | GPIO_PINS_3;
+    gpio_init(GPIOD, &gpio_initstructure);
+    
+    dac_cs(CH_DAC_1, false);
+    dac_cs(CH_DAC_2, false);
+    dac_cs(CH_DAC_3, false);
+    dac_cs(CH_DAC_4, false);
+    
+
+    crm_periph_clock_enable(CRM_SPI4_PERIPH_CLOCK, TRUE);
+    
+    spi_default_para_init(&spi_init_struct);
+    spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
+    spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
+    spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_2; //SPI_MCLK_DIV_2;
+    spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB; //SPI_FIRST_BIT_LSB; 
+    spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
+    spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW; // SPI_CLOCK_POLARITY_HIGH;
+    spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
+    spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
+    
+    spi_init(DAC_SPI, &spi_init_struct);
+    
+    spi_enable(DAC_SPI, TRUE);
+}
+
+//
+void dac_test(DAC_CHANNEL ch, uint16_t val)
+{
+    uint8_t ret;
+    
+    dac_cs(ch, true);
+
+    while (spi_i2s_flag_get(DAC_SPI, SPI_I2S_TDBE_FLAG) == RESET);
+    DAC_SPI->dt = 0;
+    
+    while (spi_i2s_flag_get(DAC_SPI, SPI_I2S_TDBE_FLAG) == RESET);
+    DAC_SPI->dt = val >> 8;
+    
+    while (spi_i2s_flag_get(DAC_SPI, SPI_I2S_TDBE_FLAG) == RESET);
+    DAC_SPI->dt = val;
+ 
+    
+    dac_cs(ch, false);
+}
+
+//
+void dac_cs(DAC_CHANNEL ch, bool state)
+{
+    switch (ch)
+    {
+        case CH_DAC_1:
+            state == true ? gpio_bits_reset(GPIOE, GPIO_PINS_12) : gpio_bits_set(GPIOE, GPIO_PINS_12);
+        break;
+        
+        case CH_DAC_2:
+            state == true ? gpio_bits_reset(GPIOD, GPIO_PINS_1) : gpio_bits_set(GPIOD, GPIO_PINS_1);
+        break;
+        
+        case CH_DAC_3:
+            state == true ? gpio_bits_reset(GPIOD, GPIO_PINS_0) : gpio_bits_set(GPIOD, GPIO_PINS_0);
+        break;
+        
+        case CH_DAC_4:
+            state == true ? gpio_bits_reset(GPIOD, GPIO_PINS_3) : gpio_bits_set(GPIOD, GPIO_PINS_3);
+        break;
+        
+        default: break;
+    }
+}
+
+//
+void dac_task(void *params)
+{
+    for (;;)
+    {
+        vTaskDelay(3000);
+        dac_test(CH_DAC_1, 0);  
+        dac_test(CH_DAC_2, 0);  
+        dac_test(CH_DAC_3, 0);  
+        dac_test(CH_DAC_4, 0);  
+        vTaskDelay(3000);
+        dac_test(CH_DAC_1, 0x7EFF);  
+        dac_test(CH_DAC_2, 0x7EFF);  
+        dac_test(CH_DAC_3, 0x7EFF);  
+        dac_test(CH_DAC_4, 0x7EFF);  
+        vTaskDelay(3000);
+        dac_test(CH_DAC_1, 0xFFFF);  
+        dac_test(CH_DAC_2, 0xFFFF);  
+        dac_test(CH_DAC_3, 0xFFFF);  
+        dac_test(CH_DAC_4, 0xFFFF);  
+    }
+}

+ 29 - 0
fw/modules/user_ringfs/user_ringfs.h

@@ -0,0 +1,29 @@
+#ifndef __DAC_TRANSPORT_H
+#define __DAC_TRANSPORT_H
+
+#include <stdbool.h>
+
+typedef enum
+{
+    CH_DAC_1 = 0,
+    CH_DAC_2,
+    CH_DAC_3,
+    CH_DAC_4,    
+    
+} DAC_CHANNEL;
+
+//
+void dac_gpio_init(void);
+
+//
+void dac_test(DAC_CHANNEL ch, uint16_t val);
+
+//
+void dac_cs(DAC_CHANNEL ch, bool state);
+
+//
+void dac_task(void *params);
+
+
+#endif  // __DAC_TRANSPORT_H
+

+ 19 - 11
fw/user/main.c

@@ -56,9 +56,9 @@ int main(void)
     
     xTaskCreate(button_task, "button_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
  
-    xTaskCreate(adc_task, "adc_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
+    //xTaskCreate(adc_task, "adc_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
     
-    //xTaskCreate(dac_task, "dac_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
+    xTaskCreate(dac_task, "dac_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
     
     taskEXIT_CRITICAL();
     
@@ -149,19 +149,19 @@ void init_task(void *argument)
     
 // -------------------------------------------------------------------------- //    
 // Сдвиговые регистры и мультиплексоры    
-    sh_init();
+    //sh_init();
     
 // Тесты аналоговых входов    
     //ai_connect_test();
-    ai_mode_test();
+    //ai_mode_test();
     //ai_connect_channel(V_ISO);  
-    ai_connect_channel(AN_INP_1);  
-    ai_connect_channel(AN_INP_7);  
+    //ai_connect_channel(AN_INP_1);  
+    //ai_connect_channel(AN_INP_7);  
     
 // -------------------------------------------------------------------------- //    
 // DAC    
-    //dac_gpio_init();
-    //dac_test(CH_DAC_1, 0);  
+    dac_gpio_init();
+    dac_test(CH_DAC_1, 30000);  
 /*    
     dac_test(CH_DAC_2, 10000);  
     dac_test(CH_DAC_3, 10000);
@@ -182,13 +182,21 @@ void init_task(void *argument)
     // Тесты таймеров
     //mux_led_test_init();
     
-#if 0    
+// -------------------------------------------------------------------------- //    
+// SPI flash
+#if 1    
     // Тесты SPI flash
     common_spi_init();
-    InitFS(PRIM_DRIVE);
-    spi_flash_test();
+    spi_flash_init();
+    //InitFS(PRIM_DRIVE);
+    //spi_flash_test();
 #endif     
     
+// -------------------------------------------------------------------------- //    
+// LOG
+    
+    log_init(false);
+    
     // Тесты USB
     //usb_eth_init();
      

+ 1 - 0
fw/user/main.h

@@ -34,6 +34,7 @@
 #include "shift_reg.h"
 #include "analog_input.h"
 #include "dac_transport.h"
+#include "log.h"
 #include <stdio.h>
 #include <stdbool.h>
 #include <string.h>

+ 450 - 0
libs/thirdparty/ringfs/ringfs.c

@@ -0,0 +1,450 @@
+/*
+ * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
+ * This program is free software. It comes without any warranty, to the extent
+ * permitted by applicable law. You can redistribute it and/or modify it under
+ * the terms of the Do What The Fuck You Want To Public License, Version 2, as
+ * published by Sam Hocevar. See the COPYING file for more details.
+ */
+
+/**
+ * @defgroup ringfs_impl RingFS implementation
+ * @details
+ *
+ * @{
+ */
+
+#include <ringfs.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "main.h"
+
+
+/**
+ * @defgroup sector
+ * @{
+ */
+
+enum sector_status {
+    SECTOR_ERASED     = 0xFFFFFFFF, /**< Default state after NOR flash erase. */
+    SECTOR_FREE       = 0xFFFFFF00, /**< Sector erased. */
+    SECTOR_IN_USE     = 0xFFFF0000, /**< Sector contains valid data. */
+    SECTOR_ERASING    = 0xFF000000, /**< Sector should be erased. */
+    SECTOR_FORMATTING = 0x00000000, /**< The entire partition is being formatted. */
+};
+
+struct sector_header {
+    uint32_t status;
+    uint32_t version;
+};
+
+static int _sector_address(struct ringfs *fs, int sector_offset)
+{
+    return (fs->flash->sector_offset + sector_offset) * fs->flash->sector_size;
+}
+
+static int _sector_get_status(struct ringfs *fs, int sector, uint32_t *status)
+{
+    return fs->flash->read(fs->flash,
+            _sector_address(fs, sector) + offsetof(struct sector_header, status),
+            status, sizeof(*status));
+}
+
+static int _sector_set_status(struct ringfs *fs, int sector, uint32_t status)
+{
+    return fs->flash->program(fs->flash,
+            _sector_address(fs, sector) + offsetof(struct sector_header, status),
+            &status, sizeof(status));
+}
+
+static int _sector_free(struct ringfs *fs, int sector)
+{
+    int sector_addr = _sector_address(fs, sector);
+    _sector_set_status(fs, sector, SECTOR_ERASING);
+    fs->flash->sector_erase(fs->flash, sector_addr);
+    fs->flash->program(fs->flash,
+            sector_addr + offsetof(struct sector_header, version),
+            &fs->version, sizeof(fs->version));
+    _sector_set_status(fs, sector, SECTOR_FREE);
+    return 0;
+}
+
+/**
+ * @}
+ * @defgroup slot
+ * @{
+ */
+
+enum slot_status {
+    SLOT_ERASED   = 0xFFFFFFFF, /**< Default state after NOR flash erase. */
+    SLOT_RESERVED = 0xFFFFFF00, /**< Write started but not yet committed. */
+    SLOT_VALID    = 0xFFFF0000, /**< Write committed, slot contains valid data. */
+    SLOT_GARBAGE  = 0xFF000000, /**< Slot contents discarded and no longer valid. */
+};
+
+struct slot_header {
+    uint32_t status;
+};
+
+static int _slot_address(struct ringfs *fs, struct ringfs_loc *loc)
+{
+    return _sector_address(fs, loc->sector) +
+           sizeof(struct sector_header) +
+           (sizeof(struct slot_header) + fs->object_size) * loc->slot;
+}
+
+static int _slot_get_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t *status)
+{
+    return fs->flash->read(fs->flash,
+            _slot_address(fs, loc) + offsetof(struct slot_header, status),
+            status, sizeof(*status));
+}
+
+static int _slot_set_status(struct ringfs *fs, struct ringfs_loc *loc, uint32_t status)
+{
+    return fs->flash->program(fs->flash, 
+            _slot_address(fs, loc) + offsetof(struct slot_header, status),
+            &status, sizeof(status));
+}
+
+/**
+ * @}
+ * @defgroup loc
+ * @{
+ */
+
+static bool _loc_equal(struct ringfs_loc *a, struct ringfs_loc *b)
+{
+    return (a->sector == b->sector) && (a->slot == b->slot);
+}
+
+/** Advance a location to the beginning of the next sector. */
+static void _loc_advance_sector(struct ringfs *fs, struct ringfs_loc *loc)
+{
+    loc->slot = 0;
+    loc->sector++;
+    if (loc->sector >= fs->flash->sector_count)
+        loc->sector = 0;
+}
+
+/** Advance a location to the next slot, advancing the sector too if needed. */
+static void _loc_advance_slot(struct ringfs *fs, struct ringfs_loc *loc)
+{
+    loc->slot++;
+    if (loc->slot >= fs->slots_per_sector)
+        _loc_advance_sector(fs, loc);
+}
+
+/**
+ * @}
+ */
+
+/* And here we go. */
+
+int ringfs_init(struct ringfs *fs, struct ringfs_flash_partition *flash, uint32_t version, int object_size)
+{
+    /* Copy arguments to instance. */
+    fs->flash = flash;
+    fs->version = version;
+    fs->object_size = object_size;
+
+    /* Precalculate commonly used values. */
+    fs->slots_per_sector = (fs->flash->sector_size - sizeof(struct sector_header)) /
+                           (sizeof(struct slot_header) + fs->object_size);
+
+    return 0;
+}
+
+int ringfs_format(struct ringfs *fs)
+{
+    /* Mark all sectors to prevent half-erased filesystems. */
+    for (int sector=0; sector<fs->flash->sector_count; sector++)
+        _sector_set_status(fs, sector, SECTOR_FORMATTING);
+
+    /* Erase, update version, mark as free. */
+    for (int sector=0; sector<fs->flash->sector_count; sector++)
+        _sector_free(fs, sector);
+
+    /* Start reading & writing at the first sector. */
+    fs->read.sector = 0;
+    fs->read.slot = 0;
+    fs->write.sector = 0;
+    fs->write.slot = 0;
+    fs->cursor.sector = 0;
+    fs->cursor.slot = 0;
+
+    return 0;
+}
+
+int ringfs_scan(struct ringfs *fs)
+{
+    uint32_t previous_sector_status = SECTOR_FREE;
+    /* The read sector is the first IN_USE sector *after* a FREE sector
+     * (or the first one). */
+    int read_sector = 0;
+    /* The write sector is the last IN_USE sector *before* a FREE sector
+     * (or the last one). */
+    int write_sector = fs->flash->sector_count - 1;
+    /* There must be at least one FREE sector available at all times. */
+    bool free_seen = false;
+    /* If there's no IN_USE sector, we start at the first one. */
+    bool used_seen = false;
+
+    bool err_sector = true;
+
+    /* Iterate over sectors. */
+    for (int sector=0; sector<fs->flash->sector_count; sector++) {
+        int addr = _sector_address(fs, sector);
+
+        /* Read sector header. */
+        struct sector_header header;
+        fs->flash->read(fs->flash, addr, &header, sizeof(header));
+
+        /* Detect partially-formatted partitions. */
+        if (header.status == SECTOR_FORMATTING) {
+        	DBG printf("ringfs_scan: partially formatted partition\r\n");
+        	if(err_sector){
+				err_sector = false;
+				_sector_free(fs, addr);
+				//fs->flash->read(fs->flash, addr, &header, sizeof(header));
+				header.status = SECTOR_FREE;
+				header.version = fs->version;
+			}else{
+				return -1;
+			}
+        }
+
+        /* Detect and fix partially erased sectors. */
+        if (header.status == SECTOR_ERASING || header.status == SECTOR_ERASED) {
+            _sector_free(fs, addr);
+            header.status = SECTOR_FREE;
+        }
+
+        /* Detect corrupted sectors. */
+        if (header.status != SECTOR_FREE && header.status != SECTOR_IN_USE) {
+        	DBG printf("ringfs_scan: corrupted sector %d\r\n", sector);
+        	if(err_sector){
+				err_sector = false;
+				_sector_free(fs, addr);
+				//fs->flash->read(fs->flash, addr, &header, sizeof(header));
+				header.status = SECTOR_FREE;
+				header.version = fs->version;
+			}else{
+				return -1;
+			}
+        }
+
+        /* Detect obsolete versions. We can't do this earlier because the version
+         * could have been invalid due to a partial erase. */
+        if (header.version != fs->version) {
+        	DBG printf("ringfs_scan: corrupted sector %d\r\n", sector);
+        	DBG printf("ringfs_scan: incompatible version 0x%08"PRIx32"\r\n", header.version);
+            if(err_sector){
+            	err_sector = false;
+            	_sector_free(fs, addr);
+            	//fs->flash->read(fs->flash, addr, &header, sizeof(header));
+            	DBG printf("ringfs_scan: incompatible version 0x%08"PRIx32"\r\n", header.version);
+            	header.status = SECTOR_FREE;
+            	header.version = fs->version;
+            }else{
+            	return -1;
+            }
+        }
+
+        /* Record the presence of a FREE sector. */
+        if (header.status == SECTOR_FREE)
+            free_seen = true;
+
+        /* Record the presence of a IN_USE sector. */
+        if (header.status == SECTOR_IN_USE)
+            used_seen = true;
+
+        /* Update read & write sectors according to the above rules. */
+        if (header.status == SECTOR_IN_USE && previous_sector_status == SECTOR_FREE)
+            read_sector = sector;
+        if (header.status == SECTOR_FREE && previous_sector_status == SECTOR_IN_USE)
+            write_sector = sector-1;
+
+        previous_sector_status = header.status;
+    }
+
+    /* Detect the lack of a FREE sector. */
+    if (!free_seen) {
+    	DBG printf("ringfs_scan: invariant violated: no FREE sector found\r\n");
+        return -1;
+    }
+
+    /* Start writing at the first sector if the filesystem is empty. */
+    if (!used_seen) {
+        write_sector = 0;
+    }
+
+    /* Scan the write sector and skip all occupied slots at the beginning. */
+    fs->write.sector = write_sector;
+    fs->write.slot = 0;
+    while (fs->write.sector == write_sector) {
+        uint32_t status;
+        _slot_get_status(fs, &fs->write, &status);
+        if (status == SLOT_ERASED)
+            break;
+
+        _loc_advance_slot(fs, &fs->write);
+    }
+    /* If the sector was full, we're at the beginning of a FREE sector now. */
+
+    /* Position the read head at the start of the first IN_USE sector, then skip
+     * over garbage/invalid slots until something of value is found or we reach
+     * the write head which means there's no data. */
+    fs->read.sector = read_sector;
+    fs->read.slot = 0;
+    while (!_loc_equal(&fs->read, &fs->write)) {
+        uint32_t status;
+        _slot_get_status(fs, &fs->read, &status);
+        if (status == SLOT_VALID)
+            break;
+
+        _loc_advance_slot(fs, &fs->read);
+    }
+
+    /* Move the read cursor to the read head position. */
+    ringfs_rewind(fs);
+
+    return 0;
+}
+
+int ringfs_capacity(struct ringfs *fs)
+{
+    return fs->slots_per_sector * (fs->flash->sector_count - 1);
+}
+
+int ringfs_count_estimate(struct ringfs *fs)
+{
+    int sector_diff = (fs->write.sector - fs->read.sector + fs->flash->sector_count) %
+        fs->flash->sector_count;
+
+    return sector_diff * fs->slots_per_sector + fs->write.slot - fs->read.slot;
+}
+
+int ringfs_count_exact(struct ringfs *fs)
+{
+    int count = 0;
+
+    /* Use a temporary loc for iteration. */
+    struct ringfs_loc loc = fs->read;
+    while (!_loc_equal(&loc, &fs->write)) {
+        uint32_t status;
+        _slot_get_status(fs, &loc, &status);
+        
+        if (status == SLOT_VALID)
+            count++;
+
+        _loc_advance_slot(fs, &loc);
+    }
+
+    return count;
+}
+
+int ringfs_cursor_position(struct ringfs *fs) {
+    return fs->cursor_position;
+}
+
+int ringfs_append(struct ringfs *fs, const void *object)
+{
+    uint32_t status;
+
+    /*
+     * There are three sectors involved in appending a value:
+     * - the sector where the append happens: it has to be writable
+     * - the next sector: it must be free (invariant)
+     * - the next-next sector: read & cursor heads are moved there if needed
+     */
+
+    /* Make sure the next sector is free. */
+    int next_sector = (fs->write.sector+1) % fs->flash->sector_count;
+    _sector_get_status(fs, next_sector, &status);
+    if (status != SECTOR_FREE) {
+        /* Next sector must be freed. But first... */
+
+        /* Move the read & cursor heads out of the way. */
+        if (fs->read.sector == next_sector)
+            _loc_advance_sector(fs, &fs->read);
+        if (fs->cursor.sector == next_sector)
+            _loc_advance_sector(fs, &fs->cursor);
+
+        /* Free the next sector. */
+        _sector_free(fs, next_sector);
+    }
+
+    /* Now we can make sure the current write sector is writable. */
+    _sector_get_status(fs, fs->write.sector, &status);
+    if (status == SECTOR_FREE) {
+        /* Free sector. Mark as used. */
+        _sector_set_status(fs, fs->write.sector, SECTOR_IN_USE);
+    } else if (status != SECTOR_IN_USE) {
+        printf("ringfs_append: corrupted filesystem\r\n");
+        return -1;
+    }
+
+    /* Preallocate slot. */
+    _slot_set_status(fs, &fs->write, SLOT_RESERVED);
+
+    /* Write object. */
+    fs->flash->program(fs->flash,
+            _slot_address(fs, &fs->write) + sizeof(struct slot_header),
+            object, fs->object_size);
+
+    /* Commit write. */
+    _slot_set_status(fs, &fs->write, SLOT_VALID);
+
+    /* Advance the write head. */
+    _loc_advance_slot(fs, &fs->write);
+
+    return 0;
+}
+
+int ringfs_fetch(struct ringfs *fs, void *object)
+{
+    /* Advance forward in search of a valid slot. */
+    while (!_loc_equal(&fs->cursor, &fs->write)) {
+        uint32_t status;
+
+        _slot_get_status(fs, &fs->cursor, &status);
+
+        if (status == SLOT_VALID) {
+            fs->flash->read(fs->flash,
+                    _slot_address(fs, &fs->cursor) + sizeof(struct slot_header),
+                    object, fs->object_size);
+            _loc_advance_slot(fs, &fs->cursor);
+            fs->cursor_position++;
+            return 0;
+        }
+
+        _loc_advance_slot(fs, &fs->cursor);
+    }
+
+    return -1;
+}
+
+int ringfs_discard(struct ringfs *fs)
+{
+    while (!_loc_equal(&fs->read, &fs->cursor)) {
+        _slot_set_status(fs, &fs->read, SLOT_GARBAGE);
+        _loc_advance_slot(fs, &fs->read);
+    }
+    fs->cursor_position = 0;
+    return 0;
+}
+
+int ringfs_rewind(struct ringfs *fs)
+{
+    fs->cursor = fs->read;
+    fs->cursor_position = 0;
+    return 0;
+}
+
+/**
+ * @}
+ */
+
+/* vim: set ts=4 sw=4 et: */

+ 195 - 0
libs/thirdparty/ringfs/ringfs.h

@@ -0,0 +1,195 @@
+/*
+ * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
+ * This program is free software. It comes without any warranty, to the extent
+ * permitted by applicable law. You can redistribute it and/or modify it under
+ * the terms of the Do What The Fuck You Want To Public License, Version 2, as
+ * published by Sam Hocevar. See the COPYING file for more details.
+ */
+
+#ifndef RINGFS_H
+#define RINGFS_H
+
+/**
+ * @defgroup ringfs_api RingFS API
+ * @{
+ */
+
+#include <stdint.h>
+#if defined ( __GNUC__ )
+#include <unistd.h>
+#endif
+#ifdef PRINTF_STDLIB
+#include <stdio.h>
+#endif
+#ifdef PRINTF_CUSTOM
+#include "tinystdio.h"
+#endif
+
+#if defined ( __ICCARM__ )
+typedef long ssize_t;
+#include <stdlib.h>
+#endif  
+   
+/**
+ * Flash memory+parition descriptor.
+ */
+struct ringfs_flash_partition
+{
+    int sector_size;            /**< Sector size, in bytes. */
+    int sector_offset;          /**< Partition offset, in sectors. */
+    int sector_count;           /**< Partition size, in sectors. */
+
+    /**
+     * Erase a sector.
+     * @param address Any address inside the sector.
+     * @returns Zero on success, -1 on failure.
+     */
+    int (*sector_erase)(struct ringfs_flash_partition *flash, int address);
+    /**
+     * Program flash memory bits by toggling them from 1 to 0.
+     * @param address Start address, in bytes.
+     * @param data Data to program.
+     * @param size Size of data.
+     * @returns size on success, -1 on failure.
+     */
+    ssize_t (*program)(struct ringfs_flash_partition *flash, int address, const void *data, size_t size);
+    /**
+     * Read flash memory.
+     * @param address Start address, in bytes.
+     * @param data Buffer to store read data.
+     * @param size Size of data.
+     * @returns size on success, -1 on failure.
+     */
+    ssize_t (*read)(struct ringfs_flash_partition *flash, int address, void *data, size_t size);
+};
+
+/** @private */
+struct ringfs_loc {
+    int sector;
+    int slot;
+};
+
+/**
+ * RingFS instance. Should be initialized with ringfs_init() befure use.
+ * Structure fields should not be accessed directly.
+ * */
+struct ringfs {
+    /* Constant values, set once at ringfs_init(). */
+    struct ringfs_flash_partition *flash;
+    uint32_t version;
+    int object_size;
+    /* Cached values. */
+    int slots_per_sector;
+
+    /* Read/write pointers. Modified as needed. */
+    struct ringfs_loc read;
+    struct ringfs_loc write;
+    struct ringfs_loc cursor;
+
+    int cursor_position;
+};
+
+/**
+ * Initialize a RingFS instance. Must be called before the instance can be used
+ * with the other ringfs_* functions.
+ *
+ * @param fs RingFS instance to be initialized.
+ * @param flash Flash memory interface. Must be implemented externally.
+ * @param version Object version. Should be incremented whenever the object's
+ *                semantics or size change in a backwards-incompatible way.
+ * @param object_size Size of one stored object, in bytes.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_init(struct ringfs *fs, struct ringfs_flash_partition *flash, uint32_t version, int object_size);
+
+/**
+ * Format the flash memory.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_format(struct ringfs *fs);
+
+/**
+ * Scan the flash memory for a valid filesystem.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_scan(struct ringfs *fs);
+
+/**
+ * Calculate maximum RingFS capacity.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Maximum capacity on success, -1 on failure.
+ */
+int ringfs_capacity(struct ringfs *fs);
+
+/**
+ * Calculate approximate object count.
+ * Runs in O(1).
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Estimated object count on success, -1 on failure.
+ */
+int ringfs_count_estimate(struct ringfs *fs);
+
+/**
+ * Calculate exact object count.
+ * Runs in O(n).
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Exact object count on success, -1 on failure.
+ */
+int ringfs_count_exact(struct ringfs *fs);
+
+/**
+ * Get current cursor position.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Current cursor position, -1 on failure.
+ */
+int ringfs_cursor_position(struct ringfs *fs);
+
+/**
+ * Append an object at the end of the ring. Deletes oldest objects as needed.
+ *
+ * @param fs Initialized RingFS instance.
+ * @param object Object to be stored.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_append(struct ringfs *fs, const void *object);
+
+/**
+ * Fetch next object from the ring, oldest-first. Advances read cursor.
+ *
+ * @param fs Initialized RingFS instance.
+ * @param object Buffer to store retrieved object.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_fetch(struct ringfs *fs, void *object);
+
+/**
+ * Discard all fetched objects up to the read cursor.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_discard(struct ringfs *fs);
+
+/**
+ * Rewind the read cursor back to the oldest object.
+ *
+ * @param fs Initialized RingFS instance.
+ * @returns Zero on success, -1 on failure.
+ */
+int ringfs_rewind(struct ringfs *fs);
+
+/**
+ * @}
+ */
+
+#endif
+
+/* vim: set ts=4 sw=4 et: */

BIN
output/fw.bin


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 455 - 335
project/ewarm/iap/iap.dep


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 595 - 569
project/ewarm/module_universal_io.dep


+ 18 - 0
project/ewarm/module_universal_io.ewp

@@ -376,11 +376,13 @@
                     <state>$PROJ_DIR$\..\..\fw\modules\misc</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\spi_flash</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\user_fatfs</state>
+                    <state>$PROJ_DIR$\..\..\fw\modules\user_ringfs</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\modbus</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\settings</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\adc</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\dac</state>
                     <state>$PROJ_DIR$\..\..\fw\modules\shift_reg</state>
+                    <state>$PROJ_DIR$\..\..\fw\modules\log</state>
                     <state>$PROJ_DIR$\..\..\fw\user</state>
                     <state>$PROJ_DIR$\..\..\libs\thirdparty\freertos\include</state>
                     <state>$PROJ_DIR$\..\..\libs\thirdparty\freertos\portable\IAR\ARM_CM4F</state>
@@ -396,6 +398,7 @@
                     <state>$PROJ_DIR$\..\..\libs\thirdparty\LwIP\port\FreeRTOS</state>
                     <state>$PROJ_DIR$\..\..\libs\thirdparty\LwIP\system</state>
                     <state>$PROJ_DIR$\..\..\libs\thirdparty\LwIP\system\arch</state>
+                    <state>$PROJ_DIR$\..\..\libs\thirdparty\ringfs</state>
                 </option>
                 <option>
                     <name>CCStdIncCheck</name>
@@ -2193,6 +2196,12 @@
                     <name>$PROJ_DIR$\..\..\fw\modules\io\output.c</name>
                 </file>
             </group>
+            <group>
+                <name>log</name>
+                <file>
+                    <name>$PROJ_DIR$\..\..\fw\modules\log\log.c</name>
+                </file>
+            </group>
             <group>
                 <name>misc</name>
                 <file>
@@ -2253,6 +2262,9 @@
                     <name>$PROJ_DIR$\..\..\fw\modules\user_fatfs\user_fatfs.c</name>
                 </file>
             </group>
+            <group>
+                <name>user_ringfs</name>
+            </group>
         </group>
         <group>
             <name>user</name>
@@ -2580,6 +2592,12 @@
                     </file>
                 </group>
             </group>
+            <group>
+                <name>ringfs</name>
+                <file>
+                    <name>$PROJ_DIR$\..\..\libs\thirdparty\ringfs\ringfs.c</name>
+                </file>
+            </group>
             <group>
                 <name>rndis</name>
                 <group>

+ 15 - 0
project/ewarm/module_universal_io.ewt

@@ -2409,6 +2409,12 @@
                     <name>$PROJ_DIR$\..\..\fw\modules\io\output.c</name>
                 </file>
             </group>
+            <group>
+                <name>log</name>
+                <file>
+                    <name>$PROJ_DIR$\..\..\fw\modules\log\log.c</name>
+                </file>
+            </group>
             <group>
                 <name>misc</name>
                 <file>
@@ -2469,6 +2475,9 @@
                     <name>$PROJ_DIR$\..\..\fw\modules\user_fatfs\user_fatfs.c</name>
                 </file>
             </group>
+            <group>
+                <name>user_ringfs</name>
+            </group>
         </group>
         <group>
             <name>user</name>
@@ -2796,6 +2805,12 @@
                     </file>
                 </group>
             </group>
+            <group>
+                <name>ringfs</name>
+                <file>
+                    <name>$PROJ_DIR$\..\..\libs\thirdparty\ringfs\ringfs.c</name>
+                </file>
+            </group>
             <group>
                 <name>rndis</name>
                 <group>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.