16-bit I/O SPI Porterweiterung mit Microchip PIC18

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

--Denys m 13:35, 29. Jan. 2013 (UTC)

von Denys Maiier

Dieser Artikel nimmt am Artikelwettbewerb 2012/2013 teil.

Vorwort

Eines Tages bin ich auf die Idee gekommen ein Key-Pad an mein PIC18 anzuschließen und habe lediglich verstanden, dass die Nutzung der eigenen Ports – Pins nicht so ganz attraktiv aussieht , alleine durch, dass der Anschluss eines 12-Tasten-Keypad 7 Port-Pins gebraucht wird. Dieses ist ziemlich einfach durch ein MSSP – Modul des Controllers zu realisieren. Aber Alles der Reihe nach….

Aufgabenstellung

Aufgabenstellung sieht folgendermaßen aus:

1. Wir brauchen ein matrizenähnliches KeyPad, welches wir am Microkontroller anschließen. Wichtig dabei ist, dass die Datenübertragung im Duplexmode (d.h. Empfang+ Sendung) realisiert wird.

2. Die Informationen der Tastatur muss korrekt interpretiert und auf dem "7-Segment Display" dargestellt werden. Die Situation, wenn mehrere Tasten gedrückt werden muss ausgeschlossen sein.

3. Es müssen so weniger Kontakt-Pins des Microkontrollers verwendet werden.

4. Die Kostenfrage. Um das Ganze zu realisieren, verwenden wir so wenig Bausteine wie möglich.


Hardware

Bild 1 zeigt, wie das Ganze hardwaremäßig realisiert wurde und im Weiteren versuche ich zu erklären wie die Erstellung funktionieren soll. PIC18 SPI.jpg

Die Grundprinzipien des MSSP – Moduls werden hier nicht betrachtet, dafür gibt es ein Datenblatt von Microchip, im Entwurf habe ich allerdings ein MSSP- Modul im SPI Modus benutzt. Wie auf dem Bild zu sehen, um die ganze Peripherie anzuschließen, habe ich nur 4 PortPins des PIC18 gebraucht: Port B(Pin B0) um das ChipSelect -Signal auszugeben, und PortPins RC3 bis RC5 des Ports C (hiermit wird MOSI, MISO und Taktsignale erzeugt). Als Porterweiterung wird ein MCP23S17(U2) von der Firma Microchip benutzt. Alle 16 Bit-Ausgänge können als Inputs oder Outputs mit oder ohne Pull-Up Widerstände konfiguriert werden. Port A des MCP23S17(U2) ist direkt am Eingäng des BCD to 7-Segment Umwandlers angeschlossen. Der 74LS248 wandelt die 4-bits Parallel Code in die 7-Segment Information um. Port A von MCP23S17(U2) soll als Ausgang eingestellt werden und die Nummer der gedruckten Taste ausgeben.

Die ersten 4 bits vom Port B sollen als Ausgänge dienen, die Pins 4 bis 6 des Ports B sind Eingänge. Diese Einstellung ist notwendige Bedingung für die Tastaturabfrage, die Tastatur wird dynamisch abgefragt. Was heißt das? Das heißt, dass nur eine Zeile (A,B,C oder D) zu jedem Zeitpunkt eine logische 1 bekommt. Danach wird geprüft, ob die Spalte 1,2 oder 3 auch eine logische 1 hat. Ist das der Fall, dann wird angenommen, dass die entsprechende Taste gedrückt wurde. (Siehe mein Video im Anhang).

Software

Der Quellcode wurde in C in der Umgebung MPLAB IDE geschrieben und in Proteus simuliert.

Der Quellcode besteht aus der Main-Datei, wo der SPI –Modus des Controllers sowie die Porteinstellungen des MCP23S17(U2) vorgenommen wurden. Außerdem beinhaltet die Main-funktion Aufruf der Funktionen „Keyboard();“ und „ISP_Transmition“, die den Tastaturzustand abfragt sowie auf dem Display anzeigt.

Funktion initISP() besteht aus TRISC und TRISB-bits. Damit stellen wir ein, ob ein entsprechender Pin Ein- oder Ausgang ist. SSPCON1bits.SSPM0- SSPCON1bits.SSPM3 - diese Bits Schalten SPI Modus mit Taktfrequenz fOSZ/4; SSPCON1bits.SSPEN schaltet MSSP an. Danach werden die Ports von MCP23S17 als Eingänge oder Ausgänge eingestellt.

Die Datenübertragung zwischen dem Microcontroller und dem MCP23S17 braucht 24 Bit, also 3 Byte.Das erste Byte trägt Information über die HW-Adresse in sich und noch ein Bit besagt ob es um Lese- oder Schreiboperationen geht. Das zweite Byte bezieht sich auf die Registeradresse des ICs (hiermit sagen wir ob Port A bzw. B ein Ein- oder Ausgang ist, greifen auf Ausgabepuffer zu oder lesen die aktuellen Werte aus). Das dritte Byte trägt die Daten. (Siehe Bild 2)

SPI Protokol.jpg

Die Funktion Keyboard(); soll die Zeilen A,B,C und D nacheinander mit 1 verbinden und die Spalten abfragen. Gewisse Binärwerte werden dann interpretiert und entsprechenden Tasten zugewiesen. • Die erhaltene Information über die aktuell gedrückte Taste wird letztendlich binär umgesetzt sowie auf den PortPins A0- bis A3 ausgegeben und in 7 Segment-Form interpretiert.


Erweiterung

