From 714d7327458f9d9d871834e9b7faab08ba650e51 Mon Sep 17 00:00:00 2001 From: DrZlo13 Date: Sat, 14 Nov 2020 19:24:38 +0300 Subject: [PATCH] FL-78 Microsd test application (#230) * variable cluster size, label info functions for targets * app record * init, mount, get label, get sn, get space * enable exfat * more stack for the stack god * remove c app and add cpp app * remove MULTI_PARTITION * fix 4gb bug * update app to new template lib, add animated waiting * tiny buffer configuration * write speed benchmark * fnv1a hash library * make DEFAULT_STACK_SIZE and MAX_TASK_COUNT defined per target * fully functional sd card app * build sd test app to release firmware * cpp, not c * light up red led if error * flags for c++ * linking with g++ * suppres snprintf warning * move format work area to heap Co-authored-by: coreglitch --- applications/applications.h | 9 + applications/applications.mk | 14 + applications/sd-card-test/sd-card-test.cpp | 846 ++++++++++++++++++ core/furi_ac.c | 2 - firmware/targets/f2/Src/fatfs/ffconf.h | 8 +- .../targets/f2/Src/fatfs/stm32_adafruit_sd.c | 2 +- .../targets/f2/Src/fatfs/stm32_adafruit_sd.h | 2 +- firmware/targets/f2/api-hal/api-hal-task.h | 6 + firmware/targets/f2/target.mk | 3 + firmware/targets/f3/Src/fatfs/ffconf.h | 8 +- .../targets/f3/Src/fatfs/stm32_adafruit_sd.c | 2 +- .../targets/f3/Src/fatfs/stm32_adafruit_sd.h | 2 +- firmware/targets/f3/api-hal/api-hal-task.h | 6 + firmware/targets/f3/target.mk | 3 + firmware/targets/local/api-hal/api-hal-task.h | 6 + firmware/targets/local/fatfs/ffconf.h | 8 +- lib/fnv1a-hash/fnv1a-hash.c | 10 + lib/fnv1a-hash/fnv1a-hash.h | 39 + lib/lib.mk | 6 +- make/rules.mk | 2 +- make/toolchain.mk | 1 + 21 files changed, 965 insertions(+), 20 deletions(-) create mode 100644 applications/sd-card-test/sd-card-test.cpp create mode 100644 lib/fnv1a-hash/fnv1a-hash.c create mode 100644 lib/fnv1a-hash/fnv1a-hash.h diff --git a/applications/applications.h b/applications/applications.h index 0759eb6c..8c9018e0 100644 --- a/applications/applications.h +++ b/applications/applications.h @@ -32,6 +32,7 @@ void lf_rfid_workaround(void* p); void nfc_task(void* p); void irukagotchi_task(void* p); void power_task(void* p); +void sd_card_test(void* p); void application_vibro(void* p); void app_gpio_test(void* p); @@ -111,6 +112,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { {.app = coreglitch_demo_0, .name = "coreglitch_demo_0", .libs = {0}}, #endif +#ifdef APP_SD_TEST + {.app = sd_card_test, .name = "sd_card_test", .libs = {1, FURI_LIB{"gui_task"}}}, +#endif + #ifdef APP_GPIO_DEMO { .app = app_gpio_test, @@ -148,6 +153,10 @@ const FlipperStartupApp FLIPPER_APPS[] = { {.app = coreglitch_demo_0, .name = "coreglitch_demo_0", .libs = {0}}, #endif +#ifdef BUILD_SD_TEST + {.app = sd_card_test, .name = "sd_card_test", .libs = {1, FURI_LIB{"gui_task"}}}, +#endif + #ifdef BUILD_VIBRO_DEMO {.app = application_vibro, .name = "application_vibro", .libs = {1, FURI_LIB{"input_task"}}}, #endif diff --git a/applications/applications.mk b/applications/applications.mk index 16a20cc0..a4d99c1b 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -20,6 +20,7 @@ BUILD_CC1101 = 1 BUILD_LF_RFID = 1 BUILD_SPEAKER_DEMO = 1 BUILD_VIBRO_DEMO = 1 +BUILD_SD_TEST = 1 BUILD_GPIO_DEMO = 1 endif @@ -193,6 +194,19 @@ APP_INPUT = 1 APP_GUI = 1 endif +APP_SD_TEST ?= 0 +ifeq ($(APP_SD_TEST), 1) +CFLAGS += -DAPP_SD_TEST +BUILD_SD_TEST = 1 +endif +BUILD_SD_TEST ?= 0 +ifeq ($(BUILD_SD_TEST), 1) +CFLAGS += -DBUILD_SD_TEST +CPP_SOURCES += $(wildcard $(APP_DIR)/sd-card-test/*.cpp) +APP_INPUT = 1 +APP_GUI = 1 +endif + APP_SPEAKER_DEMO ?= 0 ifeq ($(APP_SPEAKER_DEMO), 1) CFLAGS += -DAPP_SPEAKER_DEMO diff --git a/applications/sd-card-test/sd-card-test.cpp b/applications/sd-card-test/sd-card-test.cpp new file mode 100644 index 00000000..1fd74030 --- /dev/null +++ b/applications/sd-card-test/sd-card-test.cpp @@ -0,0 +1,846 @@ +#include "app-template.h" +#include "fatfs/ff.h" +#include "stm32_adafruit_sd.h" +#include "fnv1a-hash.h" + +// event enumeration type +typedef uint8_t event_t; + +class SdTestState { +public: + // state data + static const uint8_t lines_count = 6; + const char* line[lines_count]; + + // state initializer + SdTestState() { + for(uint8_t i = 0; i < lines_count; i++) { + line[i] = ""; + } + } +}; + +// events class +class SdTestEvent { +public: + // events enum + static const event_t EventTypeTick = 0; + static const event_t EventTypeKey = 1; + + // payload + union { + InputEvent input; + } value; + + // event type + event_t type; +}; + +// our app derived from base AppTemplate class +// with template variables +class SdTest : public AppTemplate { +public: + // vars + GpioPin* red_led_record; + GpioPin* green_led_record; + FATFS sd_fat_fs; + char sd_path[6]; + const uint32_t benchmark_data_size = 4096; + uint8_t* benchmark_data; + + // funcs + void run(); + void render(CanvasApi* canvas); + template void set_text(std::initializer_list list); + template void set_error(std::initializer_list list); + const char* fatfs_error_desc(FRESULT res); + void wait_for_button(Input input_button); + bool ask(Input input_button_cancel, Input input_button_ok); + void blink_red(); + void set_red(); + void blink_green(); + + // "tests" + void detect_sd_card(); + void show_warning(); + void init_sd_card(); + bool is_sd_card_formatted(); + void ask_and_format_sd_card(); + void mount_sd_card(); + void format_sd_card(); + void get_sd_card_info(); + + void prepare_benchmark_data(); + void free_benchmark_data(); + void write_benchmark(); + uint32_t write_benchmark_internal(const uint32_t size, const uint32_t tcount); + + void read_benchmark(); + uint32_t read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file); + + void hash_benchmark(); +}; + +// start app +void SdTest::run() { + // create pin + GpioPin red_led = led_gpio[0]; + GpioPin green_led = led_gpio[1]; + + // TODO open record + red_led_record = &red_led; + green_led_record = &green_led; + + // configure pin + gpio_init(red_led_record, GpioModeOutputOpenDrain); + gpio_init(green_led_record, GpioModeOutputOpenDrain); + + detect_sd_card(); + show_warning(); + init_sd_card(); + if(!is_sd_card_formatted()) { + format_sd_card(); + } else { + ask_and_format_sd_card(); + } + mount_sd_card(); + get_sd_card_info(); + prepare_benchmark_data(); + write_benchmark(); + read_benchmark(); + hash_benchmark(); + free_benchmark_data(); + + set_text({ + "test complete", + "", + "", + "", + "", + "press BACK to exit", + }); + wait_for_button(InputBack); + exit(); +} + +// detect sd card insertion +void SdTest::detect_sd_card() { + const uint8_t str_buffer_size = 40; + const uint8_t dots_animation_size = 4; + char str_buffer[str_buffer_size]; + const char dots[dots_animation_size][4] = {"", ".", "..", "..."}; + uint8_t i = 0; + + // detect sd card pin + while(!hal_gpio_read_sd_detect()) { + delay(100); + + snprintf(str_buffer, str_buffer_size, "Waiting%s", dots[i]); + set_text({static_cast(str_buffer), "Please insert sd card"}); + + if(i < (dots_animation_size - 1)) { + i++; + } else { + i = 0; + } + } + + blink_green(); +} + +// show warning about test +void SdTest::show_warning() { + set_text( + {"!!Warning!!", + "during the tests", + "card may be formatted", + "or data on card may be lost", + "", + "press UP DOWN OK to continue"}); + + wait_for_button(InputUp); + wait_for_button(InputDown); + wait_for_button(InputOk); +} + +// init low level driver +void SdTest::init_sd_card() { + uint8_t bsp_result = BSP_SD_Init(); + + // BSP_SD_OK = 0 + if(bsp_result) { + set_error({"SD card init error", "BSP error"}); + } + blink_green(); +} + +// test, if sd card need to be formatted +bool SdTest::is_sd_card_formatted() { + FRESULT result; + set_text({"checking if card needs to be formatted"}); + + result = f_mount(&sd_fat_fs, sd_path, 1); + if(result == FR_NO_FILESYSTEM) { + return false; + } else { + return true; + } +} + +void SdTest::ask_and_format_sd_card() { + set_text({"Want to format sd card?", "", "", "", "", "LEFT to CANCEL | RIGHT to OK"}); + if(ask(InputLeft, InputRight)) { + format_sd_card(); + } +} + +// mount sd card +void SdTest::mount_sd_card() { + FRESULT result; + set_text({"mounting sdcard"}); + + result = f_mount(&sd_fat_fs, sd_path, 1); + if(result) { + set_error({"SD card mount error", fatfs_error_desc(result)}); + } + blink_green(); +} + +// format sd card +void SdTest::format_sd_card() { + FRESULT result; + BYTE* work_area; + + set_text({"formatting sdcard", "procedure can be lengthy", "please wait"}); + delay(100); + + work_area = static_cast(malloc(_MAX_SS)); + if(work_area == NULL) { + set_error({"SD card format error", "cannot allocate memory"}); + } + + result = f_mkfs(sd_path, (FM_FAT | FM_FAT32 | FM_EXFAT), 0, work_area, _MAX_SS); + free(work_area); + + if(result) { + set_error({"SD card format error", fatfs_error_desc(result)}); + } + + result = f_setlabel("Flipper SD"); + if(result) { + set_error({"SD card set label error", fatfs_error_desc(result)}); + } + blink_green(); +} + +// get info about sd card, label, sn +// sector, cluster, total and free size +void SdTest::get_sd_card_info() { + const uint8_t str_buffer_size = 26; + char str_buffer[4][str_buffer_size]; + char volume_label[128]; + DWORD serial_num; + FRESULT result; + FATFS* fs; + DWORD free_clusters, free_sectors, total_sectors; + + // suppress "'%s' directive output may be truncated" warning about snprintf + int __attribute__((unused)) snprintf_count = 0; + + // get label and s/n + result = f_getlabel(sd_path, volume_label, &serial_num); + if(result) set_error({"f_getlabel error", fatfs_error_desc(result)}); + + snprintf_count = snprintf(str_buffer[0], str_buffer_size, "Label: %s", volume_label); + snprintf(str_buffer[1], str_buffer_size, "S/N: %lu", serial_num); + + set_text( + {static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + "", + "", + "", + "press OK to continue"}); + + blink_green(); + + wait_for_button(InputOk); + + // get total and free space + result = f_getfree(sd_path, &free_clusters, &fs); + if(result) set_error({"f_getfree error", fatfs_error_desc(result)}); + + total_sectors = (fs->n_fatent - 2) * fs->csize; + free_sectors = free_clusters * fs->csize; + + snprintf(str_buffer[0], str_buffer_size, "Cluster: %d sectors", fs->csize); + snprintf(str_buffer[1], str_buffer_size, "Sector: %d bytes", fs->ssize); + snprintf(str_buffer[2], str_buffer_size, "%lu KB total", total_sectors / 1024 * fs->ssize); + snprintf(str_buffer[3], str_buffer_size, "%lu KB free", free_sectors / 1024 * fs->ssize); + + set_text( + {static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + static_cast(str_buffer[2]), + static_cast(str_buffer[3]), + "", + "press OK to continue"}); + + blink_green(); + + wait_for_button(InputOk); +} + +// prepare benchmark data (allocate data in ram) +void SdTest::prepare_benchmark_data() { + set_text({"preparing benchmark data"}); + benchmark_data = static_cast(malloc(benchmark_data_size)); + + if(benchmark_data == NULL) { + set_error({"cannot allocate buffer", "for benchmark data"}); + } + + for(size_t i = 0; i < benchmark_data_size; i++) { + benchmark_data[i] = static_cast(i); + } + + set_text({"benchmark data prepared"}); +} + +void SdTest::free_benchmark_data() { + free(benchmark_data); +} + +// write speed test +void SdTest::write_benchmark() { + const uint32_t b1_size = 1; + const uint32_t b8_size = 8; + const uint32_t b32_size = 32; + const uint32_t b256_size = 256; + const uint32_t b4096_size = 4096; + + const uint32_t benchmark_data_size = 16384 * 4; + + uint32_t benchmark_bps = 0; + + const uint8_t str_buffer_size = 32; + char str_buffer[6][str_buffer_size] = {"", "", "", "", "", ""}; + auto string_list = { + static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + static_cast(str_buffer[2]), + static_cast(str_buffer[3]), + static_cast(str_buffer[4]), + static_cast(str_buffer[5])}; + + set_text({"write speed test", "procedure can be lengthy", "please wait"}); + delay(100); + + // 1b test + benchmark_bps = write_benchmark_internal(b1_size, benchmark_data_size / b1_size); + snprintf(str_buffer[0], str_buffer_size, "1-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 8b test + benchmark_bps = write_benchmark_internal(b8_size, benchmark_data_size / b8_size); + snprintf(str_buffer[1], str_buffer_size, "8-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 32b test + benchmark_bps = write_benchmark_internal(b32_size, benchmark_data_size / b32_size); + snprintf(str_buffer[2], str_buffer_size, "32-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 256b test + benchmark_bps = write_benchmark_internal(b256_size, benchmark_data_size / b256_size); + snprintf(str_buffer[3], str_buffer_size, "256-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 4096b test + benchmark_bps = write_benchmark_internal(b4096_size, benchmark_data_size / b4096_size); + snprintf(str_buffer[4], str_buffer_size, "4096-byte: %lu bps", benchmark_bps); + snprintf(str_buffer[5], str_buffer_size, "press OK to continue"); + set_text(string_list); + + blink_green(); + + wait_for_button(InputOk); +} + +uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count) { + uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_written; + FRESULT result; + FIL file; + + const uint8_t str_buffer_size = 32; + char str_buffer[str_buffer_size]; + + result = f_open(&file, "write.test", FA_WRITE | FA_OPEN_ALWAYS); + if(result) { + snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); + set_error({"cannot open file ", static_cast(str_buffer)}); + } + + start_tick = osKernelGetTickCount(); + for(size_t i = 0; i < count; i++) { + result = f_write(&file, benchmark_data, size, reinterpret_cast(&bytes_written)); + if(bytes_written != size || result) { + snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); + set_error({"cannot write to file ", static_cast(str_buffer)}); + } + } + stop_tick = osKernelGetTickCount(); + + result = f_close(&file); + if(result) { + snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); + set_error({"cannot close file ", static_cast(str_buffer)}); + } + + benchmark_time = stop_tick - start_tick; + benchmark_bps = (count * size) * osKernelGetTickFreq() / benchmark_time; + + return benchmark_bps; +} + +// read speed test +void SdTest::read_benchmark() { + const uint32_t benchmark_data_size = 16384 * 8; + uint32_t bytes_written; + uint32_t benchmark_bps = 0; + + const uint8_t str_buffer_size = 32; + char str_buffer[6][str_buffer_size] = {"", "", "", "", "", ""}; + auto string_list = { + static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + static_cast(str_buffer[2]), + static_cast(str_buffer[3]), + static_cast(str_buffer[4]), + static_cast(str_buffer[5])}; + + FRESULT result; + FIL file; + + const uint32_t b1_size = 1; + const uint32_t b8_size = 8; + const uint32_t b32_size = 32; + const uint32_t b256_size = 256; + const uint32_t b4096_size = 4096; + + // prepare data for read test + set_text({"prepare data", "for read speed test", "procedure can be lengthy", "please wait"}); + delay(100); + + result = f_open(&file, "read.test", FA_WRITE | FA_OPEN_ALWAYS); + if(result) { + set_error({"cannot open file ", "in prepare read"}); + } + + for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) { + result = + f_write(&file, benchmark_data, b4096_size, reinterpret_cast(&bytes_written)); + if(bytes_written != b4096_size || result) { + set_error({"cannot write to file ", "in prepare read"}); + } + } + + result = f_close(&file); + if(result) { + set_error({"cannot close file ", "in prepare read"}); + } + + // test start + set_text({"read speed test", "procedure can be lengthy", "please wait"}); + delay(100); + + // open file + result = f_open(&file, "read.test", FA_READ | FA_OPEN_EXISTING); + if(result) { + set_error({"cannot open file ", "in read benchmark"}); + } + + // 1b test + benchmark_bps = read_benchmark_internal(b1_size, benchmark_data_size / b1_size, &file); + snprintf(str_buffer[0], str_buffer_size, "1-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 8b test + benchmark_bps = read_benchmark_internal(b8_size, benchmark_data_size / b8_size, &file); + snprintf(str_buffer[1], str_buffer_size, "8-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 32b test + benchmark_bps = read_benchmark_internal(b32_size, benchmark_data_size / b32_size, &file); + snprintf(str_buffer[2], str_buffer_size, "32-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 256b test + benchmark_bps = read_benchmark_internal(b256_size, benchmark_data_size / b256_size, &file); + snprintf(str_buffer[3], str_buffer_size, "256-byte: %lu bps", benchmark_bps); + set_text(string_list); + delay(100); + + // 4096b test + benchmark_bps = read_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, &file); + snprintf(str_buffer[4], str_buffer_size, "4096-byte: %lu bps", benchmark_bps); + snprintf(str_buffer[5], str_buffer_size, "press OK to continue"); + set_text(string_list); + + // close file + result = f_close(&file); + if(result) { + set_error({"cannot close file ", "in read test"}); + } + + blink_green(); + + wait_for_button(InputOk); +} + +uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file) { + uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_readed; + FRESULT result; + + const uint8_t str_buffer_size = 32; + char str_buffer[str_buffer_size]; + uint8_t* read_buffer; + + read_buffer = static_cast(malloc(size)); + + if(read_buffer == NULL) { + snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size); + set_error({"cannot allocate memory", static_cast(str_buffer)}); + } + + f_rewind(file); + + start_tick = osKernelGetTickCount(); + for(size_t i = 0; i < count; i++) { + result = f_read(file, read_buffer, size, reinterpret_cast(&bytes_readed)); + if(bytes_readed != size || result) { + snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size); + set_error({"cannot read from file ", static_cast(str_buffer)}); + } + } + stop_tick = osKernelGetTickCount(); + + free(read_buffer); + + benchmark_time = stop_tick - start_tick; + benchmark_bps = (count * size) * osKernelGetTickFreq() / benchmark_time; + + return benchmark_bps; +} + +// hash benchmark, store data to sd with known hash +// then read, calculate hash and compare both hashes +void SdTest::hash_benchmark() { + uint32_t mcu_data_hash = FNV_1A_INIT; + uint32_t sdcard_data_hash = FNV_1A_INIT; + uint8_t* read_buffer; + uint32_t bytes_readed; + + uint32_t bytes_written; + + const uint8_t str_buffer_size = 32; + char str_buffer[3][str_buffer_size] = {"", "", ""}; + + FRESULT result; + FIL file; + + const uint32_t b4096_size = 4096; + const uint32_t benchmark_count = 20; + + // prepare data for hash test + set_text({"prepare data", "for hash test"}); + delay(100); + + // write data to test file and calculate hash + result = f_open(&file, "hash.test", FA_WRITE | FA_OPEN_ALWAYS); + if(result) { + set_error({"cannot open file ", "in prepare hash"}); + } + + for(uint32_t i = 0; i < benchmark_count; i++) { + mcu_data_hash = fnv1a_buffer_hash(benchmark_data, b4096_size, mcu_data_hash); + result = + f_write(&file, benchmark_data, b4096_size, reinterpret_cast(&bytes_written)); + + if(bytes_written != b4096_size || result) { + set_error({"cannot write to file ", "in prepare hash"}); + } + + snprintf(str_buffer[0], str_buffer_size, "writing %lu of %lu x 4k", i, benchmark_count); + set_text({"prepare data", "for hash test", static_cast(str_buffer[0])}); + delay(100); + } + + result = f_close(&file); + if(result) { + set_error({"cannot close file ", "in prepare hash"}); + } + + // show hash of data located in mcu memory + snprintf(str_buffer[0], str_buffer_size, "hash in mcu 0x%lx", mcu_data_hash); + set_text({str_buffer[0]}); + delay(100); + + // read data from sd card and calculate hash + read_buffer = static_cast(malloc(b4096_size)); + + if(read_buffer == NULL) { + set_error({"cannot allocate memory", "in hash test"}); + } + + result = f_open(&file, "hash.test", FA_READ | FA_OPEN_EXISTING); + if(result) { + set_error({"cannot open file ", "in hash test"}); + } + + for(uint32_t i = 0; i < benchmark_count; i++) { + result = f_read(&file, read_buffer, b4096_size, reinterpret_cast(&bytes_readed)); + sdcard_data_hash = fnv1a_buffer_hash(read_buffer, b4096_size, sdcard_data_hash); + + if(bytes_readed != b4096_size || result) { + set_error({"cannot read from file ", "in hash test"}); + } + + snprintf(str_buffer[1], str_buffer_size, "reading %lu of %lu x 4k", i, benchmark_count); + set_text({str_buffer[0], str_buffer[1]}); + delay(100); + } + + result = f_close(&file); + + if(result) { + set_error({"cannot close file ", "in hash test"}); + } + + free(read_buffer); + + snprintf(str_buffer[1], str_buffer_size, "hash in sdcard 0x%lx", sdcard_data_hash); + if(mcu_data_hash == sdcard_data_hash) { + snprintf(str_buffer[2], str_buffer_size, "hashes are equal, press OK"); + set_text( + {static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + "", + "", + "", + static_cast(str_buffer[2])}); + } else { + snprintf(str_buffer[2], str_buffer_size, "hash error, press BACK to exit"); + set_error( + {static_cast(str_buffer[0]), + static_cast(str_buffer[1]), + "", + "", + "", + static_cast(str_buffer[2])}); + } + + blink_green(); + + wait_for_button(InputOk); +} + +// wait for button press +void SdTest::wait_for_button(Input input_button) { + SdTestEvent event; + osMessageQueueReset(event_queue); + while(1) { + osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever); + + if(result == osOK && event.type == SdTestEvent::EventTypeKey) { + if(event.value.input.state == true) { + if(event.value.input.input == InputBack) { + exit(); + } else { + if(event.value.input.input == input_button) { + blink_green(); + break; + } else { + blink_red(); + } + } + } + } + } + osMessageQueueReset(event_queue); +} + +// ask user to proceed or cancel +bool SdTest::ask(Input input_button_cancel, Input input_button_ok) { + bool return_result; + SdTestEvent event; + osMessageQueueReset(event_queue); + while(1) { + osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever); + + if(result == osOK && event.type == SdTestEvent::EventTypeKey) { + if(event.value.input.state == true) { + if(event.value.input.input == InputBack) { + exit(); + } else { + if(event.value.input.input == input_button_ok) { + blink_green(); + return_result = true; + break; + } else if(event.value.input.input == input_button_cancel) { + blink_green(); + return_result = false; + break; + } else { + blink_red(); + } + } + } + } + } + osMessageQueueReset(event_queue); + return return_result; +} + +// blink red led +void SdTest::blink_red() { + gpio_write(red_led_record, 0); + delay(50); + gpio_write(red_led_record, 1); +} + +// light up red led +void SdTest::set_red() { + gpio_write(red_led_record, 0); +} + +// blink green led +void SdTest::blink_green() { + gpio_write(green_led_record, 0); + delay(50); + gpio_write(green_led_record, 1); +} + +// FatFs errors descriptions +const char* SdTest::fatfs_error_desc(FRESULT res) { + switch(res) { + case FR_OK: + return "ok"; + break; + case FR_DISK_ERR: + return "low level error"; + break; + case FR_INT_ERR: + return "internal error"; + break; + case FR_NOT_READY: + return "not ready"; + break; + case FR_NO_FILE: + return "no file"; + break; + case FR_NO_PATH: + return "no path"; + break; + case FR_INVALID_NAME: + return "invalid name"; + break; + case FR_DENIED: + return "denied"; + break; + case FR_EXIST: + return "already exist"; + break; + case FR_INVALID_OBJECT: + return "invalid file/dir obj"; + break; + case FR_WRITE_PROTECTED: + return "write protected"; + break; + case FR_INVALID_DRIVE: + return "invalid drive"; + break; + case FR_NOT_ENABLED: + return "no work area in volume"; + break; + case FR_NO_FILESYSTEM: + return "no valid FS volume"; + break; + case FR_MKFS_ABORTED: + return "aborted, any problem"; + break; + case FR_TIMEOUT: + return "timeout"; + break; + case FR_LOCKED: + return "file locked"; + break; + case FR_NOT_ENOUGH_CORE: + return "not enough core memory"; + break; + case FR_TOO_MANY_OPEN_FILES: + return "too many open files"; + break; + case FR_INVALID_PARAMETER: + return "invalid parameter"; + break; + + default: + return "unknown error"; + break; + } +} + +// set text, but with infinite loop +template void SdTest::set_error(std::initializer_list list) { + set_text(list); + set_red(); + wait_for_button(InputBack); + exit(); +} + +// set text, sort of variadic function +template void SdTest::set_text(std::initializer_list list) { + uint8_t line_position = 0; + acquire_state(); + printf("------------------------\n"); + + // set line strings from args + for(auto element : list) { + state.line[line_position] = element; + printf("%s\n", element); + line_position++; + if(line_position == state.lines_count) break; + } + + // set empty lines + for(; line_position < state.lines_count; line_position++) { + state.line[line_position] = ""; + printf("\n"); + } + + printf("------------------------\n"); + release_state(); +} + +// render app +void SdTest::render(CanvasApi* canvas) { + canvas->set_color(canvas, ColorBlack); + canvas->set_font(canvas, FontSecondary); + for(uint8_t i = 0; i < state.lines_count; i++) { + canvas->draw_str(canvas, 0, (i + 1) * 10, state.line[i]); + } +} + +// app enter function +extern "C" void sd_card_test(void* p) { + SdTest* app = new SdTest(); + app->run(); +} \ No newline at end of file diff --git a/core/furi_ac.c b/core/furi_ac.c index 592b68b5..fc535dbc 100644 --- a/core/furi_ac.c +++ b/core/furi_ac.c @@ -9,8 +9,6 @@ #include -#define DEFAULT_STACK_SIZE 2048 // Stack size in bytes -#define MAX_TASK_COUNT 10 #define INVALID_TASK_ID UINT16_MAX static StaticTask_t task_info_buffer[MAX_TASK_COUNT]; diff --git a/firmware/targets/f2/Src/fatfs/ffconf.h b/firmware/targets/f2/Src/fatfs/ffconf.h index 711e1b72..26abbd31 100644 --- a/firmware/targets/f2/Src/fatfs/ffconf.h +++ b/firmware/targets/f2/Src/fatfs/ffconf.h @@ -72,7 +72,7 @@ /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ -#define _USE_LABEL 0 +#define _USE_LABEL 1 /* This option switches volume label functions, f_getlabel() and f_setlabel(). / (0:Disable or 1:Enable) */ @@ -177,7 +177,7 @@ / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / funciton will be available. */ #define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ -#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */ /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / harddisk. But a larger value may be required for on-board flash memory and some @@ -205,13 +205,13 @@ / System Configurations /----------------------------------------------------------------------------*/ -#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +#define _FS_TINY 1 /* 0:Normal or 1:Tiny */ /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) / At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ -#define _FS_EXFAT 0 +#define _FS_EXFAT 1 /* This option switches support of exFAT file system. (0:Disable or 1:Enable) / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ diff --git a/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.c b/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.c index cc71e98b..9a312752 100644 --- a/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.c +++ b/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.c @@ -324,7 +324,7 @@ uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { pCardInfo->LogBlockSize = 512; pCardInfo->CardBlockSize = 512; pCardInfo->CardCapacity = - (pCardInfo->Csd.version.v2.DeviceSize + 1) * 1024 * pCardInfo->LogBlockSize; + ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL * (uint64_t)pCardInfo->LogBlockSize; pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); } else { pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); diff --git a/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.h b/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.h index 3351e6b9..e2f754dd 100644 --- a/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.h +++ b/firmware/targets/f2/Src/fatfs/stm32_adafruit_sd.h @@ -161,7 +161,7 @@ typedef struct { SD_CSD Csd; SD_CID Cid; - uint32_t CardCapacity; /*!< Card Capacity */ + uint64_t CardCapacity; /*!< Card Capacity */ uint32_t CardBlockSize; /*!< Card Block Size */ uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ diff --git a/firmware/targets/f2/api-hal/api-hal-task.h b/firmware/targets/f2/api-hal/api-hal-task.h index f1d015cb..cd5467af 100644 --- a/firmware/targets/f2/api-hal/api-hal-task.h +++ b/firmware/targets/f2/api-hal/api-hal-task.h @@ -3,5 +3,11 @@ #include #include +// Task stack size in bytes +#define DEFAULT_STACK_SIZE 2048 + +// Max system tasks count +#define MAX_TASK_COUNT 10 + bool task_equal(TaskHandle_t a, TaskHandle_t b); bool task_is_isr_context(void); diff --git a/firmware/targets/f2/target.mk b/firmware/targets/f2/target.mk index e10c4013..41b82d21 100644 --- a/firmware/targets/f2/target.mk +++ b/firmware/targets/f2/target.mk @@ -22,6 +22,9 @@ MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32L476xx -Wall -fdata-sections -ffunction-sections LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs +CPPFLAGS += -fno-rtti -fno-use-cxa-atexit -fno-exceptions +LDFLAGS += -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group + CUBE_DIR = ../lib/STM32CubeL4 C_SOURCES += \ $(CUBE_DIR)/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_pcd.c \ diff --git a/firmware/targets/f3/Src/fatfs/ffconf.h b/firmware/targets/f3/Src/fatfs/ffconf.h index 316e7113..3de4837b 100644 --- a/firmware/targets/f3/Src/fatfs/ffconf.h +++ b/firmware/targets/f3/Src/fatfs/ffconf.h @@ -72,7 +72,7 @@ /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ -#define _USE_LABEL 0 +#define _USE_LABEL 1 /* This option switches volume label functions, f_getlabel() and f_setlabel(). / (0:Disable or 1:Enable) */ @@ -177,7 +177,7 @@ / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / funciton will be available. */ #define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ -#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */ /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / harddisk. But a larger value may be required for on-board flash memory and some @@ -205,13 +205,13 @@ / System Configurations /----------------------------------------------------------------------------*/ -#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +#define _FS_TINY 1 /* 0:Normal or 1:Tiny */ /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) / At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ -#define _FS_EXFAT 0 +#define _FS_EXFAT 1 /* This option switches support of exFAT file system. (0:Disable or 1:Enable) / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ diff --git a/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.c b/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.c index cc71e98b..9a312752 100644 --- a/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.c +++ b/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.c @@ -324,7 +324,7 @@ uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { pCardInfo->LogBlockSize = 512; pCardInfo->CardBlockSize = 512; pCardInfo->CardCapacity = - (pCardInfo->Csd.version.v2.DeviceSize + 1) * 1024 * pCardInfo->LogBlockSize; + ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL * (uint64_t)pCardInfo->LogBlockSize; pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); } else { pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); diff --git a/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.h b/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.h index 3351e6b9..e2f754dd 100644 --- a/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.h +++ b/firmware/targets/f3/Src/fatfs/stm32_adafruit_sd.h @@ -161,7 +161,7 @@ typedef struct { SD_CSD Csd; SD_CID Cid; - uint32_t CardCapacity; /*!< Card Capacity */ + uint64_t CardCapacity; /*!< Card Capacity */ uint32_t CardBlockSize; /*!< Card Block Size */ uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ diff --git a/firmware/targets/f3/api-hal/api-hal-task.h b/firmware/targets/f3/api-hal/api-hal-task.h index f1d015cb..ed29d54f 100644 --- a/firmware/targets/f3/api-hal/api-hal-task.h +++ b/firmware/targets/f3/api-hal/api-hal-task.h @@ -3,5 +3,11 @@ #include #include +// Task stack size in bytes +#define DEFAULT_STACK_SIZE 4096 + +// Max system tasks count +#define MAX_TASK_COUNT 10 + bool task_equal(TaskHandle_t a, TaskHandle_t b); bool task_is_isr_context(void); diff --git a/firmware/targets/f3/target.mk b/firmware/targets/f3/target.mk index c4dff7ee..bed22073 100644 --- a/firmware/targets/f3/target.mk +++ b/firmware/targets/f3/target.mk @@ -22,6 +22,9 @@ MCU_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard CFLAGS += $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections LDFLAGS += $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs +CPPFLAGS += -fno-rtti -fno-use-cxa-atexit -fno-exceptions +LDFLAGS += -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group + CUBE_DIR = ../lib/STM32CubeWB C_SOURCES += \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_hal_gpio.c \ diff --git a/firmware/targets/local/api-hal/api-hal-task.h b/firmware/targets/local/api-hal/api-hal-task.h index 8aed7f0f..a9b8d535 100644 --- a/firmware/targets/local/api-hal/api-hal-task.h +++ b/firmware/targets/local/api-hal/api-hal-task.h @@ -3,6 +3,12 @@ #include #include +// Task stack size in bytes +#define DEFAULT_STACK_SIZE 4096 + +// Max system tasks count +#define MAX_TASK_COUNT 10 + bool task_equal(TaskHandle_t a, TaskHandle_t b); bool task_is_isr_context(void); __attribute__((unused)) void taskDISABLE_INTERRUPTS(void); diff --git a/firmware/targets/local/fatfs/ffconf.h b/firmware/targets/local/fatfs/ffconf.h index f55ebf08..4bc3aed6 100644 --- a/firmware/targets/local/fatfs/ffconf.h +++ b/firmware/targets/local/fatfs/ffconf.h @@ -71,7 +71,7 @@ /* This option switches attribute manipulation functions, f_chmod() and f_utime(). / (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ -#define _USE_LABEL 0 +#define _USE_LABEL 1 /* This option switches volume label functions, f_getlabel() and f_setlabel(). / (0:Disable or 1:Enable) */ @@ -176,7 +176,7 @@ / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / funciton will be available. */ #define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ -#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */ /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / harddisk. But a larger value may be required for on-board flash memory and some @@ -204,13 +204,13 @@ / System Configurations /----------------------------------------------------------------------------*/ -#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +#define _FS_TINY 1 /* 0:Normal or 1:Tiny */ /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) / At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ -#define _FS_EXFAT 0 +#define _FS_EXFAT 1 /* This option switches support of exFAT file system. (0:Disable or 1:Enable) / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ diff --git a/lib/fnv1a-hash/fnv1a-hash.c b/lib/fnv1a-hash/fnv1a-hash.c new file mode 100644 index 00000000..a5bfa206 --- /dev/null +++ b/lib/fnv1a-hash/fnv1a-hash.c @@ -0,0 +1,10 @@ +#include "fnv1a-hash.h" + +// FNV-1a hash, 32-bit +uint32_t fnv1a_buffer_hash(const uint8_t* buffer, uint32_t length, uint32_t hash) +{ + for (uint32_t i = 0; i < length; i++) { + hash = (hash ^ buffer[i]) * 16777619ULL; + } + return hash; +} \ No newline at end of file diff --git a/lib/fnv1a-hash/fnv1a-hash.h b/lib/fnv1a-hash/fnv1a-hash.h new file mode 100644 index 00000000..fbbdb5b8 --- /dev/null +++ b/lib/fnv1a-hash/fnv1a-hash.h @@ -0,0 +1,39 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FNV_1A_INIT 2166136261UL + +// FNV-1a hash, 32-bit +uint32_t fnv1a_buffer_hash(const uint8_t* buffer, uint32_t length, uint32_t hash); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +// constexpr FNV-1a hash for strings, 32-bit +inline constexpr uint32_t fnv1a_string_hash(const char* str) { + uint32_t hash = FNV_1A_INIT; + + while(*str) { + hash = (hash ^ *str) * 16777619ULL; + str += 1; + } + return hash; +} +#else +// FNV-1a hash for strings, 32-bit +inline uint32_t fnv1a_string_hash(const char* str) { + uint32_t hash = FNV_1A_INIT; + + while(*str) { + hash = (hash ^ *str) * 16777619ULL; + str += 1; + } + return hash; +} +#endif \ No newline at end of file diff --git a/lib/lib.mk b/lib/lib.mk index 387b5962..ab7c7401 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -50,4 +50,8 @@ endif CFLAGS += -I$(LIB_DIR)/callback-connector # app template library -CFLAGS += -I$(LIB_DIR)/app-template \ No newline at end of file +CFLAGS += -I$(LIB_DIR)/app-template + +# fnv1a hash library +CFLAGS += -I$(LIB_DIR)/fnv1a-hash +C_SOURCES += $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c \ No newline at end of file diff --git a/make/rules.mk b/make/rules.mk index 12905160..f4d0c95e 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -31,7 +31,7 @@ all: $(OBJ_DIR)/$(PROJECT).elf $(OBJ_DIR)/$(PROJECT).hex $(OBJ_DIR)/$(PROJECT).b $(OBJ_DIR)/$(PROJECT).elf: $(OBJECTS) @echo "\tLD\t" $@ - @$(CC) $(LDFLAGS) $(OBJECTS) -o $@ + @$(LD) $(LDFLAGS) $(OBJECTS) -o $@ @$(SZ) $@ $(OBJ_DIR)/$(PROJECT).hex: $(OBJ_DIR)/$(PROJECT).elf diff --git a/make/toolchain.mk b/make/toolchain.mk index 84e113b9..d6834064 100644 --- a/make/toolchain.mk +++ b/make/toolchain.mk @@ -8,6 +8,7 @@ endif CC = $(PREFIX)gcc CPP = $(PREFIX)g++ +LD = $(PREFIX)g++ AS = $(PREFIX)gcc -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size