Forum: Mikrocontroller und Digitale Elektronik AT90CAN128 & AD5668 (DA-Wandler)


von Sebastian M. (mccrazy)


Lesenswert?

Hallo liebe Forenmitglieder,

ich habe mal wieder ein Problem. Mein DA-Wandler AD5668 macht einfach 
nicht dass was er soll. Es liegt aber nicht am Chip und nicht an der 
SPI-Übertragung... Es liegt eher an meinem Code.

Hier mal mein Quellcode:
1
 
2
3
4
#define F_CPU 16000000UL  // 16 MHz
5
#define __AT90CAN128__
6
7
#include <avr/io.h>
8
#include <avr/delay.h>
9
10
#define setbit(ADR,BIT) (ADR|=(1<<BIT))
11
#define clearbit(ADR,BIT) (ADR&=~(1<<BIT))
12
13
int spi_init()
14
{
15
  //       SPI_EIN    MASTER      Speed = fck/128
16
  SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0) | (1<<SPR1);
17
}//spi_init
18
19
void mcu_init()
20
{
21
  
22
  //     CS_Analog_IN        MOSI   An_out_LDAC     An_out_Sync     An_Out_CLR
23
  DDRB = 1<<PB0 | 1<<PB1 |  1<<PB2 | 1<<PB5      | 1<<PB6        | 1<<PB7; //Ausgänge setzen
24
  PORTB  = 1<<PB0 | 1<<PB6;       //Chip-Select Analog Out und Analog IN auf high setzen
25
  
26
  DDRF  = 0xff;      //Leistungsausgänge Digital definieren
27
  PORTF  = 0x00;      //Ausgangszustand auf 0 setzen
28
  
29
  DDRA = 0x00;      //DIGITAL-Eingänge definieren
30
  PORTA = 0x00;      //Ohne Pull-Ups
31
}//mcu_init
32
33
void analog_out_ausgabe(uint8_t channel, uint16_t uebergabe_daten)
34
{  
35
  channel &= 0x0F;
36
  uint8_t parameter = 0b00000011; //Write to and update DAC-Channel n
37
  uint32_t daten = parameter<<24 | channel<<20 | uebergabe_daten<<4;  //Daten bilden für Übermittlung an AD5668
38
  //daten = 0b0000 0011 0000 1111111111111111 0000;
39
  clearbit(PORTB,PB6); //Chip-Select Analog out auf low ziehen
40
  SPDR = daten>>24;
41
  while (!(SPSR & (1<<SPIF)));
42
  SPDR = daten>>16;
43
  while (!(SPSR & (1<<SPIF)));
44
  SPDR = daten>>8;
45
  while (!(SPSR & (1<<SPIF)));
46
  SPDR = daten;
47
  while (!(SPSR & (1<<SPIF)));
48
  setbit(PORTB,PB6);
49
}//analog_out_ausgabe
50
51
/*uint16_t get_ADC_Channel(uint8_t channel)
52
{
53
  setbit(SPCR,CPHA);
54
  uint8_t anfrage = 0;
55
  uint8_t MSB = 0;
56
  uint8_t LSB = 0;
57
  uint8_t letzte_bit = 0;
58
  uint16_t messergebnis = 0;
59
  channel = (channel & 0x01)<<2 | (channel & 0x06)>>1; // Kanaldekodierung laut Datenblatt ändern
60
  anfrage = 0x87 | channel<<4;
61
  
62
  clearbit(PORTB,PB0);
63
  SPDR = anfrage;
64
  while (!(SPSR & (1<<SPIF)));
65
  //while (!(PINB & (1<<PB4)));
66
  SPDR = 0xFF;
67
  while (!(SPSR & (1<<SPIF)));
68
  MSB = SPDR;
69
  SPDR = 0xFF;
70
  while (!(SPSR & (1<<SPIF)));
71
  LSB = SPDR;
72
  SPDR = 0xFF;
73
  while (!(SPSR & (1<<SPIF)));
74
  letzte_bit = SPDR;
75
  messergebnis = (MSB<<9) + (LSB<<1);
76
  messergebnis |= letzte_bit;
77
  setbit(PORTB,PB0);
78
  clearbit(SPCR,CPHA);
79
  return messergebnis;
80
}//get_ADC_Channel
81
*/
82
83
84
int main(void)
85
{
86
  
87
  mcu_init();
88
  spi_init();
89
  uint8_t channel_auswahl = 0x00; //KANAL1_ausgewählt
90
    while(1)
91
    {
92
      
93
    for (uint16_t i = 0; i<65535; i++)
94
    {
95
      analog_out_ausgabe(channel_auswahl, i);
96
      _delay_ms(1);
97
      
98
    }
99
      
100
  
101
  
102
    
103
  
104
  
105
    }//while
106
}//main

