Forum: Mikrocontroller und Digitale Elektronik AD5292 mit SPI des ATmega32 ansteuern


von Cheng L. (cheng_l)


Lesenswert?

Hallo,

Für ein Projekt muss ich über einen digitalen Potentiometer AD5292 
mittels eines ATmega32 ansteuern. Der ATmega32 beherrscht ja SPI, also 
sollte das theoretisch möglich sein. Nur ich bekomme es einfach nicht 
hin.

Zu meiner Entwicklungsumgebung für den ATmega32 gehört das Atmel 
Evaluluationsboard V2.0.1. Dabei habe ich an dem Board alles unverändert 
gelassen (auch Jumper drin gelassen), sodass ich die Ausgänge über JP4 
rausführe. Programmieren tue ich mit Atmel Studio 6 sowie einen 
ISP-Programmer mit STK500.

Die Entwicklungsumgebung funktioniert auch soweit, da ich dies schon mit 
anderen Projekten erfolgreich getestet habe. Mir macht einfach die 
Ansteuerung des AD5292 Kopfzerbrechen. Im Netz habe ich auch speziell zu 
dieser Konfiguration nichts gefunden.

Mein Code sieht folgendermaßen aus:
1
#include <avr/io.h>
2
3
#define DDR_SPI      DDRB
4
#define DD_MOSI      DDB5
5
#define DD_SCK      DDB7
6
#define DD_SS      DDB4
7
8
void SPI_INIT (void)
9
{
10
  DDR_SPI |= (1<<DD_MOSI) | (1<<DD_SCK) | (1<<DD_SS);       // Set MOSI , SCK , and SS output
11
  SPCR = ( (1<<SPE)|(1<<MSTR) | (1<<SPR1) |(1<<SPR0));  // Enable SPI, Master, set clock rate fck/128
12
}
13
14
void SPI_DATA (char data)
15
{
16
  SPDR = data;                                                         // Send data
17
  while(!(SPSR & (1<<SPIF))){;                                         // Wait
18
    
19
  }
20
}
21
22
int main(void)
23
{
24
  
25
  SPI_INIT();
26
  PORTB = PORTB & 0b11101111;      // SS low
27
  SPI_DATA (0b00011000);             // Command #6
28
  SPI_DATA (0b00000011);
29
  SPI_DATA (0b00000110);           // Command #1
30
  SPI_DATA (0b00000000);
31
  PORTB = PORTB | 0b00010000;     // SS high
32
  
33
    while(1)
34
    {
35
36
    }
37
}

Also über SS, MOSI und SCK werden auf jeden Fall Signale rausgesendet. 
Nur am Widerstand des Potentiometers ändert sich nichts. Eigentlich 
müsste ich auch alles richtig verdrahtet haben.

Da ich das Projekt recht zügig fertigkriegen muss, wäre ich über jede 
erdenkliche Hilfestellung sehr dankbar. Eventuell haben auch andere 
Leute dasselbe Problem.

Vielen Dank im Vorraus!

Mit freundlichen Grüßen,

Cheng L.

von guest111 (Gast)


Lesenswert?

Hey,

schon Mal probiert die <spi.h> einzubinden?
Dann würde ich gern wissen wollen, woher du weißt, dass auch die 
richtigen Bits zur richtigen Zeit ankommen? Hast du ein Oszi da?
Lad´ mal bitte der Einfachheit halber den Schaltplan µC-DigPoti hoch.


MfG
guest111

von Cheng L. (cheng_l)


Angehängte Dateien:

Lesenswert?

Hallo,

Danke für deine hilfreiche Antwort!

Ich habe den Teil µC-DigPoti als Bild hochgeladen. Im Schaltplan steht 
noch Mega8, weil ich das später mal verwenden möchte. Eigentlich sollte 
ja richtig angeschlossen sein:

PB2 (SS) --> SYNC
PB4 (MISO) --> DIN
PB (SCK) --> SCLK

Ich werde das mal mit der <spi.h> ausprobieren.

Ansonsten weiss ich, dass ISP und SPI sich behindern, jedoch habe ich 
zuerst das DigiPoti nicht angeschlossen, um per ISP das Programm in den 
µC zu laden. Anschließend erst die SPI Pins belegt.

Gibt es zu dieser Konfiguration irgendwelche fertigen Code Beispiele?

Auf weitere hilfreiche Antworten würde ich mich sehr freuen.

