Действительно a зачем? sprintf это функция стандартной библиотеки языка С, которую по стандарту обязан реализовывать любой производитель компилятора языка С. Если вы пишите под десктоп например консольную аппликацию то все хорошо. Но если вы пишите программу под контроллер и используете операционную систему , то может быть краш. Дело в том что стандартная реализация функций sprintf, printf, sscanf и некоторых других содержит статические переменные,то есть такие функции не являются потоково безопасный ( thread safe). Например вы хотите посылать отладочную информацию из контроллера через последовательный интерфейс на терминал. В случае если вы используете операционную систему и функция sprintf вызывается в разных контекстах, то правильнее завернуть ее в семафор или организовать программу так что бы вызов функции был только из одной задачи. Или переписать ее. Есть еще один аспект — это размер функции занимаемый во программном флэше. А это иногда имеет значение особенно если программный флэш ,например,32кб. Использование функции sprintf в IAR занимает около 3.1кб правда без оптимизации. Кроме того редко кто использует ключи: %e, %g,%a.
Поискав в сети я не нашел ничего то бы мне понравилось поэтому возникла идея написать свой вариант sprintf. Точнее snprintf ,так как это более безопасный вариант чем функция sprintf.
Я ограничил количество спецификаторов выбрав: %s,%d,%x,%f. Для теста был создан проект в IAR для контроллера ST32L152.В результате получилась функция my_sprintf, которая имеет размер 824 байта.То есть примерно в 3.8 раз меньше чем оригинал.
int my_sprintf(char* buff, int size, const char *str, ...)
{
char* p;
bool flag = false;
int i = 0;
char temp[INT_STR_SIZE*2];
va_list list;
va_start (list, str);
int num1, num2;
unsigned char len;
//void** argAddr = (void**)((unsigned int)&str + sizeof(unsigned int));
while (*str != 0 && i < size)
{
if (*str++ == '%')
{
if (*str == '0')
flag = true;
num1 = GetNumBefore((char**)&str);
if (*str == '.')
str++;
num2 = GetNumBefore((char**)&str);
switch (*str++)
{
case 'X':
case 'x':
//val = *(unsigned int*)argAddr;
//p = convert(temp, sizeof(temp) , *(unsigned int*)argAddr, 16);
p = convert(temp, sizeof(temp), va_arg(list, unsigned int), 16);
len = strlen(p);
if (!flag)
{
num1 -= num2;
if (num1 < 0) num1 = 0;
memset(&buff[i], ' ', num1);
}
else
{
flag = false;
num2 = num1;
num1 = 0;
}
num2 -= len;
if (num2 < 0) num2 = 0;
memset(&buff[i + num1], '0', num2);
strcat(&buff[i + num1], p);
i += (num1 + num2 +len);
//i = strlen(buff);
//argAddr++;
break;
case 'D':
case 'd':
//p = itoa(temp, sizeof(temp), *(unsigned int*)argAddr);
p = itoa(temp, sizeof(temp), va_arg(list, unsigned int));
len = strlen(p);
num1 -= len;
if (num1 < 0) num1 = 0;
memset(&buff[i], ' ', num1);
strcat(&buff[i + num1],p);
i += (num1 + len);
//i = strlen(buff);
//argAddr++;
break;
case 'S':
case 's':
//p = (char*)*argAddr;
p = va_arg(list,char*);
len = strlen(p);
num1 -= len;
if (num1 < 0) num1 = 0;
memset(&buff[i], ' ', num1);
strncat(&buff[i + num1], p,len - num2);
i += (num1 + len - num2);
//i = strlen(buff);
//argAddr++;
break;
case 'F':
case 'f':
if (num2 < 0) num2 = 0;
//float v = *(double*)argAddr;
float v = va_arg(list, double);
p = ftoa(temp, sizeof(temp), v, num2);
len = strlen(p);
num1 -= len;
if (num1 < 0) num1 = 0;
memset(&buff[i], ' ', num1);
strcat(&buff[i + num1], p);
i += (num1 + len);
//i = strlen(buff);
//argAddr++;
//argAddr++;
break;
}
}
else
{
buff[i++] = *(str - 1);
buff[i] = 0;
}
}
return i-1;
}
Преобразованные числа в hex представление или в другие счисления в зависимости от базы был найдено на просторах интернета и практически полностью взят в минимальными изменениями. Тут трудно что-то изобрести.
char* convert(char* buff, int size, unsigned int num, int base)
{
char *p;
if (size >= INT_STR_SIZE)
{
p = &buff[size - 1];
*p = '\0';
while (num != 0)
{
*--p = "0123456789abcdef"[num%base];
num /= base;
}
return p;
}
else
return 0;
}
Функция itoa преобразования знакового целого числа строку. Ее нет в стандартной библиотеке, но ее реализация то же более или менее известно.
char* itoa(char* buff, int size, int val)
{
char *p;
if (val < 0)
{
val = val*(-1);
p = convert(buff, size, val, 10);
*--p = '-';
}
else
p = convert(buff, size, val, 10);
return p;
}
Еще одна вспомогательная функция которая находит строку до символа ‘.’ или до конца строки и возвращает число. И кроме этого возвращает указатель на текущее положение в исходной строке через параметр
int GetNumBefore(char** str)
{
char ch;
char temp[10];
char *p = temp;
char *q = *str;
while ((ch = *q) != '.' && ch < ':')
{
*p++ = ch;
q++;
}
*str = q;
*p = 0;
return atoi(temp);
}
GetFloatPoint это вспомогательная функция которая преобразует дробную часть в целое.
int GetFloatPoint(float val, int digits)
{
int num = (int)val;
if (num < 0) { num *= -1; val = val *(-1); } while (digits-- >0)
{
num = num * 10;
val = val * 10;
}
return val - num;
}
И наконец функция ftoa , которая кстати то же нет в стандартной библиотеке ,преобразует float в строку:
char* ftoa(char* buff, int size, float val, int digits)
{
char* p;
char* q;
p = itoa(buff, size, GetFloatPoint(val, digits));
*--p = '.';
q = itoa(buff, p - &buff[0], (int)val);
strcat(q, p);
return q;
}
При небходимости можно легко добавить другие ключи .
Проект на Visual studio 2013 выложен в Github : https://github.com/michpo/my_sprintf
Отличная статья.
//нуда с stm32f030 не как stm32f103 и т.п.
///вод код такой наклацал
// еси целую в аски тодоц , то и всё туда же возможно .
///////////////////////////////////////////////////////
uint8_t tetrada_to_str_h( uint8_t num)
{ uint8_t w=0; ///перекодили тетраду!!!!
if( num==1 )w=49 ; if( num==2 ) w=50; if( num==3 ) w=51;
if( num==4 ) w=52; if( num==5 ) w=53; if( num==6 ) w=54;
if( num==7 ) w=55; if( num==8 ) w=56; if( num==9 ) w=57;
if( num==10) w=65; if( num==11) w=66; if( num==12) w=67;
if( num==13) w=68; if( num==14) w=69; if( num==15) w=70;
if( num==0 ) w=48; return w;
}
///////////////////////////////////////////////////////
uint16_t uint_to_str_dec( uint16_t num, uint8_t* yy)
{ uint8_t dtzn=0, tzn=0,hnd=0,dec=0,edn=0;
uint16_t nm=num;
while(nm>=10000) {nm=nm-10000; dtzn++; if(dtzn>6) break;} //счет десятков тысяч
while(nm>=1000 ) {nm=nm-1000; tzn++; if( tzn>9) break;} //счет тысяч
while(nm>=100 ) {nm=nm-100; hnd++; if( hnd>9) break;} //счет сотен
while(nm>=10 ) {nm=nm-10; dec++; if( dec>9) break;} //счет десятков
edn=nm;
*yy= tetrada_to_str_h( dtzn); yy++; //в память заносим кол десятков тысяч
*yy=tetrada_to_str_h( tzn); yy++; //в память заносим кол тысяч
*yy=tetrada_to_str_h( hnd); yy++; //в память заносим кол сотен
*yy=tetrada_to_str_h( dec); yy++; //в память заносим кол десятков
*yy=tetrada_to_str_h( edn); yy++; //в память заносим единицы
*yy=nm; yy++;yy++;
*yy=num;
return nm;
}
uint8_t* uint8_t_to_str_h( uint8_t* num, uint8_t* yy)
{ // —-перекодили байт——///
*yy=tetrada_to_str_h(*num/16); yy++; //- сдвинем на 4 бит вправо
*yy=tetrada_to_str_h(*num & 15); yy—;//- очистит на 4 бит слевао
return yy;
}
/************************in stm32f030f….
//—————
int main(void)
{ HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
uint8_t cc=0;
uint8_t* jj=0; uint8_t* ww;
uint8_t gg[]={0.0,0,0},
ee[]={0,0,0,0,0,0,0,0,0};
while (1)
{ HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc,100);
i = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc); jj=ⅇ
i=65535; //—это подставил для проверки алгоритма
uint_to_str_dec(i,jj);
HAL_UART_Transmit_IT(&huart1, jj,5);
// i=0; i=i+cc;
// i=i*16*16; //сдивнем на старший байт
// i=i+cc; //занесем в младший байт;
// ww=&i; //—укажем ссылку на два байта
// jj=≫ //—укажем ссылку на 4е байта
// uint8_t_to_str_h(*ww,jj); jj=jj+2; ww++;
// uint8_t_to_str_h(*ww,jj); cc—;
// if (cc<1) cc=255;
// HAL_UART_Transmit_IT(&huart1, &gg,4);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
if( HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET ) {
HAL_Delay(500); // на выводе PB12 высокий уровень, кнопка отжата
}
else { HAL_Delay(1000); // на выводе PB12 низкий уровень, кнопка нажата
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
if( HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET ) {
HAL_Delay(500); // на выводе PB12 высокий уровень, кнопка отжата
}
else {HAL_Delay(1000); // на выводе PB12 низкий уровень, кнопка нажата
}
HAL_UART_AbortTransmit(&huart1);
}
}