Loading...
 

SW4STM32 and SW4Linux fully supports the STM32MP1 asymmetric multicore Cortex/A7+M4 MPUs

   With System Workbench for Linux, Embedded Linux on the STM32MP1 family of MPUs from ST was never as simple to build and maintain, even for newcomers in the Linux world.
And, if you install System Workbench for Linux in System Workbench for STM32 you can seamlessly develop and debug asymmetric applications running partly on Linux, partly on the Cortex-M4.
You can get more information from the ac6-tools website and download two short videos (registration required) highlighting:

System Workbench for STM32


Retargeting getchar() function

Hi,
I’m trying to retarget the getchar() function.

My simple goal is, for the moment, to wait until the ‘p’ key is pressed on the PC console.

-----------------------------------

Hereafter the working code:

> // Wait for ‘p’ char pressed
> HAL_UART_Receive (&huart3, (uint8_t *)&c, 1, 0xFFFF);
> while (c != ‘p’) {
> HAL_UART_Receive (&huart3, (uint8_t *)&c, 1, 0xFFFF);
> }

-----------------------------------

Instead, if I use the following code, the program stucks into the GETCHAR_PROTOTYPE routine:

> #ifdef GNUC
> /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
> set to ‘Yes’) calls __io_putchar() */
> #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
> #define GETCHAR_PROTOTYPE int __io_getchar(void)
> #else
> #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
> #define GETCHAR_PROTOTYPE int fgetc(FILE *f)
> #endif /* GNUC */

...

> GETCHAR_PROTOTYPE {
> int ch;
> HAL_UART_Receive (&huart3, (uint8_t *)&ch, 1, 0xFFFF);
> return ch;
> }

...

> // Wait for ‘p’ char pressed
> c = getchar();
> while (c != ‘p’) {
> c = getchar();
> }


In particular, the char is correctly captured by HAL_UART_Receive (I can see the char by using the breackpoints), but the GETCHAR_PROTOTYPE repeats infinitely without exiting and the while loop is never reached.

What is wrong with the code?

Thanks in advance!

Flavio

To make stdio functions like getchar() and printf() to work with your specific peripheral, you need to create custom versions of specific interface functions that are normally defined as no-op stubs by GCC’s runtime library.

I wish there was a way that I could attach a source file to a message. I have a complete implementation that uses a custom-written USART libaray (not ST’s HAL) that could be easily adapted (I think) to use ST HAL functions. I’ll copy it here, although with comments it’s kinda long. Hope this does not bug anyone.

Note that for the _write() function implementation, I’ve added some code to translate the ‘\v’ (vertical tab) control character into a “conditional newline”. Without this code, the _write() function implementation would be a lot simpler - just an if() enclosing a call to a single-character-out function.

There’s a few other things in here that are specific to my platform and usage that I extracted this code segment from. Hopefully those things won’t be too distracting. What is shown below is a stand-alone “library” source file (which also has a very simple accompanying .h file, not shown here) that you’d just add to your source project directory.

#include "errno.h"
#include "stdio.h"
#include "sys/stat.h"
#include "sys/unistd.h"
#include "stdio_helper_gcc.h"
#include "USART.h"

#undef errno
extern int errno;

/******************************************************************************
 * void stdio_setup()
 *
 * Perform any necessary or desired setup and/or hook-up outside of that which
 * is done by the stdio library interfacing routines (e.g. _read(), _write(),
 * etc.) needed to get stdio and the target device(s) to work optimally.
 *
 * For this target, setup is limited to turning stdio I/O buffering off for the
 * default I/O channels, so I/O operations done to them are passed to the
 * target I/O on a character-by-character basis. This means, for example, that
 * when a single character is output via putchar(), it will be transmitted to
 * the debug port immediately; it will not be locally buffered until a EOL
 * character is sent.
 *
 * This routine could also (optionally) include code that initializes and
 * configures the device(s) on the target to be used for stdio.
 * This initialization is presently being done outside of this routine.
 ******************************************************************************/

