Forum: Mikrocontroller und Digitale Elektronik SPI für ADC MCP3204 auf ATXMega256A3 initialisieren


von Jan-Henrik B. (vedaykin)


Lesenswert?

Guten Tag,

ich möchte den 12Bit 4-Kanal ADC MCP3204 (siehe Reichelt) über SPI mit 
Hilfe eines ATXMegas256A3 an Port C auslesen. Prinzipiell möchte ich den 
unten umgesetzten/gefundenen Code auf dem ATX umsetzen. Hierfür bietet 
mir die Bibilothek von Atmel verschiedenste fertige Funktionen. 
Runterladen kann man die Bibliothek unter: 
http://www.atmel.com/devices/atxmega192a3.aspx?tab=documents Beispiel: 
AVR1309

Zu Beginn geht es mir um den Befehl CS_LOW();, den es gilt mit einer 
Bibliotheksfunktion zu ersetzen. Es soll der Slave-Select Kanal auf Null 
gezogen werden, damit die Übertragung starten kann. Hierfür würde ich 
gerne den angebotenen Befehl "SPI_MasterSSLow(spi->dataPacket->ssPort, 
ssPinMask);" verwenden. Vielleicht hilft die Definition noch weiter:

#define SPI_MasterSSLow(_port, _pinBM) ( (_port)->OUTCLR = (_pinBM) )

Mir ist der Aufruf der Funktion nicht klar. Der Aufruf mit: 
SPI_MasterSSLow(PORTC, 0b00001000); ergibt die Fehlermeldung: invalid 
type of argument of '->' (have 'Port_t'). Irgendwie kriege ich nicht das 
richtige Argument konstruiert für die Funktion. Eigentlich muss doch nur 
der Port und eine Bitmaske übergeben werden oder nicht? Ich bin für 
jeden Hinweis dankbar.
1
/********************************************************************
2
3
Requests the ADC to perform conversion and send the result.
4
5
Arguments:
6
      uint8_t ch : Channel Number
7
      For MCP3204 ch is between 0-3 (Total 4 channels)
8
      For MCP3208 ch is between 0-7 (Total 8 channels)
9
10
Return Value:(TYPE uint16_t, i.e a 16bit unsigned int)
11
      The digital equivalent of analog input on selected channel.
12
13
      Since the ADCs are 12 bit the return value is between
14
      0-4095 (Including both)
15
16
********************************************************************/
17
uint16_t ReadADCEx(uint8_t ch)
18
{
19
   uint8_t byte,data_high,data_low;
20
   byte=0b00000110;
21
   if(ch>3)
22
      byte|=0b00000001;
23
   CS_LOW();
24
   SPIWrite(byte);
25
   byte=ch<<6;
26
   data_high=SPIWrite(byte);
27
   data_high&=0b00001111;
28
   data_low=SPIWrite(0xFF);
29
   CS_HIGH();
30
   return ((data_high<<8)|data_low);
31
}

von Dennis (Gast)


Lesenswert?

Dein data_high ist doch nur 8 Bit, wenn du das shiftest, ist der Inhalt 
weg. Oder täusche ich mich da grad?

von Jan-Henrik B. (vedaykin)


Lesenswert?

So ganz sicher bin ich mir da auch nicht, in der letzten Zeile bin ich 
noch nicht so 100%ig angekommen ;). Mir ging es primär erst einmal um 
den Programmablauf zum Auslesen des ADC über die Bibliotheksbefehle. Die 
Bitmanipulation am Schluss würde ich dann debuggen, falls es überhaupt 
falsch ist. Das kann man im laufenden Programm dann am besten 
nachvollziehen glaube ich. Aber danke für den interssanten Hinweis, 
eventuell hast Du mir viel Zeit bei der Fehlersuche am Schluss erspart.

von Gerhard G. (g_g)


Lesenswert?

Hallo,

Jan-Henrik Bathelt schrieb:
> Ich bin für jeden Hinweis dankbar.

Schau dir mal das an:

Projekt: Atxmega128a1 mit ADC-Wandler MCP3551 (22 Bit )
http://www.basteln-mit-avr.de/atxmega128a1.html#mcp3421

Hier findest du eine herkömmliche SPI-Routine.

