/** \file printf.c
 * Simplified printf() and sprintf() implementation - prints formatted string to
 * USART (or whereever). Most common % specifiers are supported. It costs you about
 * 3k FLASH memory - without floating point support it uses only 1k ROM!
 * \author Freddie Chopin, Martin Thomas, Marten Petschke and many others
 * \date 16.2.2012
 */

/******************************************************************************
* chip: STM32F10x
* compiler: arm-none-eabi-gcc 4.6.0
*
* global functions:
* 	int printf_(const char *format, ...)
* 	int sprintf_(char *buffer, const char *format, ...)
*
* STM32 specific functions:
*         init_UART1(void)
*	void putc_UART1 (char);                // blocking put char, used by printf()
*
* local functions:
* 	int putc_strg(int character, printf_file_t *stream)
* 	int vfprintf_(printf_file_t *stream, const char *format, va_list arg)
* 	void long_itoa (long val, int radix, int len, vfprintf_stream *stream)
*
******************************************************************************/

/*
+=============================================================================+
| includes
+=============================================================================+
*/

#include <stdarg.h>     // (...) parameter handling
#include "config.h"     // select controller type, frequency plan

#include "stm32f10x.h"	// only this STM headerfile is used

/*
+=============================================================================+
| options
+=============================================================================+
*/
//#define INCLUDE_FLOAT  // this enables float in printf() and costs you about 2kByte ROM
#define UART1_BAUDRATE  		115200
/*
+=============================================================================+
| global declarations
+=============================================================================+
*/

int printf_(const char *format, ...);
int sprintf_(char *buffer, const char *format, ...);

/*
+=============================================================================+
| local declarations
+=============================================================================+
*/

void init_UART1(void);          // STM32 specific stuff

void putc_UART1 (char);         // blocking put char; used by printf_()
void putc_strg(char);          // the put() function for sprintf()
char *SPRINTF_buffer;          //

static int vfprintf_(void (*) (char), const char *format, va_list arg); //generic print
void long_itoa (long, int, int, void (*) (char)); //heavily used by printf_()

/*
+=============================================================================+
| sample main()  file
+=============================================================================+
*/
int main(void){
   pll_start();
	init_UART1();
   printf_("\nSTM32_Start\n");
	int cnt;
   for (cnt = 0; cnt < 12; cnt++)	// test
      printf_("test unsigned negativ:      |%u|  \n", (unsigned int)(-cnt));

   char s[30];
   sprintf_(s,"Hallo!\n"); printf_("%s",s);

#ifdef INCLUDE_FLOAT
   float f=3.14f;

   printf_("test float %3f,  negativ:  %f  \n", f, -f);
#endif

   return 1;
}

/*
+=============================================================================+
| STM32 register definition only here and in interrupt handler
+=============================================================================+
*/
void init_UART1(void){

	RCC->APB2ENR |= ( RCC_APB2ENR_IOPAEN |RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN);  //Clock
	AFIO->MAPR &= ~(AFIO_MAPR_USART1_REMAP);		//UART1 at the original pins
	GPIOA->CRH  = 0x0090;
   USART1->BRR  = (APB2CLK + UART1_BAUDRATE/2)/UART1_BAUDRATE;
	             //0x008B;  Baud=FREQUENCY/(16*8,69)=115107;	// BRR simplified thanks to Uwe Hermann
   USART1->CR1 |= (USART_CR1_RE | USART_CR1_TE | USART_CR1_UE);  // RX, TX enable
}

void putc_UART1 (char c)
{
	if (c == '\n') {
		while ((USART1->SR & USART_SR_TXE) == 0);  //blocks until previous byte was sent
		USART1->DR ='\r';
	}
	while ((USART1->SR & USART_SR_TXE) == 0);  //blocks until previous byte was sent
	USART1->DR = c;
}

/*
+=============================================================================+
| end of controller specific stuff  - no further controller dependent stuff below
+=============================================================================+
*/

/*
+=============================================================================+
| global functions
+=============================================================================+
*/
int printf_(const char *format, ...)
{
	va_list arg;

	va_start(arg, format);
	vfprintf_((&putc_UART1), format, arg);
	va_end(arg);

	return 0;
}

int sprintf_(char *buffer, const char *format, ...)
{
	va_list arg;

	SPRINTF_buffer=buffer;	 //Pointer auf einen String in Speicherzelle abspeichern

	va_start(arg, format);
	vfprintf_((&putc_strg), format, arg);
	va_end(arg);

	*SPRINTF_buffer ='\0';             // append end of string

	return 0;
}


