Der MSP430 verfügt je nach Modell über eine Reihe von hardwareseitig im Chip implementierten Schnittstellen, wie I2C, SPI oder RS232, die das Handling des Kommunikationsprotokolls eigenständig übernehmen, ohne dass die Funktionalität im Einzelnen durch den Anwender selbst programmiert werden muss. Ein weiterer Vorteil ist, dass dafür auch keine CPU-Ressourcen benötigt werden, so dass die Kommunikationsprozesse im Hintergrund ablaufen können. Diese gesamte Funktionalität wird im USART-Modul, dem Universal Synchronous/Asynchronous Reciver/Transmitter zusammengefasst. Insgesamt gibt es modellabhängig maximal zwei unabhängig von einander arbeitende USART-Schnittstellen, USART0 und USART1. Damit kann man zum Beispiel gleichzeitig mit einem PC über die serielle Schnittstelle kommunizieren und Messergebnisse von I2C- oder SPI-Bausteinen erfassen. USART0 belegt beim MSP430F1612 die Pins P3.1 (SIMO0 und SDA), P3.2 (SOMI0), P3.3 (UCLK0 und SCL), P3.4 (UTXD0) und P3.5 (URXDO). Die Pins P3.6 (UTXD1), P3.7 (URXD1), sowie P5.1 (SIMO1), P5.2 (SOMI1) und P5.3 (UCLK1) werden von der Schnittstelle USART1 belegt.
Es gibt eine Reihe von Registern, die für die Steuerung der Schnittstellenparameter verwendet werden. Um den Rahmen des Buchs nicht zu sprengen, können an dieser Stelle nur die wichtigsten Register beschrieben werden, die für einfache Anwendungen konfiguriert werden müssen. Detaillierte Informationen finden sich wie immer im zugehörigen ”MSP430 family user guide”, z.B. [Tex06c]. Als weitere Spezialität vereint ja die USART-Schnittstelle drei verschiedene Betriebsarten unter einem Dach, so dass die Register, je nach gewähltem Betriebsmodus, unterschiedliche Funktionen haben. Für die Auswahl des Modus ist im gleich näher beschriebenen UxCTL-Register das Bit SYNC vorgesehen. Ist das Bit gelöscht, so arbeitet die Schnittstelle im asynchronen RS232-Modus. Wenn das Bit dagegen gesetzt ist, arbeitet die Schnittstelle im SPI-Modus, wobei der I2C-Modus als eine Sonderform des SPI-Modus betrachtet wird. Wir kommen darauf später noch zurück.
Mit dem UxCTL-Register (USART-Control-Register) legen wir den Arbeitsmodus, also das Schnittstellenprotokoll fest. Die vollständige Belegung des UxCTL-Registers im USART-Modus ist in Bild 8.12 dargestellt. Wir müssen dabei beachten, dass sich die Belegung der einzelnen Bits je nach ausgewähltem Schnittstellenmodus unterscheidet. In diesem Unterkapitel gilt natürlich die Belegung des USART-Modus.
Das Bit PENA legt fest, ob das Parity-Bit gesendet (PENA = 1) oder nicht gesendet werden soll (PENA = 0). Mit PEV legt man fest, ob die Übertragung mit gerader (PEV = 1) oder ungerader Parität (PEV = 0) arbeiten soll. Ist das Bit SPB gelöscht, wird ein Stopp-Bit, andernfalls zwei Stopp-Bits gesendet. Einige Schnittstellen arbeiten mit 7-Bit- Worten. Mit dem Bit CHAR kann der Entwickler festlegen, ob die Übertragung auf 7-Bit- (CHAR = 0) oder 8-Bit- (CHAR = 1) Worten basieren soll.
Mit dem SWRST-Bit kann ein Software-Reset für den USART eingeleitet werden. Das Bit sollte immer gesetzt werden, wenn man die Konfigurationen der Schnittstelle ändern möchte. Nachdem die Einstellungen an den Registern vorgenommen wurden, muss man das Bit löschen, um die Schnittstelle nutzen zu können. Die Bedeutung der weiteren Bits ist dem Datenblatt zu entnehmen.
Im UxTCTL-Register, dem Transmit-Control-Register wird mit den Bits SSELx die Taktquelle für die Sendeeinheit der seriellen Schnittstelle definiert. Dafür stehen zwei Bits bereit, deren Bedeutung in Tabelle 8.2 gelistet ist. Bei der Auswahl von UCLKI muss der Takt extern erzeugt werden und am entsprechenden Pin (z.B. P3.3 respektive P5.3 beim MSP43016xx) zur Verfügung stehen.
| SSELx | Taktquelle |
| 00 | UCKLI |
| 01 | ACLK |
| 10 | SMCLK |
| 11 | SMCLK |
| Tabelle 8.2.: | Auswahl der Taktquelle für die serielle Sendeeinheit |
Es bietet sich an, die Auxiliary-Clock (soweit mit Quarz bestückt, vgl. Kapitel 3.2.3) als Taktquelle zu wählen. Damit kann die serielle Schnittstelle auch im Low-Power-Modus LPM3 verwendet werden. Der DCO eignet sich nur bedingt als Taktgeber für die Baudrate, da dessen Frequenz nicht absolut festgelegt werden kann und so potentiell Synchronisationsfehler entstehen können. Wenn man doch auf den DCO als Taktquelle angewiesen ist (zum Beispiel bei fehlendem Schwingquarz) muss dieser für den Betrieb einer asynchronen Schnittstelle genau kalibriert werden (Kapitel 3.1.2).
Die Belegung der Bits des UxTCTL-Registers ist in Bild 8.13 dargestellt. Neben den SSELx-Bits, ist auch das TXEPT-Bit wichtig. Es gibt an, ob im Ausgabe-Pufferspeicher UxTXBUF noch Daten auf die Übertragung warten (TXEPT = 0) oder ob der Puffer leer ist (TXEPT = 1). Bei der Initialisierung der seriellen Schnittstelle sollte das Bit TXEPT gesetzt werden, weil der Transmit-Puffer UxTXBUF leer ist und das SWRST-Bit für die Initialisierung gesetzt wurde. Auf die Bedeutung der übrigen Bits wird an dieser Stelle nicht näher eingegangen, weil sie für die im Folgenden realisierten Beispiele nicht wichtig sind. Für weitergehende Informationen sei daher auf das Datenblatt des MSP430 verwiesen.
Aus der Frequenz der Taktquelle wird mit Hilfe des Baudrate-Control-Registers die Baudrate für eine Übertragung gewählt. Die beiden 8 Bit breiten Register UxBR0 und UxBR1 werden dabei intern als 16 Bit breites Kontrollregister verarbeitet, das den Teilerfaktor des Systemtaktes angibt. Bild 8.14 verdeutlicht den Aufbau der beiden Register. Das MSB ist demnach das achte Bit in UxBR1.
Üblicherweise ist die Taktrate kein ganzzahliges Vielfaches der Baudrate, so dass es zu Fehlern in der zeitlichen Zuordnung der Bitposition im Datenrahmen kommen kann. Im Extremfall entstehen so Übertragungsfehler (natürlich nur bei UART-Betrieb als asynchrone Schnittstelle). Um dies kompensieren zu können, existiert das Modulationsregister UxMCTL, dessen Bitbelegung in Bild 8.15 dargestellt ist. Mit diesem kann für jedes Bit einzeln dessen Position im Datenrahmen noch leicht verschoben werden.
Die Übertragungsrate berechnet sich dann zu:
![]() | (8.1) |
Der individuelle Timingfehler jedes Bits ist gegeben durch:
![]() | (8.2) |
Die Berechnung der besten Bitbelegung des Modulationsregisters in Abhängigkeit von der Baudrate ist ein iterativer Prozess. Zum Glück finden sich dafür im Internet eine Reihe von Tools (zum Beispiel: http://mspgcc.sourceforge.net/baudrate.html), die das Leben vereinfachen.
Viele Anwendungen arbeiten beispielsweise mit einer Baudrate von 9600 Bps. Bei der Wahl eines 32.768kHz-Schwingquarzes über die Taktquelle ACLK ergeben sich dann die in Listing 8.1 definierten Einstellungen.
| Listing 8.1: | kHz]Einstellung der Baud-Rate 9600 für fosc= 32.768 kHz |
Mit den beiden Module-Enable-Registern ME1 und ME2, deren Bitbelegung in Bild 8.16 dargestellt ist, kann man getrennt die Sende- und Empfangseinheit der beiden USARTs ein- und ausschalten.
Mit UTXE0 kann die Sendeeinheit und mit URXE0 die Empfangseinheit von USART0 aktiviert werden, wenn das jeweilige Bit gesetzt ist. Diese Bits sind allerdings bei den MSP430x12xx-Typen im Register ME2 abgelegt. Analog dazu kann man mit UTXE1 und URXE1 die Sende- und Empfangseinheit von USART1 aktivieren oder deaktivieren.
Bei der seriellen RS232-Schnittstelle erfolgt im Gegensatz zum I2C- oder SPI-Bus die Übertragung asynchron, das heißt die Übertragung zwischen den Teilnehmern wird nicht über eine Taktleitung des PCs oder des Mikrocontrollers synchronisiert. Somit werden nur zwei Pins des Mikrocontrollers belegt. Einer wird für das Senden (TxD) und einer für das Empfangen (RxD) von Daten benötigt. Die Abwicklung des eigentlichen Datenverkehrs erfolgt durch die eingebaute Hardware des USART-Moduls. Ein Datenaustausch mit dem USART-Modul erfolgt in beide Richtungen über einen Puffer. Zum Senden muss man die zu sendenden Bits in den Speicher UxTXBUF schreiben, ankommende Daten landen im Puffer UxRXBUF.
Da der Informationsaustausch ohne gemeinsame Taktleitung erfolgt, müssen einige Einstellungen zwischen den beiden Geräten, die über die serielle Schnittstelle kommunizieren, im Vorhinein vereinbart werden. Dazu gehört zum Beispiel die Baudrate oder die Art der Fehlerkorrektur. Dafür stellt jede USART-Schnittstelle neben den beiden Pufferspeichern noch sechs weitere Register zur Verfügung.
Mit den im vorherigen Abschnitt eingeführten Registern werden wir nun die serielle Schnittstelle für
den UART-Betrieb konfigurieren. Es ist wichtig, sich dabei an die Reihenfolge der Initialisierung der
Register zu halten. Die Listings 8.2 und 8.3 können daher als Rahmenkonfiguration
angesehen werden, die bei speziellen Anforderungen an die Schnittstelle angepasst werden
kann.
Für die Schnittstelle USART0 sind beim MSP4301612 die bidirektionalen Pins P3.4 (UTXD0) und P3.5 (URXD0) vorgesehen. Wenn unsere Anwendung eine Zeichenkette über die Schnittstelle senden will, muss somit P3.4 als Ausgang definiert und das entsprechende Bit im Function-Select-Register gesetzt werden. Wenn wir über die serielle Schnittstelle Daten empfangen wollen, muss das Bit für P3.5 im Function-Select-Register gesetzt werden. P3.5 muss darüber hinaus als Eingang definiert werden.
Wir definieren P3.4 als Ausgang und setzen das entsprechende Bit im Function-Select-Register:
Jetzt konfigurieren wir die eigentliche Schnittstelle, wie wir es im vorherigem Abschnitt kennengelernt haben:
| Listing 8.3: | Konfiguration der Schnittstelle |
Im Anschluss definieren wir nun eine nullterminierte Zeichenkette SendString. C terminiert die Zeichenkette automatisch mit Null. Damit ist sichergestellt, dass bei der Ausgabe auch nur die von uns gewünschte Länge übertragen wird. Die Ausgabe erfolgt nämlich byteweise, also Zeichen für Zeichen. Ohne eine Terminierung würde unser Programm nicht das Ende des Strings erkennen und immer weiter Zeichen ausgeben. Das würde sich auf dem PC durch die Ausgabe von vielen Leerzeichen oder Symbolen bemerkbar machen.
Für die Ausgabe müssen wir die Zeichenkette Byte für Byte und damit Zeichen für Zeichen in den Pufferspeicher TXBUF0 schreiben. Die Übertragung des Zeichens erfolgt dann vom MSP430 automatisch. Dabei müssen wir nur beachten, dass die Übertragungsgeschwindigkeit endlich ist. Ein neues Zeichen kann also nur in den Puffer geschrieben werden, wenn er leer ist und damit das vorher gesendete Zeichen übertragen wurde. Dafür lesen wir den Status UTXIFG0 im IFG1-Register aus. Für die Ausgabe unseres Strings ergibt sich folgendes Programmstück:
| Listing 8.5: | Ausgabe eines Strings |
Zum Empfangen einer Zeichenkette können wir einen großen Teil des Programms aus Listing 8.3, wie die Einstellung der Taktquelle und die Initialisierung der seriellen Schnittstelle, verwenden. Jetzt müssen wir jedoch die Empfangseinheit im Register ME1 aktiveren sowie Pin P3.5 als Eingang definieren:
| Listing 8.6: | Aktivierung der Empfangseinheit der seriellen Schnittstelle |
Die von der seriellen Schnittstelle empfangene Zeichenkette werden wir in ein CHAR-Array speichern, das wir am Anfang des Programms definieren müssen. Als maximale Länge wählen wir 20 Zeichen.
Jedes empfangene Zeichen wird im Pufferspeicher U0RXBUF abgelegt und kann dann in das CHAR-Array abgelegt werden. Wir definieren uns, dass das Ende der übertragenen Zeichenkette mit dem Zeichen \r (Return-Zeichen) markiert wird. Beim Einlesen der Zeichenkette müssen wir also auf das Zeichen \r warten und gleichzeitig sicherstellen, dass die maximale Länge (hier 20 Zeichen) nicht überschritten wird. Da ein C-String im Speicher immer mit Null terminiert werden muss, müssen wir am Ende der Zeichenkette \0 anhängen. In Listing 8.8 wird eine mögliche Routine zum Einlesen einer Zeichenkette vorgestellt.
| Listing 8.8: | Routine zum Einlesen einer Zeichenkette |
Für eine typische SPI-Bus-Anwendung müssen wir mindestens eine Chip-Select-Leitung (auch Slave-Select-Leitung genannt), eine Taktleitung, eine Datenleitung für eingehende Informationen und gegebenenfalls noch eine Datenleitung für ausgehende Daten definieren. Als USART-Einheit wählen wir nun USART1, so kann der SPI-Bus beispielsweise gleichzeitig mit der seriellen Schnittstelle über USART0 betrieben werden.
Die Taktleitung CLK ist für Pin P5.3 festgelegt. Der Pin muss somit als Ausgang definiert werden. Die Master-Out-Slave-In-Leitung (MOSI), also die Leitung, die Daten vom Master zum Slave sendet, wird mit Pin P5.1 kontaktiert. Wie bei der Taktleitung muss auch dieser Pin als Ausgang definiert werden. An Pin P5.2 wird die Master-In-Slave-Out-Leitung (MISO) angeschlossen. Über diese Leitung empfängt der Master Informationen und Daten vom Slave. Der Pin muss also als Eingang arbeiten. Der Pin für die Chip-Select-Leitung (CS-Leitung) ist prinzipiell frei wählbar und sollte für den SPI-Master als Ausgang definiert werden. Listing 8.9 verdeutlicht die Initialisierung der für den SPI-Bus benutzten Pins. Wir gehen davon aus, dass der an die CS-Leitung angeschlossene SPI-Chip bei einem Pegelwechsel von HIGH auf LOW aktiviert wird.
| Listing 8.9: | Initialisierung der Pins für den SPI-Bus |
Nun muss die Schnittstelle für den Betrieb im SPI-Modus konfiguriert werden. Um Einstellungen an den Registern ändern zu können, muss dazu wie beim asynchronen RS232-Modus aus dem vorherigen Kapitel das SWRST-Bit im USART-Control-Register gesetzt werden, hier U1CTL, da wir die zweite Schnittstelle verwenden. Erst wenn SWRST gesetzt ist, können wir die weiteren Einstellungen vornehmen. Mit dem Bit SYNC legen wir fest, dass der SPI-Modus verwendet werden soll. Die Datenlänge eines Datenrahmens beim Senden und Empfangen legen wir mit dem Bit CHAR auf acht Datenbits fest. Mit dem Bit MM definieren wir den MSP430 als Master-Controller. Wir könnten den MSP auch als Slave-Controller definieren und damit einen eigenen SPI-Baustein entwickeln. Auf diese Besonderheit gehen wir hier aber nicht weiter ein.
Wie im vorherigen Kapitel müssen wir auch nun wieder die Taktquelle und eine Clock-Frequenz auswählen. Für den Betrieb wählen wir die Sub-Main-Clock SMCLK mit einer Taktfrequenz von 8MHz. Dafür setzen wir das Bit SSEL1 im Register U1TCTL. Mit den Registern U1BR0 und U1BR1 legen wir auf Basis unserer Taktquelle die Clock-Frequenz für die SPI-Schnittstelle fest. Die gewünschte Frequenz stellt sich aus der Frequenz der Taktquelle dividiert durch die Summe aus U1BR0 und U1BR1*256 ein. Der Divisionsfaktor darf dabei nicht kleiner als 2 sein. Das Modulationsregister U1MCTL wird im SPI-Modus nicht benötigt und kann daher auf 0 gesetzt werden.
Die SPI-Schnittstelle lässt sich im so genannten 3-Pin- oder 4-Pin-Modus betreiben. Im 4-Pin-Modus wird zusätzlich Pin STE1 (P5.0) verwendet, um den Zugriff auf den Bus zu koordinieren und so einen Multi-Master-Betrieb zu ermöglichen. Bei einfacher Busstruktur benötigen wir diese Funktionalität allerdings nicht. Mit den Bits CKPH und CKPL definieren wir die Phasenverschiebung zwischen Taktsignal und Pulsfolge für den Taktausgang der SPI-Schnittstelle sowie deren Polarität. Dies verändert den so genannten SPI-Mode, wie wir ihn schon zuvor in Abschnitt 8.1.3, Bild 8.10 kennengelernt haben.
Die gesamte Initialisierung wird in Listing 8.10 vorgestellt. Für die Aktivierung der Schnittstelle muss nun noch das USPIE1-Bit (SPI-Enable) im Register ME2 und das SWRST-Bit im U1CTL-Register gelöscht werden.
| Listing 8.10: | Initialisierung der SPI-Schnittstelle |
Die Schnittstelle ist damit initialisiert und kann für den Empfang oder das Senden von Daten
verwendet werden. Für die folgenden Beispiele verwenden wir das SPI-EEPROM 93LC46A von
Microchip. Es hat den Vorteil, dass es direkt mit dem MSP verbunden werden kann, weil es mit
einer Versorgungsspannung von minimal 2.5V betrieben werden kann. Zudem hat ein
EEPROM im Gegensatz beispielsweise zu einem typischen A/D-Wandler die Möglichkeit,
sowohl Daten zu senden als auch zu empfangen. Damit können wir mit dem gleichen
Baustein sowohl den Empfang als auch das Senden von Daten über die SPI-Schnittstelle
vorstellen.
Für den Betrieb wählen wir als Chip-Select-Pin P5.0, der mit dem Pin 1 (CS) des EEPROMs verbunden wird. Als serieller Taktgeber dient Pin UCLK1 (P5.3), der mit dem Eingang CLK des EEPROMs verbunden wird. Der Pin DI des EEPROMs wird mit dem Pin MOSI, also P5.1 und der Pin DO des EEPROMs mit MISO, also P5.2 kurzgeschlossen. Die von der Bauform abhängige Pinbelegung des EEPROMs ist dessen Datenblatt zu entnehmen [Mic10].
Um das EEPROM zu aktivieren, muss die Chip-Select-Leitung (hier P5.1) auf den HIGH-Pegel gesetzt werden. Das eigentliche Senden eines Bytes an den Chip ist denkbar einfach. Dazu muss das zu sendende Byte in den Sendepuffer, hier U1TXBUF gespeichert werden. Selbst wenn wir nur etwas empfangen wollen, muss auch ein Dummy-Wert gesendet werden, da beide Vorgänge immer gleichzeitig durchgeführt werden und nur durch das Schreiben in den Sendepuffer die Übertragung gestartet wird. Beim nachfolgenden Übertrag des Sendebytes vom Eingabepuffer in das interne Shift-Register (aus diesem wird gesendet) wird UTXIFG1 gesetzt. Zu diesem Zeitpunkt könnten wir ein neues Byte in den Sendepuffer schreiben. Die Übertragung des ersten Bytes ist aber erst abgeschlossen, wenn auch die Antwort eingegangen ist, also URXIFG1 gesetzt ist. Unsere universelle Sende-Empfangsroutine ist in Listing 8.11 gezeigt.
| Listing 8.11: | Senden/Empfangen eines Bytes per SPI |
Das EEPROM kennt eine Reihe von Befehlen, mit denen es sich steuern lässt. Beispielhaft werden wir hier den Befehl ERAL (Löschen des kompletten Speichers), WRITE (Schreiben eines Bytes) und READ (Lesen eines Bytes) kennenlernen. Die Befehle für das EEPROM sind immer 10 Bit lang, wobei bei einigen Befehlen wie ERAL nur die ersten fünf Bit entscheidend sind. Die restlichen Bits werden ignoriert. Das MSB des Befehls ist dabei immer 1, gefolgt vom Opcode, dem eigentlichen Befehl und einer 7 Bit breiten Adresse für den Offset im Speicher, auf den der Befehl angewendet werden soll. Da die Übertragung im MSP immer nur byteweise erfolgt, muss der Befehl so in eine Bytefolge eingetragen werden, dass restliche Bytes am Schluss mit Nullen aufgefüllt werden.
Die Bitfolge für ERAL lautet 10010XXXXX (das X verdeutlicht, dass diese Bits vom EEPROM ignoriert werden). In hexadezimaler Schreibweise entspricht das der Zahl 0x1200. Listing 8.12 verdeutlicht das Löschen des kompletten EEPROMs. Der ERAL-Zyklus beginnt mit fallender Flanke der CS-Leitung. Vor der Ausführung des ERAL-Befehls befindet sich die DO-Leitung im HIGH-Z-Status. Um den Ready/Busy-Status abfragen zu können, muss CS nach mindestens 250ns und vor Ende des ERAL-Zyklus wieder gesetzt werden. Die DO-Leitung wird dann vom EEPROM auf Masse gezogen und wieder gesetzt sobald der ERAL-Zyklus beendet ist.
| Listing 8.12: | ERAL-Befehl |
Jetzt wollen wir ein Byte in den Speicher an Adresse 100 (= 0x64) speichern. Der Befehl in Bit-Schreibweise dafür lautet 101 A6 A5 A4 A3 A2 A1 A0, gefolgt vom Datenbyte, wobei A6-A0 der Offset im Speicher, in diesem Beispiel also 100 ist. Damit benötigen wir zum Senden insgesamt 18 Bit, also aufgerundet 3 Byte. Als Beispielwert werden wir die Zahl 123 in den Speicher schreiben. Zunächst müssen wir uns um die Berechnung der Bytefolge kümmern (Listing 8.13).
| Listing 8.13: | Berechnung der Bytefolge für die Übertragung |
Jetzt können wir, wie in Listing 8.14 gezeigt, b1, b2 und b3 an das EEPROM senden. Wie beim ERAL-Befehl detektieren wir mit dem Zustand des DO-Pins, ob der Vorgang vom EEPROM quittiert wird.
| Listing 8.14: | Wert in EEPROM speichern |
Mit dem READ-Befehl können wir die soeben gespeicherte Zahl aus dem EEPROM lesen. Der Befehl setzt sich aus der Bitfolge 110 (= 0x06), gefolgt von der 7 Bit breiten Adresse der Speicherzelle zusammen. Nachdem das EEPROM nach zehn Takten den READ-Befehl erkannt hat, sendet es über den DO-Pin eine Dummy-Null gefolgt von dem 8 Bit breiten Wert der Speicherzelle. Ähnlich wie zuvor ergibt sich daraus ein über Bytegrenzen verteiltes Befehls- und Datenbyte. Das Schema des READ-Befehls wird in Listing 8.15 verdeutlicht.
| Listing 8.15: | READ-Befehl |
Der I2C-Bus (oft auch TWI für Two-Wire-Interface genannt) lässt sich wie eine SPI-Schnittstelle auf jedem MSP430-Mikrocontroller per Software implementieren. Einige neuere MSP-Typen, wie die MSP430x15x- und MSP430x16x-Baureihe, verfügen schon über eine Hardware-I2C-Bus-Implementierung. Anders als bei der asynchronen Schnittstelle oder dem SPI-Interface unterstützt nur der USART0 den I2C-Bus. Es kann also keine weitere I2C-Bus-Schnittstelle mit dem USART1 betrieben werden.
Das I2C-Modul unterstützt neben der 7-Bit-Adressierung (+ 1 Bit für Schreib-/Lese-Modus) der Standard-Bausteine, darunter viele EEPROMs oder Echtzeituhren, auch die 10-Bit-Adressierung nach dem I2C-Standard 1.0. Die Datenrate beträgt im normalen Modus 100kbps und kann im Fast-Mode auf bis zu 400kbps erhöht werden. Die SCL-Leitung (Serial Clock) ist beim MSP430F1612 mit dem Pin P3.3 und die SDA-Leitung (Serial Data) mit Pin P3.1 verbunden. In der Initialisierung müssen die beiden Pins also vor Gebrauch konfiguriert werden:
Für den Betrieb der Schnittstelle müssen die Bits im Datenrichtungsregister nicht gesetzt werden. Diese Aufgabe übernimmt das I2C-Modul automatisch, zumal die SDA-Leitung bidirektional betrieben wird. Wie bei allen Schnittstellen ist auch beim I2C-Bus die Reihenfolge der Prozessschritte zur Initialisierung des Moduls wichtig.
Das Datenblatt gibt an, dass für die Aktivierung der Sonderfunktion der Pins P3.1 und P3.3 das Bit I2C und SYNC im Register U0CTL gesetzt werden muss. Bild 8.18 zeigt die dann geltende Registerbelegung des UxCTL-Registers im I2C-Modus.
Jetzt kann im I2CTCTL-Register, dessen Bitbelegung in Abbildung 8.19 dargestellt ist, die Taktquelle gewählt werden. Wählt man als Taktquelle SMCLK, so wird das Bit I2CSSEL1 gesetzt. Der Zustand von I2CSSEL0 ist dabei nicht wichtig. Alternativ kann man auch als Taktquelle ACLK wählen. Dafür muss I2CSSEL1 gelöscht und I2CSSEL0 gesetzt sein. Sind beide Bits gelöscht, ist das I2C-Modul deaktiviert. Die grau hinterlegten Flags können nur geändert werden, wenn das Bit I2CEN im U0CTL-Register gelöscht ist.
Das Bit I2CWORD legt fest, ob der Datenrahmen bei der Übertragung 8 Bit (I2CWORD gelöscht) oder 16 Bit (I2CWORD gesetzt) betragen soll. Mit I2CRM wird festgelegt, wie der Datenfluss vom internen I2C-Modul kontrolliert wird. Zum einen kann man manuell und selbständig per Software festlegen, wie viele Bytes innerhalb eines Datenrahmens begrenzt durch eine Start-Stopp-Bedingung gesendet werden (I2CRM = 1), zum anderen kann man auch automatisch nach einer im Register I2CNDAT festgelegten Anzahl an Bytes automatisch eine Stopp-Bedingung vom MSP aufrufen lassen (I2CRM = 0). Wir wählen I2CRM = 0 und legen später fest, dass zwischen einem Start-Stopp-Datenrahmen jeweils ein Byte gesendet wird. Auf die Bits I2CSTB, I2CSTP und I2CSTT kommen wir später noch zurück.
Als Nächstes initialisieren wir die beiden Shift-Clock-Register I2CSCLH und I2CSCLL. Sie bestimmen zusammen mit einem Vorteiler, dem I2CPSC-Register, auf das wir hier nicht näher eingehen werden, die Frequenz und das Tastverhältnis. Mit dem I2CSCLH-Register legen wir fest, wie lange der HIGH-Pegel innerhalb einer Taktperiode dauert. Analog dazu legen wir mit dem I2CSCLL-Register fest, wie lange innerhalb einer Taktperiode der LOW-Pegel dauert. Bei den Beispielen im MSP430-User-Guide [Tex06c] von Texas Instruments ist das I2CPSC-Register gelöscht (der Vorteiler ist dann 1) und I2CSCLH und I2CSCLL haben beide den Wert 3. Damit ist die Dauer des HIGH- und LOW-Pegels fünf Takte der Taktquelle (hier SMCLK) lang. In unseren Beispielen arbeiten wir meist mit einer Taktfrequenz für die Taktquelle SMCLK von 1MHz. Die Bus-Frequenz des I2C-Busses beträgt somit 100kHz.
| Listing 8.18: | I2C-Clock- und Datenrahmen-Initialisierung |
Im Register I2CSA legen wir die 7 Bit breite Slave-Adresse des Bausteins fest, der angesteuert werden soll. Analog dazu gibt es auch ein I2COA-Register, in dem beim Betrieb des MSP430 als Slave die eigene Adresse abgelegt werden kann. Wie fast alle Komponenten des MSP430 lässt sich auch die I2C-Schnittstelle mit einem Interrupt verknüpfen. Es ist sinnvoll, einen Interrupt für eingehende Daten aufzurufen und diese dann in eine Variable zu speichern. So muss man während des Programmverlaufs keine Schleife laufen lassen, um Daten über die Bus-Schnittstelle zu empfangen. Für die Interrupt-Verknüpfung setzen wir das Bit RXRDYIE im I2CIE-Register.
| Listing 8.19: | Interrupt-Aktivierung |
Im I2CIE-Register lassen sich noch weitere Interrupts definieren. Diese sind aber vor allem für den Betrieb des MSP430 im Slave-Modus nützlich, auf den wir hier nicht näher eingehen. Ein wichtiger Interrupt kann noch mit dem Bit TXRDYIFG aktiviert werden. Damit wird ein Interrupt nach einer erfolgreichen Übertragung der Daten über den Bus ausgelöst. Auf die für die eingehenden Daten erforderliche Interrupt-Service-Routine kommen wir später zurück. Jetzt wurden alle Einstellungen für den Betrieb des I2C-Busses vorgenommen. Das Modul kann nun mit der folgenden Codezeile aktiviert werden:
Wir wollen mit dem I2C-Bus nun das serielle EEPROM 24LC02 ansteuern. Als Beispiel soll wie schon zuvor in das EEPROM an Offset 100 der Wert 123 gespeichert und anschließend ausgelesen werden. Die I2C-Basisadresse des EEPROMS ist werksseitig als 1010 definiert. Intern ist das EEPROM in acht Blöcke mit jeweils 256 Byte Speicher aufgeteilt. Um die Blöcke ansteuern zu können, benötigt man zur Adressierung drei Bits (23 = 8): B0 bis B2. Diese werden in die Adresse hinein kodiert, Bild 8.20.
Die Speicherstelle liegt im ersten Block (also B0 = B1 = B2 = 0) am Speicherplatz 100. Die Adresse lautet dann in hexadezimaler Schreibweise für den Schreibzugriff 0xA0 und für den Lesezugriff 0xA1. Zuvor müssen wir im Register I2CTCTL das Bit I2CRM setzen, weil wir den Datenfluss nur per Software steuern werden. Die Adresse des EEPROMs legen wir in das Register I2CSA ab. Es werden nur die höchsten sieben Bits gespeichert, da das R/W-Bit automatisch gesetzt oder gelöscht wird. Also schreiben wir in I2CSA:
Wenn wir auf das EEPROM schreibend zugreifen wollen, müssen wir im Register U0CTL das Bit MST setzen, weil der MSP430 im Master-Modus arbeitet. Das Bit muss bei jedem Zugriff erneut gesetzt werden, weil es nach einem Datenaustausch oder einer Stopp-Bedingung automatisch gelöscht wird. Eine Start-Bedingung erzeugen wir, indem wir im Register I2CTCTL das Bit I2CSTT setzen. Dabei generiert der Controller automatisch eine Start-Bedingung und löscht danach das Bit I2CSTT. Mit dem Bit I2CTRX legen wir fest, ob wir lesend oder schreibend auf das EEPROM zugreifen werden. Alternativ könnte man den Beginn eines Datenaustausches auch mit Setzen des Bits I2CSTB einleiten. Ein Byte wird gesendet, indem man auf das Register I2CDRB schreibend zugreift. Die Stopp-Bedingung wird automatisch nach zwei übertragenen Bits generiert. Die folgenden Code-Zeilen speichern den Wert 123 in die Speicherstelle 100:
| Listing 8.22: | Wert in EEPROM speichern |
Der Ablauf zum Lesen einer Speicherstelle ist in Bild 8.21 dargestellt. Zunächst muss man schreibend die Speicheradresse der Speicherstelle senden und dann anschließend lesend das Datenbyte in Empfang nehmen. Dazu muss nach dem Senden der Speicheradresse eine erneute Start-Bedingung (Restart-Bedingung) gesendet werden. Das I2CRM-Bit muss gelöscht sein um eine Restart-Bedingung senden zu können.
Nachfolgendes Listing 8.23 zeigt das Senden der Speicheradresse und der Restart-Bedingung:
| Listing 8.23: | Offset der Speicherstelle senden |
Nun können wir in einer Interrupt-Service-Routine das empfangene Datenbyte speichern. Die Interrupt-Service-Routine hat den Aufbau:
| Listing 8.24: | Interrupt-Service-Routine zum Lesen einer Speicherstelle aus dem EEPROM |
Bei einem Interrupt der I2C-Schnittstelle werden im Register I2CIV die entsprechenden Flags des Interrupt-Aufrufs gesetzt. Wird ein Byte empfangen, so steht im Register I2CIV der Wert 10. Wenn Daten über den Bus erfolgreich gesendet wurden, so ist im Register I2CIV der Wert 12 gespeichert.