Die oben gezeigte Schaltung erlaubt uns weitere Port-Pin Erweiterungen auszuführen und bis zu 8 MCP23S17 anzuschließen. Dabei sollen die HW-Adressen, der anzuschließenden MCP23S17, sowie die Software modifiziert werden. In diesem Fall steigt die Zahl der I/O Pins bis zu 128 plus die eigenen Pins des Microkontrollers. Allerdings muss darauf geachtet werden, dass die Bearbeitungsgeschwindigkeit in diesem Fall abnimmt (Siehe Bild 3). SPI Weiterentwicklung.jpg


Anhang

Video-Beispiel

Video-Beispiel mit zusätzlicher Peripherie

Datei:Maindatei SPI PIC18.c

Datei:Keyboard SPI PIC18.c

Datei:Delay SPI PIC18.c

Mikrocontroller_Datenblatt

MCP23S17_Datenblatt

Quellcode

Main.c


 #include <p18f4520.h>


 #pragma config OSC=ECIO6, PWRT = OFF  , LVP= OFF , WDT=OFF, PBADEN=OFF 


#define   CE LATBbits.LATB0 //Fur SPI Mode





char Keyboard (void); // Funktionsprototyp
void delay (void);


//////Mit dieser Funktion werden die Daten an die I/O Pins ubergeben/////

void SPI_Transmition(char HW_adresse, char Register, char value)

{
CE=0;//Ubertragung AN

SSPBUF=64+HW_adresse+0;  //Byte 1 - HW-Adresse +  Write
while(!SSPSTATbits.BF)
{}

SSPBUF=Register;//Registernummer in HEX
while(!SSPSTATbits.BF)
{}

SSPBUF=value;// Datei-Byte

while(!SSPSTATbits.BF)
{}
CE=1;//Ubertragung AUS
}

//////Mit dieser Funktion werden die Daten von  I/O Pins ausgelesen/////

char SPI_Receive(char HW_adresse, char Register)
{

CE=0;//Ubertragung AN

SSPBUF=64+HW_adresse+1;  //Byte 1 - HW-Adresse +  Read
while(!SSPSTATbits.BF)
{}

SSPBUF=Register;//Registernummer in HEX
while(!SSPSTATbits.BF)
{}

SSPBUF=0;// Datei-Byte

while(!SSPSTATbits.BF)
{}
CE=1;//Ubertragung AUS

return SSPBUF;
}


void initSPI(void)
{
TRISBbits.RB0=0;//Output
CE=1;//High-Pegel
TRISCbits.RC3=0;//Output SCK
TRISCbits.RC5=0;//Output SDO
TRISCbits.RC4=1;//Input SDI

SSPSTATbits.CKE=0;
SSPCON1bits.CKP=1;

SSPCON1bits.SSPM0=0;
SSPCON1bits.SSPM1=0;
SSPCON1bits.SSPM2=0;
SSPCON1bits.SSPM3=0;//Master mode fOSZ/4

SSPCON1bits.SSPEN=1;//SPI Enable

//////MCP23S17  Einstellung////////////////////////////////////
///////////////////PortA ist Output////////////////////////////

SPI_Transmition(0,0x00,0); 

//////MCP23S17  Einstellung////////////////////////////////////
///////////////////PortB ist INput////////////////////////////

SPI_Transmition(0,0x01,0b11110000); //Bits 0-3 Ausgange, 4-7 Eingange
}

void main (void)

{
char temp;

initSPI();//Initialisieren SPI

while(1)// 
{

temp=Keyboard();

SPI_Transmition(0,0x14,temp);//Tastaturabfrage und Ausgabe auf dem 7-Seg Display

}

}

'''Keyboard.c'''

----


#include <p18f4520.h>

void SPI_Transmition(char HW_adresse, char Register, char value);//Prototypen

char SPI_Receive(char HW_adresse, char Register);

void delay (void);

char Keyboard (void)

{
char temp2;
delay();
SPI_Transmition(0, 0x15, 0b00000001);//Logisches 1 auf die erste Zeile
temp2=SPI_Receive(0, 0x13);

if((temp2==17)||(temp2==33)||(temp2==65))

// break- Anweisung ist im Grunde nicht notwendig///

switch(temp2)

{

case 17: return 10;break;
case 33: return 0;break;
case 65: return 11;break;

}

SPI_Transmition(0, 0x15, 0b00000010);//Logisches 1 auf die zweite Zeile

temp2=SPI_Receive(0, 0x13);

if((temp2==18)||(temp2==34)||(temp2==66))

switch(temp2)

{

case 18: return 7;break;
case 34: return 8;break;
case 66: return 9;break;

}

SPI_Transmition(0, 0x15, 0b00000100);//Logisches 1 auf die dritte Zeile

temp2=SPI_Receive(0, 0x13);

if((temp2==20)||(temp2==36)||(temp2==68))

switch(temp2)

{

case 20: return 4;break;
case 36: return 5;break;
case 68: return 6;break;

}

SPI_Transmition(0, 0x15, 0b00001000);//Logisches 1 auf die vierte Zeile
temp2=SPI_Receive(0, 0x13);

if((temp2==24)||(temp2==40)||(temp2==72))

switch(temp2)

{

case 24: return 1;break;
case 40: return 2;break;
case 72: return 3;break;

}

return 14; //Falls nichts gedruckt wurde
//Andere Tastenkombinationen werden nicht behandelt

}

'''delay.c'''

----


void delay (void)

{
int i=0;
int a=0;
for(i;i<4000;i++)

{

for(a;a<20;a++)

{}

} 
}