sd-card-test.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. #include "app-template.h"
  2. #include "stm32_adafruit_sd.h"
  3. #include "fnv1a-hash.h"
  4. #include "filesystem-api.h"
  5. #include "cli/cli.h"
  6. #include "callback-connector.h"
  7. // event enumeration type
  8. typedef uint8_t event_t;
  9. class SdTestState {
  10. public:
  11. // state data
  12. static const uint8_t lines_count = 6;
  13. const char* line[lines_count];
  14. // state initializer
  15. SdTestState() {
  16. for(uint8_t i = 0; i < lines_count; i++) {
  17. line[i] = "";
  18. }
  19. }
  20. };
  21. // events class
  22. class SdTestEvent {
  23. public:
  24. // events enum
  25. static const event_t EventTypeTick = 0;
  26. static const event_t EventTypeKey = 1;
  27. // payload
  28. union {
  29. InputEvent input;
  30. } value;
  31. // event type
  32. event_t type;
  33. };
  34. // our app derived from base AppTemplate class
  35. // with template variables <state, events>
  36. class SdTest : public AppTemplate<SdTestState, SdTestEvent> {
  37. public:
  38. // vars
  39. GpioPin* red_led_record;
  40. GpioPin* green_led_record;
  41. const uint32_t benchmark_data_size = 4096;
  42. uint8_t* benchmark_data;
  43. FS_Api* fs_api;
  44. // consts
  45. static const uint32_t BENCHMARK_ERROR = UINT_MAX;
  46. // funcs
  47. void run();
  48. void render(Canvas* canvas);
  49. template <class T> void set_text(std::initializer_list<T> list);
  50. template <class T> void set_error(std::initializer_list<T> list);
  51. void wait_for_button(Input input_button);
  52. bool ask(Input input_button_cancel, Input input_button_ok);
  53. void blink_red();
  54. void set_red();
  55. void blink_green();
  56. // "tests"
  57. void detect_sd_card();
  58. void show_warning();
  59. void get_sd_card_info();
  60. bool prepare_benchmark_data();
  61. void free_benchmark_data();
  62. void write_benchmark();
  63. uint32_t
  64. write_benchmark_internal(const uint32_t size, const uint32_t tcount, bool silent = false);
  65. void read_benchmark();
  66. uint32_t read_benchmark_internal(
  67. const uint32_t size,
  68. const uint32_t count,
  69. File* file,
  70. bool silent = false);
  71. void hash_benchmark();
  72. // cli tests
  73. void cli_read_benchmark(string_t args, void* _ctx);
  74. void cli_write_benchmark(string_t args, void* _ctx);
  75. };
  76. // start app
  77. void SdTest::run() {
  78. // create pin
  79. GpioPin red_led = led_gpio[0];
  80. GpioPin green_led = led_gpio[1];
  81. // TODO open record
  82. red_led_record = &red_led;
  83. green_led_record = &green_led;
  84. // configure pin
  85. gpio_init(red_led_record, GpioModeOutputOpenDrain);
  86. gpio_init(green_led_record, GpioModeOutputOpenDrain);
  87. app_ready();
  88. fs_api = static_cast<FS_Api*>(furi_open("sdcard"));
  89. if(fs_api == NULL) {
  90. set_error({"cannot get sdcard api"});
  91. exit();
  92. }
  93. Cli* cli = static_cast<Cli*>(furi_open("cli"));
  94. if(cli != NULL) {
  95. // read_benchmark and write_benchmark signatures are same. so we must use tags
  96. auto cli_read_cb = cbc::obtain_connector<0>(this, &SdTest::cli_read_benchmark);
  97. cli_add_command(cli, "sd_read_test", cli_read_cb, this);
  98. auto cli_write_cb = cbc::obtain_connector<1>(this, &SdTest::cli_write_benchmark);
  99. cli_add_command(cli, "sd_write_test", cli_write_cb, this);
  100. }
  101. detect_sd_card();
  102. get_sd_card_info();
  103. show_warning();
  104. set_text({"preparing benchmark data"});
  105. bool data_prepared = prepare_benchmark_data();
  106. if(data_prepared) {
  107. set_text({"benchmark data prepared"});
  108. } else {
  109. set_error({"cannot allocate buffer", "for benchmark data"});
  110. }
  111. write_benchmark();
  112. read_benchmark();
  113. hash_benchmark();
  114. free_benchmark_data();
  115. set_text({
  116. "test complete",
  117. "",
  118. "",
  119. "",
  120. "",
  121. "press BACK to exit",
  122. });
  123. wait_for_button(InputBack);
  124. exit();
  125. }
  126. // detect sd card insertion
  127. void SdTest::detect_sd_card() {
  128. const uint8_t str_buffer_size = 40;
  129. const uint8_t dots_animation_size = 4;
  130. char str_buffer[str_buffer_size];
  131. const char dots[dots_animation_size][4] = {"", ".", "..", "..."};
  132. uint8_t i = 0;
  133. // detect sd card pin
  134. while(fs_api->common.get_fs_info(NULL, NULL) == FSE_NOT_READY) {
  135. delay(100);
  136. snprintf(str_buffer, str_buffer_size, "Waiting%s", dots[i]);
  137. set_text({static_cast<const char*>(str_buffer), "Please insert sd card"});
  138. if(i < (dots_animation_size - 1)) {
  139. i++;
  140. } else {
  141. i = 0;
  142. }
  143. }
  144. blink_green();
  145. }
  146. // show warning about test
  147. void SdTest::show_warning() {
  148. set_text(
  149. {"!!Warning!!",
  150. "during the tests",
  151. "files can be overwritten",
  152. "or data on card may be lost",
  153. "",
  154. "press UP DOWN OK to continue"});
  155. wait_for_button(InputUp);
  156. wait_for_button(InputDown);
  157. wait_for_button(InputOk);
  158. }
  159. // get info about sd card, label, sn
  160. // sector, cluster, total and free size
  161. void SdTest::get_sd_card_info() {
  162. const uint8_t str_buffer_size = 26;
  163. char str_buffer[2][str_buffer_size];
  164. FS_Error result;
  165. uint64_t bytes_total, bytes_free;
  166. int __attribute__((unused)) snprintf_count = 0;
  167. result = fs_api->common.get_fs_info(&bytes_total, &bytes_free);
  168. if(result != FSE_OK) set_error({"get_fs_info error", fs_api->error.get_desc(result)});
  169. snprintf(
  170. str_buffer[0], str_buffer_size, "%lu KB total", static_cast<uint32_t>(bytes_total / 1024));
  171. snprintf(
  172. str_buffer[1], str_buffer_size, "%lu KB free", static_cast<uint32_t>(bytes_free / 1024));
  173. set_text(
  174. {static_cast<const char*>(str_buffer[0]),
  175. static_cast<const char*>(str_buffer[1]),
  176. "",
  177. "",
  178. "",
  179. "press OK to continue"});
  180. blink_green();
  181. wait_for_button(InputOk);
  182. }
  183. // prepare benchmark data (allocate data in ram)
  184. bool SdTest::prepare_benchmark_data() {
  185. bool result = true;
  186. benchmark_data = static_cast<uint8_t*>(malloc(benchmark_data_size));
  187. if(benchmark_data == NULL) {
  188. result = false;
  189. }
  190. for(size_t i = 0; i < benchmark_data_size; i++) {
  191. benchmark_data[i] = static_cast<uint8_t>(i);
  192. }
  193. return result;
  194. }
  195. void SdTest::free_benchmark_data() {
  196. free(benchmark_data);
  197. }
  198. // write speed test
  199. void SdTest::write_benchmark() {
  200. const uint32_t b1_size = 1;
  201. const uint32_t b8_size = 8;
  202. const uint32_t b32_size = 32;
  203. const uint32_t b256_size = 256;
  204. const uint32_t b4096_size = 4096;
  205. const uint32_t benchmark_data_size = 16384 * 4;
  206. uint32_t benchmark_bps = 0;
  207. const uint8_t str_buffer_size = 32;
  208. char str_buffer[6][str_buffer_size] = {"", "", "", "", "", ""};
  209. auto string_list = {
  210. static_cast<const char*>(str_buffer[0]),
  211. static_cast<const char*>(str_buffer[1]),
  212. static_cast<const char*>(str_buffer[2]),
  213. static_cast<const char*>(str_buffer[3]),
  214. static_cast<const char*>(str_buffer[4]),
  215. static_cast<const char*>(str_buffer[5])};
  216. set_text({"write speed test", "procedure can be lengthy", "please wait"});
  217. delay(100);
  218. // 1b test
  219. benchmark_bps = write_benchmark_internal(b1_size, benchmark_data_size / b1_size);
  220. snprintf(str_buffer[0], str_buffer_size, "1-byte: %lu bps", benchmark_bps);
  221. set_text(string_list);
  222. delay(100);
  223. // 8b test
  224. benchmark_bps = write_benchmark_internal(b8_size, benchmark_data_size / b8_size);
  225. snprintf(str_buffer[1], str_buffer_size, "8-byte: %lu bps", benchmark_bps);
  226. set_text(string_list);
  227. delay(100);
  228. // 32b test
  229. benchmark_bps = write_benchmark_internal(b32_size, benchmark_data_size / b32_size);
  230. snprintf(str_buffer[2], str_buffer_size, "32-byte: %lu bps", benchmark_bps);
  231. set_text(string_list);
  232. delay(100);
  233. // 256b test
  234. benchmark_bps = write_benchmark_internal(b256_size, benchmark_data_size / b256_size);
  235. snprintf(str_buffer[3], str_buffer_size, "256-byte: %lu bps", benchmark_bps);
  236. set_text(string_list);
  237. delay(100);
  238. // 4096b test
  239. benchmark_bps = write_benchmark_internal(b4096_size, benchmark_data_size / b4096_size);
  240. snprintf(str_buffer[4], str_buffer_size, "4096-byte: %lu bps", benchmark_bps);
  241. snprintf(str_buffer[5], str_buffer_size, "press OK to continue");
  242. set_text(string_list);
  243. blink_green();
  244. wait_for_button(InputOk);
  245. }
  246. uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count, bool silent) {
  247. uint32_t start_tick, stop_tick, benchmark_bps = 0, benchmark_time, bytes_written;
  248. File file;
  249. const uint8_t str_buffer_size = 32;
  250. char str_buffer[str_buffer_size];
  251. if(!fs_api->file.open(&file, "write.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
  252. if(!silent) {
  253. snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
  254. set_error({"cannot open file ", static_cast<const char*>(str_buffer)});
  255. } else {
  256. benchmark_bps = BENCHMARK_ERROR;
  257. }
  258. }
  259. start_tick = osKernelGetTickCount();
  260. for(size_t i = 0; i < count; i++) {
  261. bytes_written = fs_api->file.write(&file, benchmark_data, size);
  262. if(bytes_written != size || file.error_id != FSE_OK) {
  263. if(!silent) {
  264. snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
  265. set_error({"cannot write to file ", static_cast<const char*>(str_buffer)});
  266. } else {
  267. benchmark_bps = BENCHMARK_ERROR;
  268. break;
  269. }
  270. }
  271. }
  272. stop_tick = osKernelGetTickCount();
  273. if(!fs_api->file.close(&file)) {
  274. if(!silent) {
  275. snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size);
  276. set_error({"cannot close file ", static_cast<const char*>(str_buffer)});
  277. } else {
  278. benchmark_bps = BENCHMARK_ERROR;
  279. }
  280. }
  281. if(benchmark_bps != BENCHMARK_ERROR) {
  282. benchmark_time = stop_tick - start_tick;
  283. benchmark_bps = (count * size) * osKernelGetTickFreq() / benchmark_time;
  284. }
  285. return benchmark_bps;
  286. }
  287. // read speed test
  288. void SdTest::read_benchmark() {
  289. const uint32_t benchmark_data_size = 16384 * 8;
  290. uint32_t bytes_written;
  291. uint32_t benchmark_bps = 0;
  292. const uint8_t str_buffer_size = 32;
  293. char str_buffer[6][str_buffer_size] = {"", "", "", "", "", ""};
  294. auto string_list = {
  295. static_cast<const char*>(str_buffer[0]),
  296. static_cast<const char*>(str_buffer[1]),
  297. static_cast<const char*>(str_buffer[2]),
  298. static_cast<const char*>(str_buffer[3]),
  299. static_cast<const char*>(str_buffer[4]),
  300. static_cast<const char*>(str_buffer[5])};
  301. File file;
  302. const uint32_t b1_size = 1;
  303. const uint32_t b8_size = 8;
  304. const uint32_t b32_size = 32;
  305. const uint32_t b256_size = 256;
  306. const uint32_t b4096_size = 4096;
  307. // prepare data for read test
  308. set_text({"prepare data", "for read speed test", "procedure can be lengthy", "please wait"});
  309. delay(100);
  310. if(!fs_api->file.open(&file, "read.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
  311. set_error({"cannot open file ", "in prepare read"});
  312. }
  313. for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) {
  314. bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size);
  315. if(bytes_written != b4096_size || file.error_id != FSE_OK) {
  316. set_error({"cannot write to file ", "in prepare read"});
  317. }
  318. }
  319. if(!fs_api->file.close(&file)) {
  320. set_error({"cannot close file ", "in prepare read"});
  321. }
  322. // test start
  323. set_text({"read speed test", "procedure can be lengthy", "please wait"});
  324. delay(100);
  325. // open file
  326. if(!fs_api->file.open(&file, "read.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
  327. set_error({"cannot open file ", "in read benchmark"});
  328. }
  329. // 1b test
  330. benchmark_bps = read_benchmark_internal(b1_size, benchmark_data_size / b1_size, &file);
  331. snprintf(str_buffer[0], str_buffer_size, "1-byte: %lu bps", benchmark_bps);
  332. set_text(string_list);
  333. delay(100);
  334. // 8b test
  335. benchmark_bps = read_benchmark_internal(b8_size, benchmark_data_size / b8_size, &file);
  336. snprintf(str_buffer[1], str_buffer_size, "8-byte: %lu bps", benchmark_bps);
  337. set_text(string_list);
  338. delay(100);
  339. // 32b test
  340. benchmark_bps = read_benchmark_internal(b32_size, benchmark_data_size / b32_size, &file);
  341. snprintf(str_buffer[2], str_buffer_size, "32-byte: %lu bps", benchmark_bps);
  342. set_text(string_list);
  343. delay(100);
  344. // 256b test
  345. benchmark_bps = read_benchmark_internal(b256_size, benchmark_data_size / b256_size, &file);
  346. snprintf(str_buffer[3], str_buffer_size, "256-byte: %lu bps", benchmark_bps);
  347. set_text(string_list);
  348. delay(100);
  349. // 4096b test
  350. benchmark_bps = read_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, &file);
  351. snprintf(str_buffer[4], str_buffer_size, "4096-byte: %lu bps", benchmark_bps);
  352. snprintf(str_buffer[5], str_buffer_size, "press OK to continue");
  353. set_text(string_list);
  354. // close file
  355. if(!fs_api->file.close(&file)) {
  356. set_error({"cannot close file ", "in read test"});
  357. }
  358. blink_green();
  359. wait_for_button(InputOk);
  360. }
  361. uint32_t SdTest::read_benchmark_internal(
  362. const uint32_t size,
  363. const uint32_t count,
  364. File* file,
  365. bool silent) {
  366. uint32_t start_tick, stop_tick, benchmark_bps = 0, benchmark_time, bytes_readed;
  367. const uint8_t str_buffer_size = 32;
  368. char str_buffer[str_buffer_size];
  369. uint8_t* read_buffer;
  370. read_buffer = static_cast<uint8_t*>(malloc(size));
  371. if(read_buffer == NULL) {
  372. if(!silent) {
  373. snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size);
  374. set_error({"cannot allocate memory", static_cast<const char*>(str_buffer)});
  375. } else {
  376. benchmark_bps = BENCHMARK_ERROR;
  377. }
  378. }
  379. fs_api->file.seek(file, 0, true);
  380. start_tick = osKernelGetTickCount();
  381. for(size_t i = 0; i < count; i++) {
  382. bytes_readed = fs_api->file.read(file, read_buffer, size);
  383. if(bytes_readed != size || file->error_id != FSE_OK) {
  384. if(!silent) {
  385. snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size);
  386. set_error({"cannot read from file ", static_cast<const char*>(str_buffer)});
  387. } else {
  388. benchmark_bps = BENCHMARK_ERROR;
  389. break;
  390. }
  391. }
  392. }
  393. stop_tick = osKernelGetTickCount();
  394. free(read_buffer);
  395. if(benchmark_bps != BENCHMARK_ERROR) {
  396. benchmark_time = stop_tick - start_tick;
  397. benchmark_bps = (count * size) * osKernelGetTickFreq() / benchmark_time;
  398. }
  399. return benchmark_bps;
  400. }
  401. // hash benchmark, store data to sd with known hash
  402. // then read, calculate hash and compare both hashes
  403. void SdTest::hash_benchmark() {
  404. uint32_t mcu_data_hash = FNV_1A_INIT;
  405. uint32_t sdcard_data_hash = FNV_1A_INIT;
  406. uint8_t* read_buffer;
  407. uint32_t bytes_readed;
  408. uint32_t bytes_written;
  409. const uint8_t str_buffer_size = 32;
  410. char str_buffer[3][str_buffer_size] = {"", "", ""};
  411. File file;
  412. const uint32_t b4096_size = 4096;
  413. const uint32_t benchmark_count = 20;
  414. // prepare data for hash test
  415. set_text({"prepare data", "for hash test"});
  416. delay(100);
  417. // write data to test file and calculate hash
  418. if(!fs_api->file.open(&file, "hash.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
  419. set_error({"cannot open file ", "in prepare hash"});
  420. }
  421. for(uint32_t i = 0; i < benchmark_count; i++) {
  422. mcu_data_hash = fnv1a_buffer_hash(benchmark_data, b4096_size, mcu_data_hash);
  423. bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size);
  424. if(bytes_written != b4096_size || file.error_id != FSE_OK) {
  425. set_error({"cannot write to file ", "in prepare hash"});
  426. }
  427. snprintf(str_buffer[0], str_buffer_size, "writing %lu of %lu x 4k", i, benchmark_count);
  428. set_text({"prepare data", "for hash test", static_cast<const char*>(str_buffer[0])});
  429. delay(100);
  430. }
  431. if(!fs_api->file.close(&file)) {
  432. set_error({"cannot close file ", "in prepare hash"});
  433. }
  434. // show hash of data located in mcu memory
  435. snprintf(str_buffer[0], str_buffer_size, "hash in mcu 0x%lx", mcu_data_hash);
  436. set_text({str_buffer[0]});
  437. delay(100);
  438. // read data from sd card and calculate hash
  439. read_buffer = static_cast<uint8_t*>(malloc(b4096_size));
  440. if(read_buffer == NULL) {
  441. set_error({"cannot allocate memory", "in hash test"});
  442. }
  443. if(!fs_api->file.open(&file, "hash.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
  444. set_error({"cannot open file ", "in hash test"});
  445. }
  446. for(uint32_t i = 0; i < benchmark_count; i++) {
  447. bytes_readed = fs_api->file.read(&file, read_buffer, b4096_size);
  448. sdcard_data_hash = fnv1a_buffer_hash(read_buffer, b4096_size, sdcard_data_hash);
  449. if(bytes_readed != b4096_size || file.error_id != FSE_OK) {
  450. set_error({"cannot read from file ", "in hash test"});
  451. }
  452. snprintf(str_buffer[1], str_buffer_size, "reading %lu of %lu x 4k", i, benchmark_count);
  453. set_text({str_buffer[0], str_buffer[1]});
  454. delay(100);
  455. }
  456. if(!fs_api->file.close(&file)) {
  457. set_error({"cannot close file ", "in hash test"});
  458. }
  459. free(read_buffer);
  460. snprintf(str_buffer[1], str_buffer_size, "hash in sdcard 0x%lx", sdcard_data_hash);
  461. if(mcu_data_hash == sdcard_data_hash) {
  462. snprintf(str_buffer[2], str_buffer_size, "hashes are equal, press OK");
  463. set_text(
  464. {static_cast<const char*>(str_buffer[0]),
  465. static_cast<const char*>(str_buffer[1]),
  466. "",
  467. "",
  468. "",
  469. static_cast<const char*>(str_buffer[2])});
  470. } else {
  471. snprintf(str_buffer[2], str_buffer_size, "hash error, press BACK to exit");
  472. set_error(
  473. {static_cast<const char*>(str_buffer[0]),
  474. static_cast<const char*>(str_buffer[1]),
  475. "",
  476. "",
  477. "",
  478. static_cast<const char*>(str_buffer[2])});
  479. }
  480. blink_green();
  481. wait_for_button(InputOk);
  482. }
  483. void SdTest::cli_read_benchmark(string_t args, void* _ctx) {
  484. SdTest* _this = static_cast<SdTest*>(_ctx);
  485. const uint32_t benchmark_data_size = 16384 * 8;
  486. uint32_t bytes_written;
  487. uint32_t benchmark_bps = 0;
  488. File file;
  489. const uint32_t b1_size = 1;
  490. const uint32_t b8_size = 8;
  491. const uint32_t b32_size = 32;
  492. const uint32_t b256_size = 256;
  493. const uint32_t b4096_size = 4096;
  494. const uint8_t str_buffer_size = 64;
  495. char str_buffer[str_buffer_size];
  496. cli_print("preparing benchmark data\r\n");
  497. bool data_prepared = _this->prepare_benchmark_data();
  498. if(data_prepared) {
  499. cli_print("benchmark data prepared\r\n");
  500. } else {
  501. cli_print("error: cannot allocate buffer for benchmark data\r\n");
  502. }
  503. // prepare data for read test
  504. cli_print("prepare data for read speed test, procedure can be lengthy, please wait\r\n");
  505. if(!_this->fs_api->file.open(&file, "read.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
  506. cli_print("error: cannot open file in prepare read\r\n");
  507. }
  508. for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) {
  509. bytes_written = _this->fs_api->file.write(&file, benchmark_data, b4096_size);
  510. if(bytes_written != b4096_size || file.error_id != FSE_OK) {
  511. cli_print("error: cannot write to file in prepare read\r\n");
  512. }
  513. }
  514. if(!_this->fs_api->file.close(&file)) {
  515. cli_print("error: cannot close file in prepare read\r\n");
  516. }
  517. // test start
  518. cli_print("read speed test, procedure can be lengthy, please wait\r\n");
  519. // open file
  520. if(!_this->fs_api->file.open(&file, "read.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
  521. cli_print("error: cannot open file in read benchmark\r\n");
  522. }
  523. // 1b test
  524. benchmark_bps =
  525. _this->read_benchmark_internal(b1_size, benchmark_data_size / b1_size, &file, true);
  526. if(benchmark_bps == BENCHMARK_ERROR) {
  527. cli_print("error: in 1-byte read test\r\n");
  528. } else {
  529. snprintf(str_buffer, str_buffer_size, "1-byte: %lu bytes per second\r\n", benchmark_bps);
  530. cli_print(str_buffer);
  531. }
  532. // 8b test
  533. benchmark_bps =
  534. _this->read_benchmark_internal(b8_size, benchmark_data_size / b8_size, &file, true);
  535. if(benchmark_bps == BENCHMARK_ERROR) {
  536. cli_print("error: in 8-byte read test\r\n");
  537. } else {
  538. snprintf(str_buffer, str_buffer_size, "8-byte: %lu bytes per second\r\n", benchmark_bps);
  539. cli_print(str_buffer);
  540. }
  541. // 32b test
  542. benchmark_bps =
  543. _this->read_benchmark_internal(b32_size, benchmark_data_size / b32_size, &file, true);
  544. if(benchmark_bps == BENCHMARK_ERROR) {
  545. cli_print("error: in 32-byte read test\r\n");
  546. } else {
  547. snprintf(str_buffer, str_buffer_size, "32-byte: %lu bytes per second\r\n", benchmark_bps);
  548. cli_print(str_buffer);
  549. }
  550. // 256b test
  551. benchmark_bps =
  552. _this->read_benchmark_internal(b256_size, benchmark_data_size / b256_size, &file, true);
  553. if(benchmark_bps == BENCHMARK_ERROR) {
  554. cli_print("error: in 256-byte read test\r\n");
  555. } else {
  556. snprintf(str_buffer, str_buffer_size, "256-byte: %lu bytes per second\r\n", benchmark_bps);
  557. cli_print(str_buffer);
  558. }
  559. // 4096b test
  560. benchmark_bps =
  561. _this->read_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, &file, true);
  562. if(benchmark_bps == BENCHMARK_ERROR) {
  563. cli_print("error: in 4096-byte read test\r\n");
  564. } else {
  565. snprintf(
  566. str_buffer, str_buffer_size, "4096-byte: %lu bytes per second\r\n", benchmark_bps);
  567. cli_print(str_buffer);
  568. }
  569. // close file
  570. if(!_this->fs_api->file.close(&file)) {
  571. cli_print("error: cannot close file\r\n");
  572. }
  573. _this->free_benchmark_data();
  574. cli_print("test completed\r\n");
  575. }
  576. void SdTest::cli_write_benchmark(string_t args, void* _ctx) {
  577. SdTest* _this = static_cast<SdTest*>(_ctx);
  578. const uint32_t b1_size = 1;
  579. const uint32_t b8_size = 8;
  580. const uint32_t b32_size = 32;
  581. const uint32_t b256_size = 256;
  582. const uint32_t b4096_size = 4096;
  583. const uint32_t benchmark_data_size = 16384 * 4;
  584. uint32_t benchmark_bps = 0;
  585. const uint8_t str_buffer_size = 64;
  586. char str_buffer[str_buffer_size];
  587. cli_print("preparing benchmark data\r\n");
  588. bool data_prepared = _this->prepare_benchmark_data();
  589. if(data_prepared) {
  590. cli_print("benchmark data prepared\r\n");
  591. } else {
  592. cli_print("error: cannot allocate buffer for benchmark data\r\n");
  593. }
  594. cli_print("write speed test, procedure can be lengthy, please wait\r\n");
  595. // 1b test
  596. benchmark_bps = _this->write_benchmark_internal(b1_size, benchmark_data_size / b1_size, true);
  597. if(benchmark_bps == BENCHMARK_ERROR) {
  598. cli_print("error: in 1-byte write test\r\n");
  599. } else {
  600. snprintf(str_buffer, str_buffer_size, "1-byte: %lu bytes per second\r\n", benchmark_bps);
  601. cli_print(str_buffer);
  602. }
  603. // 8b test
  604. benchmark_bps = _this->write_benchmark_internal(b8_size, benchmark_data_size / b8_size, true);
  605. if(benchmark_bps == BENCHMARK_ERROR) {
  606. cli_print("error: in 8-byte write test\r\n");
  607. } else {
  608. snprintf(str_buffer, str_buffer_size, "8-byte: %lu bytes per second\r\n", benchmark_bps);
  609. cli_print(str_buffer);
  610. }
  611. // 32b test
  612. benchmark_bps =
  613. _this->write_benchmark_internal(b32_size, benchmark_data_size / b32_size, true);
  614. if(benchmark_bps == BENCHMARK_ERROR) {
  615. cli_print("error: in 32-byte write test\r\n");
  616. } else {
  617. snprintf(str_buffer, str_buffer_size, "32-byte: %lu bytes per second\r\n", benchmark_bps);
  618. cli_print(str_buffer);
  619. }
  620. // 256b test
  621. benchmark_bps =
  622. _this->write_benchmark_internal(b256_size, benchmark_data_size / b256_size, true);
  623. if(benchmark_bps == BENCHMARK_ERROR) {
  624. cli_print("error: in 256-byte write test\r\n");
  625. } else {
  626. snprintf(str_buffer, str_buffer_size, "256-byte: %lu bytes per second\r\n", benchmark_bps);
  627. cli_print(str_buffer);
  628. }
  629. // 4096b test
  630. benchmark_bps =
  631. _this->write_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, true);
  632. if(benchmark_bps == BENCHMARK_ERROR) {
  633. cli_print("error: in 4096-byte write test\r\n");
  634. } else {
  635. snprintf(
  636. str_buffer, str_buffer_size, "4096-byte: %lu bytes per second\r\n", benchmark_bps);
  637. cli_print(str_buffer);
  638. }
  639. _this->free_benchmark_data();
  640. cli_print("test completed\r\n");
  641. }
  642. // wait for button press
  643. void SdTest::wait_for_button(Input input_button) {
  644. SdTestEvent event;
  645. osMessageQueueReset(event_queue);
  646. while(1) {
  647. osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
  648. if(result == osOK && event.type == SdTestEvent::EventTypeKey) {
  649. if(event.value.input.state == true) {
  650. if(event.value.input.input == InputBack) {
  651. exit();
  652. } else {
  653. if(event.value.input.input == input_button) {
  654. blink_green();
  655. break;
  656. } else {
  657. blink_red();
  658. }
  659. }
  660. }
  661. }
  662. }
  663. osMessageQueueReset(event_queue);
  664. }
  665. // ask user to proceed or cancel
  666. bool SdTest::ask(Input input_button_cancel, Input input_button_ok) {
  667. bool return_result;
  668. SdTestEvent event;
  669. osMessageQueueReset(event_queue);
  670. while(1) {
  671. osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
  672. if(result == osOK && event.type == SdTestEvent::EventTypeKey) {
  673. if(event.value.input.state == true) {
  674. if(event.value.input.input == InputBack) {
  675. exit();
  676. } else {
  677. if(event.value.input.input == input_button_ok) {
  678. blink_green();
  679. return_result = true;
  680. break;
  681. } else if(event.value.input.input == input_button_cancel) {
  682. blink_green();
  683. return_result = false;
  684. break;
  685. } else {
  686. blink_red();
  687. }
  688. }
  689. }
  690. }
  691. }
  692. osMessageQueueReset(event_queue);
  693. return return_result;
  694. }
  695. // blink red led
  696. void SdTest::blink_red() {
  697. gpio_write(red_led_record, 0);
  698. delay(50);
  699. gpio_write(red_led_record, 1);
  700. }
  701. // light up red led
  702. void SdTest::set_red() {
  703. gpio_write(red_led_record, 0);
  704. }
  705. // blink green led
  706. void SdTest::blink_green() {
  707. gpio_write(green_led_record, 0);
  708. delay(50);
  709. gpio_write(green_led_record, 1);
  710. }
  711. // set text, but with infinite loop
  712. template <class T> void SdTest::set_error(std::initializer_list<T> list) {
  713. set_text(list);
  714. set_red();
  715. wait_for_button(InputBack);
  716. exit();
  717. }
  718. // set text, sort of variadic function
  719. template <class T> void SdTest::set_text(std::initializer_list<T> list) {
  720. uint8_t line_position = 0;
  721. acquire_state();
  722. printf("------------------------\n");
  723. // set line strings from args
  724. for(auto element : list) {
  725. state.line[line_position] = element;
  726. printf("%s\n", element);
  727. line_position++;
  728. if(line_position == state.lines_count) break;
  729. }
  730. // set empty lines
  731. for(; line_position < state.lines_count; line_position++) {
  732. state.line[line_position] = "";
  733. printf("\n");
  734. }
  735. printf("------------------------\n");
  736. release_state();
  737. }
  738. // render app
  739. void SdTest::render(Canvas* canvas) {
  740. canvas_set_color(canvas, ColorBlack);
  741. canvas_set_font(canvas, FontSecondary);
  742. for(uint8_t i = 0; i < state.lines_count; i++) {
  743. canvas_draw_str(canvas, 0, (i + 1) * 10, state.line[i]);
  744. }
  745. }
  746. // app enter function
  747. extern "C" void sd_card_test(void* p) {
  748. SdTest* app = new SdTest();
  749. app->run();
  750. }