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();
637 #endif // #if defined(COLORLCD)
639 int cliTest(const char ** argv
)
641 if (!strcmp(argv
[1], "new")) {
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();
656 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
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;
670 serialPrint("%s: Invalid argument \"%s\"", argv
[0], argv
[1]);
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());
686 extern int _heap_end
;
687 extern unsigned char *heap
;
689 int cliMemoryInfo(const char ** argv
)
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 */
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
));
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
);
734 int cliReboot(const char ** argv
)
737 if (!strcmp(argv
[1], "wdt")) {
738 // do a user requested watchdog test by pausing mixer thread
748 const MemArea memAreas
[] = {
749 { "RCC", RCC
, sizeof(RCC_TypeDef
) },
750 { "GPIOA", GPIOA
, sizeof(GPIO_TypeDef
) },
751 { "GPIOB", GPIOB
, sizeof(GPIO_TypeDef
) },
752 { "GPIOC", GPIOC
, sizeof(GPIO_TypeDef
) },
753 { "GPIOD", GPIOD
, sizeof(GPIO_TypeDef
) },
754 { "GPIOE", GPIOE
, sizeof(GPIO_TypeDef
) },
755 { "GPIOF", GPIOF
, sizeof(GPIO_TypeDef
) },
756 { "GPIOG", GPIOG
, sizeof(GPIO_TypeDef
) },
757 { "USART1", USART1
, sizeof(USART_TypeDef
) },
758 { "USART2", USART2
, sizeof(USART_TypeDef
) },
759 { "USART3", USART3
, sizeof(USART_TypeDef
) },
763 int cliSet(const char ** argv
)
765 if (!strcmp(argv
[1], "rtc")) {
767 int year
, month
, day
, hour
, minute
, second
;
768 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) {
769 t
.tm_year
= year
-TM_YEAR_BASE
;
775 g_rtcTime
= gmktime(&t
); // update local timestamp and get wday calculated
779 serialPrint("%s: Invalid arguments \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
782 #if !defined(SOFTWARE_VOLUME)
783 else if (!strcmp(argv
[1], "volume")) {
785 if (toInt(argv
, 2, &level
) > 0) {
789 serialPrint("%s: Invalid argument \"%s\" \"%s\"", argv
[0], argv
[1], argv
[2]);
798 #if defined(DEBUG_INTERRUPTS)
799 void printInterrupts()
802 struct InterruptCounters ic
= interruptCounters
;
803 memset(&interruptCounters
, 0, sizeof(interruptCounters
));
804 interruptCounters
.resetTime
= get_tmr10ms();
806 serialPrint("Interrupts count in the last %u ms:", (get_tmr10ms() - ic
.resetTime
) * 10);
807 for(int n
= 0; n
< INT_LAST
; n
++) {
808 serialPrint("%s: %u", interruptNames
[n
], ic
.cnt
[n
]);
811 #endif //#if defined(DEBUG_INTERRUPTS)
813 #if defined(DEBUG_TASKS)
815 void printTaskSwitchLog()
817 serialPrint("Tasks legend [<task_id>, <task name>]:");
818 for(int n
= 0; n
<= CFG_MAX_USER_TASKS
+1; n
++) {
820 serialPrint("%d: Idle", n
);
822 if (cliTaskId
== n
) {
823 serialPrint("%d: CLI", n
);
825 else if (menusTaskId
== n
) {
826 serialPrint("%d: menus", n
);
828 else if (mixerTaskId
== n
) {
829 serialPrint("%d: mixer", n
);
831 else if (audioTaskId
== n
) {
832 serialPrint("%d: audio", n
);
837 serialPrint("Tasks switch log at %u [<time>, <task_id>]:", get_tmr10ms());
838 uint32_t lastSwitchTime
= 0;
839 uint32_t * tsl
= new uint32_t[DEBUG_TASKS_LOG_SIZE
];
841 serialPrint("Not enough memory");
844 memcpy(tsl
, taskSwitchLog
, sizeof(taskSwitchLog
));
845 uint32_t * p
= tsl
+ taskSwitchLogPos
;
846 uint32_t * end
= tsl
+ DEBUG_TASKS_LOG_SIZE
;
847 for(int n
= 0; n
< DEBUG_TASKS_LOG_SIZE
; n
++) {
848 uint32_t taskId
= *p
>> 24;
849 uint32_t switchTime
= *p
& 0xFFFFFF;
850 if (lastSwitchTime
!= switchTime
) {
851 serialPrintf("\r\n%06x: ", switchTime
);
852 lastSwitchTime
= switchTime
;
854 serialPrintf("%u ", taskId
);
862 #endif // #if defined(DEBUG_TASKS)
864 #if defined(DEBUG_TIMERS)
866 void printDebugTime(uint32_t time
)
869 serialPrintf("%dms", time
/1000);
872 serialPrintf("%d.%03dms", time
/1000, time
%1000);
876 void printDebugTimer(const char * name
, DebugTimer
& timer
)
878 serialPrintf("%s: ", name
);
879 printDebugTime( timer
.getMin());
881 printDebugTime(timer
.getMax());
885 void printDebugTimers()
887 for(int n
= 0; n
< DEBUG_TIMERS_COUNT
; n
++) {
888 printDebugTimer(debugTimerNames
[n
], debugTimers
[n
]);
894 extern OS_MutexID audioMutex
;
896 void printAudioVars()
898 for(int n
= 0; n
< AUDIO_BUFFER_COUNT
; n
++) {
899 serialPrint("Audio Buffer %d: size: %u, ", n
, (uint32_t)audioBuffers
[n
].size
);
900 dump((uint8_t *)audioBuffers
[n
].data
, 32);
902 serialPrint("fragments:");
903 for(int n
= 0; n
< AUDIO_QUEUE_LENGTH
; n
++) {
904 serialPrint("%d: type %u: id: %u, repeat: %u, ", n
, (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].type
,
905 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].id
,
906 (uint32_t)audioQueue
.fragmentsFifo
.fragments
[n
].repeat
);
907 if ( audioQueue
.fragmentsFifo
.fragments
[n
].type
== FRAGMENT_FILE
) {
908 serialPrint(" file: %s", audioQueue
.fragmentsFifo
.fragments
[n
].file
);
912 serialPrint("FragmentFifo: ridx: %d, widx: %d", audioQueue
.fragmentsFifo
.ridx
, audioQueue
.fragmentsFifo
.widx
);
913 serialPrint("audioQueue: readIdx: %d, writeIdx: %d, full: %d", audioQueue
.buffersFifo
.readIdx
, audioQueue
.buffersFifo
.writeIdx
, audioQueue
.buffersFifo
.bufferFull
);
915 serialPrint("normalContext: %u", (uint32_t)audioQueue
.normalContext
.fragment
.type
);
917 serialPrint("audioMutex[%u] = %u", (uint32_t)audioMutex
, (uint32_t)MutexTbl
[audioMutex
].mutexFlag
);
921 int cliDisplay(const char ** argv
)
923 long long int address
= 0;
925 for (const MemArea
* area
= memAreas
; area
->name
!= NULL
; area
++) {
926 if (!strcmp(area
->name
, argv
[1])) {
927 dump((uint8_t *)area
->start
, area
->size
);
932 if (!strcmp(argv
[1], "keys")) {
933 for (int i
=0; i
<TRM_BASE
; i
++) {
935 uint8_t len
= STR_VKEYS
[0];
936 strncpy(name
, STR_VKEYS
+1+len
*i
, len
);
938 serialPrint("[%s] = %s", name
, keyState(i
) ? "on" : "off");
940 #if defined(ROTARY_ENCODER_NAVIGATION)
941 serialPrint("[Enc.] = %d", rotencValue
[0] / ROTARY_ENCODER_GRANULARITY
);
943 for (int i
=TRM_BASE
; i
<=TRM_LAST
; i
++) {
944 serialPrint("[Trim%d] = %s", i
-TRM_BASE
, keyState(i
) ? "on" : "off");
946 for (int i
=MIXSRC_FIRST_SWITCH
; i
<=MIXSRC_LAST_SWITCH
; i
++) {
947 mixsrc_t sw
= i
- MIXSRC_FIRST_SWITCH
;
948 if (SWITCH_EXISTS(sw
)) {
949 char swName
[LEN_SWITCH_NAME
+ 1];
950 strAppend(swName
, STR_VSWITCHES
+1+sw
*STR_VSWITCHES
[0], STR_VSWITCHES
[0]);
951 static const char * const SWITCH_POSITIONS
[] = { "down", "mid", "up" };
952 serialPrint("[%s] = %s", swName
, SWITCH_POSITIONS
[1 + getValue(i
) / 1024]);
956 else if (!strcmp(argv
[1], "adc")) {
957 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
958 serialPrint("adc[%d] = %04X", i
, (int)adcValues
[i
]);
961 else if (!strcmp(argv
[1], "outputs")) {
962 for (int i
=0; i
<MAX_OUTPUT_CHANNELS
; i
++) {
963 serialPrint("outputs[%d] = %04d", i
, (int)channelOutputs
[i
]);
966 else if (!strcmp(argv
[1], "rtc")) {
969 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
);
971 #if !defined(SOFTWARE_VOLUME)
972 else if (!strcmp(argv
[1], "volume")) {
973 serialPrint("volume = %d", getVolume());
977 else if (!strcmp(argv
[1], "uid")) {
978 char str
[LEN_CPU_UID
+1];
980 serialPrint("uid = %s", str
);
983 else if (!strcmp(argv
[1], "tim")) {
985 if (toInt(argv
, 2, &timerNumber
) > 0) {
986 TIM_TypeDef
* tim
= TIM1
;
987 switch (timerNumber
) {
1000 serialPrint("TIM%d", timerNumber
);
1001 serialPrint(" CR1 0x%x", tim
->CR1
);
1002 serialPrint(" CR2 0x%x", tim
->CR2
);
1003 serialPrint(" DIER 0x%x", tim
->DIER
);
1004 serialPrint(" SR 0x%x", tim
->SR
);
1005 serialPrint(" EGR 0x%x", tim
->EGR
);
1006 serialPrint(" CCMR1 0x%x", tim
->CCMR1
);
1007 serialPrint(" CCMR2 0x%x", tim
->CCMR2
);
1009 serialPrint(" CNT 0x%x", tim
->CNT
);
1010 serialPrint(" ARR 0x%x", tim
->ARR
);
1011 serialPrint(" PSC 0x%x", tim
->PSC
);
1013 serialPrint(" CCER 0x%x", tim
->CCER
);
1014 serialPrint(" CCR1 0x%x", tim
->CCR1
);
1015 serialPrint(" CCR2 0x%x", tim
->CCR2
);
1016 serialPrint(" CCR3 0x%x", tim
->CCR3
);
1017 serialPrint(" CCR4 0x%x", tim
->CCR4
);
1020 else if (!strcmp(argv
[1], "dma")) {
1021 serialPrint("DMA1_Stream7");
1022 serialPrint(" CR 0x%x", DMA1_Stream7
->CR
);
1024 #if defined(DEBUG_INTERRUPTS)
1025 else if (!strcmp(argv
[1], "int")) {
1029 #if defined(DEBUG_TASKS)
1030 else if (!strcmp(argv
[1], "tsl")) {
1031 printTaskSwitchLog();
1034 #if defined(DEBUG_TIMERS)
1035 else if (!strcmp(argv
[1], "dt")) {
1039 else if (!strcmp(argv
[1], "audio")) {
1042 #if defined(DISK_CACHE)
1043 else if (!strcmp(argv
[1], "dc")) {
1044 DiskCacheStats stats
= diskCache
.getStats();
1045 uint32_t hitRate
= diskCache
.getHitRate();
1046 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
);
1049 else if (toLongLongInt(argv
, 1, &address
) > 0) {
1051 if (toInt(argv
, 2, &size
) >= 0) {
1052 dump((uint8_t *)address
, size
);
1058 int cliDebugVars(const char ** argv
)
1060 #if defined(PCBHORUS)
1061 extern uint32_t ioMutexReq
, ioMutexRel
;
1062 extern uint32_t sdReadRetries
;
1063 serialPrint("ioMutexReq=%d", ioMutexReq
);
1064 serialPrint("ioMutexRel=%d", ioMutexRel
);
1065 serialPrint("sdReadRetries=%d", sdReadRetries
);
1066 #elif defined(PCBTARANIS)
1067 serialPrint("telemetryErrors=%d", telemetryErrors
);
1073 int cliRepeat(const char ** argv
)
1077 if (toInt(argv
, 1, &interval
) > 0 && argv
[2]) {
1081 while (!cliRxFifo
.pop(c
) || !(c
== '\r' || c
== '\n' || c
== ' ')) {
1082 CoTickDelay(10); // 20ms
1083 if (++counter
>= interval
) {
1084 cliExecCommand(&argv
[2]);
1090 serialPrint("%s: Invalid arguments", argv
[0]);
1095 #if defined(JITTER_MEASURE)
1096 int cliShowJitter(const char ** argv
)
1098 serialPrint( "# anaIn rawJ avgJ");
1099 for (int i
=0; i
<NUM_ANALOGS
; i
++) {
1100 serialPrint("A%02d %04X %04X %3d %3d", i
, getAnalogValue(i
), anaIn(i
), rawJitter
[i
].get(), avgJitter
[i
].get());
1101 if (IS_POT_MULTIPOS(i
)) {
1102 StepsCalibData
* calib
= (StepsCalibData
*) &g_eeGeneral
.calib
[i
];
1103 for (int j
=0; j
<calib
->count
; j
++) {
1104 serialPrint(" s%d %04X", j
, calib
->steps
[j
]);
1112 #if defined(INTERNAL_GPS)
1113 int cliGps(const char ** argv
)
1117 if (argv
[1][0] == '$') {
1118 // send command to GPS
1119 gpsSendFrame(argv
[1]);
1122 else if (!strcmp(argv
[1], "trace")) {
1123 gpsTraceEnabled
= !gpsTraceEnabled
;
1126 else if (toInt(argv
, 1, &baudrate
) > 0 && baudrate
> 0) {
1128 serialPrint("GPS baudrate set to %d", baudrate
);
1131 serialPrint("%s: Invalid arguments", argv
[0]);
1137 #if defined(BLUETOOTH)
1138 int cliBlueTooth(const char ** argv
)
1141 if (!strncmp(argv
[1], "AT", 2) || !strncmp(argv
[1], "TTM", 3)) {
1143 strAppend(strAppend(command
, argv
[1]), "\r\n");
1144 bluetoothWriteString(command
);
1145 char * line
= bluetoothReadline();
1146 serialPrint("<BT %s", line
);
1148 else if (toInt(argv
, 1, &baudrate
) > 0) {
1150 bluetoothInit(baudrate
);
1151 char * line
= bluetoothReadline();
1152 serialPrint("<BT %s", line
);
1156 serialPrint("BT turned off");
1160 serialPrint("%s: Invalid arguments", argv
[0]);
1166 const CliCommand cliCommands
[] = {
1167 { "beep", cliBeep
, "[<frequency>] [<duration>]" },
1168 { "ls", cliLs
, "<directory>" },
1169 { "read", cliRead
, "<filename>" },
1170 { "readsd", cliReadSD
, "<start sector> <sectors count> <read buffer size (sectors)>" },
1171 { "testsd", cliTestSD
, "" },
1172 { "play", cliPlay
, "<filename>" },
1173 { "print", cliDisplay
, "<address> [<size>] | <what>" },
1174 { "p", cliDisplay
, "<address> [<size>] | <what>" },
1175 { "reboot", cliReboot
, "[wdt]" },
1176 { "set", cliSet
, "<what> <value>" },
1177 { "stackinfo", cliStackInfo
, "" },
1178 { "meminfo", cliMemoryInfo
, "" },
1179 { "test", cliTest
, "new | std::exception | graphics | memspd" },
1180 { "trace", cliTrace
, "on | off" },
1181 { "help", cliHelp
, "[<command>]" },
1182 { "debugvars", cliDebugVars
, "" },
1183 { "repeat", cliRepeat
, "<interval> <command>" },
1184 #if defined(JITTER_MEASURE)
1185 { "jitter", cliShowJitter
, "" },
1187 #if defined(INTERNAL_GPS)
1188 { "gps", cliGps
, "<baudrate>|$<command>|trace" },
1190 #if defined(BLUETOOTH)
1191 { "bt", cliBlueTooth
, "<baudrate>|<command>" },
1193 { NULL
, NULL
, NULL
} /* sentinel */
1196 int cliHelp(const char ** argv
)
1198 for (const CliCommand
* command
= cliCommands
; command
->name
!= NULL
; command
++) {
1199 if (argv
[1][0] == '\0' || !strcmp(command
->name
, argv
[0])) {
1200 serialPrint("%s %s", command
->name
, command
->args
);
1201 if (argv
[1][0] != '\0') {
1206 if (argv
[1][0] != '\0') {
1207 serialPrint("Invalid command \"%s\"", argv
[0]);
1212 int cliExecCommand(const char ** argv
)
1214 if (argv
[0][0] == '\0')
1217 for (const CliCommand
* command
= cliCommands
; command
->name
!= NULL
; command
++) {
1218 if (!strcmp(command
->name
, argv
[0])) {
1219 return command
->func(argv
);
1222 serialPrint("Invalid command \"%s\"", argv
[0]);
1226 int cliExecLine(char * line
)
1228 int len
= strlen(line
);
1229 const char * argv
[CLI_COMMAND_MAX_ARGS
];
1230 memset(argv
, 0, sizeof(argv
));
1233 for (int i
=0; i
<len
; i
++) {
1234 if (line
[i
] == ' ') {
1236 if (argc
< CLI_COMMAND_MAX_ARGS
) {
1237 argv
[argc
++] = &line
[i
+1];
1241 return cliExecCommand(argv
);
1244 void cliTask(void * pdata
)
1246 char line
[CLI_COMMAND_MAX_LEN
+1];
1254 while (!cliRxFifo
.pop(c
)) {
1255 CoTickDelay(10); // 20ms
1260 serialPrint("\033[2J\033[1;1H");
1263 else if (c
== 127) {
1270 else if (c
== '\r' || c
== '\n') {
1274 if (pos
== 0 && cliLastLine
[0]) {
1275 // execute (repeat) last command
1276 strcpy(line
, cliLastLine
);
1280 strcpy(cliLastLine
, line
);
1286 else if (isascii(c
) && pos
< CLI_COMMAND_MAX_LEN
) {
1295 cliTaskId
= CoCreateTaskEx(cliTask
, NULL
, 10, &cliStack
.stack
[CLI_STACK_SIZE
-1], CLI_STACK_SIZE
, 1, false);