Rechnen mit PIC18 und C18

OP #954359
Lesenswert?

Hallo!

Ich bin erst vor ganz kurzem auf den C18 umgestiegen und das Teil nervt 
jetzt schon. Vielleicht kann mir jemand von euch helfen und sagen was da 
falsch läuft.

Genauer gesagt habe ich keine Ahnung wie die Rechung laufen soll! 
Irgendwas funktioniert dabei nicht. Es kommt immer nur 0.0V raus.

1
unsigned short long mess_erg, temp;
2
.
3
.
4
.
5
while(1) {
6
  Delay10KTCYx( 100 );
7

8
  ConvertADC();       // Start conversion
9
  while( BusyADC() );   // Wait for ADC conversion
10

11
  mess_erg  = ADRESH * 256;
12
  mess_erg += ADRESL;
13

14
  temp = (unsigned) mess_erg * 50;
15
  temp = (unsigned) temp / 1022;
16

17
  Einer = (int)(temp/10);
18
  Zehntel = (int)(temp%10);
19

20
  while(BusyUSART());
21
  WriteUSART(Einer|0x30);
22
  while(BusyUSART());  
23
  WriteUSART('.');
24
  while(BusyUSART());  
25
  WriteUSART(Zehntel|0x30);
26
  while(BusyUSART());  
27
  WriteUSART('V');
28
}

MfG
Andreas
Gast #954391
Lesenswert?

was für ein Datentyp soll denn das sein?

> unsigned short long mess_erg, temp;

entweder unsigned short
oder unsigned long

Eventl. hilft es sich die Werte von temp und mess_erg mal im Debugger 
anzuschauen wenn der PIC läuft.
Gruss K.
OP #954444
Lesenswert?

Hab jetzt ein wenig umgebaut und ich schreibe die Ausgabe noch dazu.


void main(void)
{
  int mess_erg;
  unsigned temp;
  char Einer, Zehntel;

  init();

  while(1) {
    Delay10KTCYx( 100 );

    ConvertADC();       // Start conversion
    while( BusyADC() );   // Wait for ADC conversion
    mess_erg = ReadADC();  // Read result and put in temp

// Ausgabe der unbearbeiteten ADRESL und ADRESH
    while(BusyUSART());
    WriteUSART(ADRESH);
    while(BusyUSART());
    WriteUSART(ADRESL);
    while(BusyUSART());
    WriteUSART( ' ' );

    temp = (unsigned)mess_erg * 50;
    temp = (unsigned)temp / 1024;  // Ergibt den Messwert in Zehntel

    Einer = (char)(temp/10);
    Zehntel = (char)(temp%10);

    Einer |= 0x30;
    Zehntel |= 0x30;

    while(BusyUSART());
    WriteUSART(Einer);
    while(BusyUSART());
    WriteUSART('.');
    while(BusyUSART());
    WriteUSART(Zehntel);
    while(BusyUSART());
    WriteUSART('V');
[/c]

Das ist die Ausgabe die beim Hyperterminal ankommt:
1
~ ô.:V
2
 ô.:V
3
~ ô.:V
4
~ ô.:V
5
 ô.:V
OP #954972
Lesenswert?

So folgendes. Es funktioniert nun zum Teil. Im folgenden sind 2 
Programmcodes mit Ausgabe. Die erste funktioniert und die zweite nicht 
mehr. Vielleicht wisst ihr warum.

Ich habe lediglich den Multiplikator geändert und der Variablen string 
einen weiteren Wert spendiert.

Vielleicht könnt ihr auch einen Blick auf das Config Word machen und 
sagen ob noch etwas fehlt.

1
#include <p18f2410.h>
2
#include <usart.h>
3
#include <delays.h>
4
#include <adc.h>
5

6
#pragma config OSC  = INTIO67  // Interner Oszillator, Port function on RA6 u. RA7
7
#pragma config WDT  = OFF    // Watchdog Timer Off
8
#pragma config PWRT = ON
9
#pragma config LVP = OFF     //Low Voltage ICSP 
10

11
#pragma code
12

13
// **************************************************************************************
14
// init: Initialisierung des PIC
15
// **************************************************************************************
16

17
void init(void)
18
{
19
  OSCCONbits.IRCF1 = 1;    // CPU auf 4MHz einstellen
20

21
  TRISC = 0x00;
22

23
  // USART initialisieren
24
  OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_HIGH, 25);
25

26
  // A/D Wandler initialisieren
27
  OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_4_TAD,ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS,0b1110);
28
  SetChanADC(ADC_CH0);
29

30
} //init
31

32
// **************************************************************************************
33
// main: Hauptprogramm
34
// **************************************************************************************
35

