Library 41- STDIO implementation for STM32F4
After I made a tutorial on how to use printf with STM32F4, I decided to make a library for printf and other functions that are able to output data to the stream. This library allows you to basically use unlimited different output streams, but just one can be use with printf at a time. You can use other output functions, like fprintf, which accepts stream pointer as parameter to know where to output data.
Library
Features
- Output strings using printf or any other function, like fprintf, etc
- Any function which is able to output data to the stream can be used
- Basically unlimited output streams can be used
- Version 1.1 – October 19, 2014
- Added support to read from standard input (stdin) stream or user defined
Dependencies
- CMSIS
- STM32F4xx
- TM
- defines.h
- attributes.h
- defines.h
Output format
According to the ARM stdio library, you can use printf or any other output stream fuctions. Proper use of different possible output types is described on ARM site.
Printf
Main function in this section is probably printf. With just simple call, you can at the same time convert your string and send it to your desired output. This can be like USARTs, LCDs, SPI, …, and any others which are on your mind. But with printf, you can only output data to one stream, let’s say just USART1. If you now want to display data to LCD too, you need another method for this, like using fprintf. In this section, I will describe how to proper use printf with my library.
Basically, you just need to include my library and create a function, that will output data to your location. You can create function anywhere in your project, it’s important that have the correct name and parameters. Function must be like this below.
1 2 3 4 5 6 7 8 9 10 |
/* Handle printf actions */ int TM_STDIO_StdoutHandler(int ch, FILE* f) { /* Do with your character what you need here */ /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } |
So if you want to output with printf data to USART1, you first need to initialize it in main, but then you should create a function like this:
1 2 3 4 5 6 7 8 9 10 |
/* Handle printf actions */ int TM_STDIO_StdoutHandler(int ch, FILE* f) { /* Send data to USART1 */ TM_USART_Putc(USART1, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } |
Now you can use anywhere in your project printf function.
More output streams
If you want to use more output streams, then you have to split them somehow, so you will know on which stream you have to output data. This can be done with fprintf, vfprintf and maybe any more. First argument to this function is output stream pointer, so when you use stream output function you know to which stream you have to output.
For that purpose, I make some additions in my library. First, you have to create as many variables as different output streams you will use.
If you want to use output streams for USART and LCD now, you can use printf method (default) for USART and another method (with custom output streams) for LCD.
First, you need to create a variable.
1 |
FILE LCD_Stream; |
Then, you have to create a function, with your custom name but parameters must match these in example below.
1 2 3 4 5 6 7 8 |
int LCD_Stream_OutputFunction(int ch, FILE* f) { /* Do your stuff here with new character */ /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } |
But stream still doesn’t know which function to call. You have to tell him. Do this with function like this below.
1 2 |
/* Add output function for LCD_Stream */ TM_STDIO_SetOutputFunction(&LCD_Stream, LCD_Stream_OutputFunction); |
If you now call function like in example below, then you will be able to output data to LCD.
1 2 |
/* Print something on LCD */ fprintf(&LCD_Stream, "Hello world"); |
Input stream
As of version 1.1, you are also able to get data from stream. For that purpose, you have again one standard function name for stdin (standard input) and also possibility for other custom function names for other functions. For handle stdin action, you need function like this:
1 2 3 4 5 6 7 8 |
/* Handle stdin actions */ int TM_STDIO_StdinHandler(FILE* f) { /* Return your data here */ /* End of data, string is valid */ /* You have to send -1 at the end of string */ return -1; } |
So you can now call fgets function to get string from stream.
For custom streams, like let’s say USART6 Stream, you have to create a custom function that will handler USART6 Stream input data. You can give it a name you want, it’s important that structure is valid.
1 2 3 4 5 6 7 8 9 10 11 |
/* Handle USART6 stream input = custom function name, linked with USART6 stream in the beginning of main() function */ int USART6_Stream_InputFunction(FILE* f) { /* If any data at USART, return them */ if (!TM_USART_BufferEmpty(USART6)) { return (int)TM_USART_Getc(USART6); } /* End of data, string is valid */ /* You have to send -1 at the end of string */ return -1; } |
And you have to link input function with actual stream. In the beginning of the main, you have to add something like this:
1 2 |
/* Add input function for USART6_Stream */ TM_STDIO_SetInputFunction(&USART6_Stream, USART6_Stream_InputFunction); |
And you are ready. You can now use standard input functions. For detailed, look Example 2.
Functions and enumerations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/** * File structure for stdio stream output * * Parameters: * - int (*outputFuncPointer)(int, FILE *): * Pointer to function to call when need to output data * - int (*inputFuncPointer)(FILE *): * Pointer to function to call when trying to get data from stream */ struct __FILE { int (*outputFuncPointer)(int, FILE *); int (*inputFuncPointer)(FILE *); }; /** * Link file output stream with output put character function * * Parameters: * - FILE* f * Pointer to file stream * - int (*outputFuncPointer)(int, FILE *) * Pointer to function that will be used to output data to stream * * No return */ extern void TM_STDIO_SetOutputFunction(FILE* f, int (*outputFuncPointer)(int, FILE *)); /** * Link file input stream with input get character function * * Parameters: * - FILE* f: * Pointer to file stream * - int (*inputFuncPointer)(FILE *): * Pointer to function that will be used to input data from stream * * No return */ extern void TM_STDIO_SetInputFunction(FILE* f, int (*inputFuncPointer)(FILE *)); /** * Default output handler for standard output (stdout) * Needs to be implemented by user if you want to use printf function. * This function has __weak parameters to prevent link errors if it is not implemented by user * * Parameters: * - int c: * Character for output to the stream * - FILE* f: * Pointer to file stream * * Returns character value if write is OK * Returns -1 if you want to stop write at any time */ __weak int TM_STDIO_StdoutHandler(int c, FILE* f); /** * Default input handler for standard input (stdin) * Needs to be implemented by user if you want to get data from standard input. * This function has __weak parameter to prevent link errors if it is not implemented by user * * Parameters: * - FILE* f: * Pointer to file stream * * Returns character value if exists * Returns -1 if at the end of string or no data available */ __weak int TM_STDIO_StdinHandler(FILE* f); |
Example 1
In example below, data is going out on 3 different USART channels.
- USART1
- You can print to USART1 using printf
- USART2
- You can print to USART2 using fprintf with custom output channel
- USART6
- You can print to USART6 using fprintf with another custom output channel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
/** * Keil project for STDIO output * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_stdio.h" /* You can use only 1 thing for printf */ /* If you want more, then you can use fprintf */ /* For this you need FILE pointer for each */ /* Create global variables for fprintf function */ /* for USART2 and USART6 */ FILE USART2_Stream, USART6_Stream; /* Output stream for USART2 and USART3 function references */ int USART2_Stream_OutputFunction(int ch, FILE* f); int USART6_Stream_OutputFunction(int ch, FILE* f); int main(void) { /* Initialize system */ SystemInit(); /* Initialize USART1, TX = PB6, 115200 bauds */ TM_USART_Init(USART1, TM_USART_PinsPack_2, 115200); /* Initialize USART2, TX = PD5, 115200 bauds*/ TM_USART_Init(USART2, TM_USART_PinsPack_2, 115200); /* Initialize USART6, TX = PC6, 115200 bauds */ TM_USART_Init(USART6, TM_USART_PinsPack_1, 115200); /* Add output function for USART2_Stream */ TM_STDIO_SetOutputFunction(&USART2_Stream, USART2_Stream_OutputFunction); /* Add output function for USART6_Stream */ TM_STDIO_SetOutputFunction(&USART6_Stream, USART6_Stream_OutputFunction); /* Print something on USART1 */ printf("Hello USART1 user\n"); /* Print something on USART2 */ fprintf(&USART2_Stream, "Hello USART2 user\n"); /* Print something on USART6 */ fprintf(&USART6_Stream, "Hello USART6 user\n"); while (1) { } } /* Handle stdout actions */ int TM_STDIO_StdoutHandler(int ch, FILE* f) { /* Send data to USART1 */ TM_USART_Putc(USART1, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } /* USART2_Stream function that will send char by char to stream */ int USART2_Stream_OutputFunction(int ch, FILE* f) { /* Send char via USART2 */ TM_USART_Putc(USART2, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } int USART6_Stream_OutputFunction(int ch, FILE* f) { /* Send char via USART6 */ TM_USART_Putc(USART6, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } |
Example 2
In example below, data is going out on 3 different USART channels.
- USART1
- You can print to USART1 using printf
- If you send data to USART1, it will return the same data back
- USART2
- You can print to USART2 using fprintf with custom output channel
- If you send data to USART6, it will return the same data back
- USART6
- You can print to USART6 using fprintf with another custom output channel
- If you send data to USART16 it will return the same data back
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
/** * Keil project for STDIO output & input * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_stdio.h" #include "tm_stm32f4_delay.h" /* You can use only 1 thing for printf */ /* If you want more, then you can use fprintf */ /* For this you need FILE pointer for each */ /* Create global variables for fprintf function */ /* for USART2 and USART6 */ FILE USART2_Stream, USART6_Stream; /* Output stream for USART2 and USART6 function references */ int USART2_Stream_OutputFunction(int ch, FILE* f); int USART6_Stream_OutputFunction(int ch, FILE* f); /* Input stream for USART2 and USART6 function references */ int USART2_Stream_InputFunction(FILE* f); int USART6_Stream_InputFunction(FILE* f); int main(void) { char str[100]; /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initialize USART1, TX = PB6, RX = PB7, 115200 bauds */ TM_USART_Init(USART1, TM_USART_PinsPack_2, 115200); /* Initialize USART2, TX = PD5, RX = PD6, 115200 bauds*/ TM_USART_Init(USART2, TM_USART_PinsPack_2, 115200); /* Initialize USART6, TX = PC6, RX = PC7, 115200 bauds */ TM_USART_Init(USART6, TM_USART_PinsPack_1, 115200); /* Add output function for USART2_Stream */ TM_STDIO_SetOutputFunction(&USART2_Stream, USART2_Stream_OutputFunction); /* Add input function for USART2_Stream */ TM_STDIO_SetInputFunction(&USART2_Stream, USART2_Stream_InputFunction); /* Add output function for USART6_Stream */ TM_STDIO_SetOutputFunction(&USART6_Stream, USART6_Stream_OutputFunction); /* Add input function for USART6_Stream */ TM_STDIO_SetInputFunction(&USART6_Stream, USART6_Stream_InputFunction); /* Print something on USART1 */ printf("Hello USART1 user\n"); /* Print something on USART2 */ fprintf(&USART2_Stream, "Hello USART2 user\n"); /* Print something on USART6 */ fprintf(&USART6_Stream, "Hello USART6 user\n"); while (1) { /* If data are available on stream */ /* Check data on standard header, USART1 */ /* stdin or standard input */ if (fgets(str, 100, stdin) != NULL) { /* Send data back */ /* Statement below is the same as of use printf function */ fprintf(stdout, (char *)str); } /* Check data on USART2 stream */ if (fgets(str, 100, &USART2_Stream) != NULL) { /* Send data back */ /* Send data to USART2 stream */ fprintf(&USART2_Stream, (char *)str); } /* Check data on USART6 stream */ if (fgets(str, 100, &USART6_Stream) != NULL) { /* Send data back */ /* Send data to USART2 stream */ fprintf(&USART6_Stream, (char *)str); } /* Some delay */ /* If you call your gets too often, then it can happen that USART characters are not in buffer yet */ /* and function will not do as it should */ Delayms(1000); } } /* Handle stdout actions = default name and can not be changed */ int TM_STDIO_StdoutHandler(int ch, FILE* f) { /* Send data to USART1 */ TM_USART_Putc(USART1, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } /* Handle stdin actions */ int TM_STDIO_StdinHandler(FILE* f) { /* If any data at USART, return them */ /* Do your custom implementation here when string ends */ if (!TM_USART_BufferEmpty(USART1)) { return (int)TM_USART_Getc(USART1); } /* End of data, string is valid */ /* You have to send -1 at the end of string */ return -1; } /* USART2 output stream handler = custom function name, linked with USART2 stream in the beginning of main() function */ int USART2_Stream_OutputFunction(int ch, FILE* f) { /* Send char via USART2 */ TM_USART_Putc(USART2, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } /* Handle USART2 stream input = custom function name, linked with USART2 stream in the beginning of main() function */ int USART2_Stream_InputFunction(FILE* f) { /* If any data at USART, return them */ /* Do your custom implementation here when string ends */ if (!TM_USART_BufferEmpty(USART2)) { return (int)TM_USART_Getc(USART2); } /* End of data, string is valid */ /* You have to send -1 at the end of string */ return -1; } /* USART6 output stream handler = custom function name, linked with USART6 stream in the beginning of main() function */ int USART6_Stream_OutputFunction(int ch, FILE* f) { /* Send char via USART6 */ TM_USART_Putc(USART6, (char) ch); /* Return ch, it means OK */ return ch; /* If you want to return error, then you have to send EOF (-1) */ //return -1; } /* Handle USART6 stream input = custom function name, linked with USART6 stream in the beginning of main() function */ int USART6_Stream_InputFunction(FILE* f) { /* If any data at USART, return them */ /* Do your custom implementation here when string ends */ if (!TM_USART_BufferEmpty(USART6)) { return (int)TM_USART_Getc(USART6); } /* End of data, string is valid */ /* You have to send -1 at the end of string */ return -1; } |
Projects are available on Github, download library here.
Use printf and fprintf functions to output data to the stream
Recent comments