I try to read Strings transmitted over USB to the Raspberry Pi Pico using stdio
.
The strings are supposed to be read within a callback function as soon as characters are available. I don't want to poll the interface for new characters or use a repeating timer.
Because UART0
and UART1
are needed for other purposes and all PIO-State machines are toll used for even more UART Interfaces USB is the only option.
The PICO_SDK provides the function void stdio_set_chars_available_callback(void(*)(void *) fn, void *param)
to get notified whenever characters are available to be read.
I have following code for testing purposes. Right now I don't attempt to read Strings just a single character (as an integer) and than echo the integer back to the user. When I can't read a char I can't read a string either.
Edit June 19th 2023
As I state in a Comment, this is not the actual project. The real project is over 3000 lines long and build entirely on the concepts of interrupts and callbacks. Besides UART0
and UART1
I also implemented UART2
, UART3
, UART4
and UART5
using the PIO-Statemachines. All which are needed for the project.
using getchar_timeout_us(0);
is sadly not an option it would break the achitecture and lead to problems down the line. Making parsing of the protocol, checking they byte spacing (i.e the time between Bytes) harder.
End Edit
#include <stdio.h>
#include "pico/stdlib.h"
void callback(void *ptr){
int *i = (int*) ptr; // cast void pointer back to int pointer
// read the character which caused to callback (and in the future read the whole string)
*i = getchar_timeout_us(100); // length of timeout does not affect results
}
int main(){
stdio_usb_init(); // init usb only. uart0 and uart1 are needed elsewhere
while(!stdio_usb_connected()); // wait until USB connection
int i = 0;
stdio_set_chars_available_callback(callback, (void*) &i); //register callback
// main loop
while(1){
if(i!=0){
printf("%i\n",i); //print the integer value of the character we typed
i = 0; //reset the value
}
sleep_ms(1000);
}
return 0;
}
The callback itself works I initially tested it by enabling the LED inside the callback. When I typed anything in the terminal the LED turned on. The passing of the void pointer works too. Because the value is changed by the callback.
I presume the issue lies *i = getchar_timeout_us(100);
.
The function always returns -1
not the ascii (integer) value of the character I typed. get_char_timeout_us()
returns the PICO_ERROR_TIMEOUT
macro when a timeout occurs.
The macro PICO_ERROR_TIMEOUT
has the value -1
. So in other words we timeout.
The timeout is curios, we timeout reading a char within the callback method notifying us that characters are available to read.
The 100 μs in getchar_timeout_us(100);
is chosen because it is not much greater than the minimum time of between bytes at a baudrate of 115200.
The minimum time would be 86,805 μs. Maybe to fast for a human to type but I'll wirte a python script sending the strings to the Raspberry Pi Pico.
I would expect that reading a character within the callback method notifying me that characters are available to would return the send character and not PICO_ERROR_TIMEOUT
.
My initial assumption was that the timeout of 100 μs in getchar_timeout_us(100);
was just to short for a human being to type a character. Although there should be at least a single character in buffer because else the callback shouldn't have triggered in the first place. But anyway I tried to increase the timeout at the way to 1,000,000 μs, so 1 second. Still PICO_ERROR_TIMEOUT
.
I also tried multiple terminals, usually I use the PySerial module serial.tools.miniterm
on Windows.
I also tried minicom and screen on Fedora Linux 38 (different computer), no different result.
Getting rid of the timeout all together by using getchar();
but this just leads to that the callback get blocked indefinitely.
It appears to be that the characters I typed just disappear into the void when I enter the callback.
But my assumption is that getchar_timeout_us(100);
and getchar();
are just the wrong functions.
What are the correct function to call? Or how do I use the existing function correctly to read the characters in the buffer?
Too exclude any issues I made during compile time, here is the CMakeList.txt:
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "../../pico-sdk")
set(PICO_BOARD pico CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
project(foo C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(foo src/foo.cpp )
target_include_directories(foo
PUBLIC
include/
)
pico_set_program_name(foo "foo")
pico_set_program_version(foo "0.1")
pico_enable_stdio_uart(foo 0) # disable stdio over UART we need it
pico_enable_stdio_usb(foo 1) # enable stdio over USB
# Add the standard library to the build
target_link_libraries(foo
pico_stdlib)
# Add the standard include files to the build
target_include_directories(foo PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
pico_add_extra_outputs(foo)
and my terminal output is following.
py -m serial.tools.miniterm COM14 115200
--- Miniterm on com14 115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
-1
-1
-1
-1
-1
--- exit ---
I typed Hello (72, 101, 108, 108, 111)
but I got (-1, -1, -1, -1, -1)
EDIT June 19th 2023
A barebone Echo program such as this:
#include <stdio.h>
#include "pico/stdlib.h"
int main()
{
//stdio_init_all();
stdio_usb_init(); // init usb only. uart0 and uart1 are needed elsewhere
while(!stdio_usb_connected()); // wait until USB connection
while(1){
int i = getchar_timeout_us(100);
if(i >= 0){
printf("%i", i);
}
}
return 0;
}
works perfectly fine
When I type Hello
ich get I (72, 101, 108, 108, 111)
as expected.
Making the variable i
volatile
sadly does not change the output.
Also because the vooid poniter I pass is not supposed to be volatile it throws a warning (which is expected).
Replacing i
with a global volatile int g = 0
(no example) variable either.
#include <stdio.h>
#include "pico/stdlib.h"
void callback(volatile void *ptr){
volatile int *i = (volatile int*) ptr; // cast void pointer back to int pointer
// read the character which caused to callback (and in the future read the whole string)
*i = getchar_timeout_us(100); // length of timeout does not affect results
}
int main(){
stdio_usb_init(); // init usb only. uart0 and uart1 are needed elsewhere
while(!stdio_usb_connected()); // wait until USB connection
volatile int i = 0;
stdio_set_chars_available_callback(callback, (volatile void*) &i); //register callback
// main loop
while(1){
if(i!=0){
printf("%i\n",i); //print the integer value of the character we typed
i = 0; //reset the value
}
sleep_ms(1000);
}
return 0;
}
What I noticed putting
gpio_put(PICO_DEFAULT_LED_PIN, 1)
after *i = getchar_timeout_us(100);
inside the callback will turn on the LED (this is expected).
But putting gpio_put(PICO_DEFAULT_LED_PIN, 0)
after sleep_ms(1000);
inside the main loop will not turn of the LED, as it would be expected.
End Edit