void stdio_setup(void)
{
    // Initialize "default" USART if it has not been done already

    if (! USART_Initialized(USART_NUMBER))
    {
        USART_Init(USART_NUMBER, USART_BAUDRATE, USART_RX_BUFSIZE, USART_TX_BUFSIZE);
    }

    // Turn off buffers, so I/O occurs immediately
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

/******************************************************************************
 * Notes concerning the functions defined below:
 *
 * These implementations replace "weak" stub implementations in GCC libc.
 * Parameters accepted and return types and values are dictated by what is
 * expected by libc and stdio, which is the only entity that should be calling
 * these routines.
 *
 * For the STM32 implementation, only I/O to the default file handles stdin,
 * stdout and stderr is supported. Input and output to the standard file handles
 * is directed to the U(S)ART designated for debug I/O.
 ******************************************************************************/

/******************************************************************************
 * int read(file, *data, len)
 *
 * Target-specific implementation of the stdio _read() function.
 * Reads data from devices on the target.
 *
 * file         File or device ID to read from
 * *data        Pointer to buffer where data read from the selected device
 *              is stored
 * len          Number of bytes to read from device
 *
 * Returns: Number of bytes read, or stdio error code
 ******************************************************************************/

int _read(int file, char *data, int len)
{
    int bytes_read;

    if (file != STDIN_FILENO)
    {
        errno = EBADF;
        return -1;
    }

    for (bytes_read = 0; bytes_read < len; bytes_read++)
    {
        *data = (char) USART_RxBlocking(USART_NUMBER);
        data++;
    }

    return bytes_read;
}

/******************************************************************************
 * int _write(file, *data, len)
 *
 * Target-specific implementation of the stdio _write() function.
 * Writes data to devices on the target.
 *
 * file         File or device ID to write to
 * *data        Pointer to buffer where data to be written is stored
 * len          Number of bytes to write to the device
 *
 * Returns: Number of bytes actually written, or stdio error code
 ******************************************************************************/

int _write(int file, char *data, int len)
{
    static unsigned int char_count;
    int bytes_written;
    char ch;

    if ((file != STDOUT_FILENO) && (file != STDERR_FILENO))
    {
        errno = EBADF;
        return -1;
    }

    for (bytes_written = 0; bytes_written < len; bytes_written++)
    {
        ch = *data;
        data++;

        // Translate the vertical tab char '\v' into a "conditional newline"
        // Sending '\v' will generate a \r\n (CR/LF) if the previous character
        // sent was printable (not a control char); i.e. Advance to a new line
        // if the cursor is not at the start of a new line.

        if (ch == '\v')
        {
            if (char_count > 0)
            {
                USART_TxBlocking(USART_NUMBER, '\r');
                USART_TxBlocking(USART_NUMBER, '\n');
                char_count = 0;
            }
        }
        else
        {
            USART_TxBlocking(USART_NUMBER, ch);
            if (ch >= ' ')
            {
                char_count++;
            }
            else if (ch == '\r')
            {
                char_count = 0;
            }
        }
    }

    return bytes_written;
}

/******************************************************************************
 * int _close(file)
 *
 * Target-specific implementation of the stdio file _close() function.
 *
 * Not used in this application.
 ******************************************************************************/

int _close(int file)
{
    return -1;
}

/******************************************************************************
 * int _lseek(file)
 *
 * Target-specific implementation of the stdio _lseek() (file seek) function.
 *
 * Not used in this application.
 ******************************************************************************/

int _lseek(int file, int ptr, int dir)
{
    return 0;
}

/******************************************************************************
 * int _fstat(file, *st)
 *
 * Target-specific implementation of the stdio _fstat (file status) function.
 *
 * Implemented minimally on this target, always returns status indicating that
 * the file/device is a "character" type.
 ******************************************************************************/

int _fstat(int file, struct stat *st)
{
    st->st_mode = S_IFCHR;
    return 0;
}

/******************************************************************************
 * int _isatty(file)
 *
 * Target-specific implementation of the stdio _isatty() function.
 *
 * Returns a nonzero value if the specified <file> is a "tty" style device; that
 * is, a display terminal or similar user-interactive device.
 *
 * For this target, it always returns a nonzero/true result for the
 * STDIN/STDOUT/STDERR devices.
 ******************************************************************************/

int _isatty(int file)
{
    if ((file == STDOUT_FILENO) ||
        (file == STDIN_FILENO) ||
        (file == STDERR_FILENO))
    {
        return 1;
    }

    errno = EBADF;
    return 0;
}

Thank you very much for the answer and the suggested code!
It’s well witten.

I was wondering if there is a “simpler” way to retarget the getchar() function.

Infact, I’ve already retargeted the putchar() function, redefining it in this way:

> int __io_putchar(int ch) {
> HAL_UART_Transmit (&huart3, (uint8_t *)&ch, 1, 0xFFFF);
> return ch;
> }

But when trying with the getchar() function, the following code doesn’t work:

> int __io_getchar(void) {
> int ch;
> HAL_UART_Receive (&huart3, (uint8_t *)&ch, 1, 0xFFFF);
> return ch;
> }


Any suggestion?



PS: How do you higlight the code in the post, using the gray square? :-)