Viele Grüße,

Cheng L.

von Oliver R. (orb)


Lesenswert?

Cheng L. schrieb:
> Ansonsten weiss ich, dass ISP und SPI sich behindern,

Dann hast Du da noch einen Fehler. ISP ist nur aktiv wenn Reset low ist. 
dann sollte der AD5292 hochohmig auf dem SPI sein, dessen Reset auch low 
sollte helfen.

von Cheng L. (cheng_l)


Lesenswert?

Danke für deinen Tipp.
Du meinst also, ich sollte den Reset des AD5292 auch mit den µC 
verbinden und diesen auf low setzen, wenn das Programm schon per ISP auf 
den µC geladen ist?

von Cheng L. (cheng_l)


Lesenswert?

Also ich habe es mit spi.h und Reset des AD5292 auf low versucht. Leider 
klappt das immer noch nicht.

Hier der Code für spi.h, was ich gefunden habe:
1
#ifndef _SPI_H_
2
#define _SPI_H_
3
4
#include <avr/io.h>
5
6
7
extern void spi_init();
8
extern void spi_transfer_sync (uint8_t * dataout, uint8_t * datain, uint8_t len);
9
extern void spi_transmit_sync (uint8_t * dataout, uint8_t len);
10
extern uint8_t spi_fast_shift (uint8_t data);
11
12
13
#endif /* _SPI_H_ */

Und hier nun mein neues Programm:
1
#include <avr/io.h>
2
#include "spi.h"
3
#include <avr/interrupt.h>
4
5
#define PORT_SPI    PORTB
6
#define DDR_SPI     DDRB
7
#define DD_MISO     DDB6
8
#define DD_MOSI     DDB5
9
#define DD_SS       DDB4
10
#define DD_SCK      DDB7
11
12
void spi_init()
13
// Initialize pins for spi communication
14
{
15
  DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_MISO)|(1<<DD_SS)|(1<<DD_SCK));
16
  // Define the following pins as output
17
  DDR_SPI |= ((1<<DD_MOSI)|(1<<DD_SS)|(1<<DD_SCK));
18
19
  SPCR = ((1<<SPE)|               // SPI Enable
20
          (0<<SPIE)|              // SPI Interupt Enable
21
          (0<<DORD)|              // Data Order (0:MSB first / 1:LSB first)
22
          (1<<MSTR)|              // Master/Slave select
23
          (0<<SPR1)|(1<<SPR0)|    // SPI Clock Rate
24
          (0<<CPOL)|              // Clock Polarity (0:SCK low / 1:SCK hi when idle)
25
          (0<<CPHA));             // Clock Phase (0:leading / 1:trailing edge sampling)
26
27
  SPSR = (1<<SPI2X);              // Double Clock Rate
28
}
29
 
30
void spi_transfer_sync (uint8_t * dataout, uint8_t * datain, uint8_t len)
31
// Shift full array through target device
32
{
33
  uint8_t i;
34
  for (i = 0; i < len; i++) {
35
    SPDR = dataout[i];
36
    while((SPSR & (1<<SPIF))==0);
37
    datain[i] = SPDR;
38
  }
39
}
40
 
41
void spi_transmit_sync (uint8_t * dataout, uint8_t len)
42
// Shift full array to target device without receiving any byte
43
{
44
  uint8_t i;
45
  for (i = 0; i < len; i++) {
46
    SPDR = dataout[i];
47
    while((SPSR & (1<<SPIF))==0);
48
  }
49
}
50
51
uint8_t spi_fast_shift (uint8_t data)
52
// Clocks only one byte to target device and returns the received one
53
{
54
  SPDR = data;
55
  while((SPSR & (1<<SPIF))==0);
56
  return SPDR;
57
}
58
59
int main(void)
60
{
61
  
62
  // Allocate buffer array
63
  uint8_t data_buf[1];
64
  // Initialize SPI
65
  spi_init();
66
  // SS low
67
  PORTB = PORTB & 0b11101111;
68
  
69
  // Command #6
70
  data_buf[0] = 0b00011000;
71
  spi_transmit_sync(data_buf,1);
72
  data_buf[0] = 0b00000011;
73
  spi_transmit_sync(data_buf,1);
74
  
75
  // Command #1
76
  data_buf[0] = 0b00000110;
77
  spi_transmit_sync(data_buf,1);
78
  data_buf[0] = 0b00000000;
79
  spi_transmit_sync(data_buf,1);
80
  
81
  // SS high  
82
  PORTB = PORTB | 0b00010000;
83
}

