Entwicklungsboard mit AT91SAM7Sxxx - selbstgemacht
Controller mit ARM7 Kern sind toll. Wirklich.
Der Einarbeitungsaufwand ist nicht so groß wie viele glauben und wenn man den Schritt einmal gewagt hat, wird man von den Möglichkeiten begeistert sein. Und dann wäre da auch noch der Preis: 6,25€ bei Reichelt für den AT91SAM7S64 bzw. 9,85€ für die große Variante SAM7S256 Dafür bekommt man höchstens einen ATMEGA128 mit einem Bruchteil des Speichers, Geschwindigkeit und Peripherie. AVRs setze ich nur mehr entweder im DIP-Gehäuse für schnelle Basteleien ein, oder aber wenn es besonders stromsparend sein soll.
Dieser Artikel beschreibt die Fertigung eines Entwicklungsboards zum selbst Ätzen aus leicht erhältlichen Teilen und behandelt auch kurz die Inbetriebnahme und Programmierung mit Gratisprogrammen.
Leider muss ich mich hier auf Windows (XP) beschränken.
Vogel – und Obstfreunde habens leider nicht so leicht und müssen sich auf einschlägigen Seiten informieren. Vista zickt (natürlich?) ebenfalls, es gibt jedoch einen extra Artikel darüber.
Einleitung
Die Firma ARM entwickelt Mikrocontrollerkerne verschiedener Leistungsklassen, stellt jedoch keine Endprodukte her, sondern lizenziert die Architektur an Halbleiterproduzenten. Diese ergänzen den Kern mit Peripherieeinheiten und fertigen den Chip. ARM – Lizenznehmer sind u.a. TI, Analog Devices, Nintendo, NXP (ex. Philips) und Atmel. Diese Hersteller bieten zu sehr aggressiven Preisen Mikrocontroller mit softwarekompatiblem Kern, aber unterschiedlicher Auslegung der Peripherie an. So verfügen die Modelle von Analog Devices beispielsweise über einen sehr schnellen ADC und DAC (ADuC70xx, sehr empfehlenswert wenn man was regeln muss...), NXP hat bei den LPC21xx /LPC23xx Modellen ein schnelles Speicherinterface und Atmel bei der AT91SAM-Reihe einen mächtigen DMA-Controller und viele, hochwertige Schnittstellen. NXP und Atmel implementieren bei einigen Modellen auch einen USB 2.0 Full-Speed Controller (12Mbit/s).
Qual der Wahl
Meine Wahl fiel zu Gunsten von Atmel aus, da der AT91SAM7S zwar durch den langsameren Speicher nicht die Geschwindigkeit des NXP-Controllers erreichen kann, aber dafür bessere Hardware-Schnittstellen und weniger Designfehler aufweist. Unter anderem bis zu 55MHz SPI-Takt möglich, damit kann man z. B. einen schnellen seriellen ADC ansteuern.
Datenblätter, in denen die (ganz tolle!) gewünschte Spezifikation heruntergebetet wird sind mir absolut unsympathisch und erhöhen nicht gerade das Vertrauen ins Produkt, wenn dann im fast dickeren Errata-Sheet steht, was sie nicht richtig hinbekommen haben. Ich habe auch auf anderen Gebieten schlechte Erinnerungen an Philips und mir daher, auch aufgrund der positiven Erfahrungen sowohl mit AVRs als auch ATMEL- Mitarbeitern, für AT91SAM7S entschieden. ATMEL schreibt nur Dinge die funktionieren ins Datenblatt rein und lässt andere gleich weg (TWI Master, RTC mit Uhrenquarz etc. waren sicher mal geplant, haben aber nicht funktioniert, also stehen sie auch nicht im Datenblatt) So freut man sich nicht auf Dinge die man dann aber doch nicht nutzen kann.
Thumb Mode
Die verwendete Controllerfamilie ist die zweitkleinste aus der ARM–Serie AT91SAM von Atmel mit hardwarebasierter USB- Schnittstelle. Es handelt sich hierbei um einen Controller mit dem ARM7 TDMI RISC Kern mit 3-stufiger Pipeline und gemeinsamen Instruktions- und Datenbus von ARM. Obwohl der Controller 32-bitig ausgeführt ist, kann er auch 16-Bit breiten „THUMB“-Code (Daher das T im Namen) ausführen, welcher zwar weniger mächtig, dafür aber speichersparender ist. Da der interne Bus jedoch nur 32Bit breit ist und der Flashspeicher nur mit max. 30MHz ausgelesen werden kann, muss man, um auch bei höheren Taktraten des Controllers nicht auf Befehle aus dem Flash warten zu müssen, diese entweder im 16-Bit Thumb-Modus kompilieren, sodass 2 Instruktionen parallel gelesen werden können, oder aber zeitkritische Routinen (Interruptroutinen, Hauptschleife) vor der Ausführung ins RAM kopieren, wie dies bei PCs üblich ist.
Varianten und Speicher
Der AT91SAM7S64 verfügt über 64kBytes Flash-Speicher, welcher u.a. mit Hilfe eines Bootloaders über USB oder UART programmiert werden kann, sowie über 16kByte SRAM. Reicht dies nicht aus, kann man auf die pinkompatiblen, größeren Modelle zurückgreifen.
- AT91SAM7S128 (128k Flash, 32k RAM)
- AT91SAM7S256 (256k Flash, 64k RAM)
- AT91SAM7S512 (512k Flash, 64k RAM), das „Flaggschiff“
Das Gehäuse ist entweder ein 64-Pad QFN oder ein 64-Pin LQFP. Die kleineren Variante (SAM7S16/32/321) im 48-Pin Gehäuse sind schwer erhältlich und kaum bis gar nicht billiger. Darüberhinaus verliert man die Möglichkeit für ein pinkompatibles Upgrade. Es gibt auch größere Varianten mit Ethernet – und CAN-Controller (SAM7X) sowie externem Speicherinterface (SAM7SE).
Takt- und Stromversorgung
Die Taktversorgung übernimmt ein externer Quarz mit 3 bis 20MHz, woraus mittels On-Chip PLL der Haupttakt von max. 55MHz sowie die Hilfstakte für die Peripherieeinheiten gebildet werden. Es kann auch eine externe Taktquelle mit max. 55MHz direkt eingespeist werden. Um den fest installierten USB-Bootloader zu verwenden, wird ein 18,432MHz Quarz benötigt, woraus das 48MHz Signal für den USB-Controller generiert wird. In dieser Konfiguration versorgt derselbe Takt auch den Kern, er läuft also etwas unterhalb der maximal möglichen Geschwindigkeit. Andernfalls arbeitet der Bootloader nur mit RS232. Ein integrierter Taktcontroller kann bis zu 3 verschiedene Taktsignale an Pins ausgeben, was bei einigen Projekten ein unschätzbarer Vorteil gegenüber anderen Controllern ist, bei denen man den Takt für andere ICs extra generieren müsste. Der Chip benötigt nur eine 3,3V Stromversorgung. Diese sollte jedoch relativ glatt und stabil sein. Der ARM-Kern selbst wird mit 1,8V versorgt, welche ein On-Chip Spannungsregler zur Verfügung stellt. Dieser muss aber noch richtig verbunden werden. Mit einem zusätzlichen RS232 Levelkonverter und einem Linearregler, um die 3.3V Betriebsspannung zu erzeugen, liegt der gemessene Verbrauch des Mikrocontrollers mit allen benötigten Schnittstellen im vollen Betrieb bei ca. 40mA bei 5V Versorgungsspannung. Bei batteriebetriebenen Anwendungen ist es ratsam, den eingebauten Längsregler zu deaktivieren und durch einen effizienteren kombinierten 3,3V und 1,8V Schaltregler zu ersetzen und somit den Gesamtverbrauch zu senken sowie nicht benötigte Teile abzuschalten. Der Controller verfügt außerdem über einen eingebauten Resetcontroller und Watchdog-Timer, was die notwendige externe Beschaltung weiter vereinfacht. Natürlich ist hier der Hinweis angebracht, das man bei besonderen Anforderungen besser einen externen Supervisor IC anhängen sollte.
Ausstattung
Die weitere Ausstattung liest sich wie die Wunschliste eines Mikrocontrollerprogrammierers:
- Interruptcontroller mit 8 verschiedenen Prioritäten
- 2 externe Interrupts
- JTAG Schnittstelle zum Debuggen
- I/O Controller mit 32 Pins, jeweils mit zuschaltbarem Pull-up Widerstand und Anti-Glitch-Filter zum einfachen Entprellen.
- Zähler/Timer
- 4-Kanal 16-Bit PWM-Controller
- 3 programmierbare Taktausgänge
- 8-Kanal 10-Bit ADC mit 384ksps.
Die Ein-und Ausgänge sind 5V tolerant, d.h. sie können direkt ohne Pegelwandler an mit 5V versorgte ICs angeschlossen werden. Es muss jedoch ev. beachtet werden, das nach dem Reset die IO-Pins über einen Pullup – Widerstand von 100-200kΩ auf 3,3V gezogen werden, was beim anlegen von 5V an die Pins zu einem Stromfluss durch den Controller führt. Der schon erwähnte DMA Controller ermöglicht den Empfang von Daten ohne Eingreifen des ARM-Kerns, indem er sie von der externen Schnittstelle direkt in den RAM schreibt bzw. im RAM abgelegte Daten sendet. Zur Kommunikation mit anderen ICs sind ein SPI-Interface (max. mit Controllergeschwindigkeit taktbar!), eine I²C-Schnittstelle (von Atmel TWI genannt), ein I²S Interface zur Kommunikation mit Audio – ICs, ein SSC (Synchronous Serial Controller), 2 USARTS mit erweiterten Funktionen und natürlich die allseits bekannte und beliebte (oder auch nicht) 12Mbits/s Full-Speed USB 2.0 Schnittstelle vorhanden.
Hardware
Selbst ist der Bastler
Es gibt zahlreiche günstige Entwicklerboards für die AT91SAM7S Serie. Aber selbermachen ist einfach interessanter und lehrreicher. Bestellen kann ja jeder...
Das hier vorgestellte Headerboard passt schön in die billigen Steckbrettchen („STECKBOARD 2K1V“, aber auch in andere) von Reichelt und kann von jedem selbst gebaut werden, der zweiseitige Platinen Ätzen kann. Es ist natürlich nur als Vorschlag gedacht. Ich werde jedoch hier weder auf Ätztechniken noch auf SMD Löten eingehen, da es dafür einfach schon zu viele Artikel gibt. Es sei nur gesagt, dass die „Zuerst alle Beinchen mit viel Zinn vollschmieren und danach mit Entlötlitze alles überflüssige entfernen“ Methode bei mir ausgezeichnet funktioniert.
Leider ist es aber notwendig, Durchkontaktierungen (VIAs) zu setzen. Draht durchstecken und auf beiden Seiten verlöten geht aber hier nicht, da sich bei meinem Layout einige VIAs unter dem Mikrocontroller befinden. Man braucht also Durchkontaktierungsnieten (0,6mm Innendurchmesser) z. B. auch von Reichelt als NIETEN 0,6MM. Die ziemlich teure und trotzdem sehr wackelige Presse ist aber nicht nötig. Mit etwas probieren kann man die Nieten mit einer Pinzette wunderbar durch ein 0,8mm Loch stecken und dann, je nach gewünschtem Perfektionismus, auf der anderen Seite entweder nur festlöten oder vorsichtig mit Hammer und einem ev. leicht konischen und vor allem feinen Stift auch dort flachklopfen.
Viel Spass beim Experimentieren.
Einfach ist besser
Meine Platine wurde nicht auf geringstmöglichen Platzbedarf optimiert, sondern auf leichten Aufbau, Verwendung von Standardbauteilen (alles bei Reichelt zu bekommen) und möglichst großer Flexibilität hin entwickelt. Wenn man 3 Löcher bohrt kann man anstelle des vorgesehenen DPACK-Gehäuses z. B. problemlos TO220 Spannungsregler verwenden. Ich habe einen LF33CV verwendet, jeder andere Linearregler welcher bei 4V Eingangsspannung noch 3,3V erzeugen kann geht aber auch. Falls man kein Steckbrett verwenden will können die Stiftleisten auch an der Oberseite montiert werden. Die Verbindung zu anderen Teilen kann man dann sehr einfach mit Drahtstücken herstellen.
Im Layout sind die unbedingt empfehlenswerten 100nF Blockkondensatoen nicht eingezeichnet. Man muss daher bei jedem Via, welches die Spannungsversorgungspins des Controllers mit der Rückseite verbindet, einen 100nF Kerko auf Masse löten.
Quarz und USB Bootloader
Ich habe mich weitgehend an Olimex und Original ATMEL Schaltplänen orientiert. Empfehlenswert ist auch das ATMEL-Dokument „AT91SAM7S Microcontroller Series Schematic Check List“. Der Quarz ist ein 18,432MHz Baudratenquarz, welcher vom integrierten Bootloader bevorzugt wird, und mit dem man auch wunderbar die USB- Taktrate erzeugen kann. Die KERKOS beim Quarz müssen zwar eigentlich auf den verwendeten Typ angepasst werden (Datenblatt Quarz + µC). 22pF haben bislang aber noch immer und überall funktioniert... Wird ein anderer Quarz verwendet, muss der PLL-Filter angepasst werden. Es gibt hierfür ein Berechnungstool bei ATMEL. Der Filter sowie der Quarz sollen möglichst nahe am IC liegen um Störungen zu minimieren. Da ein interner Resetcontroller eingebaut ist und dieser auch verwendet wird, muss der /RESET PIN nicht beschaltet werden. Es existieren mehrere Varianten der USB-Pullup Schaltung. Die von mir gewählte hat den Vorteil, dass der Widerstand im Normalzustand mit VCC verbunden ist und die Schaltung damit dem PC gleich beim Einstecken den Anmeldewunsch signalisiert, wie es der Bootloader haben möchte. Will man dies nicht, hängt man die Basis des npn-Transistors an einen Pin des Controllers (Olimex verwendet PA16) und kann sich so bei Bedarf anmelden lassen. Soll die Schaltung ausschließlich USB-Powered laufen und sich immer an den PC anmelden, kann der Widerstand auch direkt an VCC hängen. Es ist auch möglich den Pullup-Widerstand direkt an einen Pin des Controllers zu hängen. Im Ausgangszustand hängt die Datenleitung dann aber über 1,5k + 10-15k vom internen Pullup auf VCC. Dieser Fall ist nicht Spezifikationskonform, aber ich habe schon einige Schaltungen damit gesehen. Zusätzlich ist noch ein Spannungsteiler vorgesehen um festzustellen, ob die 5V USB-Spannung anliegen. Aufgrund der 5V-Toleranz der Eingänge wäre dies zwar nicht unbedingt nötig, aber sicher ist sicher, vor allem weil die USB-Spannung ja auch größer sein kann.
Schaltplan
Alle nicht bezeichneten Kondensatoren sind 100nF, die Widerstände 10k
Stückliste
Der AT91SAM7S256 empfiehlt sich für die Entwicklung, da er grosse Ressourcen an RAM und FLASH bereitstellt. Man kann aber natürlich auch nur den 64er nehmen, wenn man sicher ist mit 16kB RAM auszukommen.
- Vias (siehe Text)
- Spannungsregler incl. der benötigten Kondensatoren. Muss bei 4,5V- Spannungsabfall an der Schutzdiode noch 3,3V liefern können.
- Schutzdiode (bedrahtet) z. B. 1N4001 oder auch Schottky
- 100nF Kerko SMD 0603 (15x)
- 15pF Kerko SMD 0603 (2x)
- Quarz 18,432MHz, HC49U-S incl. Kondensatoren
- 15pF Kerko SMD 0603 (2x)
- 1nF + 10nF Kerko SMD 0603 für PLL-Filter
- Widerstände SMD 0603: 1k5(2x), 15k, 45k, 27Ω (2x), 10k (3x)
- SMD-LED + Vorwiderstand für 3,3V (2x)
- 2,2µF KERKO oder ELKO
- Stiftleisten (siehe Text)
- Wannenstecker 20Pol falls JTAG gewünscht
- Taster für Reset falls gewünscht
- USB-B Buchse Print
- Beschaltung für USB-Pullup-Widerstand nach Wahl (Text!)
Layout
Es werde Licht (Testlauf)
Der AT91SAM7S Bootloader ist leider ziemlich unpraktisch (NXP und Analog haben das wesentlich besser hinbekommen!) und daher nur als Notfallmaßnahme einzusetzen. Zum erstmaligen Einrichten und testen ist er aber optimal: Zuerst muss SAM-BA heruntergeladen und installiert werden. Achtung: Der Neustart ist tatsächlich notwendig, sonst funktioniert es nicht. Dann wird der TST Pin des Controllers mit VCC verbunden (per Jumper) und die Schaltung mit 5V versorgt. Man kann auch den USB-Stecker einstecken. Windows wird dann jedoch jammern, das das USB-Gerät nicht erkannt wurde, außer man hat vorher den Steuerungspin der Pullup-Beschaltung auf GND gehängt Der Transistor sperrt dann und der Widerstand hängt frei.. Nach ca. 10-15sec kann man wieder abstecken und TST entweder offen lassen oder auf GND legen (Jumper umstecken). Nun verbindet man das Board über USB mit dem PC. Es sollte sich als SAM7Sxx Testboard anmelden (Pullup auf VCC?). Ist dies erfolgreich, kann man SAM-BA starten, USB als Connection und den verwendeten Mikrocontrollertyp (+ -EK für Evaluation Kit, Atmel geht davon aus das alle ihr Paket kaufen...) auswählen. Man kann nun ein beliebiges Testprogramm, z. B. aus den zahlreichen Beispielen von ATMEL herunterladen. Leider darf ich dieses hier nicht zur Verfügung stellen, aber die Beispielprogramme sollte man sich eh ansehen… Die Frage nach dem Unlock beantwortet man mit YES und wenn er dann nachfragt ob die Bereiche wieder Gelockt werden sollten verneint man dies. Der Controller ist nun nicht mehr gesperrt und kann einfach über JTAG programmiert werden.
Hat der Test nicht funktioniert sollte man einige Dinge überprüfen:
- Spannungen 3,3V + 1,8V OK?
- USB+ mit 1,5KOhm auf VCC gezogen?
- Schwingt der Quarz? Ev. Wert der KERKOS doch überprüfen
- Wenn möglich Stromaufname mit gedrücktem RESET-Knopf messen und mit Normalbetrieb vergleichen
- Die Pins PA0-3 müssen HIGH-Pegel haben (Interne Pullups, Siehe Datenblatt)
- /RESET muss offen oder höchstens mittels Pullup auf VCC gezogen sein
- die Pins ERASE und JTAGSEL müssen offen oder auf GND gelegt sein
- Spannung am TST-Pin in beiden Fällen (VCC + GND) überprüfen
Wenn das Board nun (endlich?) funktioniert, kann man sich dem Programmieren zuwenden.
Die Softwareseite
Die ARM-Prozessorkerne werden von der Gnu Compiler Collection (gcc) voll unterstützt. Der ist gut und gratis, wie hier allgemein bekannt sein sollte. Der gcc Compiler ist in der Lage, sowohl 32Bit Code als auch 16-Bit Thumb Code sowie Mischcode zu erzeugen. Es gibt zahlreiche Open-Source Entwicklungsumgebungen für ARM Prozessoren. Hervorzuheben sind WinARM, ein eher kompaktes Paket, und YAGARTO, welches Eclipse als Entwicklungsumgebung einbindet. Es gibt bei ATMEL auch eine umfangreiche Beschreibung incl. Beispielcode, welche die Einrichtung einer kompletten Entwicklungsumgebung mit JTAG - Debugger erklärt. Auf weiterführende Erklärungen soll daher hier verzichtet werden, auch weil die Programme sehr stark weiterentwickelt werden und die Informationen daher bald veraltet wären. Empfehlenswert ist auch die Homepage von Atmel mit zahlreichen Beispielen und Application Notes. Ganz nett ist auch AT91.com, auch wenn die Informationen dort etwas verstreut liegen und ohne Anmeldung fast nix geht.
Programmierung (nur ganz kurz, versprochen...)
Allgemeine ARM Programmierung
Mikrocontroller werden im Allgemeinen in C programmiert. Assembler macht auf Maschinen dieser Leistungsklasse nur in Ausnahmefällen (Startup-Code, siehe unten) einen Sinn. Auf Grundlagen der Sprache wie z. B. Strukturen und Grundlagen der Zeiger wird hier nicht weiter eingegangen. Dafür gibt’s tolle C Tutorials oder sogar Bücher. Der verwendete Compiler kennt für die ARM-Architektur folgende Datentypen:
- char 1 Byte
- short 2 Bytes
- Int, long 4 Bytes
- long long 8 Bytes
- float 4 Bytes
- double 8 Bytes
Bei float und double handelt es sich um Fließkommazahlen. Da ihre Verarbeitung auf dem Mikrocontroller ohne Fließkommaeinheit, wie dem hier verwendeten, sehr rechenaufwändig ist, sollte man wenn möglich auf sie verzichten und auf Festkommaarithmetik zurückgreifen, da der Speicher auf diesen kleinen Systemen meistens ebenfalls kostbar ist. Die Festkommatypen gibt es jeweils vorzeichenlos (unsigned) und vorzeichenbehaftet.
Alle Peripherieeinheiten des Controllers werden durch das Setzen von Werten in bestimmten Registern konfiguriert. Ein Register ist nichts anderes als eine bestimmte Adresse im Adressbereich des Controllers. Bei einem 32Bit Controller ist jedes dieser Register 32Bit breit. Atmel liefert für jeden Controller eine header-Datei (.h) in welcher u.a. für jedes Register einfacher merkbare Namen definiert werden. Der weiter unten beschriebene PIO Controller hat z.B seine Basisadresse bei 0xFFFFF400. Da diese Adresse schwer merkbar ist, wird sie mit
#define AT91C_BASE_PIOA ((AT91PS_PIO) 0xFFFFF400) //Base Address
als AT91C_BASE_PIOA definiert. Es ist im Programm also egal ob man 0xFFFFF400 oder AT91C_BASE_PIOA schreibt. Bei AT91PS_PIO handelt es sich um einen unsigned Integer Wert. Ab dieser Adresse liegen die einzelnen Konfigurationsregister. In der header-Datei ist zum Zugriff auf diese eine Struktur definiert:
typedef struct _AT91S_PIO {
AT91_REG PIO_PER; // PIO Enable Register
AT91_REG PIO_PDR; // PIO Disable Register
AT91_REG PIO_PSR; // PIO Status Register
...
} AT91S_PIO, *AT91PS_PIO;
Zum Zugriff auf diese Speicherzellen werden Zeiger (Pointer) verwendet. Dies kann auf 2 verschiedene Arten geschehen:
AT91PS_PIO pPIO = AT91C_BASE_PIOA; // Global Pointer to PIO
//ODER
AT91S_PIO *pPIO = AT91C_BASE_PIOA; // Global Pointer to PIO
pPIO ist ein Zeiger welcher auf die Struktur PIO_PER an der Adresse AT91C_BASE_PIOA zeigt. Beide Deklarationen sind gleichwertig und ihre Verwendung von persönlichen Vorlieben abhängig. Um auf das richtige Register im richtigen Speicherbereich zuzugreifen verwendet man Zeigeroperationen:
pPIO->PIO_PER = 0xFFFFFFFF;
setzt alle 32Bit im PIO_PER Register. Das Präfix 0x bedeutet, das die Zahl hexadezimal dargestellt wird und x->y ist eine Abkürzung für den Befehl (*x).y, also ein Zeiger auf eine in einem Struct liegende Variable. Normalerweise weist man Registern jedoch nicht direkt einen Wert zu, sondern setzt einzelne Bits.
#define LED_MASK ( (1<<0)|(1<<1)|(1<<2)|(1<<3) )
(1<<n) ist eine Bitschiebeoperation. 1 wird binär um n Bits nach links verschoben. (1<<3) ergibt binär dargestellt 1000. „|“ ist der ODER – Operator. LED_MASK hat also am Ende den binären Wert 1111. Die ersten 4 Bits im Register werden nun folgendermaßen gesetzt:
pPIO->PIO_PER = LED_MASK; // Enable PIO for LEDs
Eine kleine Besonderheit des AT91SAM7 ist, das die Register kein Löschen von Bits erlauben bzw. benötigen. Man muss/kann dazu das Disable- oder Clear-Register verwenden. Das Gegenteil des vorherigen Befehls lautet daher:
pPIO->PIO_PDR = LED_MASK;// Disable PIO for LEDs
Dies erlaubt eine sehr einfache, sichere und schnelle Programmierung ohne die sonst bei Mikrocontrollern notwendigen Bitoperationen ( & AND, | OR) beim Schreiben in Register. Manchmal benötigt man von einer Zahl nur einen Teil. Um z. B. nur die unteren 8 Bits einer Zahl n zu erhalten, um diese beispielsweise über USB zu versenden, verwendet man ((n) & 0xFF) Dieser Befehl führt eine binäre AND Verknüpfung mit binär 11111111 durch. Alle höheren Bits werden damit zu 0. Die Bitschiebeoperationen << und >> kann man auch für eine schnelle Multiplikation bzw. Division durch Potenzen von 2 verwenden. ((n) >> 2) teilt n durch (2^2) = 4 und ((n) << 3) multipliziert die Zahl mit (2^3) = 8. Dies ist jedoch nicht unbedingt notwendig, da der Compiler diese Optimierungen (wenn sinnvoll und möglich) automatisch durchführt. Bei der Programmierung von Peripheriecontrollern muss beachtet werden, das diese nach dem Einschalten bzw. nach einem Reset nicht mit einem Taktsignal versorgt werden und man dieses erst einschalten muss, indem man den Power Managment Controller konfiguriert.
*AT91C_PMC_PCER = (1 << AT91C_ID_PIOA); // Enable Clock for PIO
Makefile
Das Programm make mit der dazugehörigen Konfigurationsdatei Makefile dient dazu, den Kompiliervorgang zu automatisieren. Verwendet werden dabei die Optionen all (kompiliert das gesamte Projekt), clean (löscht alle durch den Compiler erzeugten Dateien) sowie program. Diese werden als Parameter beim Aufruf des Programmes make übergeben. Die ersten Beiden sind nicht controllerspezifisch und die Optionen werden in der make-Dokumentation erklärt. Program dient dem automatischen Übertragen der kompilierten Datei auf den Controller. Dazu muss der verwendete Programmieradapter angegeben werden. Informationen hierzu findet man bei seinem JTAG-Adapter. Die Option OPT mit den Auswahlmöglichkeiten [0, 1, 2, 3, s] stellt den Optimierungsgrad des Compilers ein. 0 bedeutet keine Optimierung und sollte beim Debuggen verwendet werden. Im endgültigen Programm kann man 3 (schnellstmöglicher Code) oder s (kleinstmöglicher Code, in einigen Fällen daher sogar schneller als 3) verwenden.
Debuggen
Ein Debugger dient dazu, den Programmablauf sowie Variablenwerte während der Programmausführung zu überwachen. Aufgrund der beschränkten Ressourcen auf einem Mikrocontroller kann das nicht wie bei einem PC mit einem nebenher laufenden Programm erfolgen. Man greift daher auf die oben erwähnte JTAG-Schnittstelle zu. Ein JTAG-Adapter kommuniziert dabei mit dem Mikrocontroller und das Programm OpenOCD übersetzt zwischen JTAG-Hardware und dem in der Entwicklungsumgebung implementierten Software-Debugger. Da es viele verschiedene Möglichkeiten gibt beschränke ich mich hier auf einen Link auf die Seite von OpenOCD, wo auch verwendbare Hardware aufgezählt wird.
Startvorgang
ARM-Mikrocontroller sind nicht gleich nach dem Einschalten der Betriebsspannung bereit, Befehle auszuführen, sondern müssen zuerst konfiguriert werden. Atmel stellt für seine Controller sogenannte Startup-Files für verschiedene Konfigurationen zur Verfügung. Diese sind, zumindest teilweise, in Assemblercode geschrieben. Sie kümmern sich um die Erzeugung des Haupttaktes mittels PLL aus dem angeschlossenen Quarz, die Platzierung von Interruptroutinen im RAM und die Behandlung von Programmfehlern. Werden von einem Befehl ungültige Speicherbereiche überschrieben, kann dies abgefangen und automatisch eine Fehlerbehandlungsroutine bzw. Debugcode aufgerufen werden.
PIO
Der schon teilweise in der Einleitung erwähnte PIO (Parallel Input Output) Controller kümmert sich um einen 32-Pin breiten IO-Port (PA0 bis PA31). Jeder Pin kann als allgemeiner Ein-oder Ausgang verwendet werden oder einem der beiden Peripheriecontroller (A oder B) zugewiesen werden. Konfiguriert man den Pin als Eingang, indem man das betreffende Bit im Konfigurationsregister setzt, kann man einen internen Pullup aktivieren, eine Funktion zum Ausfiltern von Eingangssignalen kürzer als ein halber Taktzyklus aktivieren, was bei angeschlossensen Tastern das gefürchtete Prellen verhindert, oder einen Pin-Change-Interrupt aktivieren, welcher bei jedem Wechsel des Pinzustandes eine Routine aufrufen kann. Nach dem Einschalten sind, wie schon bei der Hardwarebeschreibung erwähnt, alle Pins als Eingänge mit aktiviertem Pullup konfiguriert. Der folgende Befehl schreibt den Zustand des Ports in die 32Bit breite Variable n.
n = pPIO->PIO_PDSR; // Read Pin Data
Als Ausgang konfiguriert kann jeder Pin 8mA treiben. Die ersten 4 Pins (High-Drive) sind sogar in der Lage, 16mA zu liefern. Der Ausgang kann auch als Open-Drain konfiguriert werden.
Das folgende Beispiel konfiguriert die in LED_MASK definierten Pins als Ausgänge und setzt sie auf HIGH und anschließend auf LOW:
pPIO->PIO_PER = LED_MASK; // Enable PIO for LEDs
pPIO->PIO_OER = LED_MASK; // LED1..4 are Outputs
pPIO->PIO_SODR = LED_MASK; // Turn on LED's ("1")
pPIO->PIO_CODR = LED_MASK; // Turn off LED's ("0")
Will man einen Pin nicht als allgemeinen Ein-bzw. Ausgang nutzen, sondern z. B. die SPI-Schnittstelle verwenden, muss man den Pin einem der beiden Peripheriecontroller zuweisen:
pPIO->PIO_ASR = ( AT91C_PA13_MOSI ); //MOSI Pin Controller A zuweisen und den PIO-Controller für diesen Pin deaktivieren:
pPIO->PIO_PDR = ( AT91C_PA13_MOSI ); //PIO für MOSI deaktivieren
Im Datenblatt werden die Funktionen der einzelnen Pins aufgelistet.
UART
Die Verwendung der seriellen Schnittstelle USART zur Kommunikation mit einem PC wird anhand eines Beispieles erklärt:
//Global Pointer to USART1
AT91PS_USART * pUSART = AT91C_BASE_US1;
//Initialize Serial Interface
void uart1_init (void) {
*AT91C_PMC_PCER = (1 << AT91C_ID_US1); // Enable Clock for USART1
//Die Pins 21 und 22 werden dem Peripheriecontroller A zugewiesen.
//Dieser hat somit die Kontrolle über sie.
*AT91C_PIOA_PDR = AT91C_PA21_RXD1 | // Enable RxD1 Pin
AT91C_PA22_TXD1; // Enable TxD1 Pin
//Dann wird ein Reset der Schnittstelle ausgelöst
pUSART->US_CR = AT91C_US_RSTRX | // Reset Receiver
AT91C_US_RSTTX | // Reset Transmitter
AT91C_US_RXDIS | // Receiver Disable
AT91C_US_TXDIS; // Transmitter Disable
//Im Mode Register werden alle Einstellungen durchgeführt
pUSART->US_MR = AT91C_US_USMODE_NORMAL | // Normal Mode
AT91C_US_CLKS_CLOCK | // Clock = MCK
AT91C_US_CHRL_8_BITS | // 8-bit Data
AT91C_US_PAR_NONE | // No Parity
AT91C_US_NBSTOP_1_BIT; // 1 Stop Bit
//eine Baudrate von 115200Baud wird gesetzt
pUSART->US_BRGR = (MCK/16/115200 ); // Baud Rate Divisor
//und die Schnittstelle aktiviert
pUSART->US_CR = AT91C_US_RXEN | // Receiver Enable
AT91C_US_TXEN; // Transmitter Enable
}
//Die Funktion sendet einen 8Bit Wert
int uart1_putc(int ch) {
while (!(pUSART->US_CSR & AT91C_US_TXRDY)); // Wait for Empty Tx Buffer
return (pUSART->US_THR = ch); // Transmit Characte
}
//warten, bis ein Byte empfangen wurde, und dieses ausgelesen
int uart1_getc ( void ) {
while (!(pUSART->US_CSR & AT91C_US_RXRDY)); // Wait for Full Rx Buffer
return (pUSART->US_RHR); // Read Character
}
Die serielle Schnittstelle ist sehr einfach zu verwenden und vor allem beim Fehlersuchen sehr praktisch. Zum Anschluss an einen PC benötigt man jedoch einen geeigneten Pegelkonverter, welcher den 0V/3.3V Pegel des Mikrocontrollers auf +-12V shiftet. Man muss ebenfalls auf die Taktgeschwindigkeit des Controllers aufpassen, da dieser nicht mit jeder Einstellung einen für schnelle USART-Übertragung geeigneten Takt erzeugen kann.
SPI
SPI oder Microwire ist ein von Motorola entwickeltes serielles Bussystem, das in vielen Mikrocontrollern implementiert ist. Es gibt dabei einen Master, welcher maximal 255 Slaves anspricht. Die Slaves dürfen nur nach Aufforderung durch den Master senden. Der Bus besteht aus 3 Leitungen:
- MOSI (Master Out Slave In), auch als SDO (Serial Data Out) bezeichnet
- MISO (Master In Slave Out) oder SDI (Serial Data In) sowie
- SCK (System Clock)
Zusätzlich benötigt man zu jedem Slave eine Slave Select (SS) oder Chip Select (CS) Leitung, mit welcher der Master den oder die angesprochenen Slaves selektiert. Der Bus benötigt daher relativ viele Leitungen (min. 3 + 1 Select pro Slave für bidirektionale Kommunikation), ist aber recht schnell und sehr flexibel. Daher wird er auch oft für Punkt-zu Punkt Hochgeschwindigkeitsverbindungen wie z. B. zwischen Mikrocontroller und externem Speicher oder in modernen PC zur Anbindung des BIOS-Flash-Speichers an die Southbridge verwendet. Es gibt kein festgelegtes Übertragungsprotokoll und ebenfalls mehrere Übertragungsmöglichkeiten: Daten können entweder „Little Endian“, d.h. das LSB (Least significant Bit, also niederwertigstes Bit) wird zuerst übertragen, oder aber „Big Endian“, wo das MSB (Most significant Bit) zuerst geschickt wird, gesendet werden. Je nach Gerät werden die Daten bei steigender oder fallender Taktflanke übernommen und ob ein HIGH- oder LOW-Pegel am SS Eingang den Slave aktiviert ist ebenfalls unterschiedlich. Der Takt kann im Ruhezustand ebenfalls entweder HIGH oder LOW sein und pro Übertragung können zwischen 8 und 16 Bit geschickt werden. Die SPI Implementierung im Controller muss also hochgradig konfigurierbar sein. Wie bei allen Peripherieeinheiten des AT91SAM7 muss auch bei SPI zuerst der Takt aktiviert und die Pins dem richtigen Peripheriecontroller zugewiesen werden. Um einen Wert (8 bis 16Bit breit, je nach Einstellung) zu senden, schreibt man ihn in das SPI_TDR Register. Er wird dann automatisch im Hintergrund übertragen, und sobald der Wert gesendet ist, wird je nach Konfiguration ein Interrupt aufgerufen oder nur das Bit AT91C_SPI_TDRE (Transmit Data Register Empty) im SPI_SR Register gesetzt. Ein empfangener Wert landet im SPI_RDR Register. Bei Empfang kann ebenfalls ein Interrupt ausgelöst oder nur das Bit SPI_RDRF (Receive Data Register Full) gesetzt werden.
USB
Die USB-Schnittstelle des AT91SAM7Sxxx hat eine maximale Bandbreite von 1Mbytes/s und entspricht der USB 2.0 full-speed Spezifikation. Diese sieht einen mit einem Transistor schaltbaren Pullup Widerstand auf 3,3V vor, mit dem das Gerät seinen Anmeldewunsch mitteilen kann. Man kann auf diesen auch verzichten und die Datenleitung über 1,5kOhm direkt mit 3,3V verbinden. Der Controller muss sich dann direkt nach dem Start korrekt am PC anmelden, da dieser sonst nach einer Timeout-zeit meldet, das das USB-Gerät nicht erkannt wurde. Die Einhaltung des Zeitfensters stellte jedoch auf allen getesteten Systemen kein Problem dar. Der USB-Controller muss mit 48MHz +-0,25% getaktet werden. Der dazu nötige PLL-Takt von 96MHz (2*48MHz) wird aus dem Quarztakt von 18,432MHz erzeugt und beim Startup-Vorgang eingestellt. Empfehlenswert ist das Framework von ATMEL, welches einen virtuellen COM-Port auf dem PC (getestet mit Windows XP + VISTA) zur Verfügung stellt. Näheres steht auf der ATMEL-Seite.
Fazit
Natürlich schrecken die enormen Möglichkeiten des Controllers im ersten Moment ab, aber wenn man die Anleitungen Schritt-für-Schritt durchgeht und an einem interessanten Beispielprojekt von ATMEL etwas herum spielt, wird man bald Freude daran finden.
Viel Spaß dabei!
Interessante Seiten + Downloads
- AT91SAM7S Microcontroller Series Schematic Check List
- SAM-BA
- ATMEL Evaluation Kit Besonders interessant sind die Bespiele im "AT91SAM7S-EK Software Package" (incl. .bin File!)
- YAGARTO
- Aplication Notes
- ATMEL-Software
- OpenOCDDebugger