Posielanie desatinných čísel do terminalu cez USART

Dobrý deň, rád by som sa spýtal či má niekto skúsenosti s posielaním desatinných čísel do terminálu pomocou USARTu? Niekde som čítal, že by to mala umožňovať funkcia ftoa(), ale mám pocit, že AVR Studio 5 to nepodporuje.

Neznám AVR studio5 , ale funkce ftoa je součástí standartní C knihovny stdlib, kterou jistě má i AVR studio5 - jen je potřeba ji includovat - ale je taky možné, že se jak knihovna , tak ta funkce jmenuje trochu jinak, ale určitě hodně podobně.

Jinak nechceš - li posílat data jako ASCII znaky - řešením by bylo rozkouskovat proměnnou float na jednotlivé byty - třeba pomocí unionu:

union { struct { unsigned char byte1 unsigned char byte2 unsigned char byte3 unsigned char byte4 } float desetinne_cislo }u

Dobrý deň lou, ďakujem veľmi pekne za rožšírenie obzorov. Uvidím ešte pre ktorú voľbu sa rozhodnem, aby sa mi potom lepšie spracovávali poslané data, ale veľmi ste mi pomohli.

Vzhledem k tomu, že mají jít data do terminálu, tak mu asi nebude poslání čísla ve float moc platné. Spíš bych čekal, že bude chtít poslat float převedený na text aby si ho mohl přečíst (jako člověk, který nezná uspořádání bitů ve 32b float). Tam tedy ona zmíněná funkce nebo sprintf(). U sprintf by však patrně bylo nutné float povolit pomocí parametrů překladu.

Avr-gcc má v knihovně <stdlib.h> funkci pro převod desetinného čísla na string

[code]char* dtostrf(double val, signed char width, unsigned char prec, char* s)

val - převáděné číslo
width - šířka (přidá úvodní mezery pro zarovnání)
prec - počet desetinných míst
s - cílový string
[/code]
Příklad

[code]#include <avr/io.h>
#include <stdlib.h>

double cislo = 880.0 / 280.0;
char tempstr[16];

int main(void)
{
while(1)
{
dtostrf(cislo, 8, 5, tempstr);
uart_puts(tempstr);
}
}
[/code]

Uvědomil jsem si, že ten příklad s unionem nebyl nejšikovnější - lepší by asi bylo:

union
{
float result;
char desetinne_cislo[4];                              
}u;   
   
u.result = 35,254*7,68;
usart_puts(u.desetinne_cislo);              

Jinak s desetinnými čísly se dají dělat různá kouzla.
Třeba čtyřbytový typ float,nebo double zredukovat na dvoubytový (unsigned)int vynásobením desetinného čísla x10, nebo 100, nebo 1000
podle počtu požadovaných míst za čárkou a oříznutím zbylých desetinných míst přetypováním na unsigned int:

unsigned int napeti;
float x=4,5985471225;    

napeti=(unsigned int)(x*1000);  // v proměnné cele_cislo bude hodnota: 4598

u většiny měřených veličin, jako je napětí, proud, teplota atd… si totiž běžně vystačíme s rozsahem 0 - 6553,5
(0 - 655,35 0 - 65,535 0 - 6,5535 ) případně -327,68 až +327,67 u typu signed int

při zpracování - třeba v zařízení na druhém konci UARTu pak jenom číslo zpátky vydělíme příslušným dělitelem

float y=napeti/1000; //v proměnné y bude hodnota: 4,598

nebo zformátovat řetězec

 (s)printf("U=%i,%3uV",napeti/1000,napeti%1000);     

Možná si řeknete, na co šetřit dva bajty, když to možná budou jediné dva ušetřené v celém programu. a po překladu stejně zůstane třeba půlka paměti nevyužitá.
Problém je v tom, že operace s float či double jsou časově několikanásobně náročnější než s int. Ale hlavně tu redukci oceníte, když nějaké hodnoty - třeba naměřenou teplotu budete periodicky ukládat do eepromky, která zas až tak velká nebývá - tímto způsobem se do ní vejde dvakrát tolik naměřených hodnot.

Co se týká samotného posílání dat přes UART, pak záleží na tom, co se s nima bude dít na straně přijímače.
Pokud se budou jen někde zobrazovat, pak je účelné používat výše zmiňované funkce printf, sprintf, itoa,atoi, ftoa,atof a jiné pro
převod dat na ASCII znaky. Výhoda tohoto způsobu je v možnosti využití řídících znaků (\r ,\n) a jednoduchého použití funkce printf
kterou snad každý compiller používá ve spojení s UARTem. Další výhoda je v tom, že posílané pakety můžou být různě dlouhé.

Pokud se má ale s nimi dál pracovat, nebo je ukládat, pak je zbytečné je ve vysílači konvertovat na ASCII a v přijímači zpátky . V tomto případě
používám systém, kdy data určená k posílání přes UART mám sdružená v unionu, který je totožný s přesně stejným unionem v zařízení na druhém konci linky. Výhoda je v tom, že můžu posílat mix různých datových typů (char, int,float, pole,string, strukturu) a na straně příjmu s nimi pracovat přímo bez nutnosti nějakých úprav nebo převodů.

Jako příklad (v tomto stavu ilustrační, nefukční - volá nedefinované funkce) kód pro imaginární voltmetr posílající čas a hodnoty naměřeného napětí:
koeficient je vypočítaný pro Uref=5V (5000mV) a 10b A/D - 5/1024*1000=4.8828125

[code]
typedef struct
{
char sec; // 1 byte
char min; // 1 byte
char hod; // 1 byte
int napeti; // 2 byte
const float koeficient=4.8828125; // 4 byte
char text[10]; // 10 byte
}structure_type; // 19 byte celkem ;
union
{
structure_type s;
char data[19];
}u;

void main()
{
mcu_init(); //nejaka inicializace portů, timeru atd…
USART_init(); // inicializace USARTu
I2C_init(); // inicializace linky pro komunikaci s RTC
RTC_init(); // inicializace RTC obvodu
ADC_init(); // inicializace A/D převodníku

while(1)
{
if(uplynul_cas) //kdyz se v preruseni od nejakeho casovace nastavi priznak
{
u.s.napeti=(int)(ad_prevod()*koeficient); //cteni ADC a vypocet napeti + pretyp na cele cislo
u.s.sec=read_rtc(second); //precte sekundy z RTC
u.s.min=read_rtc(minute); //precte minuty z RTC
u.s.hod=read_rtc(hour); //precte hodiny z RTC
strcpyf(s.u.text," voltmetr “); //ulozi text do pole “text”
//sprintf(s.u.text,” voltmetr "); //udela to same, ale pro tento ucel je zbytecna a proti vyse uvedene casove i pametove narocnejsi
usart_puts(u.data);
}
}
}

[/code][/code]