To answer (or more precisely, not answer) your first question, I’ve never tried redirecting specific stdio library functions, I’ve always used some variant of the code I posted above to redirect everything, so I’m afraid I can’t answer that question for you.

I don’t normally use the ST HAL code, so I’m not familiar with the calling convention for the HAL_UART_Transmit() function, but I’ll make a general point here (forgive me if I’m stating the obvious): Make sure that the transmit function you call is done in such a way that it will “block” until the transmission is completed; that is, it does not return until it has completed transmitting the data you provide, or it will block if called again and the UART is still busy transmitting the last character or string passed to it.

As for how to get the gray box code highlight, you use this syntax:

{CODE(wrap=>1)}

your code here...

Note: Still chokes when the less-than or greater-than symbols are encountered.
e.g.
#include [stdio.h]  // replace '[' and ']' with less/greater than symbols

{CODE}


It took me a while to figure out how to get help on wiki syntax here. Start by selecting the small blue “?” symbol in the upper right hand corner of the edit window.
Then click on the “Wiki Syntax” link (near top of pop up dialog).
Then select the “Wiki Syntax Help” link on the page that comes up.

One could wish that the CODE segments were rendered using a monospaced font. This is normally the case for most forums, but not this one.


Here my non blocking getchar. You need to clear the overflow otherwise the rx available will not rise again

bool getchar(uint8_t * ch)
{
       // check for overflow and clear
	if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE))
		__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_ORE);

	if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
	{
		*ch = huart1.Instance->RDR & 0x1FF;
		return true;
	}

	return false;
}


And to make printf work in blocking mode:

void _write(int file, char *ptr, int len)
{
		HAL_UART_Transmit(&huart1, (uint8_t*) ptr, len, 10);
}

I think I am having the same issue here, I’d appreciate any help.

I am using the following to replace the GETCHAR and PUTCHAR. The put char works well and I’m able to use most std print functions.

However, the getchar recieves a character from UART and returns it through the write functions, however, when I use any of the fgets / scanf functions, it just keep accepting characters and doesn’t stop. I would expect it to finish capturing characters on a return character or newline.

int __io_putchar(int ch) {
    // Code to write character 'ch' on the UART
	if (ch =='\n'){
		UART_write('\r');
	}
	return UART_write(ch);

}


int __io_getchar(void) {
    // Code to read a character from the UART
		int c;
		c = UART_read();

		UART_write(c);
		if (c == '\r'){
			UART_write('\n');
			return 0;
		}
		return c;
	}