B&W LCD receiver ID improvement (#5018)
[opentx.git] / radio / src / cli.cpp
blob2653f3699955651d98c946985ec3b2c3988c1215
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
22 #include "diskio.h"
23 #include <ctype.h>
24 #include <malloc.h>
25 #include <new>
27 #define CLI_COMMAND_MAX_ARGS 8
28 #define CLI_COMMAND_MAX_LEN 256
30 OS_TID cliTaskId;
31 TaskStack<CLI_STACK_SIZE> _ALIGNED(8) cliStack; // stack must be aligned to 8 bytes otherwise printf for %f does not work!
32 Fifo<uint8_t, 256> cliRxFifo;
33 uint8_t cliTracesEnabled = true;
34 char cliLastLine[CLI_COMMAND_MAX_LEN+1];
36 typedef int (* CliFunction) (const char ** args);
37 int cliExecLine(char * line);
38 int cliExecCommand(const char ** argv);
39 int cliHelp(const char ** argv);
41 struct CliCommand
43 const char * name;
44 CliFunction func;
45 const char * args;
48 struct MemArea
50 const char * name;
51 void * start;
52 int size;
55 void cliPrompt()
57 serialPutc('>');
60 int toLongLongInt(const char ** argv, int index, long long int * val)
62 if (*argv[index] == '\0') {
63 return 0;
65 else {
66 int base = 10;
67 const char * s = argv[index];
68 if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') {
69 base = 16;
70 s = &argv[index][2];
72 char * endptr = NULL;
73 *val = strtoll(s, &endptr, base);
74 if (*endptr == '\0')
75 return 1;
76 else {
77 serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[index]);
78 return -1;
83 int toInt(const char ** argv, int index, int * val)
85 long long int lval = 0;
86 int result = toLongLongInt(argv, index, &lval);
87 *val = (int)lval;
88 return result;
91 int cliBeep(const char ** argv)
93 int freq = BEEP_DEFAULT_FREQ;
94 int duration = 100;
95 if (toInt(argv, 1, &freq) >= 0 && toInt(argv, 2, &duration) >= 0) {
96 audioQueue.playTone(freq, duration, 20, PLAY_NOW);
98 return 0;
101 int cliPlay(const char ** argv)
103 audioQueue.playFile(argv[1], PLAY_NOW);
104 return 0;
107 int cliLs(const char ** argv)
109 FILINFO fno;
110 DIR dir;
112 FRESULT res = f_opendir(&dir, argv[1]); /* Open the directory */
113 if (res == FR_OK) {
114 for (;;) {
115 res = f_readdir(&dir, &fno); /* Read a directory item */
116 if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
117 serialPrint(fno.fname);
119 f_closedir(&dir);
121 else {
122 serialPrint("%s: Invalid directory \"%s\"", argv[0], argv[1]);
124 return 0;
127 int cliRead(const char ** argv)
129 FIL file;
130 uint32_t bytesRead = 0;
131 int bufferSize;
132 if (toInt(argv, 2, &bufferSize) == 0 || bufferSize < 0 ) {
133 serialPrint("%s: Invalid buffer size \"%s\"", argv[0], argv[2]);
134 return 0;
137 uint8_t * buffer = (uint8_t*) malloc(bufferSize);
138 if (!buffer) {
139 serialPrint("Not enough memory");
140 return 0;
143 FRESULT result = f_open(&file, argv[1], FA_OPEN_EXISTING | FA_READ);
144 if (result != FR_OK) {
145 free(buffer);
146 serialPrint("%s: File not found \"%s\"", argv[0], argv[1]);
147 return 0;
150 tmr10ms_t start = get_tmr10ms();
152 while (true) {
153 UINT read;
154 result = f_read(&file, buffer, sizeof(buffer), &read);
155 if (result == FR_OK) {
156 if (read == 0) {
157 // end of file
158 f_close(&file);
159 break;
161 bytesRead += read;
164 uint32_t elapsedTime = (get_tmr10ms() - start) * 10;
165 if (elapsedTime == 0) elapsedTime = 1;
166 uint32_t speed = bytesRead / elapsedTime;
167 serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed);
168 free(buffer);
169 return 0;
172 int cliReadSD(const char ** argv)
174 int startSector;
175 int numberOfSectors;
176 int bufferSectors;
177 if (toInt(argv, 1, &startSector) == 0 || startSector < 0 ) {
178 serialPrint("%s: Invalid start sector \"%s\"", argv[0], argv[1]);
179 return 0;
181 if (toInt(argv, 2, &numberOfSectors) == 0 || numberOfSectors < 0 ) {
182 serialPrint("%s: Invalid number of sectors \"%s\"", argv[0], argv[2]);
183 return 0;
186 if (toInt(argv, 3, &bufferSectors) == 0 || bufferSectors < 0 ) {
187 serialPrint("%s: Invalid number of buffer sectors \"%s\"", argv[0], argv[3]);
188 return 0;
191 uint8_t * buffer = (uint8_t*) malloc(512*bufferSectors);
192 if (!buffer) {
193 serialPrint("Not enough memory");
194 return 0;
197 uint32_t bytesRead = numberOfSectors * 512;
198 tmr10ms_t start = get_tmr10ms();
200 while (numberOfSectors > 0) {
201 DRESULT res = __disk_read(0, buffer, startSector, bufferSectors);
202 if (res != RES_OK) {
203 serialPrint("disk_read error: %d, sector: %d(%d)", res, startSector, numberOfSectors);
205 #if 0
206 for(uint32_t n=0; n<bufferSectors; ++n) {
207 dump(buffer + n*512, 32);
209 #endif
210 #if 0
211 // calc checksumm
212 uint32_t summ = 0;
213 for(int n=0; n<(bufferSectors*512); ++n) {
214 summ += buffer[n];
216 serialPrint("sector %d(%d) checksumm: %u", startSector, numberOfSectors, summ);
217 #endif
218 if (numberOfSectors >= bufferSectors) {
219 numberOfSectors -= bufferSectors;
220 startSector += bufferSectors;
222 else {
223 numberOfSectors = 0;
227 uint32_t elapsedTime = (get_tmr10ms() - start) * 10;
228 if (elapsedTime == 0) elapsedTime = 1;
229 uint32_t speed = bytesRead / elapsedTime;
230 serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead, elapsedTime, speed);
231 free(buffer);
232 return 0;
235 int cliTestSD(const char ** argv)
237 // Do the read test on the SD card and report back the result
239 // get sector count
240 uint32_t sectorCount;
241 if (disk_ioctl(0, GET_SECTOR_COUNT, &sectorCount) != RES_OK) {
242 serialPrint("Error: can't read sector count");
243 return 0;
245 serialPrint("SD card has %u sectors", sectorCount);
247 // read last 16 sectors one sector at the time
248 serialPrint("Starting single sector read test, reading 16 sectors one by one");
249 uint8_t * buffer = (uint8_t*) malloc(512);
250 if (!buffer) {
251 serialPrint("Not enough memory");
252 return 0;
254 for (uint32_t s = sectorCount - 16; s<sectorCount; ++s) {
255 DRESULT res = __disk_read(0, buffer, s, 1);
256 if (res != RES_OK) {
257 serialPrint("sector %d read FAILED, err: %d", s, res);
259 else {
260 serialPrint("sector %d read OK", s);
263 free(buffer);
264 serialCrlf();
266 // read last 16 sectors, two sectors at the time with a multi-block read
267 buffer = (uint8_t *) malloc(512*2);
268 if (!buffer) {
269 serialPrint("Not enough memory");
270 return 0;
273 serialPrint("Starting multiple sector read test, reading two sectors at the time");
274 for (uint32_t s = sectorCount - 16; s<sectorCount; s+=2) {
275 DRESULT res = __disk_read(0, buffer, s, 2);
276 if (res != RES_OK) {
277 serialPrint("sector %d-%d read FAILED, err: %d", s, s+1, res);
279 else {
280 serialPrint("sector %d-%d read OK", s, s+1);
283 free(buffer);
284 serialCrlf();
286 // read last 16 sectors, all sectors with single multi-block read
287 buffer = (uint8_t*) malloc(512*16);
288 if (!buffer) {
289 serialPrint("Not enough memory");
290 return 0;
293 serialPrint("Starting multiple sector read test, reading 16 sectors at the time");
294 DRESULT res = __disk_read(0, buffer, sectorCount-16, 16);
295 if (res != RES_OK) {
296 serialPrint("sector %d-%d read FAILED, err: %d", sectorCount-16, sectorCount-1, res);
298 else {
299 serialPrint("sector %d-%d read OK", sectorCount-16, sectorCount-1);
301 free(buffer);
302 serialCrlf();
304 return 0;
307 int cliTestNew()
309 char * tmp = 0;
310 serialPrint("Allocating 1kB with new()");
311 CoTickDelay(100);
312 tmp = new char[1024];
313 if (tmp) {
314 serialPrint("\tsuccess");
315 delete[] tmp;
316 tmp = 0;
318 else {
319 serialPrint("\tFAILURE");
322 serialPrint("Allocating 10MB with (std::nothrow) new()");
323 CoTickDelay(100);
324 tmp = new (std::nothrow) char[1024*1024*10];
325 if (tmp) {
326 serialPrint("\tFAILURE, tmp = %p", tmp);
327 delete[] tmp;
328 tmp = 0;
330 else {
331 serialPrint("\tsuccess, allocaton failed, tmp = 0");
334 serialPrint("Allocating 10MB with new()");
335 CoTickDelay(100);
336 tmp = new char[1024*1024*10];
337 if (tmp) {
338 serialPrint("\tFAILURE, tmp = %p", tmp);
339 delete[] tmp;
340 tmp = 0;
342 else {
343 serialPrint("\tsuccess, allocaton failed, tmp = 0");
345 serialPrint("Test finished");
346 return 0;
349 #if defined(COLORLCD)
351 extern bool perMainEnabled;
352 typedef void (*graphichTestFunc)(void);
354 void testDrawSolidFilledRectangle()
356 lcdDrawFilledRect(0, 0, LCD_W, LCD_H, SOLID, TEXT_BGCOLOR);
359 void testDrawFilledRectangle()
361 lcdDrawFilledRect(0, 0, LCD_W, LCD_H, DOTTED, TEXT_BGCOLOR);
364 void testDrawSolidFilledRoundedRectangle()
366 lcdDrawFilledRect(0, 0, LCD_W/2, LCD_H/2, SOLID, ROUND|TEXT_BGCOLOR);
369 void testDrawBlackOverlay()
371 lcdDrawBlackOverlay();
374 void testDrawSolidHorizontalLine1()
376 lcdDrawSolidHorizontalLine(0, 0, 1, 0);
379 void testDrawSolidHorizontalLine2()
381 lcdDrawSolidHorizontalLine(0, 0, LCD_W, 0);
384 void testDrawSolidVerticalLine1()
386 lcdDrawSolidVerticalLine(0, 0, 1, 0);
389 void testDrawSolidVerticalLine2()
391 lcdDrawSolidVerticalLine(0, 0, LCD_H, 0);
394 void testDrawDiagonalLine()
396 lcdDrawLine(0,0, LCD_W, LCD_H, SOLID, TEXT_COLOR);
399 void testEmpty()
403 void testDrawRect()
405 lcdDrawRect(0, 0, LCD_W, LCD_H, 2, SOLID, TEXT_COLOR);
408 void testDrawText()
410 lcdDrawText(0, LCD_H/2, "The quick brown fox jumps over the lazy dog", TEXT_COLOR);
413 void testDrawTextVertical()
415 lcdDrawText(30, LCD_H, "The quick brown fox ", TEXT_COLOR|VERTICAL|NO_FONTCACHE);
418 void testClear()
420 lcdClear();
423 #define GRAPHICS_TEST_RUN_STEP 100
424 #define RUN_GRAPHICS_TEST(name, runtime) runGraphicsTest(name, #name, runtime)
426 float runGraphicsTest(graphichTestFunc func, const char * name, uint32_t runtime)
428 uint32_t start = (uint32_t)CoGetOSTime();
429 uint32_t noRuns = 0;
430 while (((uint32_t)CoGetOSTime() - start) < runtime/2 ) {
431 for (int n=0; n<GRAPHICS_TEST_RUN_STEP; n++) {
432 func();
434 lcdRefresh();
435 noRuns += GRAPHICS_TEST_RUN_STEP;
437 uint32_t actualRuntime = (uint32_t)CoGetOSTime() - start;
438 float result = (noRuns * 500.0f) / (float)actualRuntime; // runs/second
439 serialPrint("Test %s speed: %0.2f, (%d runs in %d ms)", name, result, noRuns, actualRuntime*2);
440 CoTickDelay(100);
441 return result;
444 int cliTestGraphics()
446 serialPrint("Starting graphics performance test...");
447 CoTickDelay(100);
449 watchdogSuspend(6000/*60s*/);
450 if (pulsesStarted()) {
451 pausePulses();
453 pauseMixerCalculations();
454 perMainEnabled = false;
456 float result = 0;
457 RUN_GRAPHICS_TEST(testEmpty, 1000);
458 // result += RUN_GRAPHICS_TEST(testDrawSolidHorizontalLine1, 1000);
459 result += RUN_GRAPHICS_TEST(testDrawSolidHorizontalLine2, 1000);
460 // result += RUN_GRAPHICS_TEST(testDrawSolidVerticalLine1, 1000);
461 result += RUN_GRAPHICS_TEST(testDrawSolidVerticalLine2, 1000);
462 result += RUN_GRAPHICS_TEST(testDrawDiagonalLine, 1000);
463 result += RUN_GRAPHICS_TEST(testDrawSolidFilledRectangle, 1000);
464 result += RUN_GRAPHICS_TEST(testDrawSolidFilledRoundedRectangle, 1000);
465 result += RUN_GRAPHICS_TEST(testDrawRect, 1000);
466 result += RUN_GRAPHICS_TEST(testDrawFilledRectangle, 1000);
467 result += RUN_GRAPHICS_TEST(testDrawBlackOverlay, 1000);
468 result += RUN_GRAPHICS_TEST(testDrawText, 1000);
469 result += RUN_GRAPHICS_TEST(testDrawTextVertical, 1000);
470 result += RUN_GRAPHICS_TEST(testClear, 1000);
472 serialPrint("Total speed: %0.2f", result);
474 perMainEnabled = true;
475 if (pulsesStarted()) {
476 resumePulses();
478 resumeMixerCalculations();
479 watchdogSuspend(0);
481 return 0;
484 void memoryRead(const uint8_t * src, uint32_t size)
486 // uint8_t data;
487 while(size--) {
488 /*data =*/ *(const uint8_t volatile *)src;
489 ++src;
494 void memoryRead(const uint32_t * src, uint32_t size)
496 while(size--) {
497 *(const uint32_t volatile *)src;
498 ++src;
502 uint32_t * testbuff[100];
504 void memoryCopy(uint8_t * dest, const uint8_t * src, uint32_t size)
506 while(size--) {
507 *dest = *src;
508 ++src;
509 ++dest;
513 void memoryCopy(uint32_t * dest, const uint32_t * src, uint32_t size)
515 while(size--) {
516 *dest = *src;
517 ++src;
518 ++dest;
522 #define MEMORY_SPEED_BLOCK_SIZE (4*1024)
524 void testMemoryReadFrom_RAM_8bit()
526 memoryRead((const uint8_t *)cliLastLine, MEMORY_SPEED_BLOCK_SIZE);
529 void testMemoryReadFrom_RAM_32bit()
531 memoryRead((const uint32_t *)0x20000000, MEMORY_SPEED_BLOCK_SIZE/4);
534 void testMemoryReadFrom_SDRAM_8bit()
536 memoryRead((const uint8_t *)0xD0000000, MEMORY_SPEED_BLOCK_SIZE);
539 void testMemoryReadFrom_SDRAM_32bit()
541 memoryRead((const uint32_t *)0xD0000000, MEMORY_SPEED_BLOCK_SIZE/4);
544 extern uint8_t * LCD_FIRST_FRAME_BUFFER;
545 extern uint8_t * LCD_SECOND_FRAME_BUFFER;
548 void testMemoryCopyFrom_RAM_to_SDRAM_32bit()
550 memoryCopy((uint32_t *)LCD_FIRST_FRAME_BUFFER, (const uint32_t * )cliLastLine, MEMORY_SPEED_BLOCK_SIZE/4);
553 void testMemoryCopyFrom_RAM_to_SDRAM_8bit()
555 memoryCopy((uint8_t *)LCD_FIRST_FRAME_BUFFER, (const uint8_t * )cliLastLine, MEMORY_SPEED_BLOCK_SIZE);
558 void testMemoryCopyFrom_SDRAM_to_SDRAM_32bit()
560 memoryCopy((uint32_t *)LCD_FIRST_FRAME_BUFFER, (const uint32_t * )LCD_SECOND_FRAME_BUFFER, MEMORY_SPEED_BLOCK_SIZE/4);
563 void testMemoryCopyFrom_SDRAM_to_SDRAM_8bit()
565 memoryCopy((uint8_t *)LCD_FIRST_FRAME_BUFFER, (const uint8_t * )LCD_SECOND_FRAME_BUFFER, MEMORY_SPEED_BLOCK_SIZE);
568 #define MEMORY_TEST_RUN_STEP 100
569 #define RUN_MEMORY_TEST(name, runtime) runMemoryTest(name, #name, runtime)
571 float runMemoryTest(graphichTestFunc func, const char * name, uint32_t runtime)
573 uint32_t start = (uint32_t)CoGetOSTime();
574 uint32_t noRuns = 0;
575 while (((uint32_t)CoGetOSTime() - start) < runtime/2 ) {
576 for (int n=0; n<MEMORY_TEST_RUN_STEP; n++) {
577 func();
579 noRuns += MEMORY_TEST_RUN_STEP;
581 uint32_t actualRuntime = (uint32_t)CoGetOSTime() - start;
582 float result = (noRuns * 500.0f) / (float)actualRuntime; // runs/second
583 serialPrint("Test %s speed: %0.2f, (%d runs in %d ms)", name, result, noRuns, actualRuntime*2);
584 CoTickDelay(100);
585 return result;
589 int cliTestMemorySpeed()
591 serialPrint("Starting memory speed test...");
592 CoTickDelay(100);
594 watchdogSuspend(6000/*60s*/);
595 if (pulsesStarted()) {
596 pausePulses();
598 pauseMixerCalculations();
599 perMainEnabled = false;
601 float result = 0;
602 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_RAM_8bit, 200);
603 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_RAM_32bit, 200);
604 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_SDRAM_8bit, 200);
605 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_SDRAM_32bit, 200);
606 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_RAM_to_SDRAM_8bit, 200);
607 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_RAM_to_SDRAM_32bit, 200);
608 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_8bit, 200);
609 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_32bit, 200);
611 LTDC_Cmd(DISABLE);
612 serialPrint("Disabling LCD...");
613 CoTickDelay(100);
615 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_RAM_8bit, 200);
616 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_RAM_32bit, 200);
617 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_SDRAM_8bit, 200);
618 result += RUN_GRAPHICS_TEST(testMemoryReadFrom_SDRAM_32bit, 200);
619 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_RAM_to_SDRAM_8bit, 200);
620 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_RAM_to_SDRAM_32bit, 200);
621 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_8bit, 200);
622 result += RUN_GRAPHICS_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_32bit, 200);
624 serialPrint("Total speed: %0.2f", result);
626 LTDC_Cmd(ENABLE);
628 perMainEnabled = true;
629 if (pulsesStarted()) {
630 resumePulses();
632 resumeMixerCalculations();
633 watchdogSuspend(0);
635 return 0;
637 #endif // #if defined(COLORLCD)
639 int cliTest(const char ** argv)
641 if (!strcmp(argv[1], "new")) {
642 return cliTestNew();
644 else if (!strcmp(argv[1], "std::exception")) {
645 serialPrint("Not implemented");
647 #if defined(COLORLCD)
648 else if (!strcmp(argv[1], "graphics")) {
649 return cliTestGraphics();
651 else if (!strcmp(argv[1], "memspd")) {
652 return cliTestMemorySpeed();
654 #endif
655 else {
656 serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]);
658 return 0;
661 int cliTrace(const char ** argv)
663 if (!strcmp(argv[1], "on")) {
664 cliTracesEnabled = true;
666 else if (!strcmp(argv[1], "off")) {
667 cliTracesEnabled = false;
669 else {
670 serialPrint("%s: Invalid argument \"%s\"", argv[0], argv[1]);
672 return 0;
675 int cliStackInfo(const char ** argv)
677 serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize() * 4); // stackSize() returns size in 32bit chunks
678 serialPrint("[MENUS] %d available / %d", menusStack.available(), menusStack.size());
679 serialPrint("[MIXER] %d available / %d", mixerStack.available(), mixerStack.size());
680 serialPrint("[AUDIO] %d available / %d", audioStack.available(), audioStack.size());
681 serialPrint("[CLI] %d available / %d", cliStack.available(), cliStack.size());
682 return 0;
685 extern int _end;
686 extern int _heap_end;
687 extern unsigned char *heap;
689 int cliMemoryInfo(const char ** argv)
691 // struct mallinfo {
692 // int arena; /* total space allocated from system */
693 // int ordblks; /* number of non-inuse chunks */
694 // int smblks; /* unused -- always zero */
695 // int hblks; /* number of mmapped regions */
696 // int hblkhd; /* total space in mmapped regions */
697 // int usmblks; /* unused -- always zero */
698 // int fsmblks; /* unused -- always zero */
699 // int uordblks; /* total allocated space */
700 // int fordblks; /* total non-inuse space */
701 // int keepcost; /* top-most, releasable (via malloc_trim) space */
702 // };
703 struct mallinfo info = mallinfo();
704 serialPrint("mallinfo:");
705 serialPrint("\tarena %d bytes", info.arena);
706 serialPrint("\tordblks %d bytes", info.ordblks);
707 serialPrint("\tuordblks %d bytes", info.uordblks);
708 serialPrint("\tfordblks %d bytes", info.fordblks);
709 serialPrint("\tkeepcost %d bytes", info.keepcost);
711 serialPrint("\nHeap:");
712 serialPrint("\tstart %p", (unsigned char *)&_end);
713 serialPrint("\tend %p", (unsigned char *)&_heap_end);
714 serialPrint("\tcurr %p", heap);
715 serialPrint("\tused %d bytes", (int)(heap - (unsigned char *)&_end));
716 serialPrint("\tfree %d bytes", (int)((unsigned char *)&_heap_end - heap));
718 #if defined(LUA)
719 serialPrint("\nLua:");
720 uint32_t s = luaGetMemUsed(lsScripts);
721 serialPrint("\tScripts %u", s);
722 #if defined(COLORLCD)
723 uint32_t w = luaGetMemUsed(lsWidgets);
724 uint32_t e = luaExtraMemoryUsage;
725 serialPrint("\tWidgets %u", w);
726 serialPrint("\tExtra %u", e);
727 serialPrint("------------");
728 serialPrint("\tTotal %u", s + w + e);
729 #endif
730 #endif
731 return 0;
734 int cliReboot(const char ** argv)
736 #if !defined(SIMU)
737 if (!strcmp(argv[1], "wdt")) {
738 // do a user requested watchdog test by pausing mixer thread
739 pausePulses();
741 else {
742 NVIC_SystemReset();
744 #endif
745 return 0;
748 #if defined(PCBFLAMENCO)
749 int cliReadBQ24195(const char ** argv)
751 int index = 0;
752 if (toInt(argv, 1, &index) > 0) {
753 serialPrint("BQ24195[%d] = 0x%02x", index, i2cReadBQ24195(index));
755 else {
756 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1]);
758 return 0;
761 int cliWriteBQ24195(const char ** argv)
763 int index = 0;
764 int data = 0;
765 if (toInt(argv, 1, &index) > 0 && toInt(argv, 2, &data) > 0) {
766 i2cWriteBQ24195(index, data);
768 else {
769 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]);
771 return 0;
773 #endif
775 const MemArea memAreas[] = {
776 { "RCC", RCC, sizeof(RCC_TypeDef) },
777 { "GPIOA", GPIOA, sizeof(GPIO_TypeDef) },
778 { "GPIOB", GPIOB, sizeof(GPIO_TypeDef) },
779 { "GPIOC", GPIOC, sizeof(GPIO_TypeDef) },
780 { "GPIOD", GPIOD, sizeof(GPIO_TypeDef) },
781 { "GPIOE", GPIOE, sizeof(GPIO_TypeDef) },
782 { "GPIOF", GPIOF, sizeof(GPIO_TypeDef) },
783 { "GPIOG", GPIOG, sizeof(GPIO_TypeDef) },
784 { "USART1", USART1, sizeof(USART_TypeDef) },
785 { "USART2", USART2, sizeof(USART_TypeDef) },
786 { "USART3", USART3, sizeof(USART_TypeDef) },
787 { NULL, NULL, 0 },
790 int cliSet(const char ** argv)
792 if (!strcmp(argv[1], "rtc")) {
793 struct gtm t;
794 int year, month, day, hour, minute, second;
795 if (toInt(argv, 2, &year) > 0 && toInt(argv, 3, &month) > 0 && toInt(argv, 4, &day) > 0 && toInt(argv, 5, &hour) > 0 && toInt(argv, 6, &minute) > 0 && toInt(argv, 7, &second) > 0) {
796 t.tm_year = year-TM_YEAR_BASE;
797 t.tm_mon = month-1;
798 t.tm_mday = day;
799 t.tm_hour = hour;
800 t.tm_min = minute;
801 t.tm_sec = second;
802 g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
803 rtcSetTime(&t);
805 else {
806 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv[0], argv[1], argv[2]);
809 #if !defined(SOFTWARE_VOLUME)
810 else if (!strcmp(argv[1], "volume")) {
811 int level = 0;
812 if (toInt(argv, 2, &level) > 0) {
813 setVolume(level);
815 else {
816 serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv[0], argv[1], argv[2]);
818 return 0;
820 #endif
821 return 0;
825 #if defined(DEBUG_INTERRUPTS)
826 void printInterrupts()
828 __disable_irq();
829 struct InterruptCounters ic = interruptCounters;
830 memset(&interruptCounters, 0, sizeof(interruptCounters));
831 interruptCounters.resetTime = get_tmr10ms();
832 __enable_irq();
833 serialPrint("Interrupts count in the last %u ms:", (get_tmr10ms() - ic.resetTime) * 10);
834 for(int n = 0; n < INT_LAST; n++) {
835 serialPrint("%s: %u", interruptNames[n], ic.cnt[n]);
838 #endif //#if defined(DEBUG_INTERRUPTS)
840 #if defined(DEBUG_TASKS)
842 void printTaskSwitchLog()
844 serialPrint("Tasks legend [<task_id>, <task name>]:");
845 for(int n = 0; n <= CFG_MAX_USER_TASKS+1; n++) {
846 if (0 == n) {
847 serialPrint("%d: Idle", n);
849 if (cliTaskId == n) {
850 serialPrint("%d: CLI", n);
852 else if (menusTaskId == n) {
853 serialPrint("%d: menus", n);
855 else if (mixerTaskId == n) {
856 serialPrint("%d: mixer", n);
858 else if (audioTaskId == n) {
859 serialPrint("%d: audio", n);
862 serialCrlf();
864 serialPrint("Tasks switch log at %u [<time>, <task_id>]:", get_tmr10ms());
865 uint32_t lastSwitchTime = 0;
866 uint32_t * tsl = new uint32_t[DEBUG_TASKS_LOG_SIZE];
867 if (!tsl) {
868 serialPrint("Not enough memory");
869 return;
871 memcpy(tsl, taskSwitchLog, sizeof(taskSwitchLog));
872 uint32_t * p = tsl + taskSwitchLogPos;
873 uint32_t * end = tsl + DEBUG_TASKS_LOG_SIZE;
874 for(int n = 0; n < DEBUG_TASKS_LOG_SIZE; n++) {
875 uint32_t taskId = *p >> 24;
876 uint32_t switchTime = *p & 0xFFFFFF;
877 if (lastSwitchTime != switchTime) {
878 serialPrintf("\r\n%06x: ", switchTime);
879 lastSwitchTime = switchTime;
881 serialPrintf("%u ", taskId);
882 if ( ++p >= end ) {
883 p = tsl;
886 delete[] tsl;
887 serialCrlf();
889 #endif // #if defined(DEBUG_TASKS)
891 #if defined(DEBUG_TIMERS)
893 void printDebugTime(uint32_t time)
895 if (time >= 30000) {
896 serialPrintf("%dms", time/1000);
898 else {
899 serialPrintf("%d.%03dms", time/1000, time%1000);
903 void printDebugTimer(const char * name, DebugTimer & timer)
905 serialPrintf("%s: ", name);
906 printDebugTime( timer.getMin());
907 serialPrintf(" - ");
908 printDebugTime(timer.getMax());
909 serialCrlf();
910 timer.reset();
912 void printDebugTimers()
914 for(int n = 0; n < DEBUG_TIMERS_COUNT; n++) {
915 printDebugTimer(debugTimerNames[n], debugTimers[n]);
918 #endif
920 #include "OsMutex.h"
921 extern OS_MutexID audioMutex;
923 void printAudioVars()
925 for(int n = 0; n < AUDIO_BUFFER_COUNT; n++) {
926 serialPrint("Audio Buffer %d: size: %u, ", n, (uint32_t)audioBuffers[n].size);
927 dump((uint8_t *)audioBuffers[n].data, 32);
929 serialPrint("fragments:");
930 for(int n = 0; n < AUDIO_QUEUE_LENGTH; n++) {
931 serialPrint("%d: type %u: id: %u, repeat: %u, ", n, (uint32_t)audioQueue.fragmentsFifo.fragments[n].type,
932 (uint32_t)audioQueue.fragmentsFifo.fragments[n].id,
933 (uint32_t)audioQueue.fragmentsFifo.fragments[n].repeat);
934 if ( audioQueue.fragmentsFifo.fragments[n].type == FRAGMENT_FILE) {
935 serialPrint(" file: %s", audioQueue.fragmentsFifo.fragments[n].file);
939 serialPrint("FragmentFifo: ridx: %d, widx: %d", audioQueue.fragmentsFifo.ridx, audioQueue.fragmentsFifo.widx);
940 serialPrint("audioQueue: readIdx: %d, writeIdx: %d, full: %d", audioQueue.buffersFifo.readIdx, audioQueue.buffersFifo.writeIdx, audioQueue.buffersFifo.bufferFull);
942 serialPrint("normalContext: %u", (uint32_t)audioQueue.normalContext.fragment.type);
944 serialPrint("audioMutex[%u] = %u", (uint32_t)audioMutex, (uint32_t)MutexTbl[audioMutex].mutexFlag);
948 int cliDisplay(const char ** argv)
950 long long int address = 0;
952 for (const MemArea * area = memAreas; area->name != NULL; area++) {
953 if (!strcmp(area->name, argv[1])) {
954 dump((uint8_t *)area->start, area->size);
955 return 0;
959 if (!strcmp(argv[1], "keys")) {
960 for (int i=0; i<TRM_BASE; i++) {
961 char name[8];
962 uint8_t len = STR_VKEYS[0];
963 strncpy(name, STR_VKEYS+1+len*i, len);
964 name[len] = '\0';
965 serialPrint("[%s] = %s", name, keyState(i) ? "on" : "off");
967 #if defined(ROTARY_ENCODER_NAVIGATION)
968 serialPrint("[Enc.] = %d", rotencValue[0] / ROTARY_ENCODER_GRANULARITY);
969 #endif
970 for (int i=TRM_BASE; i<=TRM_LAST; i++) {
971 serialPrint("[Trim%d] = %s", i-TRM_BASE, keyState(i) ? "on" : "off");
973 for (int i=MIXSRC_FIRST_SWITCH; i<=MIXSRC_LAST_SWITCH; i++) {
974 mixsrc_t sw = i - MIXSRC_FIRST_SWITCH;
975 if (SWITCH_EXISTS(sw)) {
976 char swName[LEN_SWITCH_NAME + 1];
977 strAppend(swName, STR_VSWITCHES+1+sw*STR_VSWITCHES[0], STR_VSWITCHES[0]);
978 static const char * const SWITCH_POSITIONS[] = { "down", "mid", "up" };
979 serialPrint("[%s] = %s", swName, SWITCH_POSITIONS[1 + getValue(i) / 1024]);
983 else if (!strcmp(argv[1], "adc")) {
984 for (int i=0; i<NUM_ANALOGS; i++) {
985 serialPrint("adc[%d] = %04X", i, (int)adcValues[i]);
988 else if (!strcmp(argv[1], "outputs")) {
989 for (int i=0; i<MAX_OUTPUT_CHANNELS; i++) {
990 serialPrint("outputs[%d] = %04d", i, (int)channelOutputs[i]);
993 else if (!strcmp(argv[1], "rtc")) {
994 struct gtm utm;
995 gettime(&utm);
996 serialPrint("rtc = %4d-%02d-%02d %02d:%02d:%02d.%02d0", utm.tm_year+TM_YEAR_BASE, utm.tm_mon+1, utm.tm_mday, utm.tm_hour, utm.tm_min, utm.tm_sec, g_ms100);
998 #if !defined(SOFTWARE_VOLUME)
999 else if (!strcmp(argv[1], "volume")) {
1000 serialPrint("volume = %d", getVolume());
1002 #endif
1003 #if defined(STM32)
1004 else if (!strcmp(argv[1], "uid")) {
1005 char str[LEN_CPU_UID+1];
1006 getCPUUniqueID(str);
1007 serialPrint("uid = %s", str);
1009 #endif
1010 #if defined(PCBFLAMENCO)
1011 else if (!strcmp(argv[1], "bq24195")) {
1013 uint8_t reg = i2cReadBQ24195(0x00);
1014 serialPrint(reg & 0x80 ? "HIZ enable" : "HIZ disable");
1017 uint8_t reg = i2cReadBQ24195(0x08);
1018 serialPrint(reg & 0x01 ? "VBatt < VSysMin" : "VBatt > VSysMin");
1019 serialPrint(reg & 0x02 ? "Thermal sensor bad" : "Thermal sensor ok");
1020 serialPrint(reg & 0x04 ? "Power ok" : "Power bad");
1021 serialPrint(reg & 0x08 ? "Connected to charger" : "Not connected to charger");
1022 static const char * const CHARGE_STATUS[] = { "Not Charging", "Precharge", "Fast Charging", "Charge done" };
1023 serialPrint(CHARGE_STATUS[(reg & 0x30) >> 4]);
1024 static const char * const INPUT_STATUS[] = { "Unknown input", "USB host input", "USB adapter port input", "OTG input" };
1025 serialPrint(INPUT_STATUS[(reg & 0xC0) >> 6]);
1028 uint8_t reg = i2cReadBQ24195(0x09);
1029 if (reg & 0x80) serialPrint("Watchdog timer expiration");
1030 uint8_t chargerFault = (reg & 0x30) >> 4;
1031 if (chargerFault == 0x01)
1032 serialPrint("Input fault");
1033 else if (chargerFault == 0x02)
1034 serialPrint("Thermal shutdown");
1035 else if (chargerFault == 0x03)
1036 serialPrint("Charge safety timer expiration");
1037 if (reg & 0x08) serialPrint("Battery over voltage fault");
1038 uint8_t ntcFault = (reg & 0x07);
1039 if (ntcFault == 0x05)
1040 serialPrint("NTC cold");
1041 else if (ntcFault == 0x06)
1042 serialPrint("NTC hot");
1045 #endif
1046 else if (!strcmp(argv[1], "tim")) {
1047 int timerNumber;
1048 if (toInt(argv, 2, &timerNumber) > 0) {
1049 TIM_TypeDef * tim = TIM1;
1050 switch (timerNumber) {
1051 case 1:
1052 tim = TIM1;
1053 break;
1054 case 2:
1055 tim = TIM2;
1056 break;
1057 case 13:
1058 tim = TIM13;
1059 break;
1060 default:
1061 return 0;
1063 serialPrint("TIM%d", timerNumber);
1064 serialPrint(" CR1 0x%x", tim->CR1);
1065 serialPrint(" CR2 0x%x", tim->CR2);
1066 serialPrint(" DIER 0x%x", tim->DIER);
1067 serialPrint(" SR 0x%x", tim->SR);
1068 serialPrint(" EGR 0x%x", tim->EGR);
1069 serialPrint(" CCMR1 0x%x", tim->CCMR1);
1070 serialPrint(" CCMR2 0x%x", tim->CCMR2);
1072 serialPrint(" CNT 0x%x", tim->CNT);
1073 serialPrint(" ARR 0x%x", tim->ARR);
1074 serialPrint(" PSC 0x%x", tim->PSC);
1076 serialPrint(" CCER 0x%x", tim->CCER);
1077 serialPrint(" CCR1 0x%x", tim->CCR1);
1078 serialPrint(" CCR2 0x%x", tim->CCR2);
1079 serialPrint(" CCR3 0x%x", tim->CCR3);
1080 serialPrint(" CCR4 0x%x", tim->CCR4);
1083 else if (!strcmp(argv[1], "dma")) {
1084 serialPrint("DMA1_Stream7");
1085 serialPrint(" CR 0x%x", DMA1_Stream7->CR);
1087 #if defined(DEBUG_INTERRUPTS)
1088 else if (!strcmp(argv[1], "int")) {
1089 printInterrupts();
1091 #endif
1092 #if defined(DEBUG_TASKS)
1093 else if (!strcmp(argv[1], "tsl")) {
1094 printTaskSwitchLog();
1096 #endif
1097 #if defined(DEBUG_TIMERS)
1098 else if (!strcmp(argv[1], "dt")) {
1099 printDebugTimers();
1101 #endif
1102 else if (!strcmp(argv[1], "audio")) {
1103 printAudioVars();
1105 #if defined(DISK_CACHE)
1106 else if (!strcmp(argv[1], "dc")) {
1107 DiskCacheStats stats = diskCache.getStats();
1108 uint32_t hitRate = diskCache.getHitRate();
1109 serialPrint("Disk Cache stats: w:%u r: %u, h: %u(%0.1f%%), m: %u", stats.noWrites, (stats.noHits + stats.noMisses), stats.noHits, hitRate*0.1f, stats.noMisses);
1111 #endif
1112 else if (toLongLongInt(argv, 1, &address) > 0) {
1113 int size = 256;
1114 if (toInt(argv, 2, &size) >= 0) {
1115 dump((uint8_t *)address, size);
1118 return 0;
1121 int cliDebugVars(const char ** argv)
1123 #if defined(PCBHORUS)
1124 extern uint32_t ioMutexReq, ioMutexRel;
1125 extern uint32_t sdReadRetries;
1126 serialPrint("ioMutexReq=%d", ioMutexReq);
1127 serialPrint("ioMutexRel=%d", ioMutexRel);
1128 serialPrint("sdReadRetries=%d", sdReadRetries);
1129 #elif defined(PCBTARANIS)
1130 serialPrint("telemetryErrors=%d", telemetryErrors);
1131 #endif
1133 return 0;
1136 int cliRepeat(const char ** argv)
1138 int interval = 0;
1139 int counter = 0;
1140 if (toInt(argv, 1, &interval) > 0 && argv[2]) {
1141 interval *= 50;
1142 counter = interval;
1143 uint8_t c;
1144 while (!cliRxFifo.pop(c) || !(c == '\r' || c == '\n' || c == ' ')) {
1145 CoTickDelay(10); // 20ms
1146 if (++counter >= interval) {
1147 cliExecCommand(&argv[2]);
1148 counter = 0;
1152 else {
1153 serialPrint("%s: Invalid arguments", argv[0]);
1155 return 0;
1158 #if defined(JITTER_MEASURE)
1159 int cliShowJitter(const char ** argv)
1161 serialPrint( "# anaIn rawJ avgJ");
1162 for (int i=0; i<NUM_ANALOGS; i++) {
1163 serialPrint("A%02d %04X %04X %3d %3d", i, getAnalogValue(i), anaIn(i), rawJitter[i].get(), avgJitter[i].get());
1164 if (IS_POT_MULTIPOS(i)) {
1165 StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[i];
1166 for (int j=0; j<calib->count; j++) {
1167 serialPrint(" s%d %04X", j, calib->steps[j]);
1171 return 0;
1173 #endif
1175 #if defined(INTERNAL_GPS)
1176 int cliGps(const char ** argv)
1178 int baudrate = 0;
1180 if (argv[1][0] == '$') {
1181 // send command to GPS
1182 gpsSendFrame(argv[1]);
1184 #if defined(DEBUG)
1185 else if (!strcmp(argv[1], "trace")) {
1186 gpsTraceEnabled = !gpsTraceEnabled;
1188 #endif
1189 else if (toInt(argv, 1, &baudrate) > 0 && baudrate > 0) {
1190 gpsInit(baudrate);
1191 serialPrint("GPS baudrate set to %d", baudrate);
1193 else {
1194 serialPrint("%s: Invalid arguments", argv[0]);
1196 return 0;
1198 #endif
1200 #if defined(BLUETOOTH)
1201 int cliBlueTooth(const char ** argv)
1203 int baudrate = 0;
1204 if (!strncmp(argv[1], "AT", 2) || !strncmp(argv[1], "TTM", 3)) {
1205 char command[32];
1206 strAppend(strAppend(command, argv[1]), "\r\n");
1207 bluetoothWriteString(command);
1208 char * line = bluetoothReadline();
1209 serialPrint("<BT %s", line);
1211 else if (toInt(argv, 1, &baudrate) > 0) {
1212 if (baudrate > 0) {
1213 bluetoothInit(baudrate);
1214 char * line = bluetoothReadline();
1215 serialPrint("<BT %s", line);
1217 else {
1218 bluetoothDone();
1219 serialPrint("BT turned off");
1222 else {
1223 serialPrint("%s: Invalid arguments", argv[0]);
1225 return 0;
1227 #endif
1229 const CliCommand cliCommands[] = {
1230 { "beep", cliBeep, "[<frequency>] [<duration>]" },
1231 { "ls", cliLs, "<directory>" },
1232 { "read", cliRead, "<filename>" },
1233 { "readsd", cliReadSD, "<start sector> <sectors count> <read buffer size (sectors)>" },
1234 { "testsd", cliTestSD, "" },
1235 { "play", cliPlay, "<filename>" },
1236 { "print", cliDisplay, "<address> [<size>] | <what>" },
1237 { "p", cliDisplay, "<address> [<size>] | <what>" },
1238 { "reboot", cliReboot, "[wdt]" },
1239 { "set", cliSet, "<what> <value>" },
1240 { "stackinfo", cliStackInfo, "" },
1241 { "meminfo", cliMemoryInfo, "" },
1242 { "test", cliTest, "new | std::exception | graphics | memspd" },
1243 { "trace", cliTrace, "on | off" },
1244 #if defined(PCBFLAMENCO)
1245 { "read_bq24195", cliReadBQ24195, "<register>" },
1246 { "write_bq24195", cliWriteBQ24195, "<register> <data>" },
1247 #endif
1248 { "help", cliHelp, "[<command>]" },
1249 { "debugvars", cliDebugVars, "" },
1250 { "repeat", cliRepeat, "<interval> <command>" },
1251 #if defined(JITTER_MEASURE)
1252 { "jitter", cliShowJitter, "" },
1253 #endif
1254 #if defined(INTERNAL_GPS)
1255 { "gps", cliGps, "<baudrate>|$<command>|trace" },
1256 #endif
1257 #if defined(BLUETOOTH)
1258 { "bt", cliBlueTooth, "<baudrate>|<command>" },
1259 #endif
1260 { NULL, NULL, NULL } /* sentinel */
1263 int cliHelp(const char ** argv)
1265 for (const CliCommand * command = cliCommands; command->name != NULL; command++) {
1266 if (argv[1][0] == '\0' || !strcmp(command->name, argv[0])) {
1267 serialPrint("%s %s", command->name, command->args);
1268 if (argv[1][0] != '\0') {
1269 return 0;
1273 if (argv[1][0] != '\0') {
1274 serialPrint("Invalid command \"%s\"", argv[0]);
1276 return -1;
1279 int cliExecCommand(const char ** argv)
1281 if (argv[0][0] == '\0')
1282 return 0;
1284 for (const CliCommand * command = cliCommands; command->name != NULL; command++) {
1285 if (!strcmp(command->name, argv[0])) {
1286 return command->func(argv);
1289 serialPrint("Invalid command \"%s\"", argv[0]);
1290 return -1;
1293 int cliExecLine(char * line)
1295 int len = strlen(line);
1296 const char * argv[CLI_COMMAND_MAX_ARGS];
1297 memset(argv, 0, sizeof(argv));
1298 int argc = 1;
1299 argv[0] = line;
1300 for (int i=0; i<len; i++) {
1301 if (line[i] == ' ') {
1302 line[i] = '\0';
1303 if (argc < CLI_COMMAND_MAX_ARGS) {
1304 argv[argc++] = &line[i+1];
1308 return cliExecCommand(argv);
1311 void cliTask(void * pdata)
1313 char line[CLI_COMMAND_MAX_LEN+1];
1314 int pos = 0;
1316 cliPrompt();
1318 for (;;) {
1319 uint8_t c;
1321 while (!cliRxFifo.pop(c)) {
1322 CoTickDelay(10); // 20ms
1325 if (c == 12) {
1326 // clear screen
1327 serialPrint("\033[2J\033[1;1H");
1328 cliPrompt();
1330 else if (c == 127) {
1331 // backspace
1332 if (pos) {
1333 line[--pos] = '\0';
1334 serialPutc(c);
1337 else if (c == '\r' || c == '\n') {
1338 // enter
1339 serialCrlf();
1340 line[pos] = '\0';
1341 if (pos == 0 && cliLastLine[0]) {
1342 // execute (repeat) last command
1343 strcpy(line, cliLastLine);
1345 else {
1346 // save new command
1347 strcpy(cliLastLine, line);
1349 cliExecLine(line);
1350 pos = 0;
1351 cliPrompt();
1353 else if (isascii(c) && pos < CLI_COMMAND_MAX_LEN) {
1354 line[pos++] = c;
1355 serialPutc(c);
1360 void cliStart()
1362 cliTaskId = CoCreateTaskEx(cliTask, NULL, 10, &cliStack.stack[CLI_STACK_SIZE-1], CLI_STACK_SIZE, 1, false);