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
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
);
60 int toLongLongInt(const char ** argv
, int index
, long long int * val
)
62 if (*argv
[index
] == '\0') {
67 const char * s
= argv
[index
];
68 if (strlen(s
) > 2 && s
[0] == '0' && s
[1] == 'x') {
73 *val
= strtoll(s
, &endptr
, base
);
77 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[index
]);
83 int toInt(const char ** argv
, int index
, int * val
)
85 long long int lval
= 0;
86 int result
= toLongLongInt(argv
, index
, &lval
);
91 int cliBeep(const char ** argv
)
93 int freq
= BEEP_DEFAULT_FREQ
;
95 if (toInt(argv
, 1, &freq
) >= 0 && toInt(argv
, 2, &duration
) >= 0) {
96 audioQueue
.playTone(freq
, duration
, 20, PLAY_NOW
);
101 int cliPlay(const char ** argv
)
103 audioQueue
.playFile(argv
[1], PLAY_NOW
);
107 int cliLs(const char ** argv
)
112 FRESULT res
= f_opendir(&dir
, argv
[1]); /* Open the directory */
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
);
122 serialPrint("%s: Invalid directory \"%s\"", argv
[0], argv
[1]);
127 int cliRead(const char ** argv
)
130 uint32_t bytesRead
= 0;
132 if (toInt(argv
, 2, &bufferSize
) == 0 || bufferSize
< 0 ) {
133 serialPrint("%s: Invalid buffer size \"%s\"", argv
[0], argv
[2]);
137 uint8_t * buffer
= (uint8_t*) malloc(bufferSize
);
139 serialPrint("Not enough memory");
143 FRESULT result
= f_open(&file
, argv
[1], FA_OPEN_EXISTING
| FA_READ
);
144 if (result
!= FR_OK
) {
146 serialPrint("%s: File not found \"%s\"", argv
[0], argv
[1]);
150 tmr10ms_t start
= get_tmr10ms();
154 result
= f_read(&file
, buffer
, sizeof(buffer
), &read
);
155 if (result
== FR_OK
) {
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
);
172 int cliReadSD(const char ** argv
)
177 if (toInt(argv
, 1, &startSector
) == 0 || startSector
< 0 ) {
178 serialPrint("%s: Invalid start sector \"%s\"", argv
[0], argv
[1]);
181 if (toInt(argv
, 2, &numberOfSectors
) == 0 || numberOfSectors
< 0 ) {
182 serialPrint("%s: Invalid number of sectors \"%s\"", argv
[0], argv
[2]);
186 if (toInt(argv
, 3, &bufferSectors
) == 0 || bufferSectors
< 0 ) {
187 serialPrint("%s: Invalid number of buffer sectors \"%s\"", argv
[0], argv
[3]);
191 uint8_t * buffer
= (uint8_t*) malloc(512*bufferSectors
);
193 serialPrint("Not enough memory");
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
);
203 serialPrint("disk_read error: %d, sector: %d(%d)", res
, startSector
, numberOfSectors
);
206 for(uint32_t n
=0; n
<bufferSectors
; ++n
) {
207 dump(buffer
+ n
*512, 32);
213 for(int n
=0; n
<(bufferSectors
*512); ++n
) {
216 serialPrint("sector %d(%d) checksumm: %u", startSector
, numberOfSectors
, summ
);
218 if (numberOfSectors
>= bufferSectors
) {
219 numberOfSectors
-= bufferSectors
;
220 startSector
+= bufferSectors
;
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
);
235 int cliTestSD(const char ** argv
)
237 // Do the read test on the SD card and report back the result
240 uint32_t sectorCount
;
241 if (disk_ioctl(0, GET_SECTOR_COUNT
, §orCount
) != RES_OK
) {
242 serialPrint("Error: can't read sector count");
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);
251 serialPrint("Not enough memory");
254 for (uint32_t s
= sectorCount
- 16; s
<sectorCount
; ++s
) {
255 DRESULT res
= __disk_read(0, buffer
, s
, 1);
257 serialPrint("sector %d read FAILED, err: %d", s
, res
);
260 serialPrint("sector %d read OK", s
);
266 // read last 16 sectors, two sectors at the time with a multi-block read
267 buffer
= (uint8_t *) malloc(512*2);
269 serialPrint("Not enough memory");
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);
277 serialPrint("sector %d-%d read FAILED, err: %d", s
, s
+1, res
);
280 serialPrint("sector %d-%d read OK", s
, s
+1);
286 // read last 16 sectors, all sectors with single multi-block read
287 buffer
= (uint8_t*) malloc(512*16);
289 serialPrint("Not enough memory");
293 serialPrint("Starting multiple sector read test, reading 16 sectors at the time");
294 DRESULT res
= __disk_read(0, buffer
, sectorCount
-16, 16);
296 serialPrint("sector %d-%d read FAILED, err: %d", sectorCount
-16, sectorCount
-1, res
);
299 serialPrint("sector %d-%d read OK", sectorCount
-16, sectorCount
-1);
310 serialPrint("Allocating 1kB with new()");
312 tmp
= new char[1024];
314 serialPrint("\tsuccess");
319 serialPrint("\tFAILURE");
322 serialPrint("Allocating 10MB with (std::nothrow) new()");
324 tmp
= new (std::nothrow
) char[1024*1024*10];
326 serialPrint("\tFAILURE, tmp = %p", tmp
);
331 serialPrint("\tsuccess, allocaton failed, tmp = 0");
334 serialPrint("Allocating 10MB with new()");
336 tmp
= new char[1024*1024*10];
338 serialPrint("\tFAILURE, tmp = %p", tmp
);
343 serialPrint("\tsuccess, allocaton failed, tmp = 0");
345 serialPrint("Test finished");
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
);
405 lcdDrawRect(0, 0, LCD_W
, LCD_H
, 2, SOLID
, TEXT_COLOR
);
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
);
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();
430 while (((uint32_t)CoGetOSTime() - start
) < runtime
/2 ) {
431 for (int n
=0; n
<GRAPHICS_TEST_RUN_STEP
; n
++) {
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);
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: %0.2f", result
);
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 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();
575 while (((uint32_t)CoGetOSTime() - start
) < runtime
/2 ) {
576 for (int n
=0; n
<MEMORY_TEST_RUN_STEP
; n
++) {
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);
589 int cliTestMemorySpeed()
591 serialPrint("Starting memory speed test...");
594 watchdogSuspend(6000/*60s*/);
595 if (pulsesStarted()) {
598 pauseMixerCalculations();
599 perMainEnabled
= false;
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);
612 serialPrint("Disabling LCD...");
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
);
628 perMainEnabled
= true;
629 if (pulsesStarted()) {
632 resumeMixerCalculations();
638 #include "storage/modelslist.h"
641 int cliTestModelsList()
648 serialPrint("Starting fetching RF data 100x...");
649 uint32_t start
= (uint32_t)CoGetOSTime();
651 const list
<ModelsCategory
*>& cats
= modList
.getCategories();
653 for (list
<ModelsCategory
*>::const_iterator cat_it
= cats
.begin();
654 cat_it
!= cats
.end(); ++cat_it
) {
656 for (ModelsCategory::iterator mod_it
= (*cat_it
)->begin();
657 mod_it
!= (*cat_it
)->end(); mod_it
++) {
659 if (!(*mod_it
)->fetchRfData()) {
660 serialPrint("Error while fetching RF data...");
671 uint32_t actualRuntime
= (uint32_t)CoGetOSTime() - start
;
672 serialPrint("Done fetching %ix RF data: %d ms", count
, actualRuntime
*2);
677 #endif // #if defined(COLORLCD)
679 int cliTest(const char ** argv
)
681 if (!strcmp(argv
[1], "new")) {
684 else if (!strcmp(argv
[1], "std::exception")) {
685 serialPrint("Not implemented");
687 #if defined(COLORLCD)
688 else if (!strcmp(argv
[1], "graphics")) {
689 return cliTestGraphics();
691 else if (!strcmp(argv
[1], "memspd")) {
692 return cliTestMemorySpeed();
694 else if (!strcmp(argv
[1], "modelslist")) {
695 return cliTestModelsList();
699 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
705 int cliTrace(const char ** argv
)
707 if (!strcmp(argv
[1], "on")) {
708 cliTracesEnabled
= true;
710 else if (!strcmp(argv
[1], "off")) {
711 cliTracesEnabled
= false;
714 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
720 int cliStackInfo(const char ** argv
)
722 serialPrint("[MAIN] %d available / %d", stackAvailable(), stackSize() * 4); // stackSize() returns size in 32bit chunks
723 serialPrint("[MENUS] %d available / %d", menusStack
.available(), menusStack
.size());
724 serialPrint("[MIXER] %d available / %d", mixerStack
.available(), mixerStack
.size());
725 serialPrint("[AUDIO] %d available / %d", audioStack
.available(), audioStack
.size());
726 serialPrint("[CLI] %d available / %d", cliStack
.available(), cliStack
.size());
731 extern int _heap_end
;
732 extern unsigned char *heap
;
734 int cliMemoryInfo(const char ** argv
)
737 // int arena; /* total space allocated from system */
738 // int ordblks; /* number of non-inuse chunks */
739 // int smblks; /* unused -- always zero */
740 // int hblks; /* number of mmapped regions */
741 // int hblkhd; /* total space in mmapped regions */
742 // int usmblks; /* unused -- always zero */
743 // int fsmblks; /* unused -- always zero */
744 // int uordblks; /* total allocated space */
745 // int fordblks; /* total non-inuse space */
746 // int keepcost; /* top-most, releasable (via malloc_trim) space */
748 struct mallinfo info
= mallinfo();
749 serialPrint("mallinfo:");
750 serialPrint("\tarena %d bytes", info
.arena
);
751 serialPrint("\tordblks %d bytes", info
.ordblks
);
752 serialPrint("\tuordblks %d bytes", info
.uordblks
);
753 serialPrint("\tfordblks %d bytes", info
.fordblks
);
754 serialPrint("\tkeepcost %d bytes", info
.keepcost
);
756 serialPrint("\nHeap:");
757 serialPrint("\tstart %p", (unsigned char *)&_end
);
758 serialPrint("\tend %p", (unsigned char *)&_heap_end
);
759 serialPrint("\tcurr %p", heap
);
760 serialPrint("\tused %d bytes", (int)(heap
- (unsigned char *)&_end
));
761 serialPrint("\tfree %d bytes", (int)((unsigned char *)&_heap_end
- heap
));
764 serialPrint("\nLua:");
765 uint32_t s
= luaGetMemUsed(lsScripts
);
766 serialPrint("\tScripts %u", s
);
767 #if defined(COLORLCD)
768 uint32_t w
= luaGetMemUsed(lsWidgets
);
769 uint32_t e
= luaExtraMemoryUsage
;
770 serialPrint("\tWidgets %u", w
);
771 serialPrint("\tExtra %u", e
);
772 serialPrint("------------");
773 serialPrint("\tTotal %u", s
+ w
+ e
);
779 int cliReboot(const char ** argv
)
782 if (!strcmp(argv
[1], "wdt")) {
783 // do a user requested watchdog test by pausing mixer thread
793 const MemArea memAreas
[] = {
794 { "RCC", RCC
, sizeof(RCC_TypeDef
) },
795 { "GPIOA", GPIOA
, sizeof(GPIO_TypeDef
) },
796 { "GPIOB", GPIOB
, sizeof(GPIO_TypeDef
) },
797 { "GPIOC", GPIOC
, sizeof(GPIO_TypeDef
) },
798 { "GPIOD", GPIOD
, sizeof(GPIO_TypeDef
) },
799 { "GPIOE", GPIOE
, sizeof(GPIO_TypeDef
) },
800 { "GPIOF", GPIOF
, sizeof(GPIO_TypeDef
) },
801 { "GPIOG", GPIOG
, sizeof(GPIO_TypeDef
) },
802 { "USART1", USART1
, sizeof(USART_TypeDef
) },
803 { "USART2", USART2
, sizeof(USART_TypeDef
) },
804 { "USART3", USART3
, sizeof(USART_TypeDef
) },
808 int cliSet(const char ** argv
)
810 if (!strcmp(argv
[1], "rtc")) {
812 int year
, month
, day
, hour
, minute
, second
;
813 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) {
814 t
.tm_year
= year
-TM_YEAR_BASE
;
820 g_rtcTime
= gmktime(&t
); // update local timestamp and get wday calculated
824 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
827 #if !defined(SOFTWARE_VOLUME)
828 else if (!strcmp(argv
[1], "volume")) {
830 if (toInt(argv
, 2, &level
) > 0) {
834 serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
843 #if defined(DEBUG_INTERRUPTS)
844 void printInterrupts()
847 struct InterruptCounters ic
= interruptCounters
;
848 memset(&interruptCounters
, 0, sizeof(interruptCounters
));
849 interruptCounters
.resetTime
= get_tmr10ms();
851 serialPrint("Interrupts count in the last %u ms:", (get_tmr10ms() - ic
.resetTime
) * 10);
852 for(int n
= 0; n
< INT_LAST
; n
++) {
853 serialPrint("%s: %u", interruptNames
[n
], ic
.cnt
[n
]);
856 #endif //#if defined(DEBUG_INTERRUPTS)
858 #if defined(DEBUG_TASKS)
860 void printTaskSwitchLog()
862 serialPrint("Tasks legend [<task_id>, <task name>]:");
863 for(int n
= 0; n
<= CFG_MAX_USER_TASKS
+1; n
++) {
865 serialPrint("%d: Idle", n
);
867 if (cliTaskId
== n
) {
868 serialPrint("%d: CLI", n
);
870 else if (menusTaskId
== n
) {
871 serialPrint("%d: menus", n
);
873 else if (mixerTaskId
== n
) {
874 serialPrint("%d: mixer", n
);
876 else if (audioTaskId
== n
) {
877 serialPrint("%d: audio", n
);
882 serialPrint("Tasks switch log at %u [<time>, <task_id>]:", get_tmr10ms());
883 uint32_t lastSwitchTime
= 0;
884 uint32_t * tsl
= new uint32_t[DEBUG_TASKS_LOG_SIZE
];
886 serialPrint("Not enough memory");
889 memcpy(tsl
, taskSwitchLog
, sizeof(taskSwitchLog
));
890 uint32_t * p
= tsl
+ taskSwitchLogPos
;
891 uint32_t * end
= tsl
+ DEBUG_TASKS_LOG_SIZE
;
892 for(int n
= 0; n
< DEBUG_TASKS_LOG_SIZE
; n
++) {
893 uint32_t taskId
= *p
>> 24;
894 uint32_t switchTime
= *p
& 0xFFFFFF;
895 if (lastSwitchTime
!= switchTime
) {
896 serialPrintf("\r\n%06x: ", switchTime
);
897 lastSwitchTime
= switchTime
;
899 serialPrintf("%u ", taskId
);
907 #endif // #if defined(DEBUG_TASKS)
909 #if defined(DEBUG_TIMERS)
911 void printDebugTime(uint32_t time
)
914 serialPrintf("%dms", time
/1000);
917 serialPrintf("%d.%03dms", time
/1000, time
%1000);
921 void printDebugTimer(const char * name
, DebugTimer
& timer
)
923 serialPrintf("%s: ", name
);
924 printDebugTime( timer
.getMin());
926 printDebugTime(timer
.getMax());
930 void printDebugTimers()
932 for(int n
= 0; n
< DEBUG_TIMERS_COUNT
; n
++) {
933 printDebugTimer(debugTimerNames
[n
], debugTimers
[n
]);
939 extern OS_MutexID audioMutex
;
941 void printAudioVars()
943 for(int n
= 0; n
< AUDIO_BUFFER_COUNT
; n
++) {
944 serialPrint("Audio Buffer %d: size: %u, ", n
, (uint32_t)audioBuffers
[n
].size
);
945 dump((uint8_t *)audioBuffers
[n
].data
, 32);
947 serialPrint("fragments:");
948 for(int n
= 0; n
< AUDIO_QUEUE_LENGTH
; n
++) {
949 serialPrint("%d: type %u: id: %u, repeat: %u, ", n
, (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].type
,
950 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].id
,
951 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].repeat
);
952 if ( audioQueue
.fragmentsFifo
.fragments
[n
].type
== FRAGMENT_FILE
) {
953 serialPrint(" file: %s", audioQueue
.fragmentsFifo
.fragments
[n
].file
);
957 serialPrint("FragmentFifo: ridx: %d, widx: %d", audioQueue
.fragmentsFifo
.ridx
, audioQueue
.fragmentsFifo
.widx
);
958 serialPrint("audioQueue: readIdx: %d, writeIdx: %d, full: %d", audioQueue
.buffersFifo
.readIdx
, audioQueue
.buffersFifo
.writeIdx
, audioQueue
.buffersFifo
.bufferFull
);
960 serialPrint("normalContext: %u", (uint32_t)audioQueue
.normalContext
.fragment
.type
);
962 serialPrint("audioMutex[%u] = %u", (uint32_t)audioMutex
, (uint32_t)MutexTbl
[audioMutex
].mutexFlag
);
966 int cliDisplay(const char ** argv
)
968 long long int address
= 0;
970 for (const MemArea
* area
= memAreas
; area
->name
!= NULL
; area
++) {
971 if (!strcmp(area
->name
, argv
[1])) {
972 dump((uint8_t *)area
->start
, area
->size
);
977 if (!strcmp(argv
[1], "keys")) {
978 for (int i
=0; i
<TRM_BASE
; i
++) {
980 uint8_t len
= STR_VKEYS
[0];
981 strncpy(name
, STR_VKEYS
+1+len
*i
, len
);
983 serialPrint("[%s] = %s", name
, keyState(i
) ? "on" : "off");
985 #if defined(ROTARY_ENCODER_NAVIGATION)
986 serialPrint("[Enc.] = %d", rotencValue
[0] / ROTARY_ENCODER_GRANULARITY
);
988 for (int i
=TRM_BASE
; i
<=TRM_LAST
; i
++) {
989 serialPrint("[Trim%d] = %s", i
-TRM_BASE
, keyState(i
) ? "on" : "off");
991 for (int i
=MIXSRC_FIRST_SWITCH
; i
<=MIXSRC_LAST_SWITCH
; i
++) {
992 mixsrc_t sw
= i
- MIXSRC_FIRST_SWITCH
;
993 if (SWITCH_EXISTS(sw
)) {
994 char swName
[LEN_SWITCH_NAME
+ 1];
995 strAppend(swName
, STR_VSWITCHES
+1+sw
*STR_VSWITCHES
[0], STR_VSWITCHES
[0]);
996 static const char * const SWITCH_POSITIONS
[] = { "down", "mid", "up" };
997 serialPrint("[%s] = %s", swName
, SWITCH_POSITIONS
[1 + getValue(i
) / 1024]);
1001 else if (!strcmp(argv
[1], "adc")) {
1002 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
1003 serialPrint("adc[%d] = %04X", i
, (int)adcValues
[i
]);
1006 else if (!strcmp(argv
[1], "outputs")) {
1007 for (int i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
1008 serialPrint("outputs[%d] = %04d", i
, (int)channelOutputs
[i
]);
1011 else if (!strcmp(argv
[1], "rtc")) {
1014 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
);
1016 #if !defined(SOFTWARE_VOLUME)
1017 else if (!strcmp(argv
[1], "volume")) {
1018 serialPrint("volume = %d", getVolume());
1022 else if (!strcmp(argv
[1], "uid")) {
1023 char str
[LEN_CPU_UID
+1];
1024 getCPUUniqueID(str
);
1025 serialPrint("uid = %s", str
);
1028 else if (!strcmp(argv
[1], "tim")) {
1030 if (toInt(argv
, 2, &timerNumber
) > 0) {
1031 TIM_TypeDef
* tim
= TIM1
;
1032 switch (timerNumber
) {
1045 serialPrint("TIM%d", timerNumber
);
1046 serialPrint(" CR1 0x%x", tim
->CR1
);
1047 serialPrint(" CR2 0x%x", tim
->CR2
);
1048 serialPrint(" DIER 0x%x", tim
->DIER
);
1049 serialPrint(" SR 0x%x", tim
->SR
);
1050 serialPrint(" EGR 0x%x", tim
->EGR
);
1051 serialPrint(" CCMR1 0x%x", tim
->CCMR1
);
1052 serialPrint(" CCMR2 0x%x", tim
->CCMR2
);
1054 serialPrint(" CNT 0x%x", tim
->CNT
);
1055 serialPrint(" ARR 0x%x", tim
->ARR
);
1056 serialPrint(" PSC 0x%x", tim
->PSC
);
1058 serialPrint(" CCER 0x%x", tim
->CCER
);
1059 serialPrint(" CCR1 0x%x", tim
->CCR1
);
1060 serialPrint(" CCR2 0x%x", tim
->CCR2
);
1061 serialPrint(" CCR3 0x%x", tim
->CCR3
);
1062 serialPrint(" CCR4 0x%x", tim
->CCR4
);
1065 else if (!strcmp(argv
[1], "dma")) {
1066 serialPrint("DMA1_Stream7");
1067 serialPrint(" CR 0x%x", DMA1_Stream7
->CR
);
1069 #if defined(DEBUG_INTERRUPTS)
1070 else if (!strcmp(argv
[1], "int")) {
1074 #if defined(DEBUG_TASKS)
1075 else if (!strcmp(argv
[1], "tsl")) {
1076 printTaskSwitchLog();
1079 #if defined(DEBUG_TIMERS)
1080 else if (!strcmp(argv
[1], "dt")) {
1084 else if (!strcmp(argv
[1], "audio")) {
1087 #if defined(DISK_CACHE)
1088 else if (!strcmp(argv
[1], "dc")) {
1089 DiskCacheStats stats
= diskCache
.getStats();
1090 uint32_t hitRate
= diskCache
.getHitRate();
1091 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
);
1094 else if (toLongLongInt(argv
, 1, &address
) > 0) {
1096 if (toInt(argv
, 2, &size
) >= 0) {
1097 dump((uint8_t *)address
, size
);
1103 int cliDebugVars(const char ** argv
)
1105 #if defined(PCBHORUS)
1106 extern uint32_t ioMutexReq
, ioMutexRel
;
1107 extern uint32_t sdReadRetries
;
1108 serialPrint("ioMutexReq=%d", ioMutexReq
);
1109 serialPrint("ioMutexRel=%d", ioMutexRel
);
1110 serialPrint("sdReadRetries=%d", sdReadRetries
);
1111 #elif defined(PCBTARANIS)
1112 serialPrint("telemetryErrors=%d", telemetryErrors
);
1118 int cliRepeat(const char ** argv
)
1122 if (toInt(argv
, 1, &interval
) > 0 && argv
[2]) {
1126 while (!cliRxFifo
.pop(c
) || !(c
== '\r' || c
== '\n' || c
== ' ')) {
1127 CoTickDelay(10); // 20ms
1128 if (++counter
>= interval
) {
1129 cliExecCommand(&argv
[2]);
1135 serialPrint("%s: Invalid arguments", argv
[0]);
1140 #if defined(JITTER_MEASURE)
1141 int cliShowJitter(const char ** argv
)
1143 serialPrint( "# anaIn rawJ avgJ");
1144 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
1145 serialPrint("A%02d %04X %04X %3d %3d", i
, getAnalogValue(i
), anaIn(i
), rawJitter
[i
].get(), avgJitter
[i
].get());
1146 if (IS_POT_MULTIPOS(i
)) {
1147 StepsCalibData
* calib
= (StepsCalibData
*) &g_eeGeneral
.calib
[i
];
1148 for (int j
=0; j
<calib
->count
; j
++) {
1149 serialPrint(" s%d %04X", j
, calib
->steps
[j
]);
1157 #if defined(INTERNAL_GPS)
1158 int cliGps(const char ** argv
)
1162 if (argv
[1][0] == '$') {
1163 // send command to GPS
1164 gpsSendFrame(argv
[1]);
1167 else if (!strcmp(argv
[1], "trace")) {
1168 gpsTraceEnabled
= !gpsTraceEnabled
;
1171 else if (toInt(argv
, 1, &baudrate
) > 0 && baudrate
> 0) {
1173 serialPrint("GPS baudrate set to %d", baudrate
);
1176 serialPrint("%s: Invalid arguments", argv
[0]);
1182 #if defined(BLUETOOTH)
1183 int cliBlueTooth(const char ** argv
)
1186 if (!strncmp(argv
[1], "AT", 2) || !strncmp(argv
[1], "TTM", 3)) {
1188 strAppend(strAppend(command
, argv
[1]), "\r\n");
1189 bluetoothWriteString(command
);
1190 char * line
= bluetoothReadline();
1191 serialPrint("<BT %s", line
);
1193 else if (toInt(argv
, 1, &baudrate
) > 0) {
1195 bluetoothInit(baudrate
);
1196 char * line
= bluetoothReadline();
1197 serialPrint("<BT %s", line
);
1201 serialPrint("BT turned off");
1205 serialPrint("%s: Invalid arguments", argv
[0]);
1211 const CliCommand cliCommands
[] = {
1212 { "beep", cliBeep
, "[<frequency>] [<duration>]" },
1213 { "ls", cliLs
, "<directory>" },
1214 { "read", cliRead
, "<filename>" },
1215 { "readsd", cliReadSD
, "<start sector> <sectors count> <read buffer size (sectors)>" },
1216 { "testsd", cliTestSD
, "" },
1217 { "play", cliPlay
, "<filename>" },
1218 { "print", cliDisplay
, "<address> [<size>] | <what>" },
1219 { "p", cliDisplay
, "<address> [<size>] | <what>" },
1220 { "reboot", cliReboot
, "[wdt]" },
1221 { "set", cliSet
, "<what> <value>" },
1222 { "stackinfo", cliStackInfo
, "" },
1223 { "meminfo", cliMemoryInfo
, "" },
1224 { "test", cliTest
, "new | std::exception | graphics | memspd" },
1226 { "trace", cliTrace
, "on | off" },
1228 { "help", cliHelp
, "[<command>]" },
1229 { "debugvars", cliDebugVars
, "" },
1230 { "repeat", cliRepeat
, "<interval> <command>" },
1231 #if defined(JITTER_MEASURE)
1232 { "jitter", cliShowJitter
, "" },
1234 #if defined(INTERNAL_GPS)
1235 { "gps", cliGps
, "<baudrate>|$<command>|trace" },
1237 #if defined(BLUETOOTH)
1238 { "bt", cliBlueTooth
, "<baudrate>|<command>" },
1240 { NULL
, NULL
, NULL
} /* sentinel */
1243 int cliHelp(const char ** argv
)
1245 for (const CliCommand
* command
= cliCommands
; command
->name
!= NULL
; command
++) {
1246 if (argv
[1][0] == '\0' || !strcmp(command
->name
, argv
[0])) {
1247 serialPrint("%s %s", command
->name
, command
->args
);
1248 if (argv
[1][0] != '\0') {
1253 if (argv
[1][0] != '\0') {
1254 serialPrint("Invalid command \"%s\"", argv
[0]);
1259 int cliExecCommand(const char ** argv
)
1261 if (argv
[0][0] == '\0')
1264 for (const CliCommand
* command
= cliCommands
; command
->name
!= NULL
; command
++) {
1265 if (!strcmp(command
->name
, argv
[0])) {
1266 return command
->func(argv
);
1269 serialPrint("Invalid command \"%s\"", argv
[0]);
1273 int cliExecLine(char * line
)
1275 int len
= strlen(line
);
1276 const char * argv
[CLI_COMMAND_MAX_ARGS
];
1277 memset(argv
, 0, sizeof(argv
));
1280 for (int i
=0; i
<len
; i
++) {
1281 if (line
[i
] == ' ') {
1283 if (argc
< CLI_COMMAND_MAX_ARGS
) {
1284 argv
[argc
++] = &line
[i
+1];
1288 return cliExecCommand(argv
);
1291 void cliTask(void * pdata
)
1293 char line
[CLI_COMMAND_MAX_LEN
+1];
1301 while (!cliRxFifo
.pop(c
)) {
1302 CoTickDelay(10); // 20ms
1307 serialPrint("\033[2J\033[1;1H");
1310 else if (c
== 127) {
1317 else if (c
== '\r' || c
== '\n') {
1321 if (pos
== 0 && cliLastLine
[0]) {
1322 // execute (repeat) last command
1323 strcpy(line
, cliLastLine
);
1327 strcpy(cliLastLine
, line
);
1333 else if (isascii(c
) && pos
< CLI_COMMAND_MAX_LEN
) {
1342 cliTaskId
= CoCreateTaskEx(cliTask
, NULL
, 10, &cliStack
.stack
[CLI_STACK_SIZE
-1], CLI_STACK_SIZE
, 1, false);