Was mache ich bloss falsch?

von guest111 (Gast)


Lesenswert?

Also deine Schaltung kann man ja echt vergessen. Dort kann ich nix 
erkennen, weil A) alle Leiterzüge kreuz und quer gehen
und  B) nur ein Teil der Schaltung zu erkennen ist.

Nichtsdestotrotz...warum übergibst du der Funktion spi_transmit_sync() 
denn die Datenlänge 1Bit?? Du willst doch 1Byte haben also 8Bit.
Schau dir bitte noch mal genau Seite 9 und Seite 22 an. Auf Seite 9 ist 
auch ein schönes Beispiel, wie man Daten schreibt (Figure 3). Dazu musst 
du das RESET Pin eigentlich nicht beachten, weil das nur für das Löschen 
des RDAC Registers genutzt wird. Aber du kannst es auch einfach 
überschreiben.
Ach...und wenn du den Widerstand des Potis ändern möchtest, musst du 
nach Zeile 2 der Tabelle 11 auf Seite 22 vorgehen.

Nur so nebenbei PORTB = PORTB & 11... ist vom Stil her Quatsch. Eher 
PORTB=11...

MfG
guest111

von Cheng L. (cheng_l)


Lesenswert?

guest111 schrieb:
> Also deine Schaltung kann man ja echt vergessen. Dort kann ich nix
> erkennen, weil A) alle Leiterzüge kreuz und quer gehen
> und  B) nur ein Teil der Schaltung zu erkennen ist.
>
> Nichtsdestotrotz...warum übergibst du der Funktion spi_transmit_sync()
> denn die Datenlänge 1Bit?? Du willst doch 1Byte haben also 8Bit.
> Schau dir bitte noch mal genau Seite 9 und Seite 22 an. Auf Seite 9 ist
> auch ein schönes Beispiel, wie man Daten schreibt (Figure 3). Dazu musst
> du das RESET Pin eigentlich nicht beachten, weil das nur für das Löschen
> des RDAC Registers genutzt wird. Aber du kannst es auch einfach
> überschreiben.
> Ach...und wenn du den Widerstand des Potis ändern möchtest, musst du
> nach Zeile 2 der Tabelle 11 auf Seite 22 vorgehen.
>
> Nur so nebenbei PORTB = PORTB & 11... ist vom Stil her Quatsch. Eher
> PORTB=11...
>
> MfG
> guest111

Ja, du hast recht, dass meine Schaltung ziemlich unübersichtlich ist. Es 
hat aber auch Gründe, deshalb gebe ich eben nur den relevanten Teil 
preis. Generell sollte ich alles richtig angeschlossen haben:

SS (PB4) beim Atmega32 --> Pin 13 auf J4 des Eva-Boards --> SYNC (Pin 
12) beim AD5292

MOSI (PB5) beim Atmega32 --> Pin 14 auf J4 des Eva-Boards --> DIN (Pin 
10) beim AD5292

SCK (PB7) beim Atmega32 --> Pin 16 auf J4 des Eva-Boards --> SCLK (Pin 
11) beim AD5292

Mein C Code habe ich auch etwas abgeändert. Dabei habe ich mir das 
Timing Diagram von Seite 9 genauer angeschaut. Anfangs braucht man wohl 
eine fallende Flanke beim SYNC, um den Datentransfer zu aktivieren. Auf 
Seite 23 gibt es eine kleine Tabelle, die besagt, dass zuerst 0x1803 
geschrieben werden muss, damit der Chip überhaupt die Wiper-Position 
updaten kann. Anschließend habe ich kann ich erst Befehle #1 von Seite 
22 einschleusen (ich habe beispielsweise 0x0500 geschrieben. Jedoch 
klappt es weiterhin nicht, per DMM messe ich immer noch 10 KOhm, also 
der Wiper befindet sich immer noch in der Mitte.

Hier mein C Code:
1
#include <avr/io.h>
2
#include "spi.h"
3
#include <avr/interrupt.h>
4
5
#define PORT_SPI    PORTB
6
#define DDR_SPI     DDRB
7
#define DD_MISO     DDB6
8
#define DD_MOSI     DDB5
9
#define DD_SS       DDB4
10
#define DD_SCK      DDB7
11
12
void spi_init()
13
// Initialize pins for spi communication
14
{
15
  DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_MISO)|(1<<DD_SS)|(1<<DD_SCK));
