[C] code geeft integers en doubles niet goed weer

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Vimbeor
  • Registratie: Augustus 2001
  • Laatst online: 21-08 08:50
Hallo mensen,

Ik heb een stukje code in C wat gegevens via UART een microcontroller uit gaat sturen, alleen worden de doubles en integers niet goed weergegeven. Uiteindelijk wil ik een double met één getal voor, en twee getallen na de komma (bijvoorbeeld 4,32) naar een andere microcontroller sturen, maar het weergeven van een simpel getal zonder komma's lukt al niet. Tot en met 9 gaat het goed, daarna werkt ie alle tekens van het keyboard af. Dit is bij een double en integer allebei hetzelfde. Hoe komt dit?

Hier mijn code:

#include <avr/io.h>
#include <util/delay.h>

#define BAUD 38400 // F_CPU is 8000000 Hz
#define UBRR_VALUE ( ((F_CPU) + 8UL*(BAUD)) / (16UL*(BAUD)) - 1UL )

void uart_init(unsigned int ubrr)
{
UBRRL = ubrr;
UBRRH = (ubrr >> 8);
UCSRC = _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0);
UCSRB = _BV(TXEN);
}

int main(void)
{
double i;

uart_init(UBRR_VALUE);

while (1) {
for (i=0; i<=100; i++) {
while ( !(UCSRA & _BV(UDRE)) ) {};
UDR = i + '0';
}
_delay_ms(500);
}
}

Hier is wat in de Terminal wordt weergegeven:
Afbeeldingslocatie: http://farm4.static.flickr.com/3621/3632812972_a361871a4f_o.jpg

Wie kan mij helpen? Alvast bedankt!

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Duh. Wat denk je zelf dat deze code doet?
C:
1
2
3
for (i=0; i<=100; i++) {
  UDR = i + '0';
}

(Zet je code trouwens tussen [code]...[/code] tags voor verbeterde leesbaarheid)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zjosh
  • Registratie: November 2004
  • Laatst online: 18-09 13:28
Ik heb niet echt ervaring met Atmel, maar bij PIC kan je met de standaard libraries includen printf(); gebruiken. Wat je nu doet is de waarden direct op de uart zetten. Dit werkt niet omdat hij bijvoorbeeld 35 gaat interpreteren als 35 + '0' wat uitkomt op 1 of andere letter.

Kijk of je iets kan vinden in de trand van printf, of een andere functie waar je data geformateerd mee op de uart kan zetten.

Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Je moet de getallen eerst omzetten in een string, voor je ze op deze manier kan uitsturen. Normaal gesproken gebruik je hier itoa() of iets dergelijks voor:

code:
1
2
3
4
5
6
7
8
9
10
11
    for (i=0; i<=100; i++) 
    {
        char buffer[ 8 ], *p;
        itoa( i, buffer, 10 );
        for( p=buffer; 0 != *p; p++ )
        {
            while ( !(UCSRA & _BV(UDRE)) ) {};
            UDR = *p;
        }
    }
}


De floating point versie:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    for (i=0; i<=100; i++) 
    {
        char buffer[ 16 ], *p;
        int len;
        itoa( i * 100, buffer, 10 );
        len = strlen( buffer );
        /* Schuif laatste 2 cijfer + sluitnul op */
        memmove( &buffer[ len - 1 ], &buffer[ len - 2 ], 3 );
        /* komma invoegen */
        buffer[ len - 2 ] = ',';
        for( p=buffer; 0 != *p; p++ )
        {
            while ( !(UCSRA & _BV(UDRE)) ) {};
            UDR = *p;
        }
    }

Als je kunt kun je floatingpoints trouwens beter vermijden. Microcontrollers zijn hier *erg* traag mee.

Acties:
  • 0 Henk 'm!

  • Vimbeor
  • Registratie: Augustus 2001
  • Laatst online: 21-08 08:50
Ok bedankt! Hier kan ik denk wel weer even mee vooruit!

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17:02
Mijzelf schreef op dinsdag 16 juni 2009 @ 17:29:
Je moet de getallen eerst omzetten in een string, voor je ze op deze manier kan uitsturen.
Als je zowel intergers als floating points wilt verturen zou ik toch echt snprintf ( .. ) gebruiken. Het is een grotere functie ( code size ) maar is heel wat meer bruikbaar dan de code die jij hier post.