Der Proz. ist im Prinzip der selbe, MCP3551 ist bis auf einige Befehle 
(MUX) auch kompatible.

Gruß G.G.

von Gerhard G. (g_g)


Lesenswert?

Hallo,

war der falsche Link:

Projekt: Atxmega128a1 mit ADC-Wandler MCP3551 (22 Bit )

http://www.basteln-mit-avr.de/atxmega128a1.html#mcp3551

der passt jetzt!

Gruß G.G.

von Jan-Henrik B. (vedaykin)


Lesenswert?

Hallo G.G.,

danke für den Link, das hat schonmal viel zum Verständins beigetragen. 
Wenn ich das richtig verstanden habe, so werden eigene Funktionen 
geschrieben, um auf bestimmte Bausteine zuzugreifen, z.B. bei der 
Übertragung der 24 Bit.

Ich würde gerne versuchen auf die vorgefertigten Funktionen von Atmel 
zurückgreifen, bevor ich anfange alles selber zu programmieren. Die 
ersten Schritte sind unten zu sehen.

Ich würde gerne über den Befehl SPI_MasterTransceiveByte ein Byte über 
PIN5 MOSI ausgeben. Das scheint aber so noch nicht zu funktionieren. 
Kann man hier einen offensichtlichen Fehlaufruf des Befehls erkennen? 
Compilieren lässt sich das Programm soweit fehlerfrei. Oder ist es von 
Vorteil eher alles selber zu programmieren?
1
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
Variablendeklaration
3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
4
5
/*! \brief SPI slave on PORT D. */
6
SPI_Slave_t spiSlaveD = {NULL, NULL};
7
/*! \brief Data packet. */
8
SPI_DataPacket_t dataPacket;
9
/* Instantiate pointer to ssPort. */
10
PORT_t *ssPort = &PORTC;
11
/*! \brief SPI master module on PORT C. */
12
SPI_Master_t spiMasterC;
13
14
int gintI; //Testvariable
15
16
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17
ADC-Auslesen
18
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
19
20
read_MCP3204( void)
21
{
22
  uint8_t byte = 0xff;
23
    uint8_t ergebnis;
24
  gintI++;
25
    SPI_MasterSSLow(ssPort, PIN4_bm);  //Übertragung starten, SS auf low, kann ich auf Oszi messen.
26
  delay_us(10);
27
  ergebnis = SPI_MasterTransceiveByte(&spiMasterC, byte);    // Hier würde ich gerne ein Byte über PortC MOSI (Pin5) übertragen
28
  delay_us(10);
29
  SPI_MasterSSHigh(ssPort, PIN4_bm); //Übertragung stoppen, SS auf high, kann ich auf Oszi messen.
30
  delay_us(10);
31
  
32
    return;
33
}
34
35
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36
Hauptprogramm/Main
37
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
38
39
int main( void ){  
40
  
41
  sei();
42
  ClockSystemIni();
43
  PSIBusIni();
44
45
  /* Initialize SPI master on port C. */
46
  SPI_MasterInit(&spiMasterC,
47
                 &SPIC,
48
                 &PORTC,
49
                 false,
50
                 SPI_MODE_0_gc,
51
                 SPI_INTLVL_OFF_gc,
52
                 false,
53
                 SPI_PRESCALER_DIV4_gc);
54
  
55
  while (true)
56
  {    
57
    read_MCP3204();
58
  }
59
}
60
61
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
Initialisierungsfunktionen
63
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
64
65
void PSIBusIni( void )
66
{
67
  /* Init SS pin as output with wired AND and pull-up. */
68
  PORTC.DIRSET = PIN4_bm;
69
  PORTC.PIN4CTRL = PORT_OPC_WIREDANDPULL_gc;
70
  /* Set SS output to high. (No slave addressed). */
71
  PORTC.OUTSET = PIN4_bm;
72
  
73
  /* Initialize SPI master on port C. */
74
  SPI_MasterInit(&spiMasterC,
75
                 &SPIC,
76
                 &PORTC,
77
           false,
78
                 SPI_MODE_0_gc,
79
                 SPI_INTLVL_LO_gc,
80
                 false,
81
                 SPI_PRESCALER_DIV4_gc);
82
83
  /* Enable low and medium level interrupts in the interrupt controller. */
84
  PMIC.CTRL |= PMIC_MEDLVLEN_bm | PMIC_LOLVLEN_bm;
85
86
}
87
88
//Configure XMega Clock, clock system @ 32Mhz , PERCLK is 16 Mhz
89
void ClockSystemIni( void )
90
{
91
  OSC.CTRL = OSC_RC32MEN_bm;                      //Start internal 32MHz RC oscillator
92
  do {
93
  } while ( ( OSC.STATUS & OSC_RC32MRDY_bm ) == 0 );          //Wait while oscillator stabilizes
94
  CCP = CCP_IOREG_gc;
95
  CLK.PSCTRL = (CLK_PSADIV_1_gc | CLK_PSBCDIV_2_2_gc);        //Enable prescaler A div by 1 and B and C to div by 2
96
  CCP = CCP_IOREG_gc;
97
  CLK.CTRL = CLK_SCLKSEL_RC32M_gc;                  //Select 32 MHz as master clock
98
}