16
  // Define the following pins as output
17
  DDR_SPI |= ((1<<DD_MOSI)|(1<<DD_SS)|(1<<DD_SCK));
18
19
  SPCR = ((1<<SPE)|               // SPI Enable
20
          (0<<SPIE)|              // SPI Interupt Enable
21
          (0<<DORD)|              // Data Order (0:MSB first / 1:LSB first)
22
          (1<<MSTR)|              // Master/Slave select
23
          (0<<SPR1)|(1<<SPR0)|    // SPI Clock Rate
24
          (0<<CPOL)|              // Clock Polarity (0:SCK low / 1:SCK hi when idle)
25
          (0<<CPHA));             // Clock Phase (0:leading / 1:trailing edge sampling)
26
27
  SPSR = (1<<SPI2X);              // Double Clock Rate
28
}
29
 
30
void spi_transmit_sync (uint8_t dataout)
31
// Shift full array to target device without receiving any byte
32
{
33
  SPDR = dataout;
34
  while((SPSR & (1<<SPIF))==0);
35
}
36
37
int main(void)
38
{  
39
  // Allocate variable
40
  uint8_t data_buf;
41
  // Initialize SPI
42
  spi_init();
43
  
44
  // SS high
45
  PORTB = 0b00001000;
46
  // SS low --> falling edge
47
  PORTB = 0b00000000;
48
  // Command #6 --> enable update of wiper position through digital interface
49
  data_buf = 0x18;
50
  spi_transmit_sync(data_buf);
51
  data_buf = 0x03;
52
  spi_transmit_sync(data_buf);
53
  // SS high
54
  PORTB = 0b00001000;
55
  
56
  // SS high
57
  PORTB = 0b00001000;
58
  // SS low --> falling edge
59
  PORTB = 0b00000000;
60
  // Command #1 --> wiper moves to 1/4 full-scale position
61
  data_buf = 0x50;
62
  spi_transmit_sync(data_buf);
63
  data_buf = 0x00;
64
  spi_transmit_sync(data_buf);
65
  // SS high  
66
  PORTB = 0b00001000;
67
}

Ich bin teilweise echt am verzweifeln. Über jeglichen Ratschlag und 
Hilfe wäre ich sehr dankbar!

von Purzel H. (hacky)


Lesenswert?

Um mir solche Muehsale zu ersparen nehm ich fuer zeitunkritische 
Trivialitaeten jeweils einen Soft SPI, und bilde das vom Datenblatt 
verlangte timing einfach ueber die oroginalen SPI Portpins nach. Das hat 
man in 5 minuten erledigt.

von Cheng L. (cheng_l)


Lesenswert?

Siebzehn mal Fuenfzehn schrieb:
> Um mir solche Muehsale zu ersparen nehm ich fuer zeitunkritische
> Trivialitaeten jeweils einen Soft SPI, und bilde das vom Datenblatt
> verlangte timing einfach ueber die oroginalen SPI Portpins nach. Das hat
> man in 5 minuten erledigt.

Okay, und wie würdest du es dann machen?

von dummy (Gast)


Lesenswert?

// SS high
  PORTB = 0b00001000;

Dort wird PB3 auf High gezogen. SS ist aber PB4.

von Cheng L. (cheng_l)


Lesenswert?

dummy schrieb:
> // SS high
>   PORTB = 0b00001000;
>
> Dort wird PB3 auf High gezogen. SS ist aber PB4.

Danke, dass du in meinem Code einen Fehler gefunden hast, aber es geht 
leider immer noch nicht...

von guest111 (Gast)


Lesenswert?

Hast du denn nicht mal die Möglichkeit deine Bits zu überprüfen? Das 
macht es doch alles einfacher! Erst, wenn du sicher weist, dass die Bits 
an der richtigen Stelle ankommmen, kannst du verzweifeln :)

MfG
guest111

von Purzel H. (hacky)


Lesenswert?

> ..Okay, und wie würdest du es dann machen?

Naja, im Datenblatt steht ja das timing, welche Signale sich wie zu 
verhalten haben. Das macht man dann eben genau so.

PORTB,4 = 1  usw.

: Bearbeitet durch User
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.