Ich lasse in der Hauptschleife einfach die Variable i hochzählen und 
sende diesen Wert dann an den DA-Wandler. Normalerweise sollte ja dann 
der Ausgang des Wandlers von 0-U_Ref laufen.... Wenn ich die Daten 
selbst in meine "daten"-Variable schreibe (Auskommentierte zeile) dann 
geht der Ausgang auf 5V hoch. Wenn ich aber meine Schleife und die 
dazugehörige Routine zur Bildung meines Wertes zum Übertragen bilde dann 
läuft der Ausgang nur zwischen 0-ca. 300mV hoch und fängt dann bei 0 
wieder an.
Könnt ihr mir weiterhelfen und mir meinen Fehler aufzeigen?

Viele Grüße
Sebastian

von cskulkw (Gast)


Lesenswert?

Also, für so etwas ist PCLint ganz gut...

Sebastian Müller schrieb:
> void analog_out_ausgabe(uint8_t channel, uint16_t uebergabe_daten)
>
> {
>
>   channel &= 0x0F;
>
>   uint8_t parameter = 0b00000011; //Write to and update DAC-Channel n
>
>   uint32_t daten = parameter<<24 | channel<<20 | uebergabe_daten<<4;  //Daten 
bilden für Übermittlung an AD5668
>
>   //daten = 0b0000 0011 0000 1111111111111111 0000;
>
>   clearbit(PORTB,PB6); //Chip-Select Analog out auf low ziehen
>
>   SPDR = daten>>24;
>
>   while (!(SPSR & (1<<SPIF)));
>
>   SPDR = daten>>16;
>
>   while (!(SPSR & (1<<SPIF)));
>
>   SPDR = daten>>8;
>
>   while (!(SPSR & (1<<SPIF)));
>
>   SPDR = daten;
>
>   while (!(SPSR & (1<<SPIF)));
>
>   setbit(PORTB,PB6);
>
> }//analog_out_ausgabe

Du verläßt Dich darauf, dass der Compiler die Typumwanldung aus Deiner 
32-Bitvariablen selbsttätig und in Deinem Sinne durchführt.

In einem Serienprojekt hatten wir PC-Lint eingesetzt. Und der monierte 
immer solche Schiebeoperationen in Kombi mit Casts.

1. würde ich den Inhalt von "Parameter" nicht erst in die 32-Bitvariable 
einfügen, um sie anschließend wieder herauszufiltern. Als 
Sofortmassnahme würde ich da erst einmal weglassen.

2. Wenn Du aus Verständnisgründen die 32-Bitvariable brauchst, dann 
empfehle ich Dir nach dem Schieben eine Bitmaske und einen CAST zu 
verwenden. Z. B. so:

SPDR = (uint8)((daten & 0x000FF000)>>16);

3. Vielleicht hast Du beim Schieben übersehen, dass Du eine 16-Variable 
2 mal sehen mußt.

/*oberes Halbwort*/
SPDR = (unit8)((daten & 0xFF00)>>8);
/*unteres Halbwort*/
SPDR = (unit8)(daten & 0x00FF);

4. Vielleicht solltest Du mal über eine Bitvariablenstruktur nachdenken.

struct
{
   uint32   Bit_1      :1;
   uint32   Parameter  :8;
   uint32   Daten      :16;
   uint32   DerRest    :7;
} ZusendendeDaten /*(In Summe 32 Bit.)*/

Du kannst Dann über einen Pointer auf uint8 dann ohne viel Schieberei 
die Bytes im Kontext zur 32-Bitvariable komfortabel in das SPDR 
schreiben.

uint8* ByteVariablenZeiger = (uint8*) &ZusendendeDaten;

/*schreibe Inhalt aus Zeigervariablen in SPDR und inkrementiere den
  Zeiger, um auf den Inhalt der nächsten 8-Bits zu zeigen.*/
SPDR = *ByteVariablenZeiger++;
/* Byte 2 */
SPDR = *ByteVariablenZeiger++;
/* Byte 3*/
SPDR = *ByteVariablenZeiger++;
/* Byte 4 */
SPDR = *ByteVariablenZeiger;

Also mein Vorschlag in eine Priziperklärung.

Ich verwende diese Art für die CAN-Datenorganisation. Das funktioniert 
dann mit einfachen Zuweise und Leseoperationen zuverlässig.

Ich hoffe, dass ich Dir hier helfen konnte. Viel Erfolg

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.