En als je dan toch bezig bent schrijf dan die code om naar een interrupt driven verhaal, hoef je tenminste niet te wachten tot je string eruit is over de poort.
Als je kunt kun je floatingpoints trouwens beter vermijden. Microcontrollers zijn hier *erg* traag mee.
Is wel erg over een kam scheren: 8bits AVR uP zullen niet al te snel zijn vergeleken met integer operaties maar het is niet zo dat ze meteen onbruikbaar zijn; blocking communicatie zoals de code die jij post kost heel wat meer tijd iha.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
farlane schreef op dinsdag 16 juni 2009 @ 19:57:
8bits AVR uP zullen niet al te snel zijn vergeleken met integer operaties maar het is niet zo dat ze meteen onbruikbaar zijn; blocking communicatie zoals de code die jij post kost heel wat meer tijd iha.
Dat staat te bezien. Gezien de spec van de OP stuurt hij 4 bytes = 4msec@9600baud = 4000 clocks@1MHz. Op een ATmega128 kost een floating point vermenigvuldiging zo'n 2000 clocks, dus afhankelijk van hoe de OP aan zijn waarden komt, kan het verkrijgen hiervan makkelijk een veelvoud van de communicatietijd zijn.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17:02
Mijzelf schreef op woensdag 17 juni 2009 @ 10:37:
[...]
Dat staat te bezien. Gezien de spec van de OP stuurt hij 4 bytes = 4msec@9600baud = 4000 clocks@1MHz. Op een ATmega128 kost een floating point vermenigvuldiging zo'n 2000 clocks, dus afhankelijk van hoe de OP aan zijn waarden komt, kan het verkrijgen hiervan makkelijk een veelvoud van de communicatietijd zijn.
Dat betekent dus dat in de tijd dat jij zit te wachten tot de string eruit is ik 2 fp vermenigvuldigingen kan doen.

Daar komt bij dat de blocking call op 16MHz nog steeds 4ms kosten terwijl de fp operaties telkens sneller gaan.

Overigens had je het over 'microcontrollers', het is leuk dat je er een AtMega128 bij pakt, maar er zijn genoeg uP's die inmiddels wat(understatement) meer performance bieden. Zie bv http://www.arm.com/products/CPUs/ARM_Cortex-M3.html

