Forum: Mikrocontroller und Digitale Elektronik SPI mit mehreren Slaves intelligent selektieren


von Karsten K. (karsten42)


Lesenswert?

Moin Moin,

Ich habe ein SPI-Master mit bis zu 8 gleiche SPI Slaves. Somit sind also 
8 CS Leitungen zu bedienen je nachdem welcher Slave gerade dran ist. Das 
bringt bei generischen read/write funktionen ewig lange switch/case oder 
if-else if blöcke mit sich. Siehe hier ein Beispiel für zwei SPI slaves 
die mit der variablen "mcp" ausgewählt werden.
1
..
2
#include utils.h
3
4
#define MCP2515_CA_A B,2
5
#define MCP2515_CA_A D,5
6
7
uint8_t can_read_status(uint8_t type, uint8_t mcp)
8
{
9
  uint8_t data;
10
  
11
  if(mcp)
12
    RESET(MCP2515_CS_A);
13
  else
14
    RESET(MCP2515_CS_B);
15
  
16
  spi_putc(type);
17
  data = spi_putc(0xff);
18
  
19
  if(mcp)
20
    SET(MCP2515_CS_A);
21
  else
22
    SET(MCP2515_CS_B);
23
  
24
  return data;
25
}

Meine Idee ist es nun anstatt der code intensiven if/else blöcke ein 
Array über einen index ( wie z.B. die variable mcp ) anzusprechen. Die 
Macros RESET/SET expandieren zu
1
PORTB &= ~(1 << 2)// Reset 
2
PORTB |= (1 << 2) // Set

Der Preprocessor macht aus den PORTB Makro für RESET dann:
1
(*(volatile uint8_t *)((0x05) + 0x20)) &= ~(1<<2);

Jetzt könnte man ja diesen Ausdruck in ein array aus strukturen packen 
in Form von
1
struct dynport {
2
3
 xxxxxx port // PORT
4
 uint8_t pin //  terminal
5
}
6
7
...
8
9
struct dynport ports[] {
10
11
  { PORTD, 2},
12
  { PORTA, 3},
13
  ....
14
}

Die Frage ist nun wie ich den Typ von port in struct dynport definieren 
muss???

Oder gibt es da eine elegantere Lösung?


Daaanke für jeden erhellenden Tipp :-)

Gruß
Karsten

von Der (Gast)


Lesenswert?

Laut gedacht:

Wie wäre es mit einer Funktion / Makro:
RESET_CURRENT_CS(mcp);
SET_CURRENT_CS(mcp);
und in dieser dann eine switch/case Anweisung

von Karl H. (kbuchegg)


Lesenswert?

Karsten K. schrieb:


> Jetzt könnte man ja diesen Ausdruck in ein array aus strukturen packen
> in Form von
>
1
> struct dynport {
2
> 
3
>  xxxxxx port // PORT
4
>  uint8_t pin //  terminal
5
> }
6
> 
7
> ...
8
> 
9
> struct dynport ports[] {
10
> 
11
>   { PORTD, 2},
12
>   { PORTA, 3},
13
>   ....
14
> }
15
>
>
> Die Frage ist nun wie ich den Typ von port in struct dynport definieren
> muss???

Um die Frage zu beantworten.
Der korrekte Datentyp wäre ein
  volatile uint8_t*

also ein

struct dynport {
  volatile uint8_t* port;
  uint8_t           pin;
};

struct dynport ports[] {
{
  { &PORTD, 2 },
  { &PORTA, 3 },
   ....
};

  *(ports[0].port) &= ( 1 << ports[0].pin );

>
> Oder gibt es da eine elegantere Lösung?

Im zweiten Teil der Funktion musst du ja keine Fallunterscheidung mehr 
machen.

  if(mcp)
    SET(MCP2515_CS_A);
  else
    SET(MCP2515_CS_B);

du wendest SET einfach auf alle an, auch auf die, die du vorher nicht 
mit RESET behandelt hast. Damit vereinfacht sich das an dieser Stelle. 
Und die Auswahl des zu benutzenden SPI-Clients lagerst du in eine 
Funktion aus, die von allen SPI-Funktionen aufgerufen wird.

Denn: obiger Zugriff

  *(ports[0].port) &= ( 1 << ports[0].pin );

ist nämlich genau von der Sorte, die man eigentlich nicht haben möchte. 
Alles ist variabel. Damit bleibt dem Compiler überhaupt nichts anderes 
übrig als das alles in der maximalen Langform zu implementieren. 
Optimierbar ist da überhaupt nichts mehr.

von Peter D. (peda)


Lesenswert?

Du legst einfach alle 8 /CS auf den gleichen Port und übergibst dann der 
SPI-Funktion nur die Maske, also 0x01, 0x02 .. 0x80.
1
#define SPI_CS_PORT PORTA
2
3
void spi_task( uint8_t mask )
4
{
5
  SPI_CS_PORT = ~mask;  // select one SPI slave
6
  // do SPI stuff here
7
  SPI_CS_PORT = 0xFF;  // deselect all SPI slave
8
}

von Karsten K. (karsten42)


Lesenswert?

Hallo Karl Heinz, Hallo Peter

Danke für die Info!
Ich bin da wohl im Wald und sehe nur Bäume! Ich war soooo auf PORT[x] 
und entspr. Anschluss fixiert.

Herzlichen Dank für eure überragende Hilfe!

Gruß
Karsten

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.