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.
27 #define CLI_COMMAND_MAX_ARGS 8
28 #define CLI_COMMAND_MAX_LEN 256
30 RTOS_TASK_HANDLE cliTaskId
;
31 RTOS_DEFINE_STACK(cliStack
, CLI_STACK_SIZE
);
33 Fifo
<uint8_t, 256> cliRxFifo
;
34 uint8_t cliTracesEnabled
= true;
35 char cliLastLine
[CLI_COMMAND_MAX_LEN
+1];
37 typedef int (* CliFunction
) (const char ** args
);
38 int cliExecLine(char * line
);
39 int cliExecCommand(const char ** argv
);
40 int cliHelp(const char ** argv
);
61 int toLongLongInt(const char ** argv
, int index
, long long int * val
)
63 if (*argv
[index
] == '\0') {
68 const char * s
= argv
[index
];
69 if (strlen(s
) > 2 && s
[0] == '0' && s
[1] == 'x') {
73 char * endptr
= nullptr;
74 *val
= strtoll(s
, &endptr
, base
);
78 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[index
]);
84 int toInt(const char ** argv
, int index
, int * val
)
86 long long int lval
= 0;
87 int result
= toLongLongInt(argv
, index
, &lval
);
92 int cliBeep(const char ** argv
)
94 int freq
= BEEP_DEFAULT_FREQ
;
96 if (toInt(argv
, 1, &freq
) >= 0 && toInt(argv
, 2, &duration
) >= 0) {
97 audioQueue
.playTone(freq
, duration
, 20, PLAY_NOW
);
102 int cliPlay(const char ** argv
)
104 audioQueue
.playFile(argv
[1], PLAY_NOW
);
108 int cliLs(const char ** argv
)
113 FRESULT res
= f_opendir(&dir
, argv
[1]); /* Open the directory */
116 res
= f_readdir(&dir
, &fno
); /* Read a directory item */
117 if (res
!= FR_OK
|| fno
.fname
[0] == 0) break; /* Break on error or end of dir */
118 serialPrint(fno
.fname
);
123 serialPrint("%s: Invalid directory \"%s\"", argv
[0], argv
[1]);
128 int cliRead(const char ** argv
)
131 uint32_t bytesRead
= 0;
133 if (toInt(argv
, 2, &bufferSize
) == 0 || bufferSize
< 0 ) {
134 serialPrint("%s: Invalid buffer size \"%s\"", argv
[0], argv
[2]);
138 uint8_t * buffer
= (uint8_t*) malloc(bufferSize
);
140 serialPrint("Not enough memory");
144 FRESULT result
= f_open(&file
, argv
[1], FA_OPEN_EXISTING
| FA_READ
);
145 if (result
!= FR_OK
) {
147 serialPrint("%s: File not found \"%s\"", argv
[0], argv
[1]);
151 tmr10ms_t start
= get_tmr10ms();
155 result
= f_read(&file
, buffer
, sizeof(buffer
), &read
);
156 if (result
== FR_OK
) {
165 uint32_t elapsedTime
= (get_tmr10ms() - start
) * 10;
166 if (elapsedTime
== 0) elapsedTime
= 1;
167 uint32_t speed
= bytesRead
/ elapsedTime
;
168 serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead
, elapsedTime
, speed
);
173 int cliReadSD(const char ** argv
)
178 if (toInt(argv
, 1, &startSector
) == 0 || startSector
< 0 ) {
179 serialPrint("%s: Invalid start sector \"%s\"", argv
[0], argv
[1]);
182 if (toInt(argv
, 2, &numberOfSectors
) == 0 || numberOfSectors
< 0 ) {
183 serialPrint("%s: Invalid number of sectors \"%s\"", argv
[0], argv
[2]);
187 if (toInt(argv
, 3, &bufferSectors
) == 0 || bufferSectors
< 0 ) {
188 serialPrint("%s: Invalid number of buffer sectors \"%s\"", argv
[0], argv
[3]);
192 uint8_t * buffer
= (uint8_t*) malloc(512*bufferSectors
);
194 serialPrint("Not enough memory");
198 uint32_t bytesRead
= numberOfSectors
* 512;
199 tmr10ms_t start
= get_tmr10ms();
201 while (numberOfSectors
> 0) {
202 DRESULT res
= __disk_read(0, buffer
, startSector
, bufferSectors
);
204 serialPrint("disk_read error: %d, sector: %d(%d)", res
, startSector
, numberOfSectors
);
207 for(uint32_t n
=0; n
<bufferSectors
; ++n
) {
208 dump(buffer
+ n
*512, 32);
214 for(int n
=0; n
<(bufferSectors
*512); ++n
) {
217 serialPrint("sector %d(%d) checksumm: %u", startSector
, numberOfSectors
, summ
);
219 if (numberOfSectors
>= bufferSectors
) {
220 numberOfSectors
-= bufferSectors
;
221 startSector
+= bufferSectors
;
228 uint32_t elapsedTime
= (get_tmr10ms() - start
) * 10;
229 if (elapsedTime
== 0) elapsedTime
= 1;
230 uint32_t speed
= bytesRead
/ elapsedTime
;
231 serialPrint("Read %d bytes in %d ms, speed %d kB/s", bytesRead
, elapsedTime
, speed
);
236 int cliTestSD(const char ** argv
)
238 // Do the read test on the SD card and report back the result
241 uint32_t sectorCount
;
242 if (disk_ioctl(0, GET_SECTOR_COUNT
, §orCount
) != RES_OK
) {
243 serialPrint("Error: can't read sector count");
246 serialPrint("SD card has %u sectors", sectorCount
);
248 // read last 16 sectors one sector at the time
249 serialPrint("Starting single sector read test, reading 16 sectors one by one");
250 uint8_t * buffer
= (uint8_t*) malloc(512);
252 serialPrint("Not enough memory");
255 for (uint32_t s
= sectorCount
- 16; s
<sectorCount
; ++s
) {
256 DRESULT res
= __disk_read(0, buffer
, s
, 1);
258 serialPrint("sector %d read FAILED, err: %d", s
, res
);
261 serialPrint("sector %d read OK", s
);
267 // read last 16 sectors, two sectors at the time with a multi-block read
268 buffer
= (uint8_t *) malloc(512*2);
270 serialPrint("Not enough memory");
274 serialPrint("Starting multiple sector read test, reading two sectors at the time");
275 for (uint32_t s
= sectorCount
- 16; s
<sectorCount
; s
+=2) {
276 DRESULT res
= __disk_read(0, buffer
, s
, 2);
278 serialPrint("sector %d-%d read FAILED, err: %d", s
, s
+1, res
);
281 serialPrint("sector %d-%d read OK", s
, s
+1);
287 // read last 16 sectors, all sectors with single multi-block read
288 buffer
= (uint8_t*) malloc(512*16);
290 serialPrint("Not enough memory");
294 serialPrint("Starting multiple sector read test, reading 16 sectors at the time");
295 DRESULT res
= __disk_read(0, buffer
, sectorCount
-16, 16);
297 serialPrint("sector %d-%d read FAILED, err: %d", sectorCount
-16, sectorCount
-1, res
);
300 serialPrint("sector %d-%d read OK", sectorCount
-16, sectorCount
-1);
311 serialPrint("Allocating 1kB with new()");
313 tmp
= new char[1024];
315 serialPrint("\tsuccess");
320 serialPrint("\tFAILURE");
323 serialPrint("Allocating 10MB with (std::nothrow) new()");
325 tmp
= new (std::nothrow
) char[1024*1024*10];
327 serialPrint("\tFAILURE, tmp = %p", tmp
);
332 serialPrint("\tsuccess, allocaton failed, tmp = 0");
335 serialPrint("Allocating 10MB with new()");
337 tmp
= new char[1024*1024*10];
339 serialPrint("\tFAILURE, tmp = %p", tmp
);
344 serialPrint("\tsuccess, allocaton failed, tmp = 0");
346 serialPrint("Test finished");
350 #if defined(COLORLCD)
352 extern bool perMainEnabled
;
353 typedef void (*timedTestFunc_t
)(void);
355 void testDrawSolidFilledRectangle()
357 lcdDrawFilledRect(0, 0, LCD_W
, LCD_H
, SOLID
, TEXT_BGCOLOR
);
360 void testDrawFilledRectangle()
362 lcdDrawFilledRect(0, 0, LCD_W
, LCD_H
, DOTTED
, TEXT_BGCOLOR
);
365 void testDrawSolidFilledRoundedRectangle()
367 lcdDrawFilledRect(0, 0, LCD_W
/2, LCD_H
/2, SOLID
, ROUND
|TEXT_BGCOLOR
);
370 void testDrawBlackOverlay()
372 lcdDrawBlackOverlay();
375 void testDrawSolidHorizontalLine1()
377 lcdDrawSolidHorizontalLine(0, 0, 1, 0);
380 void testDrawSolidHorizontalLine2()
382 lcdDrawSolidHorizontalLine(0, 0, LCD_W
, 0);
385 void testDrawSolidVerticalLine1()
387 lcdDrawSolidVerticalLine(0, 0, 1, 0);
390 void testDrawSolidVerticalLine2()
392 lcdDrawSolidVerticalLine(0, 0, LCD_H
, 0);
395 void testDrawDiagonalLine()
397 lcdDrawLine(0,0, LCD_W
, LCD_H
, SOLID
, TEXT_COLOR
);
406 lcdDrawRect(0, 0, LCD_W
, LCD_H
, 2, SOLID
, TEXT_COLOR
);
411 lcdDrawText(0, LCD_H
/2, "The quick brown fox jumps over the lazy dog", TEXT_COLOR
);
414 void testDrawTextVertical()
416 lcdDrawText(30, LCD_H
, "The quick brown fox ", TEXT_COLOR
|VERTICAL
|NO_FONTCACHE
);
424 #define RUN_GRAPHICS_TEST(name, runtime) runTimedFunctionTest(name, #name, runtime, 100)
426 float runTimedFunctionTest(timedTestFunc_t func
, const char * name
, uint32_t runtime
, uint16_t step
)
428 const uint32_t start
= RTOS_GET_MS();
430 uint32_t actualRuntime
= 0;
431 while ((actualRuntime
= RTOS_GET_MS() - start
) < runtime
) {
432 for (uint16_t n
=0; n
< step
; n
++) {
438 const float result
= (noRuns
* 500.0f
) / (float)actualRuntime
; // runs/second
439 serialPrint("Test %s speed: %lu.%02u, (%lu runs in %lu ms)", name
, uint32_t(result
), uint16_t((result
- uint32_t(result
)) * 100.0f
), noRuns
, actualRuntime
);
444 int cliTestGraphics()
446 serialPrint("Starting graphics performance test...");
449 watchdogSuspend(6000/*60s*/);
450 if (pulsesStarted()) {
453 pauseMixerCalculations();
454 perMainEnabled
= false;
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: %lu.%02u", uint32_t(result
), uint16_t((result
- uint32_t(result
)) * 100.0f
));
474 perMainEnabled
= true;
475 if (pulsesStarted()) {
478 resumeMixerCalculations();
484 void memoryRead(const uint8_t * src
, uint32_t size
)
488 /*data =*/ *(const uint8_t volatile *)src
;
494 void memoryRead(const uint32_t * src
, uint32_t size
)
497 *(const uint32_t volatile *)src
;
502 uint32_t * testbuff
[100];
504 void memoryCopy(uint8_t * dest
, const uint8_t * src
, uint32_t size
)
513 void memoryCopy(uint32_t * dest
, const uint32_t * src
, uint32_t size
)
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 RUN_MEMORY_TEST(name, runtime) runTimedFunctionTest(name, #name, runtime, 100)
570 int cliTestMemorySpeed()
572 serialPrint("Starting memory speed test...");
575 watchdogSuspend(6000/*60s*/);
576 if (pulsesStarted()) {
579 pauseMixerCalculations();
580 perMainEnabled
= false;
583 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_RAM_8bit
, 200);
584 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_RAM_32bit
, 200);
585 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_SDRAM_8bit
, 200);
586 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_SDRAM_32bit
, 200);
587 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_RAM_to_SDRAM_8bit
, 200);
588 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_RAM_to_SDRAM_32bit
, 200);
589 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_8bit
, 200);
590 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_32bit
, 200);
593 serialPrint("Disabling LCD...");
596 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_RAM_8bit
, 200);
597 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_RAM_32bit
, 200);
598 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_SDRAM_8bit
, 200);
599 result
+= RUN_MEMORY_TEST(testMemoryReadFrom_SDRAM_32bit
, 200);
600 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_RAM_to_SDRAM_8bit
, 200);
601 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_RAM_to_SDRAM_32bit
, 200);
602 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_8bit
, 200);
603 result
+= RUN_MEMORY_TEST(testMemoryCopyFrom_SDRAM_to_SDRAM_32bit
, 200);
605 serialPrint("Total speed: %lu.%02u", uint32_t(result
), uint16_t((result
- uint32_t(result
)) * 100.0f
));
609 perMainEnabled
= true;
610 if (pulsesStarted()) {
613 resumeMixerCalculations();
619 #include "storage/modelslist.h"
622 int cliTestModelsList()
629 serialPrint("Starting fetching RF data 100x...");
630 const uint32_t start
= RTOS_GET_MS();
632 const list
<ModelsCategory
*>& cats
= modList
.getCategories();
634 for (list
<ModelsCategory
*>::const_iterator cat_it
= cats
.begin();
635 cat_it
!= cats
.end(); ++cat_it
) {
637 for (ModelsCategory::iterator mod_it
= (*cat_it
)->begin();
638 mod_it
!= (*cat_it
)->end(); mod_it
++) {
640 if (!(*mod_it
)->fetchRfData()) {
641 serialPrint("Error while fetching RF data...");
652 serialPrint("Done fetching %ix RF data: %lu ms", count
, (RTOS_GET_MS() - start
));
657 #endif // #if defined(COLORLCD)
659 int cliTest(const char ** argv
)
661 if (!strcmp(argv
[1], "new")) {
664 else if (!strcmp(argv
[1], "std::exception")) {
665 serialPrint("Not implemented");
667 #if defined(COLORLCD)
668 else if (!strcmp(argv
[1], "graphics")) {
669 return cliTestGraphics();
671 else if (!strcmp(argv
[1], "memspd")) {
672 return cliTestMemorySpeed();
674 else if (!strcmp(argv
[1], "modelslist")) {
675 return cliTestModelsList();
679 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
685 int cliTrace(const char ** argv
)
687 if (!strcmp(argv
[1], "on")) {
688 cliTracesEnabled
= true;
690 else if (!strcmp(argv
[1], "off")) {
691 cliTracesEnabled
= false;
694 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
700 int cliStackInfo(const char ** argv
)
702 serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize());
703 serialPrint("[MENUS] %d available / %d", menusStack
.available(), menusStack
.size());
704 serialPrint("[MIXER] %d available / %d", mixerStack
.available(), mixerStack
.size());
705 serialPrint("[AUDIO] %d available / %d", audioStack
.available(), audioStack
.size());
706 serialPrint("[CLI] %d available / %d", cliStack
.available(), cliStack
.size());
711 extern int _heap_end
;
712 extern unsigned char *heap
;
714 int cliMemoryInfo(const char ** argv
)
717 // int arena; /* total space allocated from system */
718 // int ordblks; /* number of non-inuse chunks */
719 // int smblks; /* unused -- always zero */
720 // int hblks; /* number of mmapped regions */
721 // int hblkhd; /* total space in mmapped regions */
722 // int usmblks; /* unused -- always zero */
723 // int fsmblks; /* unused -- always zero */
724 // int uordblks; /* total allocated space */
725 // int fordblks; /* total non-inuse space */
726 // int keepcost; /* top-most, releasable (via malloc_trim) space */
728 struct mallinfo info
= mallinfo();
729 serialPrint("mallinfo:");
730 serialPrint("\tarena %d bytes", info
.arena
);
731 serialPrint("\tordblks %d bytes", info
.ordblks
);
732 serialPrint("\tuordblks %d bytes", info
.uordblks
);
733 serialPrint("\tfordblks %d bytes", info
.fordblks
);
734 serialPrint("\tkeepcost %d bytes", info
.keepcost
);
736 serialPrint("\nHeap:");
737 serialPrint("\tstart %p", (unsigned char *)&_end
);
738 serialPrint("\tend %p", (unsigned char *)&_heap_end
);
739 serialPrint("\tcurr %p", heap
);
740 serialPrint("\tused %d bytes", (int)(heap
- (unsigned char *)&_end
));
741 serialPrint("\tfree %d bytes", (int)((unsigned char *)&_heap_end
- heap
));
744 serialPrint("\nLua:");
745 uint32_t s
= luaGetMemUsed(lsScripts
);
746 serialPrint("\tScripts %u", s
);
747 #if defined(COLORLCD)
748 uint32_t w
= luaGetMemUsed(lsWidgets
);
749 uint32_t e
= luaExtraMemoryUsage
;
750 serialPrint("\tWidgets %u", w
);
751 serialPrint("\tExtra %u", e
);
752 serialPrint("------------");
753 serialPrint("\tTotal %u", s
+ w
+ e
);
759 int cliReboot(const char ** argv
)
762 if (!strcmp(argv
[1], "wdt")) {
763 // do a user requested watchdog test by pausing mixer thread
773 const MemArea memAreas
[] = {
774 { "RCC", RCC
, sizeof(RCC_TypeDef
) },
775 { "GPIOA", GPIOA
, sizeof(GPIO_TypeDef
) },
776 { "GPIOB", GPIOB
, sizeof(GPIO_TypeDef
) },
777 { "GPIOC", GPIOC
, sizeof(GPIO_TypeDef
) },
778 { "GPIOD", GPIOD
, sizeof(GPIO_TypeDef
) },
779 { "GPIOE", GPIOE
, sizeof(GPIO_TypeDef
) },
780 { "GPIOF", GPIOF
, sizeof(GPIO_TypeDef
) },
781 { "GPIOG", GPIOG
, sizeof(GPIO_TypeDef
) },
782 { "USART1", USART1
, sizeof(USART_TypeDef
) },
783 { "USART2", USART2
, sizeof(USART_TypeDef
) },
784 { "USART3", USART3
, sizeof(USART_TypeDef
) },
785 { nullptr, nullptr, 0 },
788 int cliSet(const char ** argv
)
790 if (!strcmp(argv
[1], "rtc")) {
792 int year
, month
, day
, hour
, minute
, second
;
793 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) {
794 t
.tm_year
= year
-TM_YEAR_BASE
;
800 g_rtcTime
= gmktime(&t
); // update local timestamp and get wday calculated
804 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
807 #if !defined(SOFTWARE_VOLUME)
808 else if (!strcmp(argv
[1], "volume")) {
810 if (toInt(argv
, 2, &level
) > 0) {
814 serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
823 #if defined(DEBUG_INTERRUPTS)
824 void printInterrupts()
827 struct InterruptCounters ic
= interruptCounters
;
828 memset(&interruptCounters
, 0, sizeof(interruptCounters
));
829 interruptCounters
.resetTime
= get_tmr10ms();
831 serialPrint("Interrupts count in the last %u ms:", (get_tmr10ms() - ic
.resetTime
) * 10);
832 for(int n
= 0; n
< INT_LAST
; n
++) {
833 serialPrint("%s: %u", interruptNames
[n
], ic
.cnt
[n
]);
836 #endif //#if defined(DEBUG_INTERRUPTS)
838 #if defined(DEBUG_TASKS)
840 void printTaskSwitchLog()
842 serialPrint("Tasks legend [<task_id>, <task name>]:");
843 for(int n
= 0; n
<= CFG_MAX_USER_TASKS
+1; n
++) {
845 serialPrint("%d: Idle", n
);
847 if (cliTaskId
== n
) {
848 serialPrint("%d: CLI", n
);
850 else if (menusTaskId
== n
) {
851 serialPrint("%d: menus", n
);
853 else if (mixerTaskId
== n
) {
854 serialPrint("%d: mixer", n
);
856 else if (audioTaskId
== n
) {
857 serialPrint("%d: audio", n
);
862 serialPrint("Tasks switch log at %u [<time>, <task_id>]:", get_tmr10ms());
863 uint32_t lastSwitchTime
= 0;
864 uint32_t * tsl
= new uint32_t[DEBUG_TASKS_LOG_SIZE
];
866 serialPrint("Not enough memory");
869 memcpy(tsl
, taskSwitchLog
, sizeof(taskSwitchLog
));
870 uint32_t * p
= tsl
+ taskSwitchLogPos
;
871 uint32_t * end
= tsl
+ DEBUG_TASKS_LOG_SIZE
;
872 for(int n
= 0; n
< DEBUG_TASKS_LOG_SIZE
; n
++) {
873 uint32_t taskId
= *p
>> 24;
874 uint32_t switchTime
= *p
& 0xFFFFFF;
875 if (lastSwitchTime
!= switchTime
) {
876 serialPrintf("\r\n%06x: ", switchTime
);
877 lastSwitchTime
= switchTime
;
879 serialPrintf("%u ", taskId
);
887 #endif // #if defined(DEBUG_TASKS)
889 #if defined(DEBUG_TIMERS)
891 void printDebugTime(uint32_t time
)
894 serialPrintf("%dms", time
/1000);
897 serialPrintf("%d.%03dms", time
/1000, time
%1000);
901 void printDebugTimer(const char * name
, DebugTimer
& timer
)
903 serialPrintf("%s: ", name
);
904 printDebugTime( timer
.getMin());
906 printDebugTime(timer
.getMax());
910 void printDebugTimers()
912 for(int n
= 0; n
< DEBUG_TIMERS_COUNT
; n
++) {
913 printDebugTimer(debugTimerNames
[n
], debugTimers
[n
]);
919 extern RTOS_MUTEX_HANDLE audioMutex
;
921 void printAudioVars()
923 for(int n
= 0; n
< AUDIO_BUFFER_COUNT
; n
++) {
924 serialPrint("Audio Buffer %d: size: %u, ", n
, (uint32_t)audioBuffers
[n
].size
);
925 dump((uint8_t *)audioBuffers
[n
].data
, 32);
927 serialPrint("fragments:");
928 for(int n
= 0; n
< AUDIO_QUEUE_LENGTH
; n
++) {
929 serialPrint("%d: type %u: id: %u, repeat: %u, ", n
, (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].type
,
930 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].id
,
931 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].repeat
);
932 if ( audioQueue
.fragmentsFifo
.fragments
[n
].type
== FRAGMENT_FILE
) {
933 serialPrint(" file: %s", audioQueue
.fragmentsFifo
.fragments
[n
].file
);
937 serialPrint("FragmentFifo: ridx: %d, widx: %d", audioQueue
.fragmentsFifo
.ridx
, audioQueue
.fragmentsFifo
.widx
);
938 serialPrint("audioQueue: readIdx: %d, writeIdx: %d, full: %d", audioQueue
.buffersFifo
.readIdx
, audioQueue
.buffersFifo
.writeIdx
, audioQueue
.buffersFifo
.bufferFull
);
940 serialPrint("normalContext: %u", (uint32_t)audioQueue
.normalContext
.fragment
.type
);
942 serialPrint("audioMutex[%u] = %u", (uint32_t)audioMutex
, (uint32_t)MutexTbl
[audioMutex
].mutexFlag
);
946 int cliDisplay(const char ** argv
)
948 long long int address
= 0;
950 for (const MemArea
* area
= memAreas
; area
->name
!= nullptr; area
++) {
951 if (!strcmp(area
->name
, argv
[1])) {
952 dump((uint8_t *)area
->start
, area
->size
);
957 if (!strcmp(argv
[1], "keys")) {
958 for (int i
=0; i
<TRM_BASE
; i
++) {
960 uint8_t len
= STR_VKEYS
[0];
961 strncpy(name
, STR_VKEYS
+1+len
*i
, len
);
963 serialPrint("[%s] = %s", name
, keys
[i
].state() ? "on" : "off");
965 #if defined(ROTARY_ENCODER_NAVIGATION)
966 serialPrint("[Enc.] = %d", rotencValue
/ ROTARY_ENCODER_GRANULARITY
);
968 for (int i
=TRM_BASE
; i
<=TRM_LAST
; i
++) {
969 serialPrint("[Trim%d] = %s", i
-TRM_BASE
, keys
[i
].state() ? "on" : "off");
971 for (int i
=MIXSRC_FIRST_SWITCH
; i
<=MIXSRC_LAST_SWITCH
; i
++) {
972 mixsrc_t sw
= i
- MIXSRC_FIRST_SWITCH
;
973 if (SWITCH_EXISTS(sw
)) {
974 char swName
[LEN_SWITCH_NAME
+ 1];
975 strAppend(swName
, STR_VSWITCHES
+1+sw
*STR_VSWITCHES
[0], STR_VSWITCHES
[0]);
976 static const char * const SWITCH_POSITIONS
[] = { "down", "mid", "up" };
977 serialPrint("[%s] = %s", swName
, SWITCH_POSITIONS
[1 + getValue(i
) / 1024]);
981 else if (!strcmp(argv
[1], "adc")) {
982 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
983 serialPrint("adc[%d] = %04X", i
, (int)adcValues
[i
]);
986 else if (!strcmp(argv
[1], "outputs")) {
987 for (int i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
988 serialPrint("outputs[%d] = %04d", i
, (int)channelOutputs
[i
]);
991 else if (!strcmp(argv
[1], "rtc")) {
994 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
);
996 #if !defined(SOFTWARE_VOLUME)
997 else if (!strcmp(argv
[1], "volume")) {
998 serialPrint("volume = %d", getVolume());
1002 else if (!strcmp(argv
[1], "uid")) {
1003 char str
[LEN_CPU_UID
+1];
1004 getCPUUniqueID(str
);
1005 serialPrint("uid = %s", str
);
1008 else if (!strcmp(argv
[1], "tim")) {
1010 if (toInt(argv
, 2, &timerNumber
) > 0) {
1011 TIM_TypeDef
* tim
= TIM1
;
1012 switch (timerNumber
) {
1028 serialPrint("TIM%d", timerNumber
);
1029 serialPrint(" CR1 0x%x", tim
->CR1
);
1030 serialPrint(" CR2 0x%x", tim
->CR2
);
1031 serialPrint(" DIER 0x%x", tim
->DIER
);
1032 serialPrint(" SR 0x%x", tim
->SR
);
1033 serialPrint(" EGR 0x%x", tim
->EGR
);
1034 serialPrint(" CCMR1 0x%x", tim
->CCMR1
);
1035 serialPrint(" CCMR2 0x%x", tim
->CCMR2
);
1037 serialPrint(" CNT 0x%x", tim
->CNT
);
1038 serialPrint(" ARR 0x%x", tim
->ARR
);
1039 serialPrint(" PSC 0x%x", tim
->PSC
);
1041 serialPrint(" CCER 0x%x", tim
->CCER
);
1042 serialPrint(" CCR1 0x%x", tim
->CCR1
);
1043 serialPrint(" CCR2 0x%x", tim
->CCR2
);
1044 serialPrint(" CCR3 0x%x", tim
->CCR3
);
1045 serialPrint(" CCR4 0x%x", tim
->CCR4
);
1048 else if (!strcmp(argv
[1], "dma")) {
1049 serialPrint("DMA1_Stream7");
1050 serialPrint(" CR 0x%x", DMA1_Stream7
->CR
);
1052 #if defined(DEBUG_INTERRUPTS)
1053 else if (!strcmp(argv
[1], "int")) {
1057 #if defined(DEBUG_TASKS)
1058 else if (!strcmp(argv
[1], "tsl")) {
1059 printTaskSwitchLog();
1062 #if defined(DEBUG_TIMERS)
1063 else if (!strcmp(argv
[1], "dt")) {
1067 else if (!strcmp(argv
[1], "audio")) {
1070 #if defined(DISK_CACHE)
1071 else if (!strcmp(argv
[1], "dc")) {
1072 DiskCacheStats stats
= diskCache
.getStats();
1073 uint32_t hitRate
= diskCache
.getHitRate();
1074 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
);
1077 else if (toLongLongInt(argv
, 1, &address
) > 0) {
1079 if (toInt(argv
, 2, &size
) >= 0) {
1080 dump((uint8_t *)address
, size
);
1086 int cliDebugVars(const char ** argv
)
1088 #if defined(PCBHORUS)
1089 extern uint32_t ioMutexReq
, ioMutexRel
;
1090 extern uint32_t sdReadRetries
;
1091 serialPrint("ioMutexReq=%d", ioMutexReq
);
1092 serialPrint("ioMutexRel=%d", ioMutexRel
);
1093 serialPrint("sdReadRetries=%d", sdReadRetries
);
1094 #elif defined(PCBTARANIS)
1095 serialPrint("telemetryErrors=%d", telemetryErrors
);
1101 int cliRepeat(const char ** argv
)
1105 if (toInt(argv
, 1, &interval
) > 0 && argv
[2]) {
1109 while (!cliRxFifo
.pop(c
) || !(c
== '\r' || c
== '\n' || c
== ' ')) {
1110 RTOS_WAIT_MS(20); // 20ms
1111 if (++counter
>= interval
) {
1112 cliExecCommand(&argv
[2]);
1118 serialPrint("%s: Invalid arguments", argv
[0]);
1123 #if defined(JITTER_MEASURE)
1124 int cliShowJitter(const char ** argv
)
1126 serialPrint( "# anaIn rawJ avgJ");
1127 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
1128 serialPrint("A%02d %04X %04X %3d %3d", i
, getAnalogValue(i
), anaIn(i
), rawJitter
[i
].get(), avgJitter
[i
].get());
1129 if (IS_POT_MULTIPOS(i
)) {
1130 StepsCalibData
* calib
= (StepsCalibData
*) &g_eeGeneral
.calib
[i
];
1131 for (int j
=0; j
<calib
->count
; j
++) {
1132 serialPrint(" s%d %04X", j
, calib
->steps
[j
]);
1140 #if defined(INTERNAL_GPS)
1141 int cliGps(const char ** argv
)
1145 if (argv
[1][0] == '$') {
1146 // send command to GPS
1147 gpsSendFrame(argv
[1]);
1150 else if (!strcmp(argv
[1], "trace")) {
1151 gpsTraceEnabled
= !gpsTraceEnabled
;
1154 else if (toInt(argv
, 1, &baudrate
) > 0 && baudrate
> 0) {
1156 serialPrint("GPS baudrate set to %d", baudrate
);
1159 serialPrint("%s: Invalid arguments", argv
[0]);
1165 #if defined(BLUETOOTH)
1166 int cliBlueTooth(const char ** argv
)
1169 if (!strncmp(argv
[1], "AT", 2) || !strncmp(argv
[1], "TTM", 3)) {
1170 bluetooth
.writeString(argv
[1]);
1171 char * line
= bluetooth
.readline();
1172 serialPrint("<BT %s", line
);
1174 else if (toInt(argv
, 1, &baudrate
) > 0) {
1176 bluetoothInit(baudrate
, true);
1177 char * line
= bluetooth
.readline();
1178 serialPrint("<BT %s", line
);
1182 serialPrint("BT turned off");
1186 serialPrint("%s: Invalid arguments", argv
[0]);
1192 const CliCommand cliCommands
[] = {
1193 { "beep", cliBeep
, "[<frequency>] [<duration>]" },
1194 { "ls", cliLs
, "<directory>" },
1195 { "read", cliRead
, "<filename>" },
1196 { "readsd", cliReadSD
, "<start sector> <sectors count> <read buffer size (sectors)>" },
1197 { "testsd", cliTestSD
, "" },
1198 { "play", cliPlay
, "<filename>" },
1199 { "print", cliDisplay
, "<address> [<size>] | <what>" },
1200 { "p", cliDisplay
, "<address> [<size>] | <what>" },
1201 { "reboot", cliReboot
, "[wdt]" },
1202 { "set", cliSet
, "<what> <value>" },
1203 { "stackinfo", cliStackInfo
, "" },
1204 { "meminfo", cliMemoryInfo
, "" },
1205 { "test", cliTest
, "new | std::exception | graphics | memspd" },
1207 { "trace", cliTrace
, "on | off" },
1209 { "help", cliHelp
, "[<command>]" },
1210 { "debugvars", cliDebugVars
, "" },
1211 { "repeat", cliRepeat
, "<interval> <command>" },
1212 #if defined(JITTER_MEASURE)
1213 { "jitter", cliShowJitter
, "" },
1215 #if defined(INTERNAL_GPS)
1216 { "gps", cliGps
, "<baudrate>|$<command>|trace" },
1218 #if defined(BLUETOOTH)
1219 { "bt", cliBlueTooth
, "<baudrate>|<command>" },
1221 { nullptr, nullptr, nullptr } /* sentinel */
1224 int cliHelp(const char ** argv
)
1226 for (const CliCommand
* command
= cliCommands
; command
->name
!= nullptr; command
++) {
1227 if (argv
[1][0] == '\0' || !strcmp(command
->name
, argv
[0])) {
1228 serialPrint("%s %s", command
->name
, command
->args
);
1229 if (argv
[1][0] != '\0') {
1234 if (argv
[1][0] != '\0') {
1235 serialPrint("Invalid command \"%s\"", argv
[0]);
1240 int cliExecCommand(const char ** argv
)
1242 if (argv
[0][0] == '\0')
1245 for (const CliCommand
* command
= cliCommands
; command
->name
!= nullptr; command
++) {
1246 if (!strcmp(command
->name
, argv
[0])) {
1247 return command
->func(argv
);
1250 serialPrint("Invalid command \"%s\"", argv
[0]);
1254 int cliExecLine(char * line
)
1256 int len
= strlen(line
);
1257 const char * argv
[CLI_COMMAND_MAX_ARGS
];
1258 memset(argv
, 0, sizeof(argv
));
1261 for (int i
=0; i
<len
; i
++) {
1262 if (line
[i
] == ' ') {
1264 if (argc
< CLI_COMMAND_MAX_ARGS
) {
1265 argv
[argc
++] = &line
[i
+1];
1269 return cliExecCommand(argv
);
1272 void cliTask(void * pdata
)
1274 char line
[CLI_COMMAND_MAX_LEN
+1];
1282 while (!cliRxFifo
.pop(c
)) {
1283 RTOS_WAIT_MS(20); // 20ms
1288 serialPrint("\033[2J\033[1;1H");
1291 else if (c
== 127) {
1298 else if (c
== '\r' || c
== '\n') {
1302 if (pos
== 0 && cliLastLine
[0]) {
1303 // execute (repeat) last command
1304 strcpy(line
, cliLastLine
);
1308 strcpy(cliLastLine
, line
);
1314 else if (isascii(c
) && pos
< CLI_COMMAND_MAX_LEN
) {
1323 RTOS_CREATE_TASK(cliTaskId
, cliTask
, "CLI", cliStack
, CLI_STACK_SIZE
, CLI_TASK_PRIO
);