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 <mail@s3f.ru>
This commit is contained in:
		
							parent
							
								
									afcfcd4d4a
								
							
						
					
					
						commit
						714d732745
					
				| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										846
									
								
								applications/sd-card-test/sd-card-test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										846
									
								
								applications/sd-card-test/sd-card-test.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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 <state, events>
 | ||||
| class SdTest : public AppTemplate<SdTestState, SdTestEvent> { | ||||
| 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 <class T> void set_text(std::initializer_list<T> list); | ||||
|     template <class T> void set_error(std::initializer_list<T> 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<const char*>(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<BYTE*>(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<const char*>(str_buffer[0]), | ||||
|          static_cast<const char*>(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<const char*>(str_buffer[0]), | ||||
|          static_cast<const char*>(str_buffer[1]), | ||||
|          static_cast<const char*>(str_buffer[2]), | ||||
|          static_cast<const char*>(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<uint8_t*>(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<uint8_t>(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<const char*>(str_buffer[0]), | ||||
|         static_cast<const char*>(str_buffer[1]), | ||||
|         static_cast<const char*>(str_buffer[2]), | ||||
|         static_cast<const char*>(str_buffer[3]), | ||||
|         static_cast<const char*>(str_buffer[4]), | ||||
|         static_cast<const char*>(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<const char*>(str_buffer)}); | ||||
|     } | ||||
| 
 | ||||
|     start_tick = osKernelGetTickCount(); | ||||
|     for(size_t i = 0; i < count; i++) { | ||||
|         result = f_write(&file, benchmark_data, size, reinterpret_cast<UINT*>(&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<const char*>(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<const char*>(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<const char*>(str_buffer[0]), | ||||
|         static_cast<const char*>(str_buffer[1]), | ||||
|         static_cast<const char*>(str_buffer[2]), | ||||
|         static_cast<const char*>(str_buffer[3]), | ||||
|         static_cast<const char*>(str_buffer[4]), | ||||
|         static_cast<const char*>(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<UINT*>(&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<uint8_t*>(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<const char*>(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<UINT*>(&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<const char*>(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<UINT*>(&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<const char*>(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<uint8_t*>(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<UINT*>(&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<const char*>(str_buffer[0]), | ||||
|              static_cast<const char*>(str_buffer[1]), | ||||
|              "", | ||||
|              "", | ||||
|              "", | ||||
|              static_cast<const char*>(str_buffer[2])}); | ||||
|     } else { | ||||
|         snprintf(str_buffer[2], str_buffer_size, "hash error, press BACK to exit"); | ||||
|         set_error( | ||||
|             {static_cast<const char*>(str_buffer[0]), | ||||
|              static_cast<const char*>(str_buffer[1]), | ||||
|              "", | ||||
|              "", | ||||
|              "", | ||||
|              static_cast<const char*>(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 <class T> void SdTest::set_error(std::initializer_list<T> list) { | ||||
|     set_text(list); | ||||
|     set_red(); | ||||
|     wait_for_button(InputBack); | ||||
|     exit(); | ||||
| } | ||||
| 
 | ||||
| // set text, sort of variadic function
 | ||||
| template <class T> void SdTest::set_text(std::initializer_list<T> 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(); | ||||
| } | ||||
| @ -9,8 +9,6 @@ | ||||
| 
 | ||||
| #include <string.h> | ||||
| 
 | ||||
| #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]; | ||||
|  | ||||
| @ -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. */ | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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           */ | ||||
|  | ||||
| @ -3,5 +3,11 @@ | ||||
| #include <cmsis_os.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| // 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); | ||||
|  | ||||
| @ -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 \
 | ||||
|  | ||||
| @ -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. */ | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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           */ | ||||
|  | ||||
| @ -3,5 +3,11 @@ | ||||
| #include <cmsis_os.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| // 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); | ||||
|  | ||||
| @ -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 \
 | ||||
|  | ||||
| @ -3,6 +3,12 @@ | ||||
| #include <cmsis_os.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| // 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); | ||||
|  | ||||
| @ -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. */ | ||||
|  | ||||
							
								
								
									
										10
									
								
								lib/fnv1a-hash/fnv1a-hash.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/fnv1a-hash/fnv1a-hash.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
							
								
								
									
										39
									
								
								lib/fnv1a-hash/fnv1a-hash.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								lib/fnv1a-hash/fnv1a-hash.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #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 | ||||
| @ -51,3 +51,7 @@ CFLAGS			+= -I$(LIB_DIR)/callback-connector | ||||
| 
 | ||||
| # app template library
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/app-template | ||||
| 
 | ||||
| # fnv1a hash library
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash | ||||
| C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13