[ Voor 3% gewijzigd door farlane op 17-06-2009 22:28 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Verwijderd

De grote vraag is natuurlijk; waar haal je je double vandaan? standaard heeft de atmega geen inputs waar je doubles uit haalt, dus misschien (of zelfs waarschijnlijk) verkrijg je via een dure berekening je double die je vervolgens naar een andere atmega wilt versturen waarbij (omdat het een double is) weer een dure operatie moet plaatsvinden. en dat alles voor twee cijfers achter de komma ... is het niet handiger je waarde met honderd te vermenigvuldigen en dan gewoon met ints te werken? Scheelt je ook een flinke massa overhead in math libraries! Als je die int dan echt inclusief comma wilt versturen;
code:
1
2
3
4
5
6
7
8
char buffer[8];
int somevalue = 432;
char i;
sprintf(buffer, "%d,%d", somevalue / 100, somevalue ~ 100);
for(i=0; i<strlen(buffer); i++) {
    while ( !(UCSRA & _BV(UDRE));
    UDR = buffer[i];
}


Een include van string.h voor sprintf is misschien nodig (afhankelijk van je compiler/instellingen).

De waarde van somevalue kan in dit geval natuurlijk niet groter dan 32K worden ...

Edit: @Mijzelf;
Dat staat te bezien. Gezien de spec van de OP stuurt hij 4 bytes = 4msec@9600baud = 4000 clocks@1MHz.
Vimbeor schreef;
#define BAUD 38400 // F_CPU is 8000000 Hz
Volgens mij staat daar toch echt 38k4@8Mhz? Maar dan nog, het is natuurlijk maar net de vraag hoe vaak die waarde berekend wordt en hoe vaak 'ie verstuurd wordt ...

[ Voor 16% gewijzigd door Verwijderd op 18-06-2009 01:33 . Reden: Buffer 8 groot gemaakt i.v.m. sign ]


Acties:
  • 0 Henk 'm!

  • Vimbeor
  • Registratie: Augustus 2001
  • Laatst online: 21-08 08:50
Ik heb nu van de double met 100 vermenigvuldigd en er een integer van gemaakt die in een buffer wordt gezet en wordt verstuurd. Dit werkt wel, maar nu worden de getallen 2x weergegeven. dus ipv. 430 443300. Hier mijn huidige code:

code:
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
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>


#define lcdcommand(d)  (lcdwrite((d),PORTB&0xFE));  // RS low
#define lcdputc(d)     (lcdwrite((d),PORTB|0x01));  // RS high

#define F_CPU 4000000UL

#define BAUD   38400   // F_CPU is 8000000 Hz
#define UBRR_VALUE ( ((F_CPU) + 8UL*(BAUD)) / (16UL*(BAUD)) - 1UL )

void uart_init(unsigned int ubrr)
{
  UBRRL = ubrr;
  UBRRH = (ubrr >> 8);
  UCSRC = _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0);
  UCSRB = _BV(TXEN);
}

void lcdwrite(char d, char type)
{
  PORTB = type;         // RS low or high
  PORTB = PORTB|0x02;   // make E high
  PORTA = d;            // assign data
  PORTB = PORTB&0xFD;   // make E low
  _delay_us(1520);      // wait 1,52 ms worst case delay
}

void initlcd(void)
{
  DDRA = 0xFF;          // port A: 8-bit data
  DDRB = DDRB|0x03;     // pin 0 and 1 port B: RS en E
  _delay_ms(15);
  lcdcommand(0x38);
  lcdcommand(0x38);
  lcdcommand(0x38);
  lcdcommand(0x38);     // 8-bits, 2 lines, 5x8 font
  lcdcommand(0x0C);     // display on, cursor off, blink off
  lcdcommand(0x06);     // move cursor right
  lcdcommand(0x01);     // clear display
}

void lcdputs(char *s)
{
  char c;

  while ( (c = *s++) ) {
    lcdputc(c);
  }
}

volatile double d = 5;

int i = 1,b=1;

ISR(INT0_vect)
{
d = d + 0.1;
}

ISR(INT1_vect)
{
d = d - 0.1;
}

ISR(INT2_vect)
{
i++;
if(i > 3){
i=1;
}
}


int main(void)
{
    
   GICR = 0xE0;            //_BV(INT2);    int 0 1 en 2 = 0xE0
   MCUCR = 0x0A;           // aan het einde veranderen in 0x0A

   sei();

  initlcd();                        // initialization LCD
  char *buffer;
  buffer = "aaaaa";
    
  lcdputs("Welkom");                // write text
  _delay_ms(500);

  dtostrf (d, 4, 2, buffer );

  lcdcommand(0x01);                 // clear display

  lcdputs("Voer uw long-");         // write text
  lcdcommand(1<<7|0x40);            // start at line 2
  lcdputs("inhoud in: ");           // write text
  lcdputs( buffer );
  lcdputs("L");


  while (1){

  if (i == 1)
  {
  lcdcommand(0x10);                 // move cursor 1 place left
  lcdcommand(0x10);
  lcdcommand(0x10);
  lcdcommand(0x10);
  lcdcommand(0x10);
  dtostrf (d, 4, 2, buffer );    
  lcdputs( buffer );
  lcdputs("L");
  }

  if (i == 2 && b == 1)
  {
  lcdcommand(0x01);                 // clear display
  lcdputs("Plaats");                // write text
  lcdcommand(1<<7|0x40);            // start at line 2
  lcdputs("de Inhaler");            // write text
  b=0;
  }  

  if (i == 3 && b == 0)
  {
  int l;
  l = d * 100;

  uart_init(UBRR_VALUE);

  char buffer[ 8 ], *p;
        itoa( l, buffer, 10 );
        for( p=buffer; 0 != *p; p++ )
        {
            UDR = *p;
            while ( !(UCSRA & _BV(UDRE)) ) {};
            


  lcdcommand(0x01);                 // clear display
  lcdputs("Gegevens");              // write text
  lcdcommand(1<<7|0x40);            // start at line 2
  lcdputs("verstuurd.");            // write text
  b = 1;
  }
  }
}
}


Weet iemand waar dit aan kan liggen?

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 17:02
Als je een statemachine wilt bouwen gebruik dan alsjeblieft namen die ook iets vertellen over wat ze doen. ( i, b )

Overigens, in dit geval ben ik het wel met _Mijzelf_ eens, rekenen met doubles in een interrupt routine is waarschijnlijk niet echt verstandig met die ielige AtMega's. Vermenigvuldig alles wat je met d doet ( nadat je die een fatsoenlijke naam hebt gegeven ) met 100 en haal l helemaal weg.

Wbt je probleem, ik weet niet wat de boelding is precies maar er lijkt iets te veel in je for loop te staan. Je ziet dat echter niet omdat je enorm slordig aan het werken bent.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Verwijderd

Ik ben het volledig met farlane eens wat betreft je code, fout door slordigheid. Ik zie verder nog steeds geen enkele reden om een double te gebruiken voor die longinhoud? Gebruik gewoon een unsigned int (longinhoud zal immers nooit negatief zijn) en reken in centiliter in plaats van liter ... dan kan je je double eruit gooien.
Pagina: 1