Forum: Mikrocontroller und Digitale Elektronik Rechnen mit PIC18 und C18


von Andreas R. (blackpuma)


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

von Frank (Gast)


Lesenswert?

Was passiert wenn du "mess_erg" ausgibst?

von Andreas R. (blackpuma)


Lesenswert?

Wie kann ich mir das ausgeben lassen?

von Der M. (steinadler)


Lesenswert?

Der Fehler muss beim Lesen vom ADC auftreten.
Habe das mal eben durchsimuliert. Da treten nämlich keine Fehler auf.
Also die Rechnung selbst stimmt.
Wobei ich aus der 1022 eine 1024 machen würde.

Hast du denn den richtigen Kanal erst ausgewählt?

von Andreas R. (blackpuma)


Lesenswert?

Ja das muss ich noch ändern. die 1022 hab ich von cc5x übernommen.

Wenn ich mir ADRESL und ADRESH auf die Serielle schicke dann 
funktioniert das. Also Werte werden sicher gemessen. Hab da einen 
Drucksensor dran und wenn ich da reinpuste oder ansauge ändert sich der 
Wert.

von Der M. (steinadler)


Lesenswert?

Verunde mal die Werte mit den 10Bit nach dem Einlesen.

Also:
  mess_erg  = ADRESH * 256;
  mess_erg += ADRESL;
  mess_erg &= 0x3F

von Gast (Gast)


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.

von Der M. (steinadler)


Lesenswert?

Habe jetzt nochmal probiert.
Aus unsigned short long werden übrigens 24Bit.

Mach es doch gleich so:
  mess_erg  = ADRES;

von Andreas R. (blackpuma)


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

von Frank (Gast)


Lesenswert?

Versuch es mal hiermit:

Einer += 0x30;
Zehntel += 0x30;

von Andreas R. (blackpuma)


Lesenswert?

Hab das jetzt mit += gemacht und jetzt kommt das raus:

Die ersten beiden Kryptischen sind ADRESH und ADRESL.
1
 $.JV

von Der M. (steinadler)


Lesenswert?

Bist du sicher, dass das Ergebnis in ADCH:ADCL "right adjusted" ist???
ADCON2.ADFM musst du auf 1 setzen.

von Frank (Gast)


Lesenswert?

Kann es mit der Typumwandlung von "unsigned temp" nach "signed char" zu 
tun haben? Versuch es doch mal mit "unsigned char"

von Andreas R. (blackpuma)


Lesenswert?

Die Initialisierung des A/D-Wandlers sieht so aus.
1
// A/D Wandler initialisieren
2
  OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_4_TAD,ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS,0b1110);
3
  SetChanADC(ADC_CH0);

Hab auch schon unsigned char versucht. Ohne Erfolg.

von Andreas R. (blackpuma)


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

von eProfi (Gast)


Lesenswert?

temp = mess_erg * (unsigned)500;

vermutlich wird temp zu groß werden, um in 24 Bits zu passen.

von Der M. (steinadler)


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;

von Andreas R. (blackpuma)


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

von Michael Wilhelm (Gast)


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

von Der M. (steinadler)


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.

von Andreas R. (blackpuma)


Lesenswert?

OK Danke. Das mit dem cast hat funktioniert. Die Ausgabe sollte auch für 
einen 12 bit A/D Wandler passen.
1
4.38476V
2
4.38964V
3
4.38964V
4
4.24316V
5
4.37500V
6
4.38964V
7
4.38964V

Danke euch allen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.