/*
+=============================================================================+
| local functions
+=============================================================================+
*/
// putc_strg() is the putc()function for sprintf_()
void putc_strg(char character)
{
	*SPRINTF_buffer = (char)character;	// just add the character to buffer
	 SPRINTF_buffer++;

}

/*--------------------------------------------------------------------------------+
 * vfprintf_()
 * Prints a string to stream. Supports %s, %c, %d, %ld %ul %02d %i %x  %lud  and %%
 *     - partly supported: long long, float (%ll %f, %F, %2.2f)
 *     - not supported: double float and exponent (%e %g %p %o \t)
 *--------------------------------------------------------------------------------+
*/
static int vfprintf_(void (*putc)(char), const char* str,  va_list arp)
{
	int d, r, w, s, l;  //d=char, r = radix, w = width, s=zeros, l=long
	char *c;            // for the while loop only

#ifdef INCLUDE_FLOAT
	float f;
	long int m, mv, p, w2;
#endif

	while ((d = *str++) != 0) {
		if (d != '%') {
			(*putc)(d);
			continue;
		}
		d = *str++;
		w = r = s = l = 0;
		if (d == '%') {
			(*putc)(d);
			d = *str++;
		}
		if (d == '0') {
			d = *str++; s = 1;  //padd with zeros
		}
		while ((d >= '0')&&(d <= '9')) {
			w += w * 10 + (d - '0');
			d = *str++;
		}
		if (s) w = -w;      //padd with zeros if negative

	#ifdef INCLUDE_FLOAT
		w2 = 2;            //default decimal places=2
		if (d == '.'){
			d = *str++; w2 = 0; }
		while ((d >= '0')&&(d <= '9')) {
			w2 += w2 * 10 + (d - '0');
			d = *str++;
		}
	#endif

		if (d == 's') {
			c = va_arg(arp, char*);
			while (*c)
				(*putc)(*(c++));
			continue;
		}
		if (d == 'c') {
			(*putc)((char)va_arg(arp, int));
			continue;
		}
		if (d == 'u') {     // %ul
			r = 10;
			d = *str++;
		}
		if (d == 'l') {     // long =32bit
			l = 1;
			if (r==0) r = -10;
			d = *str++;
		}
		if (d =='\0') break;    //avoid crashing if format string is buggy
		if (d == 'u') r = 10;//     %lu,    %llu
		else if (d == 'd' || d == 'i') {if (r==0) r = -10;}  //can be 16 or 32bit int
		else if (d == 'X' || d == 'x') r = 16;               // 'x' added by mthomas
		else if (d == 'b') r = 2;
   #ifdef INCLUDE_FLOAT
		else if (d == 'f' || d == 'F') {
			f=va_arg(arp, double);
			if (f >= 0.0) {
				r = 10;
            mv = f;
            m = mv;
			}
			else {
				r = -10;
            mv = f;
				f = -f;
            m = f;              // f and m are always positive
			}
			long_itoa(mv, r, w, (putc));
			if (w2!=0) {
            putc('.');
            f=f-m;
            w=-w2; p=1;
            while (w2--) p = p*10;
            m=f*p;
            long_itoa(m, 10, w, (putc));
			}
			l=3; //do not continue with long
		}
	#endif
		else str--;                                         // normal character

		if (r==0) continue;  //
		if (l==0) {
			if (r > 0){      //unsigned
				long_itoa((unsigned long)va_arg(arp, int), r, w, (putc)); //needed for 16bit int, no harm to 32bit int
			}
			else            //signed
				long_itoa((long)va_arg(arp, int), r, w, (putc));
		} else if (l==1){  // long =32bit
				long_itoa((long)va_arg(arp, long), r, w, (putc));        //no matter if signed or unsigned
		}
	}

	return 0;
}


void long_itoa (long val, int radix, int len, void (*putc) (char))
{
	char c, sgn = 0, pad = ' ';
	char s[20];
	int  i = 0;


	if (radix < 0) {
		radix = -radix;
		if (val < 0) {
			val = -val;
			sgn = '-';
		}
	}
	if (len < 0) {
		len = -len;
		pad = '0';
	}
	if (len > 20) return;
	do {
		c = (char)((unsigned long)val % radix); //cast!
		if (c >= 10) c += ('A'-10); //ABCDEF
		else c += '0';            //0123456789
		s[i++] = c;
		val = (unsigned long)val /radix; //cast!
	} while (val);

	if ((sgn!=0) && (pad!='0')) s[i++] = sgn;
	while (i < len)
		s[i++] = pad;
	if ((sgn!=0) && (pad=='0')) s[i++] = sgn;

	do
		(*putc)(s[--i]);
	while (i);
}


/******************************************************************************
* END OF FILE
******************************************************************************/