36
void main(void)
37
{
38
  int mess_erg;
39
  unsigned short long temp;
40
  char string[10];
41

42
  init();
43

44
  while(1) {
45
    Delay10KTCYx( 100 );
46

47
    ConvertADC();       // Start conversion
48
    while( BusyADC() );   // Wait for ADC conversion
49
    mess_erg = ReadADC();  // Read result and put in mess_erg
50

51
    temp = mess_erg * (unsigned)50;
52
    temp = temp / (unsigned)1024;
53

54
    string[1]=(temp % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
55
      temp /= 10;
56
    string[0]=(temp % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
57

58
    while(BusyUSART());
59
    WriteUSART( string[0] );
60
    while(BusyUSART());  
61
    WriteUSART('.');
62
    while(BusyUSART());  
63
    WriteUSART( string[1] );
64
    while(BusyUSART());  
65
    WriteUSART('V');
66

67
    while(BusyUSART());    // Neue Zeile
68
    WriteUSART( 0x0D );
69
    while(BusyUSART());
70
    WriteUSART( 0x0A );
71
  } //while
72
}

Die Ausgabe stimmt. Ich habe sie mit einem Multimeter am Sensor 
gemessen.

1
4.3V
2
4.3V
3
4.3V
4
4.3V
5
4.3V
6
4.3V


Nachdem das erste Programm funktioniert wollte ich die mehr Genauigkeit 
also wollte ich eine Kommastelle mehr haben.

1
#include <p18f2410.h>
2
#include <usart.h>
3
#include <delays.h>
4
#include <adc.h>
5

6
#pragma config OSC  = INTIO67  // Interner Oszillator, Port function on RA6 u. RA7
7
#pragma config WDT  = OFF    // Watchdog Timer Off
8
#pragma config PWRT = ON
9
#pragma config LVP = OFF     //Low Voltage ICSP 
10

11
#pragma code
12

13
// **************************************************************************************
14
// init: Initialisierung des PIC
15
// **************************************************************************************
16

17
void init(void)
18
{
19
  OSCCONbits.IRCF1 = 1;    // CPU auf 4MHz einstellen
20

21
  TRISC = 0x00;
22

23
  // USART initialisieren
24
  OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_HIGH, 25);
25

26
  // A/D Wandler initialisieren
27
  OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_4_TAD,ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS,0b1110);
28
  SetChanADC(ADC_CH0);
29

30
} //init
31

32
// **************************************************************************************
33
// main: Hauptprogramm
34
// **************************************************************************************
35

36
void main(void)
37
{
38
  int mess_erg;
39
  unsigned short long temp;
40
  char string[10];
41

42
  init();
43

44
  while(1) {
45
    Delay10KTCYx( 100 );
46

47
    ConvertADC();       // Start conversion
48
    while( BusyADC() );   // Wait for ADC conversion
49
    mess_erg = ReadADC();  // Read result and put in mess_erg
50

51
    temp = mess_erg * (unsigned)500;
52
    temp = temp / (unsigned)1024;
53

54
    string[2]=(temp % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
55
      temp /= 10;
56
    string[1]=(temp % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
57
      temp /= 10;
58
    string[0]=(temp % 10) +'0';         // Modulo rechnen, dann den ASCII-Code von '0' addieren
59

60
    while(BusyUSART());
61
    WriteUSART( string[0] );
62
    while(BusyUSART());  
63
    WriteUSART('.');
64
    while(BusyUSART());  
65
    WriteUSART( string[1] );
66
    while(BusyUSART());  
67
    WriteUSART( string[2] );
68
    while(BusyUSART());  
69
    WriteUSART('V');
70

71
    while(BusyUSART());    // Neue Zeile
72
    WriteUSART( 0x0D );
73
    while(BusyUSART());
74
    WriteUSART( 0x0A );
75
  } //while
76
}

Die ausgabe stimmt nun nicht mehr.

1
0.53V
2
0.53V
3
0.53V
4
0.53V
5
0.53V
6
0.53V

Danke für eure Hilfe! Ich kämpfe schon den ganzen Tag mit dem Zeug.
Andreas
#955167
Lesenswert?

eProfi wrote:
> temp = mess_erg * (unsigned)500;
>
> vermutlich wird temp zu groß werden, um in 24 Bits zu passen.

Das glaube ich eher nicht, denn
1024 * 500 =   512000 (0x7D000)

Probiers mal so:
1
temp = (unsigned short long)mess_erg * (unsigned short long)500;
2
temp = temp / (unsigned short long)1024;
OP #955206
Lesenswert?

@Micha:
Danke hat funktioniert. Kannst du mir erklären was das jetzt eigentlich 
macht das ich auf die Lösung auch selber kommen kann? Jetzt bekomme ich 
als Ergebnis

1
4.389V
2
4.389V
3
4.384V
4
4.384V
5
4.389V

Bei noch einer Kommastelle mehr hab ich das gleiche Problem das die 
Werte wieder nicht passen.

Ich habe im Moment einen 10Bit Wandler aber möchte bald einen 12 od. 14 
Bit Wandler und da brauche ich die bessere Auflösung.

Danke
Andreas
Gast #955209
Lesenswert?

Durch diesen Cast auf der rechten Site der Rechnung wird der Compiler 
gezwungen die ganze Rechnung mit den größten Datentyp durchzuführen. 
Vorher, bei 16 Bit , hat es einen Überlauf gegeben.

MW
#955212
Lesenswert?

Also ich habe die Erfahrung gemacht, dass es sich am besten rechnen 
lässt, wenn man die Werte vorher alle in den Ergebnisdatentyp castet.

Du musst erst einmal sicherstellen, dass alle Rechnungen auch in deinen 
Ergebnisdatentyp reinpassen.
z.B. passt bei einem 12-Bit-Wandler (4096 * 5000) nicht mehr in 24 bit.
Hierzu müsstest du dann unsigned long nehmen (ohne short).

Bzgl. der weiteren Kommastelle musst du deine Ausgaberoutine nochmal 
anpassen, da hier auch die Tausender mit berücksichtigt werden müssen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren