Forum: Mikrocontroller und Digitale Elektronik DAC ansteuern in C


von Stefan G. (stefan1982)


Lesenswert?

Hallo Forumsmitglieder,

ich will in einem Projekt mit einem Mikrocontroller (Atmega8)eine 
analoge Ausgabe machen. Ein Wert im Programm des yC soll analog 
dargestellt werden.

Zuerst habe ich das Ganze über ein R2R Netzwerk gelöst. Allerdings würde 
ich das Ganze gerne ein wenig eleganter mit einem DAC IC lösen....dann 
spare ich mir Ausgänge am yC und es sieht ansprechender auf der Platine 
aus.

Das Internet schmeißt mir als ersten Baustein diesen

LTC1661CN8 raus. Gibt es bei Conrad.

Allerdings habe ich leider nicht so die Peilung, wie ich etwas in diesen 
Baustein hineinschreibe (ins ShiftRegister).

Ich habe zwar schon Ansätze im Internet gefunden, allerdings nur in 
Assembler . Da ich nur Kenntnisse in C habe, bringt mich das nicht 
weiter.

Es wäre echt cool, wenn Ihr das hin bekommt, dass ich das endlich auf 
die Reihe bekomme, wie man Registern und CS/LD, SCK richtig umgeht.....

Vielleicht gibt es ja schon Tutorials, die mir weiterhelfen,

Danke im Voraus.

von Karl H. (kbuchegg)


Lesenswert?

Das ist im Prinzip auch nur ein Schieberegister, so wie zb auch der 
74595

AVR-Tutorial: Schieberegister

(Ja, ich weiß. Ist das Assembler-Tutorial. Aber sieh dir trotzdem den 
Abschnitt über die Funktionsweise an. Ist im Grunde ganz simpel)

von Wolfgang H. (frickelkram)


Lesenswert?

Hi Stefan,
da ist ein C-Beispiel drin
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=66155&start=0

suche mal nach ATMega8 SPI und LTC1661, dann findest Du jede Menge Zeug 
zu dem Thema.

Statt SPI kannst Du das auch barfuß programmieren, wie Karl Heinz schon 
schreibt, ein Schieberegister. Auf Seite 4 im Datenblatt hat Du das 
Timing Diagramm. Auf Seite 9 die Beschreibung der Bits ...

von Stefan G. (stefan1982)


Lesenswert?

Super vielen dank,

ich werde es mir anschauen und durcharbeiten..... und melde mich dann 
wieder ;-)

von Stefan G. (stefan1982)


Lesenswert?

Hallo,

also nach einer Woche Internetdurchstöbern bin ich leider nicht all zu 
weit gekommen. Da mein oben erwähnter DAC Baustein von Conrad nicht 
lieferbar war, bin ich zu folgendem umgestiegen:

http://www.reichelt.de/index.html?ACTION=3;ARTICLE=109774;SEARCH=MCP%204902-E/P

Soweit so gut: Das Datenblatt gibt mir folgende Infos:

Der Baustein benötigt folgende Inputs:

Negiert: LDAC -> Aktualisiert Vout wenn low

Negiert: CS -> Chip select Input: Low Signal, das die Clock und den 
Datentranfer fordert

SDI -> Serial Data Input
SCK -> Serial Clock Input

V outA gibt mir die gewünschte Ausgangsspannung. Diese ist abhängig von 
der Referenz von Vss zu Vdd....Also in meinem Fall 5 V



Ich hab nun also meine Variable. Diese digitalisiere ich, sodass ich 8 
einzelne Bit habe.

Nun muss ich dies 8 Bits, die entweder 0 oder 1 sind in das 
Schieberegister bekommen.

Negiert: LDAC setze ich auf low, oder lasse es toggeln, damit es mir 
ständig den output aktualisiert.

Aber wie läuft das mit dem CS,SDI und SCK.........

Ich habe mir die Tutorials oben angeschaut, aber es hilft mir nicht 
weiter!!!

Vielleicht habt ihr ein Denkanstoß für mich!!!!

von Michael (Gast)


Lesenswert?

Im Datenblatt auf Seite 25 stehen Diagramme die es sehr gut erklären.
Habe den 4922 auch erst kürzlich eingesetzt. Es gibt hier im Forum auch 
Programmbeispiele für die Ansteuerung. Such mal nach 4922.

von Peter D. (peda)


Lesenswert?

Stefan Grunner schrieb:
> Ich hab nun also meine Variable. Diese digitalisiere ich, sodass ich 8
> einzelne Bit habe.

Wozu?
Du brauchst ein Byte.

Stefan Grunner schrieb:
> Nun muss ich dies 8 Bits, die entweder 0 oder 1 sind in das
> Schieberegister bekommen.

Genau dazu ist das SPI gedacht, nimm es.

In
REGISTER 5-3: WRITE COMMAND REGISTER FOR MCP4902 (8-BIT DAC)
und
FIGURE 5-3: Write Command for MCP4902 (8-bit DAC).
ist doch alles hübsch beschrieben.

Du mußt 2 Bytes senden. Das Datenbyte mußt Du entsprechend verschieben.
1
  uint16_t dac_word = daten << 4;
2
  dac_word |= FLAGS; // die ersten 4 bits entsprechend setzen
3
  DAC_CS = 0;
4
  spi_write( dac_word >> 8 ); // MSB
5
  spi_write( dac_word );      // LSB
6
  DAC_CS = 1;

von Stefan G. (stefan1982)


Lesenswert?

Hallo,

also ich wollte das jetzt mal so probieren wie @Peter Dannegger das 
erklärt hat.

Allerdings ist mir nicht so ganz klar:
Meine Variable "daten" setze ich auf 256....Also müsste der Ausgang die 
max Spannung ausgeben.
FLAGS setze ich auf 7 => 1011;

Aber wie sieht die spi_write () aus?????

Ich würde das liebend gerne umsetzen, aber noch versteh ich nur Bahnhof 
:-(...



Ich habe das Ganze mal von Hand zu Fuß versucht...,der DAC gibt zwar ein 
Lebenzeichen aber noch nicht so wie gewollt.
1
while(1)
2
  {
3
    for(int i = 0; i < 30; i++)
4
    {
5
      if(i && 1)
6
      {
7
        switch(i)
8
        {
9
        case 1:
10
          SDI = 1;    // Auch direkt Port setzen.....Wenn 1 Dann Kanal A
11
          break;
12
        case 3:
13
          SDI = 0;  //BUF 0 
14
          break;
15
        case 5:
16
          SDI = 1;    //GA 1 
17
          break;
18
        case 7:
19
          SDI = 1;    // SHDN  
20
          break;
21
        case 9 - 22:
22
          SDI = Datareg[8  - (i - 9/ 2)];
23
          break;
24
          default:
25
          SDI = 0;
26
        break;
27
        }
28
      SCK = 1;
29
        
30
    } 
31
    else 
32
    {
33
      SCK = 0;  
34
      SDI = 0;
35
    }
36
    
37
      if(SDI == 1)
38
      {// Pin setzten
39
        PORTB |= (PB1 << 1);
40
      } 
41
      else
42
      {// Pin rücksetzten
43
        PORTB &= ~(PB1 << 1);
44
      }
45
    
46
      if(SCK == 1)
47
      {// Pin setzten
48
        PORTB |= (PB2 << 1);
49
      } 
50
      else
51
      {// Pin rücksetzten
52
        PORTB &= ~(PB2 << 1);
53
      }
54
      wait(1);  
55
    } 
56
  }

Chip select liegt auf Masse sowie LDAC
Vdd an 5 V und Vss auf Masse.

Vref habe ich auf 5V oder 0V das macht irgendwie zur Zeit kein 
Unterschied.

von ozo (Gast)


Lesenswert?

Moin,

dein ATmega8 hat SPI eingebaut! Schau mal ins Datenblatt des 
Controllers.

Grüße

von Stefan G. (stefan1982)


Lesenswert?

Ja und genau da liegt das Problem, damit kenne ich mich nicht 
aus...deswegen bin ich hier!!!

von Peter II (Gast)


Lesenswert?

Stefan Grunner schrieb:
> Ja und genau da liegt das Problem, damit kenne ich mich nicht
> aus...deswegen bin ich hier!!!

also kannst du nicht Lesen was im Datenblatt steht, dann solltest du das 
erstmal lernen.

von ozo (Gast)


Lesenswert?

Dann führ dir doch mal das hier direkt vom Hersteller zu Gemüte:
http://www.atmel.com/Images/doc2585.pdf

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:

> Ich habe das Ganze mal von Hand zu Fuß versucht...,der DAC gibt zwar ein
> Lebenzeichen aber noch nicht so wie gewollt.


Kann man natürlich zu Fuss machen, wenn man unnedingt will.
Wo liegt denn das Problem? Ist doch ganz simpel.

Der IC ist aus deiner Sicht einfach nur ein 16 Bit Schieberegister

d.h. er hat erst mal ein 16 Bit Register. Hier ist es
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

und er hat 2 Anschlüsse: SDI und SCK
Das eine ist der Dateneingang, das andere ist der Clock Eingang
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                         SDI    SCK

Um da jetzt Daten reinzukriegen, brauchst du also 16 Bits. Ein uint16_t 
bietet sich da förmlich an.

1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                         SDI    SCK
6
7
8
   
9
   +---------------------+
10
   | 1011 0000 1100 1111 |
11
   +---------------------+
12
   variable

In welcher Reihenfolge muss jetzt was passieren?
Du extrahierst dir das erste Bit aus dem Byte. (Also das ganz links). In 
dem Fall ist das eine 1

1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                         SDI    SCK
6
7
     1
8
     ^
9
   +-|-------------------+
10
   | 1011 0000 1100 1111 |
11
   +---------------------+
12
   variable

diese 1 kommt auf die SDI Leitung
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          1
6
                                                         SDI    SCK
7
8
     1
9
     ^
10
   +-|-------------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

jetzt einmal SCK auf High und SCK auf Low und der IC übernimmt diese 1 
in das Schieberegister
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          1
6
                                                         SDI    SCK
7
8
     1
9
     ^
10
   +-|-------------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

weiter gehts. Das nächste Bit ist drann. Wieder wird festgestellt ob es 
0 oder 1 ist
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          1
6
                                                         SDI    SCK
7
8
      0
9
      ^
10
   +--|------------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

und SDI entsprechend eingestellt
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          0
6
                                                         SDI    SCK
7
8
      0
9
      ^
10
   +--|------------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

und wieder: SCK einmal High und einmal Low und der IC übernimmt das ins 
Schieberegister. Der bisherige Inhalt des Schieberegisters wird dabei um 
1 Stelle nach links geschoben und die 0 am rechten Ende eingeschrieben.
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 | 0 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          0
6
                                                         SDI    SCK
7
8
      0
9
      ^
10
   +--|------------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

nächstes Bit.
Bit extrahieren
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 | 0 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          0
6
                                                         SDI    SCK
7
8
       1
9
       ^
10
   +---|-----------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

auf die SDI Leitung legen
1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 | 0 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          1
6
                                                         SDI    SCK
7
8
       1
9
       ^
10
   +---|-----------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

SCK einmal HIGH und wieder zurück auf Low und diese 1 wird ins 
Schieberegister übernommen

1
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2
  |   |   |   |   |   |   |   |   |   |   |   |   |   | 1 | 0 | 1 |
3
  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4
5
                                                          1
6
                                                         SDI    SCK
7
8
       1
9
       ^
10
   +---|-----------------+
11
   | 1011 0000 1100 1111 |
12
   +---------------------+
13
   variable

und so wandert ein Bit nach dem anderen im Gänsemarsch ins 
Schieberegister.

2 Nebenbedingungen gibt es noch.
Damit der Baustein überhaupt reagiert, muss die CS Leitungt auf Low 
gezogen sein, während mit dem IC gearbeitet wird. Und damit die 
eingeschriebenen Bits dann auch wirksam werden, muss man einmal am den 
LDAC Pin von High auf Low und wieder zurück auf High wechseln.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wenn man das zu Fuss ausprogrammiert, dann darf man natürlich in C nicht 
mit Bitoperationen auf Kriegsfuss stehen. Aber jemand der schon eine LED 
gezielt ein/aus geschaltet hat und sich mit Lauflichter seine Sporen 
verdient hat, kann sowas nicht schrecken.


Also: Wo liegt da jetzt das Problem?
1
#define PORT_DAC  PORT....
2
#define CS_PIN    ....
3
#define SDI_PIN   ....
4
#define SCK_PIN   ....
5
#define LDAC_PIN  ....
6
7
8
9
void writeSpi( uint16_t wert )
10
{
11
  uint8_t i;
12
13
  // CS ( Chip Select auf Low, damit der IC überhaupt reagiert
14
  PORT_DAC &= ~( 1 << CS_PIN );
15
16
  // ein Bit nach dem anderen
17
  for( i = 0; i < 16; ++i )
18
  {
19
    if( wert & 0x8000 )  // ist es 1 ?
20
      PORT_DAC |= ( 1 << SDI_PIN );
21
    else                 // nein, es ist 0
22
      PORT_DAC &= ~( 1 << SDI_PIN );
23
24
    // SDI ist richtig eingestellt, jetzt SCK toggeln, damit der IC
25
    // das auch übernimmt
26
    PORT_DAC |= ( 1 << SCK_PIN );
27
    PORT_DAC &= ~( 1 << SCK_PIN );
28
29
    // und das nächste Bit ist drann.
30
    // Wir machen das so, dass wir einfach Wert um 1 Stelle nach links
31
    // schieben. Dann kommt das nächste Bit wieder an der Position 0x8000
32
    // zu liegen.
33
    Wert <<= 1;
34
  }
35
36
  // alle 16 Bits sind draussen.
37
  // Jetzt einmal LDAC von High auf Low und zurück ...
38
  PORT_DAC &= ~( 1 << LDAC_PIN );
39
  PORT_DAC |= ( 1 << LDAC_PIN );
40
  // ... und der IC hat diese 16 Bit aktiviert.
41
42
  // Wir sind fertig. CS ausschalten, damit sich der IC nicht angesprochen
43
  // fühlt, wenn sich an den anderen 3 Leitungen was tut
44
  PORT_DAC |= ( 1 << CS_PIN );
45
}


So macht man das dann eben zu Fuss, wenn man die Hardware-SPI nicht 
verwenden kann oder will.
Das DDRD Register nicht vergessen einzuschalten. Und der 
Nichtstu-Zutsand für den IC ist: CS muss High sein, LDAC muss High sein. 
Den sollte man natürlich auch am Programmanfang mal einstellen.

von Stefan G. (stefan1982)


Lesenswert?

Mmhhhhh ok,

->meine SDI Leitung, setze ich die am Ende von jedem Durchlauf wieder 
auf 0???

Du beschreibst jetzt die ersten 4 Einstellungsbits wie im Datenblatt 
beschrieben, aber danach kommen doch schon die 8 Datenbits, warum also 
die

1011 "0000" 1100 1111

Ich schreibe also alles ins Register und danach setze ich einmal LDAC 
von Hight to Low und wieder zurück

von Stefan G. (stefan1982)


Lesenswert?

Ok, ich versuche das jetzt mal von Hand zu Fuß wie du es oben 
beschrieben hast

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:
> Mmhhhhh ok,
>
> ->meine SDI Leitung, setze ich die am Ende von jedem Durchlauf wieder
> auf 0???

Die interessiert keinen.
Mir der SDI Leitung kannst du machen was du willst. Ob die auf 0 oder 
auf 1 steht interessiert den IC erst dann wieder, wenn SCK das nächste 
mal von 0 auf 1 (und wieder zurück) wechselt und CS auf Low ist. Das 
passiert aber erst laut Code, nachdem SDI auf den gewünschten Wert 
eingestellt wurde. Und erst dann macht SCK seinen High-Low-High Tanz.


> Du beschreibst jetzt die ersten 4 Einstellungsbits wie im Datenblatt
> beschrieben, aber danach kommen doch schon die 8 Datenbits, warum also
> die
>
> 1011 "0000" 1100 1111

Ich hab 16 Bits erfunden.

Die Rausschreiberoutine interessiert ja nicht, was die Bits bedeuten. 
Die bekommt einen 16 Bit Wert und schreibt die raus. Nicht mehr und 
nicht weniger.
Das da einige Bits eine Konfigurationseigenschaft haben, machst du in 
der Funktion darüber. Die setzt die notwendigen Bits in einen uint16_t 
und übergibt dann diese 16 Bit an die Rausschreib-Funktion.

>
> Ich schreibe also alles ins Register und danach setze ich einmal LDAC
> von Hight to Low und wieder zurück

Genau so stehst im Datenblatt und im Timing-Diagramm!
Du musst dir das Datenblatt und die Diagramme schon genau ansehen. Dazu 
hat sie der Hersteller rein gemacht.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Genau so stehst im Datenblatt und im Timing-Diagramm!
> Du musst dir das Datenblatt und die Diagramme schon genau ansehen. Dazu
> hat sie der Hersteller rein gemacht.

Wobei ich einen Fehler gemacht habe.
Denn laut Timing-Diagramm auf Seite 7(*), geht zuerst CS wieder zurück 
auf High und erst dann wird LDAC Low-High durchgeführt.

Es kann zwar sein, dass das keine Rolle spielt, aber im Zweifelsfall hat 
der Hersteller recht. Also:
1
 ....
2
3
  }
4
5
  // alle 16 Bits sind draussen.
6
  // Wir sind fertig. CS ausschalten, damit sich der IC nicht angesprochen
7
  // fühlt, wenn sich an den SDI und SCK Leitungen was tut
8
  PORT_DAC |= ( 1 << CS_PIN );
9
10
  // Jetzt einmal LDAC von High auf Low und zurück ...
11
  PORT_DAC &= ~( 1 << LDAC_PIN );
12
  PORT_DAC |= ( 1 << LDAC_PIN );
13
  // ... und der IC hat diese 16 Bit aktiviert.
14
}

(*) und genau so steht es auch dann im Text im Kapitel über das Serial 
Interface

: Bearbeitet durch User
von Stefan G. (stefan1982)


Lesenswert?

So,

könnte das so hinhauen???


1
#define PORT_DAC  PORTB
2
#define CS_PIN    PB1
3
#define SDI_PIN   PB2
4
#define SCK_PIN   PB3
5
#define LDAC_PIN  PB4
6
7
8
9
10
void writeSpi( uint16_t wert )
11
{
12
  uint8_t i;
13
  PORT_DAC &= ~( 1 << CS_PIN );    // CS ( Chip Select auf Low, damit der IC überhaupt reagiert
14
  for( i = 0; i < 16; ++i )      // ein Bit nach dem anderen
15
  {
16
    if( wert & 0x8000 )  // ist es 1 ?
17
      PORT_DAC |= ( 1 << SDI_PIN );
18
    else                 // nein, es ist 0
19
      PORT_DAC &= ~( 1 << SDI_PIN );
20
21
    // SDI ist richtig eingestellt, jetzt SCK toggeln, damit der IC
22
    // das auch übernimmt
23
    PORT_DAC |= ( 1 << SCK_PIN );
24
    PORT_DAC &= ~( 1 << SCK_PIN );
25
26
    // und das nächste Bit ist drann.
27
    // Wir machen das so, dass wir einfach Wert um 1 Stelle nach link 
28
    // schieben. Dann kommt das nächste Bit wieder an der Position 0x8000
29
    // zuliegen.
30
    wert <<= 1;
31
  }
32
33
  PORT_DAC |= ( 1 << CS_PIN );
34
  // alle 16 Bits sind draussen.
35
  // Jetzt einmal LDAC von High auf Low und zurück ...
36
  PORT_DAC &= ~( 1 << LDAC_PIN );
37
  PORT_DAC |= ( 1 << LDAC_PIN );
38
  // ... und der IC hat diese 16 Bit aktiviert.
39
40
  // Wir sind fertig. CS ausschalten, damit sich der IC nicht angesprochen
41
  // fühlt, wenn sich an den anderen 3 Leitungen was tut
42
  
43
}
44
45
int main(void)
46
{
47
48
DDRB |= (1 << PB1);   // PB0 als Ausgang
49
DDRB |= (1 << PB2);    
50
DDRB |= (1 << PB3);
51
DDRB |= (1 << PB4);
52
DDRB |= (1 << PB5);
53
54
  while(1)
55
  {
56
    writeSpi(0xB0);  // Beschreibung der Konfigurationsbits mit 1011_0000
57
    writeSpi(0xFF); // Beschreibung der Daten in Register mit 1111_1111, also max.
58
  }
59
  
60
}

von Stephan (Gast)


Lesenswert?

Du kannst eine analoge Größe natürlich auch mit der eingebauten PWM 
ausgeben.

Ich bin mir nicht sicher ob Dir das klar ist und es erscheint mir doch 
etwas simpler.

Das µ gibt es übrigens auf jeder Tastaur beim "M" + Alt Gr.

Gruß Stephan

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:
> So,
>
> könnte das so hinhauen???

Brenns auf den Mega und probiers aus.
Dann weißt du es ganz genau.

von Karl H. (kbuchegg)


Lesenswert?

> [C]
> int main(void)
> {
>
>   DDRB |= (1 << PB1);   // PB0 als Ausgang
>  DDRB |= (1 << PB2);
>  DDRB |= (1 << PB3);
>  DDRB |= (1 << PB4);
>  DDRB |= (1 << PB5);
>
>  while(1)

warum hier DDRB, PB1, PB2, PB3, ....
Was denkst du, warum ich dafür #defines vorgeschlagen habe? Damit du sie 
dann nicht benutzt und dich statt dessen lieber wieder in deinen 
Pinnummern verhaspelst.

2-ter Punkt
UNd ich sag extra noch
1
 Und der Nichtstu-Zutsand für den IC ist: CS muss High sein, LDAC
2
 muss High sein. Den sollte man natürlich auch am Programmanfang
3
 mal einstellen.

Du musst noch 100% genauer sein, wenn du in der Programmierung weiter 
kommen willst.


3-ter Punkt
Du hast zwar den Code korrigiert, wie ich das vorgeschlagen habe, aber 
jetzt stimmen die Kommentare nicht mehr.

-> Du musst noch 200% genauer werden, wenn du weiter kommen willst.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

>   while(1)
>  {
>    writeSpi(0xB0);  // Beschreibung der Konfigurationsbits mit 1011_0000
>    writeSpi(0xFF); // Beschreibung der Daten in Register mit 1111_1111, also 
max.
>  }


Nein.
Welchen Teil von: nimm einen uint16_t und setz da drinnen die Bits die 
du brauchst, verstehst du nicht?
1
int main()
2
{
3
  uint16_t DAC_Wert = 0;
4
5
6
  ...
7
8
  DAC_Wert = 0;             // erst mal alle Bits auf 0
9
                            // dann werden alle 1 Bits gesetzt, die benötigt werden
10
11
  DAC_Wert |= ( 1 << 15 );  // A oder B ?  Hier: B, weil das Bit gesetzt ist
12
  DAC_Wert |= ( 1 << 14 );  // Vref Buffer?   Ja
13
  DAC_Wert |= ( 1 << 13 );  // Output Gain.   1 mal
14
  DAC_Wert |= ( 1 << 12 );  // SHut Down?    Nein, keiner
15
16
  DAC_Wert |= 1024;         // UNd die Spannung mittels Wert einstellen
17
                            // Der Wert darf natuerlich nicht über 12 Bit
18
                            // hinaus gehen.
19
                            // Wertebereich daher: 0 bis 4095
20
21
  writeSpi( DAC_Wert );


Hast du denn nie mit LED rtumgespielt?
Deine Fähigkeiten Bits zu manipulieren sind sehr bescheiden.

: Bearbeitet durch User
von Stefan G. (stefan1982)


Lesenswert?

Mhhh so funktioniert es nicht,

im Datenblatt des DAC ist CS und LADC negiert.....das habe ich nicht 
beachtet....Werde das gleich probieren

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:
> Mhhh so funktioniert es nicht,
>
> im Datenblatt des DAC ist CS und LADC negiert.....das habe ich nicht
> beachtet....

Doch, hab ich.
Oder warum glaubst du, dass ich CS mittels

PORT_DAC &= ~( 1 << CS_PIN );

auf 0 ziehe, bzw. die Reihenfolge hier

  PORT_DAC &= ~( 1 << LDAC_PIN );
  PORT_DAC |= ( 1 << LDAC_PIN );

so ist, wie sie ist.

> Werde das gleich probieren

Korrigier erst mal den ganzen Rest.

von Stefan G. (stefan1982)


Lesenswert?

Naja,

ich habe bislang keine Defines gemacht und benutze immer die 
PB-Bezeichnung....

.....Es ist leider garnicht so einfach sich sowas mit Grundkenntnis 0% 
beizubringen....ich bitte deshalb ein wenig Nachsicht, wenn ich mich ein 
wenig in euren Augen verpeilt anstelle.

Nächster Versuch:

Aber wo schreibe ich meinen Wert der ausgegeben werden soll rein???
1
#define PORT_DAC  PORTB
2
#define CS_PIN    PB1
3
#define SDI_PIN   PB2
4
#define SCK_PIN   PB3
5
#define LDAC_PIN  PB4
6
7
void writeSpi( uint16_t Wert )...
8
9
int main(void)
10
{
11
12
DDRB |= (1 << CS_PIN);   // PBx als Ausgang
13
DDRB |= (1 << SDI_PIN);    
14
DDRB |= (1 << SCK_PIN);
15
DDRB |= (1 << LDAC_PIN);
16
17
PORT_DAC |= (1<<CS_PIN);  // Chip select zu Begin setzen
18
PORT_DAC |= (1<<LDAC_PIN);  // LDAC zu Begin setzen
19
20
uint8_t DAC_Wert = 0;
21
22
DAC_Wert = 0;
23
24
// Setzen der Status Bits
25
DAC_Wert |= (1<<15);    // Kanal B setzen
26
DAC_Wert |= (1<<14);    // Vref Buffer gesetzt
27
DAC_Wert |= (1<<13);    //
28
DAC_Wert |= (1<<12);
29
30
31
DAC_Wert |= 256;    // 8 Bit => 20mV pro Bit
32
33
  while(1)
34
  {
35
    
36
    writeSpi(DAC_Wert); // Beschreibung der Daten in Register mit 1111_1111
37
  }
38
  
39
}

von Karl H. (kbuchegg)


Lesenswert?

Achtung:
Ich hatte da im Posting von 11:29 ein paar böse Fehler drinnen.
(Ich tippe ja hier direkt und kann das nicht vorher testen, weil ich 
keine Hardware hier habe)

unter anderem hab ich irrtümlich uint8_t geschrieben, wo eigentlich ein 
uint16_t sein sollte.

Also: die ganze Bitsetzerei noch mal kontrollieren!

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:


> int main(void)
> {
>
> DDRB |= (1 << CS_PIN);   // PBx als Ausgang
> DDRB |= (1 << SDI_PIN);
> DDRB |= (1 << SCK_PIN);
> DDRB |= (1 << LDAC_PIN);
>
> PORT_DAC |= (1<<CS_PIN);  // Chip select zu Begin setzen
> PORT_DAC |= (1<<LDAC_PIN);  // LDAC zu Begin setzen
>
> uint8_t DAC_Wert = 0;

Das muss logischerweise ein uint16_t sein!

Denn genau so einen (mit 16 Bit) will ja auch die Funktion haben.
(UNd ausserdem macht was anderes auch keinen Sinn)

von Karl H. (kbuchegg)


Lesenswert?

> Aber wo schreibe ich meinen Wert der ausgegeben werden soll rein???

Na hier

DAC_Wert |= 256;    // 8 Bit => 20mV pro Bit

Die 256 können von 0 bis 4095 variieren.
Und genau deswegen macht es auch keinen Sinn, das ganze als 2 Stück 8 
Bit Operationen anzusehen. Die ganze Operation mit diesem DAC ist eine 
16 Bit Operation. Der auszugebende Wert ist ein 16 Bit Wert.
Nur das halt einige Bits eine spezielle BEdeutung haben.
Das ändert aber nichts daran, dass man das ganze auf dieser Ebene schon 
als 16 Bit OPeration ansieht.

Daher auch der uint16_t

: Bearbeitet durch User
von Stefan G. (stefan1982)


Lesenswert?

Ok das habe ich abgeändert.

Wenn ich das Ganze jetzt anschließe erhalte ich an VoutB eine Spannung 
von 0,35 Volt.
Da sollten jedoch bei dem 8 Bit Converter dann bei 256 Bit 5 Volt 
anliegen.

Ich habe VrefB auf 5 Volt
         Vdd auf 5 Volt
         Vss auf Masse


1
#define PORT_DAC  PORTB
2
#define CS_PIN    PB1
3
#define SDI_PIN   PB2
4
#define SCK_PIN   PB3
5
#define LDAC_PIN  PB4
6
7
void writeSpi( uint16_t Wert )...
8
  
9
int main(void)
10
{
11
DDRB |= (1 << CS_PIN);   // PBx als Ausgang
12
DDRB |= (1 << SDI_PIN);    
13
DDRB |= (1 << SCK_PIN);
14
DDRB |= (1 << LDAC_PIN);
15
16
PORT_DAC |= (1<<CS_PIN);  // Chip select zu Begin setzen
17
PORT_DAC |= (1<<LDAC_PIN);  // LDAC zu Begin setzen
18
19
uint16_t DAC_Wert = 0;
20
DAC_Wert = 0;
21
22
DAC_Wert |= (1<<15);    // Kanal B setzen
23
DAC_Wert |= (1<<14);    // Vref Buffer gesetzt
24
DAC_Wert |= (1<<13);    //
25
DAC_Wert |= (1<<12);
26
27
DAC_Wert |= 256;    // 8 Bit => 5 Volt
28
29
  while(1)
30
  {
31
    
32
    writeSpi(DAC_Wert); // Beschreibung der Daten in Register mit 1111_1111
33
  }
34
  
35
}

von Karl H. (kbuchegg)


Lesenswert?

> Wenn ich das Ganze jetzt anschließe erhalte ich an VoutB
> eine Spannung von 0,35 Volt.

> DAC_Wert |= 256;    // 8 Bit => 5 Volt

Na passt doch.

Der Wertebereich ist hier 0 bis 4095
1
  4095    .....   5V
2
   256    .....    x
3
  ----------------------
4
5
        256 * 5
6
   x = ---------- = 0.31
7
          4095

das da jetzt 0.31 rechnerisch rauskommen und nicht 0.35 kann an deinem 
Messgerät liegen oder daran, dass deine Referenzspannung nicht genau 
5.0V ist, bzw. daran dass dein DAC nur 8 Bit hat und nicht die vollen 12
Aber in der Größenordnung sind wir dort.


Der Hersteller hat mitgedacht.
Ob du einen 12Bit oder einen 10Bit oder einen 8Bit DAC benutzt, ist für 
das Programm egal. Du hast IMMER einen Wertebereich von 0 bis 4095, egal 
welcher IC das ist. Deswegen sind die Datenbits im Datenblatt ja auch so 
verschoben und die jeweiligen Low-Bits werden ignoriert.
Ein 12Bit DAC kann den Unterschied von 4094 auf 4095 noch auflösen, ein 
10Bit DAC kann das nicht.
Aber: die Zahlen laufen immer von 0 bis 4095. Die Bits sind extra dafpr 
so angeordnet.

Wenn du jetzt Eingangswerte von 0 bis 255 hast, dann musst du die eben 
auf 0 bis 4095 aufblasen, damit die Datenbits für 0..255 richtig zu 
liegen kommen. 4 mal nach rechts schieben und sie sind genau da wo sie 
sein sollen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> liegen kommen. 4 mal nach rechts schieben und sie sind genau da wo sie
> sein sollen.

das andere rechts.
Im Volksmund auch links genannt.

Und du solltest dann auch tunlichst keine größeren Ausgansgwerte als 0 
bis 255 haben. Denn sonst verschiebst du dir ein paar 1 Bits in den 
Bereich der Konfigurationsregister hinein.

Sicherheitshalber sollte man das ausmaskieren, wenn es da ein Problem 
geben könnte.
1
  int Spannung = 300;
2
3
  DAC_Wert |= ( Spannung << 4 ) & 0x0FFF;

Eventuell ein Limit einführen
1
  int Spannung = 300;
2
3
  if( Spannung > 255 )
4
    Spannung = 255;
5
  if( Spannung < 0 )
6
    Spannung = 0;
7
8
  DAC_Wert |= Spannung << 4;

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

So.
Nächster Schritt

Jetzt wird der Teil, der hier so lapidar ausprogrammiert ist
1
uint16_t DAC_Wert = 0;
2
DAC_Wert = 0;
3
4
DAC_Wert |= (1<<15);    // Kanal B setzen
5
DAC_Wert |= (1<<14);    // Vref Buffer gesetzt
6
DAC_Wert |= (1<<13);    //
7
DAC_Wert |= (1<<12);
8
9
DAC_Wert |= 256;    // 8 Bit => 5 Volt
10
11
....    
12
    writeSpi(DAC_Wert); // Beschreibung der Daten in Register mit 1111_1111
13
....

ein ein Funktionsschema eingepasst.

Wie willst du es machen? Brauchst du Zugang zu den möglichen 
Konfigurationen? A und B wahrscheinlich schon, VRef eher nicht, Gain 
eher auch nicht. Was ist mit abschalten?

Je nachdem überlegt man sich jetzt erst mal, wie man diese Funktionen 
benutzen will, wie die Aufrufe aussehen sollen, so dass sie sinnvoll und 
gut zu verwenden sind.
1
int main()
2
{
3
  ...
4
5
  InitDAC();
6
7
  DACSet( DAC_GAIN_1, DAC_ENABLE );
8
9
  while( 1 )
10
  {
11
    DACOutput8( CHANNEL_A, 128 );
12
...

ist nur ein Vorschlag. Fällt dir was besseres ein? Speziell die 
Konfiguration mit der Angabe was man haben will - welche Möglichkeiten 
gibt es noch.


Wenn du dann was hast, was dir soweit gefällt, dann ... die 
entsprechenden Funktionen schreiben, #define anlegen wenn man welche 
braucht. etc.

von Stefan G. (stefan1982)


Lesenswert?

Suuuper,

so scheint es jetzt zu funktionieren.....

Zugang brauch ich dazu nicht.....der digitale Wert der umgewandelt 
werden soll ändert sich automatisch ständig und wird deswegen immer 
wieder neu ausgegeben. Das Register soll also ständig neu beschrieben 
werden.


Vielen lieben Dank ""Karl Heinz Buchegger"", ich werde das ganze jetzt 
mal Sacken lassen und gehe es heut Abend nochmal durch um zu sehen, ob 
ich das ganze wirklich verstanden habe.

Und wenn du morgen noch ein wenig Geduld hast :-) , bekommen wir das 
ganze direkt über die SPI hin und nicht mit Hand zu Fuß....

Danke nochmal!!!!

von Karl H. (kbuchegg)


Lesenswert?

Stefan Grunner schrieb:

> Zugang brauch ich dazu nicht.....der digitale Wert der umgewandelt
> werden soll ändert sich automatisch ständig und wird deswegen immer
> wieder neu ausgegeben.

Das ist schon klar.
Aber du wirst das ja nicht dauernd in den Folgeprojekten 
ausprogrammieren.

Eine Funktion
1
void DACOutput8( uint8_t Wert )
2
{
3
  uint16_t DAC_Wert = 0;
4
  DAC_Wert = 0;
5
6
  DAC_Wert |= (1<<15);    // Kanal B setzen
7
  DAC_Wert |= (1<<14);    // Vref Buffer gesetzt
8
  DAC_Wert |= (1<<13);    //
9
  DAC_Wert |= (1<<12);
10
11
  DAC_Wert |= Wert;
12
  writeSpi(DAC_Wert);
13
}

ist ja schnell geschrieben.
Nur ist diese eine Funktion natürlich nicht alles, um den DAC ab sofort 
dann in der weiteren Programmierung als Black Box betrachten zu können.

Da braucht es ja auch noch ein paar Support Funktionen, damit man nicht 
jedesmal bei den Konfigurationsbits den Source Code direkt ändern muss.

Das ganze kommt dann in ein eigenes C-File, noch ein Header File dazu 
und ab damit in die persönliche Source Code Library, in der das dann 
vorrätig liegt, wenn man den Baustein das nächste mal benötigt. Genauso 
wie LCD Funktionen, genauso wie ADC Funktionen, genauso wie ...
Die Kunst in der Programmierung besteht nicht darin, besonders gut 
hacken zu können. Die Kunst besteht darin sich Bausteine zu machen, die 
man problemlos und sicher einsetzen kann, wenn man sie braucht.

: Bearbeitet durch User
von Stefan G. (stefan1982)


Lesenswert?

Mmmhhhh ok.....

wie schauts hiermit aus?

Ich lagere die Defines in eine Header aus und kann meine Settings direkt 
an der Funktion abändern!

1
#define KanalA DAC_Wert |= (1<<15)
2
#define KanalB DAC_Wert &= ~(1<<15)
3
4
#define VrefBufset DAC_Wert |= (1<<14)
5
#define VrefBufnoset DAC_Wert &= ~(1<<14)
6
7
#define GainOn DAC_Wert |= (1<<13)    // 1x
8
#define GainOff DAC_Wert &= ~(1<<13)  // 2x
9
10
#define SHDNon DAC_Wert |= (1<<12)
11
#define SHDNoff DAC_Wert &= ~(1<<12)
12
13
14
void DACOutput8(int Kanal,int VrefBuf,int Gain,int SHDN)
15
{
16
  uint16_t DAC_Wert = 0;
17
  DAC_Wert = 0;
18
19
  Kanal;    // Kanal B setzen
20
  VrefBuf;  // Vref Buffer gesetzt
21
  Gain;    // 1x oder 2x  
22
  SHDN;    //
23
  
24
  DAC_Wert |= Wert;
25
  writeSpi(DAC_Wert);
26
}

: Bearbeitet durch User
von Coder (Gast)


Lesenswert?

Er könnte auch einen 8-Bit DAC mit ner PWM machen.

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.