The Code Sourcery Lite tool chain is provided with the newlib C Library from Redhat. Because this is an embedded toolchain some stub functions known as System Functions must be provided by the embedded system that would normally be provided by a host operating system.
The is shown by the fact that if you try to compile a C program that uses printf you will see the following error message:
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text+0x12): undefined reference to `_sbrk'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text+0x16): undefined reference to `_write'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-closer.o): In function `_close_r':
closer.c:(.text+0x12): undefined reference to `_close'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-lseekr.o): In function `_lseek_r':
lseekr.c:(.text+0x16): undefined reference to `_lseek'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-readr.o): In function `_read_r':
readr.c:(.text+0x16): undefined reference to `_read'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-fstatr.o): In function `_fstat_r':
fstatr.c:(.text+0x14): undefined reference to `_fstat'
C:\Program Files (x86)\CodeSourcery\Sourcery G++ Lite\arm-none-eabi\lib\thumb2\libc.a(lib_a-isattyr.o): In function `_isatty_r':
isattyr.c:(.text+0x12): undefined reference to `_isatty'
collect2: ld returned 1 exit status
Build error occurred, build is stopped
As you can see we need to provide the stub functions above for our program to compile and work. There are in fact a few more stub functions that we will need to provided for full C support but most can be a trivial implementation in an embedded system.
The main functions we need to concentrate on are _sbrk which is used by malloc _write which is used for all IO output and _readwhich is used for all IO input, all the other functions can simply return an error.
The newlib system call’s we need to provide are documented here
Place the file below in your startup_src directory which was created earlier to provide basic newlib IO support.
/*
* newlib_stubs.c
*
* Created on: 2 Nov 2010
* Author: nanoage.co.uk
*/
#include
#include
#include
#include
#include "stm32f10x_usart.h"
#ifndef STDOUT_USART
#define STDOUT_USART 2
#endif
#ifndef STDERR_USART
#define STDERR_USART 2
#endif
#ifndef STDIN_USART
#define STDIN_USART 2
#endif
#undef errno
extern int errno;
/*
environ
A pointer to a list of environment variables and their values.
For a minimal environment, this empty list is adequate:
*/
char *__env[1] = { 0 };
char **environ = __env;
int _write(int file, char *ptr, int len);
void _exit(int status) {
_write(1, "exit", 4);
while (1) {
;
}
}
int _close(int file) {
return -1;
}
/*
execve
Transfer control to a new process. Minimal implementation (for a system without processes):
*/
int _execve(char *name, char **argv, char **env) {
errno = ENOMEM;
return -1;
}
/*
fork
Create a new process. Minimal implementation (for a system without processes):
*/
int _fork() {
errno = EAGAIN;
return -1;
}
/*
fstat
Status of an open file. For consistency with other minimal implementations in these examples,
all files are regarded as character special devices.
The `sys/stat.h' header file required is distributed in the `include' subdirectory for this C library.
*/
int _fstat(int file, struct stat *st) {
st->st_mode = S_IFCHR;
return 0;
}
/*
getpid
Process-ID; this is sometimes used to generate strings unlikely to conflict with other processes. Minimal implementation, for a system without processes:
*/
int _getpid() {
return 1;
}
/*
isatty
Query whether output stream is a terminal. For consistency with the other minimal implementations,
*/
int _isatty(int file) {
switch (file){
case STDOUT_FILENO:
case STDERR_FILENO:
case STDIN_FILENO:
return 1;
default:
//errno = ENOTTY;
errno = EBADF;
return 0;
}
}
/*
kill
Send a signal. Minimal implementation:
*/
int _kill(int pid, int sig) {
errno = EINVAL;
return (-1);
}
/*
link
Establish a new name for an existing file. Minimal implementation:
*/
int _link(char *old, char *new) {
errno = EMLINK;
return -1;
}
/*
lseek
Set position in a file. Minimal implementation:
*/
int _lseek(int file, int ptr, int dir) {
return 0;
}
/*
sbrk
Increase program data space.
Malloc and related functions depend on this
*/
caddr_t _sbrk(int incr) {
extern char _ebss; // Defined by the linker
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0) {
heap_end = &_ebss;
}
prev_heap_end = heap_end;
char * stack = (char*) __get_MSP();
if (heap_end + incr > stack)
{
_write (STDERR_FILENO, "Heap and stack collision\n", 25);
errno = ENOMEM;
return (caddr_t) -1;
//abort ();
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
/*
read
Read a character to a file. `libc' subroutines will use this system routine for input from all files, including stdin
Returns -1 on error or blocks until the number of characters have been read.
*/
int _read(int file, char *ptr, int len) {
int n;
int num = 0;
switch (file) {
case STDIN_FILENO:
for (n = 0; n < len; n++) {
#if STDIN_USART == 1
while ((USART1->SR & USART_FLAG_RXNE) == (uint16_t)RESET) {}
char c = (char)(USART1->DR & (uint16_t)0x01FF);
#elif STDIN_USART == 2
while ((USART2->SR & USART_FLAG_RXNE) == (uint16_t) RESET) {}
char c = (char) (USART2->DR & (uint16_t) 0x01FF);
#elif STDIN_USART == 3
while ((USART3->SR & USART_FLAG_RXNE) == (uint16_t)RESET) {}
char c = (char)(USART3->DR & (uint16_t)0x01FF);
#endif
*ptr++ = c;
num++;
}
break;
default:
errno = EBADF;
return -1;
}
return num;
}
/*
stat
Status of a file (by name). Minimal implementation:
int _EXFUN(stat,( const char *__path, struct stat *__sbuf ));
*/
int _stat(const char *filepath, struct stat *st) {
st->st_mode = S_IFCHR;
return 0;
}
/*
times
Timing information for current process. Minimal implementation:
*/
clock_t _times(struct tms *buf) {
return -1;
}
/*
unlink
Remove a file's directory entry. Minimal implementation:
*/
int _unlink(char *name) {
errno = ENOENT;
return -1;
}
/*
wait
Wait for a child process. Minimal implementation:
*/
int _wait(int *status) {
errno = ECHILD;
return -1;
}
/*
write
Write a character to a file. `libc' subroutines will use this system routine for output to all files, including stdout
Returns -1 on error or number of bytes sent
*/
int _write(int file, char *ptr, int len) {
int n;
switch (file) {
case STDOUT_FILENO: /*stdout*/
for (n = 0; n < len; n++) {
#if STDOUT_USART == 1
while ((USART1->SR & USART_FLAG_TC) == (uint16_t)RESET) {}
USART1->DR = (*ptr++ & (uint16_t)0x01FF);
#elif STDOUT_USART == 2
while ((USART2->SR & USART_FLAG_TC) == (uint16_t) RESET) {
}
USART2->DR = (*ptr++ & (uint16_t) 0x01FF);
#elif STDOUT_USART == 3
while ((USART3->SR & USART_FLAG_TC) == (uint16_t)RESET) {}
USART3->DR = (*ptr++ & (uint16_t)0x01FF);
#endif
}
break;
case STDERR_FILENO: /* stderr */
for (n = 0; n < len; n++) {
#if STDERR_USART == 1
while ((USART1->SR & USART_FLAG_TC) == (uint16_t)RESET) {}
USART1->DR = (*ptr++ & (uint16_t)0x01FF);
#elif STDERR_USART == 2
while ((USART2->SR & USART_FLAG_TC) == (uint16_t) RESET) {
}
USART2->DR = (*ptr++ & (uint16_t) 0x01FF);
#elif STDERR_USART == 3
while ((USART3->SR & USART_FLAG_TC) == (uint16_t)RESET) {}
USART3->DR = (*ptr++ & (uint16_t)0x01FF);
#endif
}
break;
default:
errno = EBADF;
return -1;
}
return len;
}
The above assumes that USART2 will be used for stdout / stderr and stdin however you will need to initialise usart2 first for any output to occur.
If you wish to use another usart then you can set the defines for
STDOUT_USART
STDERR_USART
STDIN_USART
to the USART number you want in the Cross GCC’s defined symbols section (-D) i.e
STDOUT_USART=1
to change to usart1
Below is a simple example which will initialise USART2 to 115200 baud, 8bits , 1 stop bit , parity-none.
/**
Main file for use with newlib
main.c
**/
#include "stm32f10x.h"
#include
void usart2_init();
int main(void)
{
//by default stdin/stdout are on usart2
usart2_init();
// turn off buffers, so IO occurs immediately
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
iprintf("Greetings Earthlings");
while (1) {}
}
void usart2_init(){
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* Configure USART Tx as push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART configuration */
USART_Init(USART2, &USART_InitStructure);
/* Enable USART */
USART_Cmd(USART2, ENABLE);
}
Note that you will probably want to turn off buffering on stdin otherwise you will have to type 1024 characters before they start flooding back to your program. Similar for printf either turn off buffering or use fflush to flush the output when needed, note that a newline seems to flush the output as well.
** Check you linker script has a .ARM.exidx section otherwise you will get random crashes, the original linker script from part one omitted this and has since been updated **