von Jan-Henrik B. (vedaykin)


Lesenswert?

Hier noch weitere Infos zum Befehl Mastertransceive:
1
/*! \brief SPI mastertransceive byte
2
 *
3
 *  This function clocks data in the DATA register to the slave, while data
4
 *  from the slave is clocked into the DATA register. The function does not
5
 *  check for ongoing access from other masters before initiating a transfer.
6
 *  For multimaster systems, checkers should be added to avoid bus contention.
7
 *
8
 *  SS line(s) must be pulled low before calling this function and released
9
 *  when finished.
10
 *
11
 *  \note This function is blocking and will not finish unless a successful
12
 *        transfer has been completed. It is recommended to use the
13
 *        interrupt-driven driver for applications where blocking
14
 *        functionality is not wanted.
15
 *
16
 *  \param spi        The SPI_Master_t struct instance.
17
 *  \param TXdata     Data to transmit to slave.
18
 *
19
 *  \return           Data received from slave.
20
 */
21
uint8_t SPI_MasterTransceiveByte(SPI_Master_t *spi, uint8_t TXdata)
22
{
23
  /* Send pattern. */
24
  spi->module->DATA = TXdata;
25
26
  /* Wait for transmission complete. */
27
  while(!(spi->module->STATUS & SPI_IF_bm)) {
28
29
  }
30
  /* Read received data. */
31
  uint8_t result = spi->module->DATA;
32
33
  return(result);
34
}

von Jan-Henrik B. (vedaykin)


Lesenswert?

HAHAHA, Moment, der Fühler des Oszis hatte sich leicht gelöst... es wird 
en Byte übertragen. Tja, wieder mal ein zwei Stunden fehlersuche 
umsonst... Ich werde bestimmt bald weitere Fragen zu diesem Thema haben 
und dann auch in diesem Thread posten. Danke erstmal.

von Jan-Henrik B. (vedaykin)


Angehängte Dateien:

Lesenswert?

Die Kommunikation mit dem MCP3204 funkioniert soweit. Ich kann eine 
analoge Spannung auf Kanal 0 geben und der Wert in der Testvariable 
gintI variiert zwischen 0 und 4095, 12Bit halt. Das Auslesen des ADCs 
läuft mit ca. 2,7kHz, das ist mir ein wenig zu langsam und ich hoffe es 
gibt noch eine Menge Optimierungsbedarf/Möglichkeiten. Der komplette 
Code ist unten gepostet.

Auf dem angehängten Bild ist gelb markiert die Masterclock zu sehen. 
Diese läuft mit ca. 130kHz, gibt es eine Möglichkeit die noch zu 
verschnellern? Dazwischen sind auch noch sehr lange Pausen, 
wahrscheinlich durch meinen Code erzeugt. Ohne diese könnte man glaube 
ich schon auf 5kHz Messzyklus kommen, wo ich hinmöchte. Ich kenne mich 
nicht sehr gut aus mit dem SPI-Bus, aber komme ich hier schon annähernd 
an das Maximum was möglich ist?

