Closed panciunio closed 1 year ago
If you want the debug printf to go out via serial / UART, then you need to activate the macro responsible for it, by default it's commented out
Otherwise it tries to push out the debug messages via the flashing interface (SDI), readable with the minichlink
program.
Just minute before I found this #CFLAGS+=-DSTDOUT_UART definition in Makefile :-) But many thanks for your advice. Could you please explain how to do it with minichlink? Should be data available on /dev/ttyACM0 (my WCH-link) after flashing process?
No in the not STDOUT_UART case the data is passed via the debug registers in the debug connection, they don't show up on the serial device (/dev/tty*
). As the readme says per
https://github.com/cnlohr/ch32v003fun#a-fast-printf-debug-over-the-programming-interface
you need to use ./minichlink -T
to get those prints.
Now everything is clear and works fine. There is no possibility to see data from ACM0 device directly on terminal e.g. minicom
app but ./minichlink -T
is very usefull. Is there any example how to catch data from serial port by UART?
Hm I didn't see a UART RX example, but if you look at the _write
implementation and UART init
you could come up with an extension of that, that activates the RX mode and implements the _read
function so that standard-IO functions like scanf()
or getchar()
just work, see e.g. https://shawnhymel.com/1873/how-to-use-printf-on-stm32/ for reference.
Let me just test this for myself :)
Hmm I got it working in my PlatformIO enviornment but I had to comment out some functions in ch32v003fun.c
because otherwise it would generate duplicate definitions for standard-C functions.. need to investigate it more. But when I comment those out (as would be mentioned in the compile errors), this program works nicely:
(serial terminal settings: 115200 baud, local echo turned on, WCH-LinkE's "TX" connected to PD6, "RX" connected to PD5)
#define SYSTEM_CORE_CLOCK 48000000
#define APB_CLOCK SYSTEM_CORE_CLOCK
#include "ch32v003fun.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int _read(int fd, char *ptr, int len)
{
if (fd == STDIN_FILENO) {
// wait for a character to be received
while(!(USART1->STATR & USART_STATR_RXNE)) {}
uint8_t rxData = USART1->DATAR;
*ptr = rxData;
//always only return 1 byte
return 1;
}
return -1;
}
int main() {
SystemInit48HSI();
SetupUART(UART_BRR);
// we need to additional turn on RX mode
USART1->CTLR1 |= USART_Mode_Rx;
Delay_Ms(1000); // startup delay
while (1) {
printf("Please enter some text: ");
// read from stdin FILE* / stream
char buf[128];
fgets(buf, sizeof(buf), stdin);
// will return after hitting newline, but newline chracter is included.
// beauty massage: overwrite it with NUL character here
char *pos;
if ((pos=strchr(buf, '\n')) != NULL)
*pos = '\0';
if ((pos=strchr(buf, '\r')) != NULL)
*pos = '\0';
printf("You entered: \"%s\"\n", buf);
}
}
Of course... I'm rather noob, more concentrated on hardware than software but I see big potential with CH32V003 - so small but so powerfully :-) Let me check your example and confirm that working fine... suppose shall be added as additional example...
EDIT: I have no idea how to find functions definitions you mentioned... maybe my environment is so different than your?
pancio@desktop:/sandbox/ch32v003fun/examples/uartdemo$ make clean
rm -rf uartdemo.elf uartdemo.bin uartdemo.hex uartdemo.lst uartdemo.map uartdemo.hex || true
pancio@desktop:/sandbox/ch32v003fun/examples/uartdemo$ make
riscv64-unknown-elf-gcc -o uartdemo.elf ../../ch32v003fun/ch32v003fun.c uartdemo.c -DSTDOUT_UART -g -Os -flto -ffunction-sections -static-libgcc -march=rv32ec -mabi=ilp32e -I/usr/include/newlib -I../../ch32v003fun/../extralibs -I../../ch32v003fun -nostdlib -I. -Wall -T ../../ch32v003fun/ch32v003fun.ld -Wl,--gc-sections -L../../ch32v003fun/../misc -lgcc
/usr/lib/riscv64-unknown-elf/bin/ld: /tmp/uartdemo.elf.5bCtq0.ltrans0.ltrans.o: in function `.L0 ':
/sandbox/ch32v003fun/examples/uartdemo/uartdemo.c:30: undefined reference to `_impure_ptr'
/usr/lib/riscv64-unknown-elf/bin/ld: /tmp/uartdemo.elf.5bCtq0.ltrans0.ltrans.o:/sandbox/ch32v003fun/examples/uartdemo/uartdemo.c:30: undefined reference to `_impure_ptr'
/usr/lib/riscv64-unknown-elf/bin/ld: /tmp/uartdemo.elf.5bCtq0.ltrans0.ltrans.o:/sandbox/ch32v003fun/examples/uartdemo/uartdemo.c:33: undefined reference to `fgets'
collect2: error: ld returned 1 exit status
make: *** [../../ch32v003fun/ch32v003fun.mk:27: uartdemo.elf] Error 1
pancio@desktop:/sandbox/ch32v003fun/examples/uartdemo$
if I comment fgets(buf, sizeof(buf), stdin);
program is compiled without any warning and putting many lines on terminal:
Actually I had a hard time getting it to compile with this makefile / standard toolchain because it doesn't link / have the standard nosys.specs and nano.specs that I'm linking with.
As an intermediate solution I rewrote it to not rely on Standard-IO functions, but do this instead:
#define SYSTEM_CORE_CLOCK 48000000
#define APB_CLOCK SYSTEM_CORE_CLOCK
#include "ch32v003fun.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
int _read(int fd, char *ptr, int len) {
if (fd == STDIN_FILENO) {
// wait for a character to be received
while (!(USART1->STATR & USART_STATR_RXNE)) { }
uint8_t rxData = USART1->DATAR;
*ptr = rxData;
// always only return 1 byte
return 1;
}
return -1;
}
// user input buffer and position in buffer
static char buf[128];
static unsigned pos = 0;
bool get_user_input() {
char c;
if (pos < sizeof(buf) && _read(STDIN_FILENO, &c, 1) == 1) {
// assume terminal program is set to either \r\n or \n as line ending, thus \n is always end of line
if (c == '\n') {
return true;
}
// swallow \r (line ending normalization basically)
if (c == '\r')
return false;
buf[pos++] = c;
// we don't have a full line yet
return false;
}
else {
// something bad happened, reset
pos = 0;
memset(buf, 0, sizeof(buf));
return false;
}
}
int main() {
SystemInit48HSI();
SetupUART(UART_BRR);
// we need to additional turn on RX mode
USART1->CTLR1 |= USART_Mode_Rx;
Delay_Ms(1000); // startup delay
while (1) {
printf("Please enter some text: ");
// read from user until newline (blocking)
while (!get_user_input()) { }
printf("You entered: \"%s\"\n", buf);
// reset for next run
pos = 0;
memset(buf, 0, sizeof(buf));
}
}
this should compile without problems and still works, and is a significantly smaller firmware as a side-effect.
Good job Max,
Working fine:
So maybe some commit to branch?
Thanks a lot for help!
Best Regards,
(You can turn on local echo mode in miniterm with Ctrl + T followed by Ctrl + E, in _read()
, write back rxData
into the UART1->DATAR
register for an actual echo)
Great that it works, but let's get this issue back on track. The original question was about printf() not showing up as UART output in the I2C OLED demo, but we resolved that with the unactivated STDOUT_UART
macro.
Adding a proper UART RX is another point, I've opened #152 for that.
As investigated by @maxgerhardt there is no issue with STDOUT_UART. By accident new feature regarding UART RX were discovered. Thank you @maxgerhardt
Hi Cnlohr,
At first - many thanks for your effort and for all contributors! It's very useful project and I'm very happy using it! I hope it will be available for CH32V103 too :-)
I tried to use an example I2C_OLED with partially success... OLED working as expected but serial output doesn't work - no one message appeared on serial console. To confirm that any connection and configuration is ok, I used other example: UARTDEMO where everything works fine and each message are visible on my console:
I compared both examples and I can't find any difference between into serial initialization, so I suppose the issue coming somewhere else in rest of code. Could you please advise where I shall to look?
Best Regards, pancio