Ich bin für alle Optimierungshinweise dankbar!
1
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
Variablendeklaration
3
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
4
5
/* Instantiate pointer to ssPort. */
6
PORT_t *ssPort = &PORTC;
7
/*! \brief SPI master module on PORT C. */
8
SPI_Master_t spiMasterC;
9
int gintI; //Testvariable
10
11
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12
ADC-Auslesen
13
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
14
15
uint16_t read_MCP3204(void)
16
{
17
  uint8_t byte,data_high,data_low,ch = 0;
18
  byte=0b00000110;
19
  SPI_MasterSSLow(ssPort, PIN4_bm);  //Übertragung starten, SS auf low
20
  SPI_MasterTransceiveByte(&spiMasterC, byte);  
21
  byte=ch<<6;
22
  SPI_MasterTransceiveByte(&spiMasterC, byte);
23
  data_high&=0b00001111;
24
  data_low = SPI_MasterTransceiveByte(&spiMasterC, 0x00);
25
  SPI_MasterSSHigh(ssPort, PIN4_bm); //Übertragung stoppen, SS auf high
26
  
27
  return ((data_high<<8)|data_low);
28
}
29
30
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
Hauptprogramm/Main
32
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
33
34
int main( void ){  
35
  
36
  sei();
37
  ClockSystemIni();
38
  PSIBusIni();
39
40
  /* Initialize SPI master on port C. */
41
  SPI_MasterInit(&spiMasterC,
42
                 &SPIC,
43
                 &PORTC,
44
                 false,
45
                 SPI_MODE_0_gc,
46
                 SPI_INTLVL_OFF_gc,
47
                 false,
48
                 SPI_PRESCALER_DIV16_gc);
49
50
  while (true)
51
  {    
52
    gintI = read_MCP3204();
53
  }
54
}
55
56
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57
Initialisierungsfunktionen
58
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
59
60
void PSIBusIni(void)
61
{
62
  /* Init SS pin as output with wired AND and pull-up. */
63
  PORTC.DIRSET = PIN4_bm;
64
  PORTC.PIN4CTRL = PORT_OPC_WIREDANDPULL_gc;
65
  /* Set SS output to high. (No slave addressed). */
66
  PORTC.OUTSET = PIN4_bm;
67
  
68
  /* Initialize SPI master on port C. */
69
  SPI_MasterInit(&spiMasterC,
70
                 &SPIC,
71
                 &PORTC,
72
           false,
73
                 SPI_MODE_0_gc,
74
                 SPI_INTLVL_LO_gc,
75
                 false,
76
                 SPI_PRESCALER_DIV4_gc);
77
78
  /* Enable low and medium level interrupts in the interrupt controller. */
79
  PMIC.CTRL |= PMIC_MEDLVLEN_bm | PMIC_LOLVLEN_bm;
80
81
}
82
83
//Configure XMega Clock, clock system @ 32Mhz , PERCLK is 16 Mhz
84
void ClockSystemIni(void)
85
{
86
  OSC.CTRL = OSC_RC32MEN_bm;                      //Start internal 32MHz RC oscillator
87
  do {
88
  } while ( ( OSC.STATUS & OSC_RC32MRDY_bm ) == 0 );          //Wait while oscillator stabilizes
89
  CCP = CCP_IOREG_gc;
90
  CLK.PSCTRL = (CLK_PSADIV_1_gc | CLK_PSBCDIV_1_1_gc);        //Enable prescaler A div by 1 and B and C to div by 2
91
  CCP = CCP_IOREG_gc;
92
  CLK.CTRL = CLK_SCLKSEL_RC32M_gc;                  //Select 32 MHz as master clock
93
}

von diddi (Gast)


Lesenswert?

Interessantes Selbstportrait

von Jan-Henrik B. (vedaykin)


Lesenswert?

Die Hoffnung stirbt zu letzt...

von Jan-Henrik B. (vedaykin)


Lesenswert?

Hinweis: Ändert man die Zeile im Code zu:

CLK.PSCTRL = (CLK_PSADIV_1_gc | CLK_PSBCDIV_1_1_gc); (alt)
CLK.PSCTRL = (CLK_PSADIV_2_gc | CLK_PSBCDIV_1_1_gc); (neu)

läufts nun mit ca. 200kHz. Beste Optimierung -O2 nicht vergessen.

Grüße,

Jan

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.