20 008 Do okumentation n Prrojeekt: Dattenloggger. µCo ontrolleer geste euerterr Daten nlogger mit SD D Speicherkaartenan nbindung. Dokum mentation übeer die Techn nikerarbeit voon Jürgen Häärig im Zusam mmenhang mit derr Weiterbilduung an der Werner-Sieme W ens-Schule ffür Abendtecchniker 20042008. Verfassser: Jürgen H Härig Betreuen nder Lehrer: Herr T. Schn nabel 09.05.2008 2 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 1 Erklärung Hiermit erklären ich, dass die vorliegende Technikerarbeit eine eigenständige Leistung darstellt und nicht auf der Basis einer bereits vorhandenen Techniker‐, Diplom,‐ oder ähnlicher Arbeit erstellt wurde. Bei der Durchführung und Ausarbeitung wurden nur die zulässigen Hilfsmittel verwendet. Mir ist bewusst, dass bei einem Verstoß gegen diese Erklärung innerhalb der gesetzlichen Einspruchsfristen, auch im Nachhinein, die Leistungsbewertung aberkannt werden kann. Damit erlischt die Berechtigung zum Tragen der Berufsbezeichnung des staatlich geprüften Technikers. Hiermit versichern ich, dass ich folgende praktischen Teile und Gliederungspunkte selbständig bearbeitet und verfasst haben: • Software: ‐ Erstellen des Softwarekonzeptes ‐ Erstellen des Programmablaufplanes ‐ Erstellen des Quelltextes • Hardware: ‐ Planen und teilweises Herstellen der einzelnen Bauteile ‐ (Gehäuse, Platinen) • Dokumentation: ‐ Erstellen des Pflichtenheftes, sowie der Dokumentation Bedanken möchte ich mich, bei Holger Klabunde, Ulrich Radig und Peter Fleury für die Bereitstellung der Quellcodes, zur SD‐Karten‐ und LCD‐Ansteuerung. Großbottwar, den ....................... Jürgen Härig Jürgen Härig | Erklärung 2 3 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 2 Impressum Autor: Jürgen Härig Texte, Design, Bilder, Skizzen und Code: Jürgen Härig Programmierung des µControllers: Jürgen Härig (mit Hilfe des WinAVR GCC Compilers und geschrieben in der AVR Studio Entwicklungsumgebung) Programmierung des .Net basierenden „DataLog“ Programmes: Jürgen Härig (mit Hilfe des, Microsoft Visual C# Express Edition, .Net Programmierstudios. Copyright by Microsoft) Aufbau und Konstruktion der Elektronik des Datenlogger – Prototypen: Jürgen Härig Auflistung extern genutzter Code‐Teile der µC‐Software: ‐ Ulrich Radig: Nutzung von Codeteilen, der von Ulrich Radig geschriebenen LCD_Print ()‐Funktion aus der C‐Datei lcd.c, Version: 28.05.2004 . ‐ Peter Fleury: Nutzung von Code zur Ansteuerung des LC‐Displays über den 4Bit‐Nibble‐Modus, durch Nutzung der Dateien: lcd.c und lcd.h, Version: v 1.14.2.1 2006/01/29 12:16:41 . ‐ Holger Klabunde: Nutzung des Projektes: FATSingleOpt41 Beta WinAVR4.1.1 , zur Ansteuerung einer SD/MMC‐Speicherkartemit Zugriff auf das FAT Dateisystem, unter Nutzung einer µController SPI Schnittstelle. Jürgen Härig | Impressum 3 4 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Inhaltsverzeichnis 1 Erklärung ..................................................................................................................... 2 2 Impressum ................................................................................................................... 3 3 Vorwort ....................................................................................................................... 7 4 Kurzfassung ................................................................................................................. 8 5 Idee ............................................................................................................................. 9 6 Pflichtenheft .............................................................................................................. 10 7 Hardware Aufbau....................................................................................................... 14 7.1 Vorrauslaufende Hardware Tests ................................................................................... 14 7.1.1 Ursprüngliches LC Display ............................................................................................. 14 7.1.2 LEM Modul als Strom‐Messmodul ................................................................................ 15 7.1.3 ATmega16 Mikrokontroller ........................................................................................... 16 7.2 Hardware Auswahl und Begründung .............................................................................. 17 7.3 Auswahl des ATmega128 Mikrokontrollers..................................................................... 18 7.4 Genutze Bauteile ............................................................................................................ 19 7.4.1 Crumb128 Baustein ....................................................................................................... 19 7.4.2 LC Display ‐ EA DIP204B‐4NLW ...................................................................................... 22 7.4.3 Strommess Modul ‐ ACS712‐20A‐T ............................................................................... 23 7.4.4 Spannungsversorgung ................................................................................................... 24 7.4.5 SD Speicherkarte – Hama 512MB (max. 2GigaByte möglich) ....................................... 24 7.4.6 SD Karten Einschub ........................................................................................................ 27 7.4.7 UART Schnittstelle (RS232) ............................................................................................ 27 7.4.8 Steuertaster ................................................................................................................... 27 7.4.9 Festspannungsregler ..................................................................................................... 28 7.4.10 Gehäuse ......................................................................................................................... 29 7.4.11 Bauteil Stückliste ........................................................................................................... 31 7.4.12 Platine ............................................................................................................................ 33 7.5 Schaltplan ...................................................................................................................... 35 8 Software .................................................................................................................... 37 8.1 Mikrokontroller Software ............................................................................................... 37 8.1.1 Gewählte Entwicklungssoftware und Compiler ............................................................ 38 8.1.2 Vorrauslaufende Software‐Entwicklungen und Tests ................................................... 39 8.1.3 Extern genutzte Source‐Codes ...................................................................................... 41 8.1.4 Software Module und Funktionsbeschreibungen ......................................................... 42 8.1.5 Hauptprogramm Ablaufplan ......................................................................................... 60 8.2 Windows Software ......................................................................................................... 61 8.2.1 Microsoft Visual C# 2008 Express Edition ..................................................................... 61 Jürgen Härig | Impressum 4 Dokumentation 5 Projekt: Datenlogger, von Jürgen Härig 8.2.2 8.2.3 8.2.4 9 Technikerarbeit 2008 Externe Source‐Codes und Icons ................................................................................... 61 Programm Erklärung...................................................................................................... 62 Schnittstellen für die Datenaufzeichnung ..................................................................... 66 Bedienungsanleitung ................................................................................................. 68 9.1 9.2 9.3 10 Hardware ....................................................................................................................... 68 Windows Software ......................................................................................................... 69 SD Karte ......................................................................................................................... 70 Inbetriebnahme...................................................................................................... 71 10.1 Hardware ....................................................................................................................... 71 10.2 Software ........................................................................................................................ 73 11 Anhang ................................................................................................................... 75 11.1.1 main.c ............................................................................................................................ 75 11.1.2 main.h ............................................................................................................................ 96 11.1.3 adc.c ............................................................................................................................... 99 11.1.4 date.c ............................................................................................................................. 99 11.1.5 rs232.c ......................................................................................................................... 100 11.1.6 rs232.h ......................................................................................................................... 102 11.2 Externe Quellcodes ...................................................................................................... 102 11.2.1 compact.c .................................................................................................................... 102 11.2.2 compact.h .................................................................................................................... 108 11.2.3 dir.c .............................................................................................................................. 110 11.2.4 dir.h ............................................................................................................................. 124 11.2.5 dos.c ............................................................................................................................ 124 11.2.6 dos.h ............................................................................................................................ 139 11.2.7 dosdefs.h ..................................................................................................................... 142 11.2.8 drivefree.c ................................................................................................................... 144 11.2.9 drivefree.h ................................................................................................................... 145 11.2.10 dumpsect.c .............................................................................................................. 146 11.2.11 dumpsect.h .............................................................................................................. 146 11.2.12 fat.c .......................................................................................................................... 147 11.2.13 fat.h ......................................................................................................................... 160 11.2.14 find_x.c .................................................................................................................... 165 11.2.15 find_x.h .................................................................................................................... 166 11.2.16 lcd.c .......................................................................................................................... 167 11.2.17 lcd.h ......................................................................................................................... 180 11.2.18 mem‐check.c ........................................................................................................... 185 11.2.19 mem‐check.h ........................................................................................................... 186 11.2.20 mmc_spi.c ................................................................................................................ 186 11.2.21 mmc_spi.h ............................................................................................................... 202 11.2.22 mydefs.h .................................................................................................................. 208 Jürgen Härig | Impressum 5 6 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 11.2.23 readraw.c ................................................................................................................. 208 11.3 Datenblätter ................................................................................................................ 211 11.3.1 Datenblatt – Crumb128 ............................................................................................... 211 11.3.2 Datenblatt ‐ 7805 Festspannungsregler 5V ................................................................ 213 11.3.3 Datenblatt ‐ ACS0712 Strommess Modul .................................................................... 214 11.3.4 Datenblatt ‐ SD Karten Slot ......................................................................................... 220 11.3.5 Datenblatt ‐ Festspannungsregler 3.3V ...................................................................... 222 11.3.6 Datenblatt ‐ ES204 LC‐Display .................................................................................... 226 11.3.7 Datenblatt ‐ HUT Schienen Gehäuse .......................................................................... 230 11.3.8 Datenblatt ‐ Steuertaster ............................................................................................ 232 Jürgen Härig | Impressum 6 7 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 3 Vorwort Im Zuge der Ausbildung zum Kommunikations‐ und Informations‐Techniker, der Abendschule an der Werner Siemens Schule Stuttgart, wurde die Aufgabe gestellt eine Technikerarbeit zu entwickeln. Ich beschloss, die Technikerarbeit als privates Projekt zu erstellen und fand auch sehr schnell eine passende Idee. Die Idee war, einen Mikrokontrollergesteuertes Datenaufzeichnungsgerät zu entwickeln. Problem an dem Projekt war allerdings die mangelnde Speicherkapazität in einem Mikrokontroller. Diese können meist nur wenige (kilo)bytes an Daten über längere Zeit speichern. Da ich anfing, dass in der Schule gelehrte Wissen über Mikrokontroller, auch im Hobby bereich anzuwenden kam mir zu Ohr, dass inzwischen auch so genannte Massenspeicher‐Medien mit Mikrokontrollern angesteuert werden können. Dank eines Freundes wurde ich auf das Projekt von Holger Klabunde aufmerksam. Dieser entwickelte eine Mikrokontroller Bibliothek, die zum ansteuern einer SD (Secure Digital)‐Speicherkarte über eine SPI (Serial Peripheral Interface)‐Schnittestelle notwendig sind. Mir war bewusst, dass ich für meine Technikerarbeit das nötige Wissen, um selbst eine solche Ansteuerbibliothek zu erstellen, fehlt. Daher entschloss ich mich, die von Holger Klabunde bereit gestellte Bibliothek zu nutzen um das Projekt Datenlogger effektiver zu gestalten. Jürgen Härig | Vorwort 7 8 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 4 Kurzfassung Diese Dokumentation umschreibt das Projekt: Datenlogger, welchem im Rahmen der Technikerarbeit an der Werner‐Siemens‐Schule erstellt wurde. Die Dokumentation enthält große Teile der Entwicklung, vom Pflichtenheft, über erste Versuchsreihen bis zum fertigen Produkt. Es werden Erklärungen und Abbildungen von Bauteilen und Schaltplänen zu finden sein, sowie Datenblätter und Erläuterungen bestimmter Bauteile. Der gesammte Quell‐Code des Mikrokontrollers ist im Anhang vorzufinden und ist durch Kommentare an bestimmten Stellen erklärt. Für bestimmte Programmteile wurden Programm Ablaufpläne Pläne erstellt, die genaueren Einblick in komplexe Programmfunktionen erlauben. Jürgen Härig | Kurzfassung 8 9 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 5 Idee Woher kam die Idee zur Entwicklung eines µController gesteuerten Strommesswert Datenloggers? Die Idee kam durch die heutige Zeit, in der sich die Menschheit langsam über die Folgen der Energieverschwendung Gedanken macht. Dabei wird oftmals nicht daran gedacht, dass einige der schlimmsten Energieverschwender im eigenen Haushalt vorkommen. Als Beispiel möchte ich elektronische Geräte die man in jedem Haushalt findet nennen, die oftmals einen ungewissen Energiebedarf haben, der sich bei der gewohnten täglichen Verwendung kaum erfassen lässt. Fast jeder Mensch hat in seinem Haushalt Unmengen an elektronischen Geräten, überall im Haus oder der Wohnung verteilt. Viele dieser Geräte sind in täglicher Benutzung oder laufen gar über Tage oder Monate hinweg dauerhaft! Meine Idee zielt auf die graphisch auswertbare Erfassung der Stromaufnahme von elektronischen Geräten an. Dank einer SecureDigital Speicherkarte, die enorme Datenmengen aufnehmen kann, ist mein Datenlogger in der Lage Messdaten der Stromverbraucher über Strom‐Mess‐Module zu erfassen und über längeren Zeitraum aufzuzeichnen. Diese gesammelten Daten können dann von der Speicherkarte gelesen und mittels einer Excel‐ Tabelle graphisch dargestellt und präsentiert werden. Dies kann dabei helfen, stromhungrige Geräte aufzuspüren, wie z.B. defekte Kühlschränke, Heizungen oder dauerhaft betriebene elektronische Gerätschaften. Natürlich kann mittels des Diagramms auch erkannt werden, wann über den Tag sich Geräte automatisch oder gewollt ein‐ bzw. ausgeschalten werden. Diese aufgezeichneten Werte können dann zum Vergleich mit Folgemonaten oder Tagen verwendet werden, was bei der Einschätzung des täglichen, monatlichen oder gar jährlichen Stromverbrauches einzelner Zimmer oder Geräte helfen kann. Das Produkt ist für Endkunden bestimmt, die den Energieverbrauch in Ihrem Haushalt aufzeichnen und überwachen wollen. Natürlich besteht die Möglichkeit mit dem Gerät auch andere Werte über längeren Zeitraum aufzuzeichnen, insofern analoge Signale bestehen die der Datenlogger aufnehmen kann. Jürgen Härig | Idee 9 10 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 6 Pflichtenheft Elektronischer Datenlogger 1. Allgemeines: Aufbau eines elektronischen Datenloggers, zur Erfassung von Strommesswerten aus einem 230V Wechselstromkreis. 2. Zielsetzung: Ein µController der Atmega Serie wird über acht Analog/Digital‐Wandlungs‐Eingänge, Strom‐Messwerte zyklisch von einem elektronischen Strommess‐Baustein einlesen und in einem sicheren Speicher ablegen. Dieser Speicher soll eine SD‐Karte sein, welche dann an einem Computer zur Auswertung ausgelesen werden kann. Ein LC‐Display wird dem Anwender gewisse Kontroll‐ und Konfigurationsmöglichkeiten bieten. Zum Beispiel: Änderung von Datum und Uhrzeit der Internen Uhr, Starten und Stoppen der Datenaufzeichnung, Anzeige von aktuellen Messwerten, etc. 3. Aufgabe des Projektleiters: Aufbau eines Programm Ablaufplans für die Steuer‐Software. Einarbeiten in C Programmierkenntnisse in Bereichen: – AD Wandlung – Interrupts – Timer – UART – EEPROM – LCD Ansteuerung – SD‐Karten Schnittstelle – Einlesen und Auswerten von Wechselströmen (FFT) – FAT Dateisystem der SD‐Karte Finden von geeigneter Hardware in den Bereichen: – – – – – – – LC‐Display Strommess‐Baustein ((True)RMS Baustein) SD‐Karten‐Leser (Filter gegen Netzstörungen) Bedien‐Elemente Befestigungen der zu messenden Stromleitungen. Jürgen Härig | Pflichtenheft 10 11 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Gehäuse für das fertige Gerät, bzw. Modul erstellen und einbauen. In den Bereichen, (True)RMS muss eventuell abgeschätzt werden, ob der Teile des Projektes in Software oder Hardware umgesetzt werden. Aus Sicherheitsgründen wird das Gerät für eine Messung unter 36Volt vorbereitet, um gefahrlosen Umgang zur Demonstration zu gewährleisten. Durch andere Dimensionierung und Abgleich, sollte das Gerät aber in der Lage sein auch höhere Ströme zu messen und aufzuzeichnen. 4. Hardware – Voraussetzungen: Die genutzte Hardware wird die Möglichkeit geben, zyklisch die Messungen von bis zu acht analogen Datenquellen einzulesen und zu speichern. Die Analogwerte von den Strommess‐Bausteinen bzw. TrueRMS‐Bausteinen, werden von den Analog/Digital‐Wandlern, die in dem µController integriert sind, ausgewertet, umgewandelt und zur Speicherung in der SD‐Karte bereit gestellt. Als Speichermedium ist eine SecureDigital (SD)‐Karte geplant. Die Daten werden dann auf dem Dateisystem der SD‐Karte, in einer Datei abgelegt. Sollte diese Art der Speicherung nicht softwaretechnisch lösbar sein, werden die anfallenden Daten im internen EEPROM des ATmega gespeichert um dann über eine UART Schnittstelle ausgelesen zu werden. Die Aufzeichnungsdauer und Datenquantität wird damit verringert. Das Gerät wird über ein Bedien‐Element verfügen, dass voraussichtlich drei Taster beinhalten wird. Über diese Taster können verschiedene Menü‐Inhalte auf den Display ausgewählt werden. In einigen dieser Menü´s können Konfigurationen verändert und angepasst werden. 5. Software – Voraussetzungen: Die Software wird einen Großteil des Projektes einnehmen. Die Software wird in C geschrieben und mit einem, unter GPL stehenden, Compiler (GCC) compiliert. Die genutzten Software‐Routinen für die LCD‐, SD Karten‐Ansteuerung werden nicht vom Projektleiter geschrieben sein, da diese Aufgaben den Rahmen des Projektes sprengen würde. Die Schnittstellen zu den Funktionen werden vielleicht teilweise modifiziert werden müssen. Jürgen Härig | Pflichtenheft 11 12 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Geplant ist eine softwarebasierende RMS Auswertung der Messwerte, welche jedoch auch von einem Hardware‐Baustein übernommen werden kann. Sollte die RMS Messung zu große Probleme bereiten und nur DC Werte eingelesen werden sollen, wird diese Messmethode komplett entfallen. Die Anzahl der AD Messungen wird abhängig von der Taktfrequenz des µControllers sein. Die Anzahl der Messungen für eine verwertbare Rechnung notwendig sind, ist noch unbekannt. Die gesammelten Messwerte werden in einer Datei abgelegt werden, welche sich auf der SD Karte befinden wird. Die Dateistruktur soll dem Folgenden Beispiel ähnlich sein: Datum Strom Strom StromStrom xxxxx Wert1 Wert2 Wert3 Wert4 Wert5 Wert6 Wert7 Wert8 Wert Strom Strom Strom Strom Spannung 6. Menü ‐ Steuerung und Visualisierung Das Gerät wird ein LC‐Display haben, das mit einem Bedien‐Element aus drei Tastern versehen sein wird. Über diese Taster und das Display werden gewisse Anzeige‐ und Konfigurations‐Optionen ermöglicht. Geplante Menüs sind: – – Hauptmenü ‐ Zusammenfassung der laufenden Datenlogs. ‐ SD‐Karten Status. Zeit/Datum Konfiguration – Datum und Zeit Anzeige – SD‐Karten Konfiguration ‐ Status und Aktivierung der SD‐Karte – A/D‐Wandler ‐ Status und Messwert der einzelnen Wandler ‐ In den Menüs sind gewisse Änderungen möglich. Jürgen Härig | Pflichtenheft 12 13 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Zeit/Datum Konfiguration: Änderung des Datums und der Zeit, welche bei der SD‐Karte mit gespeichert werden. SD‐Karten Konfiguration: Einbinden und Auswerfen der SD‐Karte. A/D‐Wandler: Aktivieren und deaktivieren der Datenaufzeichnung des Einzelnen A/D‐Wandlers. Aufzeichnung der Messwerte auf der 7. Benutzerschnittstellen: – LC‐Display – Bedien‐Element (Bestehend aus drei Tastern) 8. Externe Schnittstellen: – SD‐Kartenleser – LCD‐Anschluss – Bedien‐Elemente (Taster) – A/D‐Wandler Eingänge – Spannungsversorgung – (Reserve Akku/Batterie?) Jürgen Härig | Pflichtenheft 13 14 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7 Hardware Aufbau 7.1 Vorrauslaufende Hardware Tests 7.1.1 Ursprüngliches LC Display Wärend der Entwicklung meiner Technikerarbeit, nutzte ich ein 4x20 Zeichen LC‐Display von EA, mit einem KS0063 LCD‐Treiber.Das Display verfügt über eine Hintergrundbeleuchtung sowie einer Möglichkeit, den Kontrastwert der Schriftzeichen zu verändern. Länge: 100mm ‐ Breite: 60mm ‐ Tiefe: 11mm Für dieses Display galt ein Standard Ansteuerungs‐Datenblatt, mit dessen hilfe ich begann meinen ersten Prototypen zu erstellen. Um das Display ansteuern zu können, nutzte ich eine Verdrahtungsvariante für den so genannten 8Bit Modus. Dieser Modus erlaubt es, über 8 Datenleitungen, und drei Steuerungs und Kontroll‐ Leitungen, das Display in Betrieb zu nehmen. Mit zur Ansteuerung es Displays gehört die Einstellung der Displayhelligkeit und Kontrastwertes, um ein problemfreies ablesen in vielen Helligkeits Umgebungen zu gewährleisten. Dafür sind zwei verstellbare Spannungsteiler, welche über Potentiometer veränder werden können. Wie sich wärend der Entwicklung herausstellte, war das Display zu groß, um in das gewünschte Gehäuse aufgenommen werden zu können. Jürgen Härig | Hardware Aufbau 14 15 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.1.2 LEM Modul als Strom­Messmodul Das von mir anfangs gewählte Strom‐Messmodul, war das LTS‐6 NP Modul von LEM. Dieses Modul ist in der Lage, bis zu 6 Ampere Strom, im positiven (Verbraucher) sowie negativen (Einspeisgerät) bereich zu Messen. Es beherrscht neben der Wechselstrom‐Messung auch auch die Messung von Gleichströmen, ohne das es viel Linearität verliert. Das soll bedeuten, dass egal ob man Wechsel‐ oder Gleichströme misst, man immer die selben Messpegel hat, und keine verschiedenen Faktoren zur Messwert Berechnung benötigt. Das Modul wird mit +5V versorgt und liefert einen Ausgabepegel der im stromfreien Zustand der Strom‐Messbrücke, +2,5V beträgt. Dieser so genannte Offset des Ausgabepegels ist bei solchen Messmodulen nötig, da sie im positiven wie auch negativen Bereich messen, was bei Wechselstrom‐ Messungen von nöten ist. Vorteil an diesem Strom‐Messmodul war die sehr hohe Präzision bei der Messung. Diese betrug bei Messungen von 6 Ampere nur ca. 0,2 bis 0,7%. Also nur 0,012 V bis 0,035 V am Analog‐Digital‐ Wandler des Mikrokontrollers. Nachteil war an diesem Modul wiederum die Bauform und größe, sowie der zu geringe Messbereich und der hohe Preis, welcher ca. $20 US‐Dollar bzw. 13€uro. Jürgen Härig | Hardware Aufbau 15 16 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.1.3 ATmega16 Mikrokontroller Zu Beginn des Projektes war geplant den ATmega16 Mikrokontroller von Atmel, als Steuerungsbaustein zu nutzen. Der ATmega16 besitzt 16kByte an Flash Speicher, sowie 1kByte RAM. Die ersten Versuche und Tests mit dem ATmega16, bei der Entwicklung des Datenloggers, verlieren weitesgehend ohne Probleme. Der Baustein eignete sich sehr gut, die Grundlagen der Mikrokontroller Programmierung zu erlernen und zu vertiefen. Dank des Internets und Kollegen aus der Technikerschule konnte somit sehr viel Wissen gesammelt werden, die bei der Entwicklung des Datenlogger Projektes von Vorteil waren. Die hohe Verbreitung des Mikrokontrollers in den Hobby‐Bastler Bereichen, sowie die relativ hohe Kompatibilität der Quellcodes zur Ansteuerung der Timer, Interrupt, UART und AD‐Wandler Register, verhalf zu einem schnellen Fortschritt bei der Progammierung des Hauptprogrammes. Jedoch merkte ich sehr schnell, dass die Kapazitäten des ATmega16 nicht für alle Teile meines Datenlogger Projektes ausreichten. Die SD‐Karten Ansteuerfunktion von Holger Klabunde beanspruchte einiges an Flash sowie RAM Speicherplatz und zwang mich dazu einen leistungsstärkeren Mikrokontroller für das Projekt zu suchen. Im nach hinein bemerkte ich auch, dass die Baugröße und die nötige elektronische Beschaltung für die serielle Schnittstelle zuviel Platz einnehmen würde. Jürgen Härig | Hardware Aufbau 16 17 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.2 Hardware Auswahl und Begründung Die Selektion der Bauteile geschah wärend der Entwicklung des Datenloggers. Dadurch entwickelte sich erst wärend des Projektes ein Bild, wie groß und Umfangreich der Datenlogger als Endgerät sein würde. Erste Entwicklungen auf dem ATmega16 Experimentierboard von Pollin, sowie der Zuhilfe nahme eines Steckboardes halfen mir sehr, die Grundlagen für das Projekt zu setzen. Probleme und unbeachte Hindernisse zeigten sich schon in den ersten Phasen des Projektes. Einige der Probleme waren: ‐ EMV Probleme durch offene Pin’s am Mikrokontroller. ‐ Schlechte Kontaktierung durch ungeeignete Komponentenstecker an LCD so wie SD‐Karten‐Slot ‐ Spannungseinbrüche durch zu schwach dimensionierte Spannungsteiler und Festspannungsregler. ‐ Große, nur schwer Integrierbare Bauteile, die ungewöhnliche Spannungsversorgungen benötigten, welche in dem Gerät später nicht vor zu finden sein sollten. Viele dieser Probleme konnten nur durch optimierte Auswahl der benötigten Bauteile sowie Erarbeitung von fachlichen Wissen gelöst werden. Von der anfänglichen Entwicklung bis zum finalen Prototypen des Datenloggers wurden viele Komponenten auf ihre Tauglichkeit geprüft und wenn nötig nach Umwegen gesucht um jene Bauteile nicht nutzen zu müssen. Jürgen Härig | Hardware Aufbau 17 18 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.3 Auswahl des ATmega128 Mikrokontrollers Die Auswahl des ATmega128 Mikrokontrollers war schlicht nötig, als es dazu kam, dass der SD Karten Quellcode in das Projekt implementiert wurde. Der ATmega128 bietet 128kByte Flash und 4kByte RAM. Das Projekt Datenlogger nutzt im Moment ca. 24kByte an Flash und etwas mehr als 3kByte RAM. Die größe des Flash und RAM Speichers waren für die Auswahl des Bausteins ausschlag gebend. Da der ATmega128 zusammen mit dem praktisch einsetzbaren Crumb128 ausgeliefert werden kann, beschränkte ich die Wahl auf diesen. Da der ATmega128 vom Quellcode her, dem ATmega16 sehr ähnelt, war die Portierung des Codes sehr einfach gestaltet. Nur wenige Register mussten im Sourcecode umbenannt werden um problemfrei für den modereneren Baustein compiliert werden zu können. Für das Projekt war mitunter die Tatsache entscheidend, dass der ATmega128 über alle nötigen Schnittstellen mitbrachte, die für den Datenlogger nötig sind. Dazu gehören: ‐ 10 Bit Analog‐Digital Wandler mit hoher Einles‐Frequenz. ‐ UART Schnittestelle für die optionale RS232 Ausgabe. ‐ SPI Interface für die Integrierung des SD‐Karten Quellcodes von Holger Klabunde. ‐ Sehr hohe Taktgeschwindigkeit und die Möglichkeit eine zweite Taktquelle nutzen zu können. ‐ Ausreichend Flash und RAM Speicher um Speichermangel bei der Entwicklung vor zu beugen. Jürgen Härig | Hardware Aufbau 18 19 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.4 Genutze Bauteile 7.4.1 Crumb128 Baustein Modulplatine mit einem ATmega128 Baustein, RS232‐ und USB Schnittstelle, zudem zwei Quarze mit 14,7456MHz und 32,768kHz Frequenz. Dieser Baustein bot für die Entwicklung des Datenlogger Projektes einen enormen Vorteil, da es modulartig auf eine Platine aufgesteckt werden kann. Der zur Verfügungstehende 32,768kHz Quarz, auch Uhrenquarz genannt, dient als Taktgrundlage für die Interne Echtzeituhr, mit dessen Hilfe die geloggten Daten einen zeitlichen Bezugspunkt bekommen. Dieser Quarz ist an den TOSC1 und TOSC2 Eingang des ATmega128 Bausteins gelegt, und steht somit mit dem Timer0 in Verbindung. Durch setzen des ASSR Registers für die Timer‐Taktquelle des Timer0, wird der 32,768kHz Takt als Clock für den Timer0 gesetzt. Das Crumb128 Board besitzt zudem noch zwei Kommunikationsschnittstellen, die mit der UART0 und UART1 Schnittstelle des Mikrokontrollers verbunden sind. Jürgen Härig | Hardware Aufbau 19 20 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Kommunikations Bausteine: CP2102 von Silabs Laboratories: Ein UART nach USB Wandlungsbaustein. Dieser Baustein ist in der Lage, einen virtuellen RS232 COM Port für einen Computer zu generieren. Nötig ist dafür nur die Installation der Treiber und die Schnittstelle steht sofort zur Verfügung. MAX3221 von MAXIM: Ein UART nach RS232 Pegelwandler, der die nötigen Spannungspegel für die serielle Datenkommunikation bietet. Takt Quarze: 14,7456MHz Quarz: Ist an den XTAL1 und XTAL2 des ATmega128 Bausteins angeschlossen und liefert den Programmtakt des Mikrokontrollers. 32,768kHz Quarz: Ist an den TOSC1 und TOSC2 des ATmega128 Bausteins angeschlossen und liefert eine externe Taktquelle für den Timer0. Diese Art von Quarz wird bei dieser Taktfrequenz auch Uhrenquarz genannt, da damit sehr häufig Uhren angesteuert werden. ISP Programmierschnittstelle: Das ISP, auch „In Sytem Programmer“, genannt, ist eine Programmierschnittstelle des ATmega128 Mikrokontrollers. Über diese Schnittstelle kann der Baustein ziemlich schnell programmiert werden, ohne dafür aus dem Projekt oder Gerät entfert werden zu müssen. Die Pin‐Belegung dieses IPS’s sind nach dem AVR‐ISP6‐Standard angelegt. Jürgen Härig | Hardware Aufbau 20 21 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 AD‐Eingang Entstörung: Um die AD‐Wandler zu entstören, wurde auf die Vorlage einer Entstöreinrichtung aus dem Datenblatt des ATmega128 zurück gegriffen. Die AREF Referenzspannung wurde auf 2.5 V eingestellt. Fuse‐Bits: Jürgen Härig | Hardware Aufbau 21 22 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.4.2 LC Display ­ EA DIP204B­4NLW Das genutzte 4x20 Zeichen LC Display mit Dot‐Matrix Anzeige und Hintergrundbeleuchtung wurde von EA, Electronic Assembly entwickelt, und entspricht durch seine kompakte größe die Ansprüchen des Projektes. Durch den KS0073 Baustein wird dem Display eine nahezu 100%tige HD44780 Kompatibilität garantiert. Ausschlaggebend für die Wahl dieses LCD’s war die effiziente Gestalltung der Steuerplatine, welche bei den meisten Displays viel zu überdimensioniert sind. Die Steuerplatine besitzt exakt die selben Maße wie das LCD‐Gehäuse. Länge: 75mm; Breite: 27mm; Tiefe: ca. 10mm Diese Maße waren bei der Wahl des Displays von großer Bedeutung, da das ausgewählte Gehäuse nur eine begrenzte Montagefläche bietet, um auch noch drei Taster und den SD‐Karten‐Slot aufzunehmen. Die Ansteuerung des Displays, welches mit zwei KS0073 Bausteinen bestückt ist, unterschied sich dank des Quellcodes von Peter Fleury kaum von dem anderen Display. Die 4Bit Nibble Ansteuerung, welche nur vier Datenleitungen für die Übertragung der Displayzeichen und Steuerbefehlen benötigt war auch weiterhin möglich. Jürgen Härig | Hardware Aufbau 22 23 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.4.3 Strommess Modul ­ ACS712­20A­T Ein Strommessbaustein von Allegro der mit dem HALL Effekt in der Lage ist, bis zu 20 Ampere Gleich‐ und Wechselspannung zu messen. Die geringe Baugröße und der kleine Preis im Gegensatz zu bisherigen Strom‐Messmodulen, überredete zur Nutzung dieses Bausteins. Trotz der ungewöhnlich kleinen Bauform verspricht der Hersteller eine problemlose Messung von Strömen bis zu 20 Ampere. Diese Strom‐Messmodule gehören zu den wichtigsten Teilen des Datenlogger Projekts, da sie die Ströme der angeschlossenen Verbraucher messen. Eine Messtoleranz durch Störungen, von ±1,5%, das in etwa ±0,06 Volt am Analog‐Digital‐Wandler entspricht. Auf den 20 Ampere Messbereich bedeutet das eine Abweichung von ca. ±70 mA. Diese Abweichung vom realen Messwert wird durch ein Software‐Modul im Hauptprogramm etwas gedämpft. Hoch präzise Messungen sind mit diesem Strom‐Messmodul nicht möglich Jürgen Härig | Hardware Aufbau 23 24 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.4.4 Spannungsversorgung Ein Standard 12 V DC Netzteil mit mindestens 250mA Leistung. 7.4.5 SD Speicherkarte – Hama 512MB (max. 2GigaByte möglich) Für dieses Projekt ist eine SD Speicherkarte von Hama it 512 MB Speicherkapazität gewählt worden. Dies ist die, für das Projekt vorgesehene SD Speicherkarte mit ca. 512MB Flash‐Speicher, formatiert mit einem FAT16 Dateisystem. Lesegeschwindigkeit: 3,6 MegaByte pro Sekunde (Referenzwert von SanDisk) Schreibgeschwindigkeit: 0,8 MegaByte pro Sekunde (Referenzwert von SanDisk) Die SD (Secure Digital Memory Card) Speicherkarte, zu deutsch: „Sichere digitale Speicherkarte“ ist ein von SanDisk entwickelter Flash Datenspeicher. Dieser kann wie eine Festplatte genutzt werden, hat aber durch die Flash‐Speicherstruktur eine enorm schnelle Zugriffszeit auf die elektronisch gespeicherten Daten. Durch die Ansteuerung Der Entwickler, SanDisk, gibt für die Ansteuerung seiner Speicherkarten gewisse Vorgaben, was Ansteuerzeiten sowie Timings für die Kommunikation mit der Karte an. Da viele Hersteller bei der Herstellung das Problem haben, dass sie sich nicht präzise an die vom Entwickler angegebenen Standards halten, kann es Probleme mit SD‐Speicherkarten unterschiedlicher Hersteller und auch Karten größen geben. Wenn die Speicherkarte nicht den Rahmenbedingungen des Entwicklers entspricht, wird der Datenlogger die Speicherkarte nicht als nutzbares Medium erkennen. Jürgen Härig | Hardware Aufbau 24 25 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Die SD‐Karte: Die SD Speicher‐Karte ist ein modernes und beliebtes Speichermedium für mobile Geräte wie Handys, Digitalkameras und MP3 Abspielgeräte. Die SD‐Karte entspricht vom Aufbau her der MMC (Multi Media Card), diese wurde zusammen mit der Siemens‐Tochter Ingentix und SanDisk entwickelt. Mit der Beachtung einiger Standards, können MMC’s wie SD‐Karten angesteuert werden. Da für das Projekt Datenlogger, nur eine SD‐Karte vorgesehen war, wurde keine Kompatibilität mit MMC’s implementiert. Das schränkt vor allem die Fehleranfälligkeit bei der Ansteuerung der Speicherkarten ein. Es wurde das FAT16 Dateisystem gewählt, da dieses für die gewählte Kartengröße am besten schien. Das Dateisystem: Das FAT steht für „File Allocation Table“ (Dateizuordnungstabelle) und ist ein Dateisystem das von Microsoft entwickelt wurde. Häufig genutzte FAT Dateisysteme: FAT12: Formatierung von DOS Disketten bzw. 3,5“ Disketten. FAT16: Wird in heutigen mobilen Datenträgern (ausgenommen Disketten) genutzt, und kann Dateien bis zu einer größe von 2 GigaByte verwalten. FAT32: Wird in mobilen Datenträgerung und Speicherkarten genutzt, die mit mehr als 2 GigaByte Speicherkapazität arbeiten. Wird von neueren DOS‐Systemen, sowie fast jedem moderenen Betriebssystem unterstützt. FATplus: Eine Erweiterung des FAT16 und FAT32 Dateisystems, dass die maximale Größe einer Datei von 4 auf 256 GigaByte herhöht. Aufbau des FAT‐Dateisystems: Das FAT ist in fünf Abschnitte aufgeteilt, die wie folgend aufgebaut sind: ‐ Bootsektor: Der Bootsektor enthält unter anderem ausführbaren x86 Maschinencode, der das Betriebsystem laden soll. An anderen Stellen enthält er aber noch Informatonen über das FAT‐System. ‐ Reservierte Sektoren: Der reservierte Sektorbereich steht für eventuelle Bootloader zur Verfügung, die beim Booten eines Betriebsystems nötig sein können. Jürgen Härig | Hardware Aufbau 25 26 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 ‐ FAT: Die FAT ist Tabellenartig aufgebaut und besitzt eine feste Größe. In dieser Tabelle wird vermerkt, welche Cluster des Dateisystems noch frei sind. Ein Cluster besteht aus einem oder mehreren Sektoren und wird als Zuordnungsdatei zusammen gefasst die von einer Datei belegt werden. Dem Datenbereich ist eine feste Anzahl von Clustern eingeteilt. Jeder Cluster ist in der FAT eingetragen und wird dort mit bestimmten Kennzeichnungen versehen, um den Status des Cluster wiederzugeben. ‐ Stammverzeichnis: Die sogenannte „root directory“, auch Stammverzeichnis genannt, folgt der FAT und enthält die maximale Anzahl der Verzeichniseinträge. Sie ist in Tabellenform angeordnet und wird beim formatiern festgelegt. Das Stammverzeichnis kann, bei 8‐ Zeichen langen Dateinamen, ca. 256 mögliche Datei/Ordnernamen im Hauptverzeichnis speichern. Die Anzahl von Dateinamen/Ordnern ist in Unterverzeichnissen nicht beschränkt. ‐ Datenbereich Ein Verzeichniseintrag im Datenbereich ist 32 Byte groß. Dieser beschreibt die Datei, welche auf dem Datenträger abgelegt wurde, mit den Attributen: Dateiname, ohne Erweiterung Dateiendung Dateiattribute: Versteckt, Systemdatei, Volume‐Label, Unterverzeichnis, Archiv, ungenutzt Reserviert Zeit Datum Offset des Start‐Clusters Dateigröße in Bytes Jürgen Härig | Hardware Aufbau 26 27 Dokumentation Projektt: Datenloggger, von Jürrgen Härig Tecchnikerarbeeit 2008 7.4.6 SD Karten S n Einschu ub Als SD Kaartenslot wu urde der Carrd Connectorr FPS 009 – 3 300 –BL, von n Yamaichi Ellectronis, mit Karten‐ und Schrreibschutzerrkennung gew wählt. Dieser Karteneinschu ub wurde weegen dem Kaarten‐ und Scchreibschutzzerkennungs‐‐Kontakten ggewählt. Diese lieefern der SD‐‐Kartenroutin ne die Inform mation, ob eiine SD Karte eingesteckt ist, oder ob sie schreibggeschützt wu urde. Somit kkönnen Benu utzerfehler ab bgefangen w werden, die b beim versehe entlichen abziehen n der Karte, w wärend einees Schreibzuggriffes auftre eten können.. Der SD‐K Karteneinsch hub gestattett den Zugriff auf alle Kon ntakte der ein ngesteckten SD‐Karte, ob bgleich für die A Ansteuerung über die SPI Schnittstellee nicht alle K Kontakte gen nutzt werden n müssen. 7.4.7 UART Sch U hnittstelle e (RS232)) Der Dateenlogger verfügt über ein ne RS232 kom mpatible Sch hnittstelle, w welche die Nu utzung des D DataLog Program mmes auf einem Windows Computer erlaubt. Ein MAX X3221 Pegelw wandler ist m mit der UARTT0 Schnittstelle des Mikro okontrollers verbunden u und erlaubt SSende, sowiee Empfangen n von Daten zu, bzw. von n dem Mikrokkontroller zu u einer seriellen Computeerschnittstellle. Als Stand dard Übertraagungsgesch hwindigkeit w wurden 9600 0 Baud mit 8 Datenbits, eeinem Stop‐B Bit und ohne Paritätsbits gew wählt. Diese Einstellung ist vom Benu utzer nicht änderbar, son ndern kann n nur mmcode veräändert werden. durch errneutes Proggrammieren mit geänderttem Program 7.4.8 Steuertas S ster Es wurdeen drei Tasteer für den Daatenlogger vo orgesehen, ü über die sich alle Funktio onen des Datenlogggers erreich hen und wen nn möglich ändern lassen n. Die Dateen verfügen ü über einen h hörbaren Ansschlags‐Laut,, über den deer Benutzer über das erfolgreiiche drücken n des Tasterss informiert w wird. Jürgen Häärig | Hardwaare Aufbau 27 28 Dokumentation Projektt: Datenloggger, von Jürrgen Härig Tecchnikerarbeeit 2008 7.4.9 Festspann nungsreg gler Es wurdeen für das Prrojekt zwei FFestspannunggsregler gew wählt, welchee die Komponenten mit d dem jeweilig nötigen Span nnungspegel versorgen. 7.4.9.1 1 7805 – SStandard 5 5V Festspaannungsre egler Der 7805 5 Festspannu ungsregler isst ein Standaardbaustein, welcher einee stabile Spaannungsverso orgung von +5V Gleichspann nung für die komplette H Hauptplatine bietet. Als V Versorgungssspannung für den Festspan nnungsreglerr werden vom m Projektleitter +12V empfohlen Bei optim maler Kühlun ng durch enttsprechend d dimensionierrten Kühlkörp per, ist dieseer Spannungssregler in der Laage 1 A Strom mleistung zu bieten. Diesser Stromwert wird allerd dings niemals erreicht we erden, da der D Datenlogger m maximal 150 0mA benötigtt. Aus therrmischen Sicherheitsgrün nden wurde dennoch ein n kleiner Kühlkörper auf d dem Festspan nnungsreglerr montiert, u um ein besseere wärme Ab bfuhr zu gew währleisten. 7.4.9.2 2 LM317 vvariabler FFestspann nungsregle er Der LM3 317 variable Festspannun ngsregler ist in der Lage aaus einer höh heren Spann nung eine variable wählbare Festspannung zu generieren. Im aktueellen Projekt generiert dieser Bausteiin aus +12 V,, einen +3.3 V Spannungsspegel um die SD Speicherrkarte optimal mit laststaabiler Spannung zu verso orgen. Jürgen Häärig | Hardwaare Aufbau 28 29 Dokumentation Projekt: Datenlogger, von Jürgen Härig 7.4.10 Technikerarbeit 2008 Gehäuse Als Gehäuse wurde ein Standard HUT Schienen Gehäuse der Sorte C700‐HUT gewählt. Breite: 105mm; Höhe: 70mm; Tiefe: 90mm. Details, siehe Datenblatt im Anhang. Besonders bedeutend an dem Gehäuse ist die HUT Schienen Montagemöglickeit, was den Einsatz in Haus Zählerkästen oder in Schaltschränken sehr vereinfacht. Dadurch ist eine schnelle und problemfreie Montage bei Kunden ohne weiteres möglich, ebenfall wird dadurch die Demontage sehr erleichtert. Das Gehäuse bietet einen vollständiges Plastikgehäuse aus widerstandsfähigen, Flammwidrigen Material nach UL94 Klasse V‐2, und schützt den Anwender vor hohen Spannungen, die innerhalb des Gerätes gemessen werden können. Auf der Gehäuse Front ist das LC Display montiert, welches dem Benutzer Steuerungs‐ und Kontrollmöglichkeiten für den Datenlogger gibt. Neben dem LC Display ist ein kleiner Spalt, der die Aufnahme der SD Speicherkarte ermöglicht und die Speicherkarte fast komplett aufnimmt, um unbedachtes abziehen der Karte zu verhindern. Auf der Gehäuse oberseite sind die drei Steuerungs‐Taster verfügbar, über welche sich das Gerät bedienen lässt. Bemaßung der Front: Jürgen Härig | Hardware Aufbau 29 30 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Ansicht von oben: nicht verbunden Strom­Messmodule des Datenloggers 1 2 3 4 5 6 7 8 SD Kartenslot LC Display RS232 Anschluss + ‐ Spannungsvers. Linker Mittlerer Rechter Taster GND Tx Rx RS232 Datenleitungen Jürgen Härig | Hardware Aufbau 30 31 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.4.11 Bauteil Stückliste Exportiert von Datenlogger.sch am 01.05.2008 22:18:04 EAGLE Version 4.16 Copyright (c) 1988‐2005 CadSoft Bauteil Wert Bezeichnung Bauteilbezeichnung Packet Bibliothek C1 1µF CPOL‐EUE2,5‐6E E2,5‐6E rcl C2 0.1µF C‐EU050‐035X075 C050‐035X075 rcl C3 0.33µF C‐EUC1206K C1206K rcl C4 0.1µF C‐EUC1206K C1206K rcl C5 1nF C‐EUC1206 C1206 rcl C6 1nF C‐EUC1206 C1206 rcl C7 1nF C‐EUC1206 C1206 rcl C8 1nF C‐EUC1206 C1206 rcl C9 1nF C‐EUC1206 C1206 rcl C10 1nF C‐EUC1206 C1206 rcl C11 1nF C‐EUC1206 C1206 rcl C12 1nF C‐EUC1206 C1206 rcl C13 0.1µF C‐EUC1206 C1206 rcl C14 0.1µF C‐EUC1206 C1206 rcl IC1 LM317 78XXL 78XXL v‐reg IC2 7805T 7805T TO220H linear JP1 CRUMB128_ CON1 PINHD‐2X16 2X16 pinhead JP2 CRUMB128_ CON2 PINHD‐2X16 2X16 pinhead JP3 LC Display PINHD‐2X5 2X05 pinhead JP4 SD_CARD PINHD‐2X5 2X05 pinhead JP5 Strom 0 Eingang PINHD‐1X1 1X01 pinhead JP6 Strom 0 Ausgang PINHD‐1X1 1X01 pinhead JP7 Strom 1 Eingang PINHD‐1X1 1X01 pinhead JP8 Strom 1 Ausgang PINHD‐1X1 1X01 pinhead JP9 Strom 2 Eingang PINHD‐1X1 1X01 pinhead JP10 Strom 2 Ausgang PINHD‐1X1 1X01 pinhead JP11 Strom 3 Eingang PINHD‐1X1 1X01 pinhead JP12 Strom 3 Ausgang PINHD‐1X1 1X01 pinhead JP13 Strom 4 Eingang PINHD‐1X1 1X01 pinhead JP14 Strom 4 Ausgang PINHD‐1X1 1X01 pinhead JP15 PINHD‐1X2 1X02 pinhead JP16 Strom 5 Eingang PINHD‐1X1 1X01 pinhead Jürgen Härig | Hardware Aufbau 31 Dokumentation 32 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 JP17 Strom 5 Ausgang PINHD‐1X1 1X01 pinhead JP18 Strom 6 Eingang PINHD‐1X1 1X01 pinhead JP19 Strom 6 Ausgang PINHD‐1X1 1X01 pinhead JP20 Strom 7 Eingang PINHD‐1X1 1X01 pinhead JP21 Strom 7 Ausgang PINHD‐1X1 1X01 pinhead R1 240 Ohm R‐EU_M1406 M1406 rcl R2 5k Ohm R‐TRIMM3339P RTRIM3339P rcl R3 3.3k Ohm R‐EU_M2309 M2309 rcl R4 3.3k Ohm R‐EU_M2309 M2309 rcl R5 3.3k Ohm R‐EU_M2309 M2309 rcl R6 3.3k Ohm R‐EU_M2309 M2309 rcl R7 1.8k Ohm R‐EU_M2309 M2309 rcl R8 1.8k Ohm R‐EU_M2309 M2309 rcl R9 1.8k Ohm R‐EU_M2309 M2309 rcl R10 1.8k Ohm R‐EU_M2309 M2309 rcl U$2 ACS712 ACS712 SOIC8 Pauls_parts U$3 ACS712 ACS712 SOIC8 Pauls_parts U$4 ACS712 ACS712 SOIC8 Pauls_parts U$5 ACS712 ACS712 SOIC8 Pauls_parts U$6 ACS712 ACS712 SOIC8 Pauls_parts U$7 ACS712 ACS712 SOIC8 Pauls_parts U$8 ACS712 ACS712 SOIC8 Pauls_parts U$10 ACS712 ACS712 SOIC8 Pauls_parts R11 10k‐Ohm R‐EU_M2309 M2309 rcl R12 10k‐Ohm R‐EU_M2309 M2309 rcl L1 10µH L‐EU_ 0207/7 0207/7 rcl Zusätzliche Bauteile: Menge Bezeichnung Beschreibung 1 C700‐HUT HUT‐Schienen Gehäuse 1 c_fps009_3000 SD‐Kartenslot 1 Crumb128 µController‐Board 10 AKL 073‐02 Lötbare Schraubklemmen mit zwei Anschlüssen. (16A) 1 AKL 073‐03 Lötbare Schraubklemmen mit drei Anschlüssen. (16A) 1 D‐SUB BU 09EU 9 pol. SUB‐D Buchse 1 EA DIP204‐4 4x20 LC‐Display 1 H25PR160 Lochrasterplatine Jürgen Härig | Hardware Aufbau 32 33 Dokumentation Projekt: Datenlogger, von Jürgen Härig 7.4.12 Technikerarbeit 2008 Platine Alle Bauteile wurden auf einer 80mm x 100mm Platine aufgebracht und verbunden. Prototypenbestückung Hauptplatine: Board LCD‐Buchse Crumb128 SD‐Karte 8x Strom‐Mess module Rx Tx Gnd Festspannungswandler ‐ + Jürgen Härig | Hardware Aufbau 33 34 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 LCD‐Adapterplatine: SD‐Karten‐Adapterplatine: Jürgen Härig | Hardware Aufbau 34 35 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 7.5 Schaltplan Datenlogger Jürgen Härig | Hardware Aufbau 35 36 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Hardware Aufbau 36 37 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8 Software 8.1 Mikrokontroller Software Der ATmega128, welcher das Steuer‐ und Kontrollprogramm für den Datenlogger enthält, bietet 128kiloByte an Flash‐Speicher und 4kiloByte SRAM für das Programm. Um das Programm für den Datenlogger schreiben zu können, mussten vom Autor des Projektes zahlreiche Versuche mit dem ATmega16 vorgenommen werden. Es war eine intensive Einarbeitung in die C bzw. C++ Programmiersprache in Bezug auf Mikrokontroller nötig um die entsprechenden Programmierkenntnisse zur Erstellung komplexer Programme zu erlangen. Besonders intensive Einarbeitung war bei folgenden Projektteilen nötig: ‐ AD Wandler Ansteuerung ‐ LCD Ansteuerung, was Delay und Timing Nutzung mit einschloss. ‐ UART Datenübertragung sowie Empfang. ‐ Timer und Timer‐Interrupts. ‐ Nutzung von Holger Klabundes Program‐Bibliothek. ‐ Erstellen von einer State‐Machine, welche über ein Flag‐System arbeitet. Es wurde zu Beginn des Projektes eine Bibliotheken zur Ansteuerung eines HD44780 kompatiblen LC‐ Display entwickelt und programmiert. Diese Bibliothek fand später keine Verwendung im Datenlogger Projekt, da das Display über eine 4‐Bit Nibble‐Ansteuerung funktionieren sollte. Die entwickelte Bibliothek war allerdings für eine 8‐Bit Nibble‐Ansteuerung entworfen und wurde deswegen nicht weitergeführt. Als Alternative wurde die LCD Ansteuer‐Bibliothek von Peter Fleury genutzt, welches eine umfassende Kompatiblität zu zahlreichen Displays und Mikrokontrollern, sowie der benötigten 4‐Bit Nibble Ansteuerung vorzuweisen hatte. Die Bibliothek wurde später um die lcd_print()‐Funktion von Ulrich Radig erweitert, welche eine einfachere Ausgabe der Display‐Texte erlaubt. Der Quellcode zur Ansteuerung der SD‐Speicherkarte, sowie die Funktionen die für den Zugriff auf das FAT Dateisystem wurden von Holger Klabunde entwicklet und geschrieben. Diese Funktionen wurde nicht verändert, sondern nur im dem Rahmen verändert, die von Holger Klabunde vorgesehen waren. Jürgen Härig | Software 37 38 Dokumentation Projektt: Datenloggger, von Jürrgen Härig Tecchnikerarbeeit 2008 8.1.1 Gewählte e Entwickllungssofttware und d Compile er Als Compiler wurde vvon Anfang aan der Open Source Com mpiler, GCC, ggenutzt. Zu Begin nn des Projekktes wurde d der Quellcode mit dem Programm, Prrogrammers Notepad geschrieeben, was sicch bei kleinen n und übersichtlichen Co odes als sehr effektiv erw wiesen hat. D Die Versioneen 1.0 bis 3.2 2 des Datenlo oggers, wurd den mit dem m Programmeers Notepad geschrieben n das im WinAVR‐Packet enth halten ist. Im Laufee der Entwickklung wurde es aber notw wenig die etwas umfangreichere Enttwicklungsum mgebung von Atm mel zu nutzen n, dem AVR SStudio 4. Diesse Entwicklu ungsumgebung erlaubte ees, Projekte mit mehrereen Dateien leeichter verwaalten und Programmiere en zu können n, da ein Deb bugger sehr b bei Fehler‐ o oder Lösungsssuche halfen. Zudem veereinfachte u und beschleu unigte die Nu utzung des AVR Studios, im Zusammenhang mit eeinem STK20 00 Programm mieradapterss, das prograammieren de es ontrollers übeer die ISP Sch hnittstelle. Mikroko Jü ürgen Härig | Software 38 39 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.2 Vorrauslaufende Software­Entwicklungen und Tests Die Datenlogger‐Software wurde schrittweise entwickelt. Oft war es nötig Funktionen erst längere Zeit zu studieren und zu testen, bis entschieden wurden, dass die für das Datenlogger‐Projekt nützlich sein würden. Dabei wurden große Schritte in der Entwicklung der Datenlogger‐Software durch Hauptversionsschritte abgegrenzt, um eine übersichtliche Entwicklungsstruktur zu haben. Es wurden zusätzliche Tests mit kleinen Unterprojekten vollzogen, welche beim verstehen der Register bzw. Routinen sehr hilfreich waren. Diese werden nicht in dieser Auflistung angezeigt. Software ‐ Versionen: v1.0 bis v1.4 Entwicklungsname: home‐Version Inhalt: Die Versionen v1.0 bis 1.4 beinhalteten die ersten Tests mit der UART Schnittestelle, Tastendruck‐Auswertung mit Entprellung, sowie die selbst geschriebene LCD‐ Ansteuerungs‐Routine. Die LCD‐Routine ist in der Lage ein Display mit einer 8‐Bit Schnittstelle anzusteuern und sogar den CGRAM für Sonderzeichenprogrammierung zu nutzen. v2.0 bis v2.4 Entwicklungsname: radig‐nibble Inhalt: Diese Versionen beinhalten die ersten Entwicklungen mit dem Quellcode von Ulrich Radig, über die 4‐Bit Nibble Ansteuerung des LCD’s. Auch finden sich die ersten Menüstrukturen und der erste Einsatz der State‐Machine für die Hauptprogrammschleife. Eine experimentelle Uhr wurde zuletzt eingebaut, aber arbeitete sehr unpräzise. v3.0 bis v3.2 Entwicklungsname: radig nibble adc eeprom Inhalt: In dieser Versionen wurden die State‐Machine für die Tasterbedienung, sowie die variablen Menü‐Inhalte komplett integriert. Die ersten AD‐Wandler Messungen sind über das Menü anzeigbar. Die Software‐Uhr speicherte die Uhrzeit im internen EEPROM und las sie nach einem Neustart von dort wieder aus. Jürgen Härig | Software 39 40 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 v4.0 bis v4.91 Entwicklungsname: lcd (bzw: crum128, ab Version 4.7) Inhalt: Diese Code Version wurde von grund auf neu geschrieben, da das Programmers Notepad nichtmehr den Anforderungen einer Entwicklungsumgebung entsprach. Das Projekt wurde komplexer und selbst kleine Code‐Probleme führten zu Compiler‐ Fehlern. Der komplette eigene Source‐Code wurde für das AVR Studio 4 neu geschrieben, da im Programmers Notepad andere Programm‐Syntaxe erlaubt waren, welche mit dem AVR Studio nichtmehr kompatible waren. Timer‐Interrupts für die Displayaktuallisierung und Uhrzeit, sowie Datumsfunktion wurden implementiert, sowie eine UART Schnittestelle zur Ausgabe der Strom‐ Messwerte. Die ab der Version 4.7 wurde das Crum128 Modul genutzt und die Software auf den ATmega128 übertragen. v5.0 bis v5.9 Entwicklungsname: lcd$Versionsnummer$_crumb128 (Version 5.9: datenlogger_5.9_crum128) Inhalt: Diese Code Versionen beinhalten alle die Software‐Routine zum ansteuern einer SD‐ Speicherkarte über die SPI Schnittstelle des ATmega128, sowie der Zugriff auf das FAT Dateisystem. Der hinzugefügte Quellcode stammt von Holger Klabunde. Integration, sowie zahlreiche Tests mit den SD‐Karten Funktionen waren nötig, um die Bibliotheken in das bestehende Datenlogger‐Projekt einfügen zu können. Es mussten neue State‐Machines für die SD‐Routine enwickelt werden, sowie die Erstellung eines einheitlichen Daten‐Protokols, welches die Messwerte über die UART Schnittstelle schickte und auf die SD‐Karte schrieb. Jürgen Härig | Software 40 41 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8 Bit LCD Ansteuerung Für die Ansteuerung des LCD’s wurde zu Beginn des Projektes eine eigene Ansteuer‐Routine geschrieben, welche aber nur mit dem einen LCD zusammenarbeitete nur über einen 8‐Bit Anschluss der Datenleitung zu gebrauchen war. Die Entwicklung dieser LCD‐Routine kostete einiges an Zeit, da für das alte Display sogar Sonderzeichen mittels CGRAM‐Programmierung in das Display geladen werden mussten. 8.1.3 Extern genutzte Source­Codes Die Externen Source‐Codes sind im Anhang aufgelistet. Bit Nibble Code Nibble und LCD Ansteuerfunktion von Peter Fleury lcd_print() Funktion zur formatierten Ausgabe von Display‐Inhalten, von Ulrich Radig. Manche der genutzten Funktionen mussten für das Projekt modifiziert werden, um nahtlos eingefügt werden zu können. Modifikationen der externen Source‐Codes Upgrade der Peter Fleury Funktionen, um die lcd_print()‐Funktion von Ulrich Radig. (Siehe Anhang) SD Karten Code Anpassungen Änderung von Header‐Dateien, das Entfernen von RS232‐(Debug) Ausgaben, bzw. das Umschreiben der Funktion für die eigene UART‐Funktion. Jürgen Härig | Software 41 42 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4 Software Module und Funktionsbeschreibungen 8.1.4.1 LCD Ansteuerung Die lcd_print()‐Funktion ist einem Projekt von Ulrich Radig entnommen und wurde einwenig modifiziert, um im Code von Peter Fleury korrekt zu funktionieren. void lcd_print (uint8_t spalte,uint8_t zeile,char *Buffer,...) Das Prinzip dieser Funktion ist dem der printf()‐Funktion sehr ähnlich und vereinfach das Zeichnen von Menü’s und Messwert‐Ergebnissen auf dem LCD ungemein. Parameter: uint8_t spalte: Dieser Parameter erhält einen unsigned 8 Bit Intergerwert, der für die Spalte des LCD’s steht. Bei einem 20 zeiligen LCD kann man die Werte 0 bis 19 nutzen, um die Anfangsposition des zu schreibenden Textes zu wählen. uint8_t zeile: Der Parameter zeilem, empfängt ebenfalls einen unsigned 8 Bit Intergerwert, welcher für die Auswahl der Zeile steht, in der die folgende Ausgabe geschehen soll. char *Buffer: Hier wird ein Char‐Array erwartet, welches in den Pointer, *Buffer geladen wird. Dieses Array enthält die Text/Datenausgabe, welche auf dem LCD ausgegeben werden soll. „…“ Diese drei Punkte stehen für Variablen deren Inhalt in die lcd_print()‐ Funktion übergeben werden sollen. Dies wird bei der Ausgabe von Variablen durch diese Funktion deutlich. Beispiel Aufruf in C‐Code: int version = 5; int subversion = 9; lcd_print(0,1,"Datenlogger %i.%i",version,subversion); Ausgabe auf dem LC‐Display: D a t e n l o g g e r 5 . 9 Jürgen Härig | Software 42 43 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.2 Taster Entprellung Für das Hauptproblem eines jeden tastergesteuerten Mikrokontrollerprogramms, gehört das prellen von mechanischen Tastern, welches im Programm zu fehlern führen kann. Daher wurde eine eigene Entprell‐Funktion, mit menüabhängiger Tasterauswertung, geschrieben. Diese Entprellfunktion, arbeitet sehr dynamisch, da es beim Auswerten des Tastendrucks, das Hauptprogramm nicht blockiert, was bei vielen Funktionen und Timingarbeiten zu schwerwiegenden Fehlern führen könnte. Das Prinzip der Entprell‐Funktion: Das Hauptprogramm ist in einer Hauptschleife eingefasst, das der Mikrokontroller bei jede Taktzylus durchläuft. Wenn nun ein Taster gedrückt wird, wird eine Summierer gestartet, der bei jedem Scheifendurchlauf um einen Wert inkrementiert, bist ein Grenzwert erreicht ist. Dann wird der gedrückte Taster mit einem Lock‐Flag (gesperrt‐Flag) gekennzeichnet und der Tastendruck als gültig gewertet und eine „OnPush“‐Funktion („Bei drücken“‐Funktion) für diesen Taster gestartet. Diese Funktion durchläuft nun die Auswertungs‐Funktion, welche abhängig von gewählten Menü, sowie State‐Machine‐Flag für bestimmte Funktionen, wie Uhrzeiteinstellung oder Messwert‐ Kanalwahl, den Tasten‐Druck auswertet. Ist die Auswert‐Funktion beendet, wartet die Entprell‐Funktion auf den Moment, in dem der Taster wieder losgelassen wird. Das Lock‐Flag, welches für den Taster gesetzt wurde, verhindert nun ein nochmaliges Ausführen der Taster‐Auswertfunktion. Sollte der Taster nicht länger gedrückt sein, wird der zuvor aufsummierte Wert vom Schwellwert ab, bei jedem Hauptprogramm durchlauf, dekementiert. Bis der Wert Null erreicht ist, dann wird das Lock‐Flag für den Taster entfernt und es kann optional eine „OnRelease“‐Funktion („Bei freigabe“‐ Funktion) gestartet werden. Diese Funktion wird im Datenlogger‐Projekt nicht verwendet. Jürgen Härig | Software 43 44 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP für Funktion: Mittlere Taster Entprellung Jürgen Härig | Software 44 45 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP des Funktionsaufrufs eines Tasterdrucks Jürgen Härig | Software 45 46 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.3 Analog Digital Wandlung AD Kanal lesen: Für das auslesen der AD Kanäle ist die Funktion, adc_read(), zuständig. Diese Funktion wird mit einem Intergerwert als einziger Übergabeparameter aufgerufen und liefert nach einigen µSekunden einen 10 Bit Intergerwert, als Rückgabewert. Dieser kann in einer 16 Bit unsigned Interger Variable aufgefangen und verarbeitet werden. Diese Funktion ist Teil der AD‐Kanal Messung die im untern Teil erörtert wird. Vorteil an dieser Art der AD Kanal Auslesung ist die sehr praktische Aufrufart des Befehls. Die Funktion läßt sich ohne Probleme in Zählschleifen integrieren, welche schnellst möglich den gewandelten Wert zurück liefern. Diese Funktion arbeitet ohne Interrupt, um Kollisionen mit anderen Interrupts zu vermeiden. AD Kanal Messung: Die Analog/Digitalmessung für das Datenlogger Programm läuft in einer pseudo‐Funktion ab, die nur bei aktiviertem Signal‐Flag für AD‐Messung durchlaufen wird. 1. Ist AD‐Messen‐Flag aktiv, wird die AD‐Kanalmessung gestartet. a. Zunächst wird abgefragt, ob eine Wechsel‐ oder Gleichstrom Messung der Fall ist. b. Im Falle der Gleichstrom Messung wird die acd_read()‐Funktion mit dem Parameter des auszulesenden AD‐Kanals (0…7) ausgeführt und auf den erhalt eines Rückgabewertes gewartet. Der Rückgabewert wird in einem eindimensionalem Array mit 8 Fächern gespeichert und für andere Funktionen bereit gestellt. c. Im Fall der Wechselstrom Messung wird der Timer 3 des ATmega mit dem Prescaler von 128 geladen. Jürgen Härig | Software 46 47 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Berechnung des Timers: Berechnung des 50Hz Zeitfensters: Größe des 16 Bit Timer 3 Registers bestimmen: Startwert des Timer 3 Overflow Registers: Nach der Initialisierung des 20ms‐Timers wird eine kopfgesteuerte while‐Schleife gestartet, in der bei jedem Durchlauf die adc_read()‐Funktion für den aktuellen Kanal ausgeführt wird. Bei jedem Durchlauf der Schleife wird der Spitzenwert der Messung ermittelt und in einer Variablen gespeichert. Dies geschieht so lange, bis die while‐Schleife bei erreichen des Timers Overflows nach 20ms verlassen wird. Dann wird der Timer gestoppt und der ermittelte Spitzenwert im Array‐Fach des ausgelesenen Kanals gespeichert. 2. Sollte das AD‐Messen‐Flag nicht aktiv sein, wird der Offset für diesen AD‐Kanal in das Messwert‐Array geschrieben, um eine frühere Messung auf Null zurück zu setzen. Der Offset‐ Wert symbolisiert den Null‐Wert der Messung, da das Strom‐Messmodul bei Stromfreiheit einen Wert in der größe des Offset‐Wertes liefert. 3. Am Ende der AD‐Messung wird der Kanal‐Zähler für den nächsten auszulesenden Kanal um eins erhöht, bis der Wert Jürgen Härig | Software 47 48 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP der AD Mess‐Funktion Jürgen Härig | Software 48 49 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.4 RMS Berechnung Um den Effektivwert einer sinusförmigen Wechselspannung zu bestimmen, nutzt man die Spitzenwert Berechnung. Wärend der 20ms dauernden AD‐Messung, wird der Spitzenwert der Wechselspannung mit ca. 800 Messzyklen erfasst und ausgewertet. Von dem berechnen, wird der Offset des AD‐Wandlers abgezogen und das Ergebnis dann mit 0.707106 multipliziert. Das Ergebnis der Berechnung ist der Effektivwert der Messung, welche dann auf dem Display und den Datenausgaben ausgegeben wird. Formel: (Spitzenmesswert – Offset) *0.707106 = Effektivwert der 50Hz Spannung. Diese Formel gilt nur für 50Hz sinusförmige Wechselspannungen. Da der Messbereich der AD‐Messung immer 20ms beträgt, ist die Funktion in der Lage immer den Spitzenwert zu ermitteln. Grafik der Sinusabtastung: Jürgen Härig | Software 49 50 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP der Messkanal‐Ausgabe und RMS Berechnung Jürgen Härig | Software 50 51 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.5 Menü Ausgabe Folgende Menü‘s können auf das LCD gezeichnet werden. Viele dieser Menü’s haben variable Werte, die erneuert werden müssen oder sich vom Benutzer, durch Tastendruck ändern lassen. Hauptmenü des Datenloggers: A A < u k K s t o g i n = . v f D a t e n l o g g e r = - - S t a t u s : e L o g s : X i g . S t a t u s > Status‐Menü der Strom‐Messmodule: S t S S p a n < K a a t n n t r u a u s : o m : n g : l I n a k t i X X . X X X X X V X v A > Datum‐ und Zeit‐Menü: - - D a t Z e < A u = u i s Z m t g e i t K o n f i : D D . M M . : h h : m m : a b e g . = - - Y Y Y Y . s s M e n ü > Datenausgabe‐Menü: - - - = D a t e n a u s g a b e = - - o R S 2 3 2 S D C a r d o < S A r t Z e i t > Spannungsart‐Menü: - - - = S p a n n u n g s a r t = - - A r t : X V o l t : X X X A u s g a b e > Jürgen Härig | Software 51 52 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.6 PAP der Menü‐Funktionen PAP Hauptmenü zeichnen Jürgen Härig | Software 52 53 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP Neuzeichnung variabler Menüinhalte. Jürgen Härig | Software 53 54 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP Messkanäle updaten Jürgen Härig | Software 54 55 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.7 Datenausgabe auf SD Karte & RS232 SD‐Karten Fehlercode Auswertung: Vor dem schreiben auf die SD‐Karte, werden die SD‐Karten Fehlercodes überprüft. PAP Fehlermeldungs Auswertung Jürgen Härig | Software 55 56 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 RTS – Funktion: Steuert die Datenausgabe auf die RS232 Schnittstelle und die SD Karte. Das RTS‐Flag wird alle 10 Sekunden in der Sekunden‐Interruptfunktion gesetzt. PAP der RTS‐Funktion Jürgen Härig | Software 56 57 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 PAP der Datenausgabe Jürgen Härig | Software 57 58 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.8 Timer und Interrupts 8.1.4.8.1 Sekunden Timer Für die Berechnung des Sekundentaktes fiel die Wahl auf den 32,768kHz Quarzes der über den TOSC1 und TOSC2 Eingang an den ATmega128 angebunden ist. Formeln für die Berechnung des Timers: Berechnung des Timers: Größe des 8 Bit Timer Registers bestimmen: Startwert des Timers berechnen: Der Startwert Null, lässt den Timer jede Sekunde den Interrupt ausführen, welcher neben der Zeit Berechnung folgende wichtigen Funktionen beeinflusst: ‐ ‐ ‐ ‐ Setzt das Update‐Flag für variable Menüinhalte. Setzt alle 10 Sekunden das RTS‐Flag für die Datenübertragung via RS232 & SD‐Karte. Berechnet den Mittelwert der Strommessung für die Datenausgabe über die RTS Funktion. Timer‐Takt‐Flag setzen, für blinkende Funktions‐Zeichen auf dem LCD. (z.B. Punkte im Uhr‐Menü) Jürgen Härig | Software 58 59 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.4.9 Datum und Uhrzeit Aufbau der Datums‐Funktion mit Schaltjahr‐Erkennung. Jürgen Härig | Software 59 60 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.1.5 Hauptprogramm Ablaufplan Jürgen Härig | Software 60 61 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.2 Windows Software Der Datenlogger besitzt eine UART Schnittstelle die über ein RS232 Kabel, Daten an einen Computer senden kann, welcher dank dem DataLog Programms die Daten aufzeichnen und sogar in einem Diagramm präsentieren kann. Systemanforderungen: ‐ Microsoft Windows 2000/XP/Vista Beriebssystem ‐ Mindestens. Microsoft .Net Framework 2.0 ‐ serielle Schnittstelle mit einer RS232 Buchse 8.2.1 Microsoft Visual C# 2008 Express Edition Die von Microsoft bereit gestellte C# Entwicklungsoberfläche wurde bei der Programmierung des DataLog Programmes genutzt. Vorteil der gewählten Programmieroberfläche ist das von Microsoft entwickelte Framework, DotNet, welches auf fast jedem aktuellen Windows Computer vorzufinden ist. Programmänderungen des DataLog Programmes sind schnell und ohne Probleme vornehmbar und können sogar vom Kunden vorgenommen werden, insofern C# Kenntnisse und der Umgang mit dem Visual C# 2008 Programmierstudio vorhanden sind. Da dieses Framework immer die nötigen Programmdateien mit bringt, ist schon ein kleines Installationsprogramm ausreichend um DataLog ohne nachladen von DLL’s oder Treibern zu installieren. Neuere Programmversionen sind ohne viele Dialoge oder Installationsanleitungen einspielbar und ersetzen die jeweils vorhandene Version. 8.2.2 Externe Source­Codes und Icons Beim schreiben des DataLog Programmes wurde auf externe Bibliotheken zurück gegriffen, die zur besseren Visualisierung von zum Beispiel dem Line‐Graph Diagramms genutzt wurden. Die genutzten Quellcodes wurden von den Programmierern auf der Seite: http://www.codeproject.com bereit gestellt und mit passenden Beispielen dokumentiert. Für die externen Souce‐Codes sind die jeweiligen Entwickler des Codes verantwortlich. Es wurden keine Änderungen an deren Codes vorgenommen und nur vorgesehen Funktionen genutzt. Jürgen Härig | Software 61 62 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.2.2.1 Graph Modul Der in DataLog genutzte Line‐Graph, zur Visualisierung der Strommesskurve im Bezug auf die Zeit wurde dank der Bereitstellung der zedgraph DLL möglich. http://zedgraph.org 8.2.2.2 Tray Symbol Der Tray‐Button, mit welchem das DataLog Programm in die Tray‐Bar (der rechte untere Bildschirmbereich von Windows, in dem auch die Systemuhr zu sehen ist) schickt, wurde dank des Source‐Codes von Tyron Madlener ( mytodo@v‐cm.net ) erstellt. Dieser Code ermöglicht das Anzeigen einer kleinen Punktförmigen Schaltfläche am oberen Programmfensterrand. Dieser Button ist von Microsoft nicht vorgesehen gewesen, bedient sich aber in heutigen Tagen einiger Beliebtheit um Programme in die sog. Tray‐Bar zu schicken. Unter dem Betriebssystem Microsoft Vista bei aktiver Aero‐Oberfläche ist diese Schaltknopf allerdings nicht sichtbar! 8.2.2.3 Icons für die Schaltflächen Die sogenannten Icons, kleine Bilder die zur Verdeutlichung von Schaltflächenfunktionen, die in diesem Projekt genutzt werden, stammen von: http://www.famfamfam.com Diese Icons sind von dem Ersteller als Freeware verfügbar, insofern bei der Nutzung der Icons, dem Ersteller mitgeteilt wird, in welchem Projekt diese genutzt werden. Die Projekte dürfen nicht kommerzieller Natur sein. In der Programm‐Info wird auf die Herkunft der Icons eingegangen. 8.2.3 Programm Erklärung Das Programm DataLog soll dem Benutzer, neben der SD Karten Aufzeichnung die Möglichkeit geben, die Daten des Datenloggers direkt auf dem Computer aufzeichnen und direkt Darstellen zu lassen. Das Programm besitzt eine einfache und durch intuitive Icon’s deutlich gemachte Bedienoberfläche, welche alle wichtigen Programmfunktionen direkt oder indirekt über Untermenüs zugreifbar macht. Dank des DotNet Frameworks kann es sehr schnell auf jedem Windows Computer installiert und gestartet werden. Es ist schon kurz nach der Installation verfügbar und bedarf keiner besonderen Einstellung, außer der Wahl des seriellen Anschlusses und des drückens von „Verbinden“, um mit dem Datenlogger in Verbindung zu treten. Jürgen Härig | Software 62 63 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Um das Programm nutzen zu können muss im Datenlogger unter dem Menü „Datenausgabe“ die RS232 Verbindung als Ausgabeoption gewählt werden. Dann sollte nach kurzer Zeit das erste Datenpacket des Datenloggers an den Computer gesendet und vom DataLog Programm übersetzt werden. Der Benutzer hat nun die Möglichkeit sich die empfangenen Werte dank dreier Anzeigeoptionen einzusehen. ‐ Balkenanzeige: Zeigt die empfangenen Daten in zwei Bereichen getrennt an. Der Bereich „Zeit und Spannung“, zeigt das Datum, Uhrzeit und einheitliche Spannung an, bei der das letzte Datenpacket empfangen wurde. Der Bereich „Messwerte“ zeigt mittels eines Balkens und Integrierter Zahlenwerte, den letzten Strommesswert an. Da der Strom auch negativ (also ins Netz einspeisend) angezeigt werden kann befindet sich der Nullwert in der Mitte. Bei Belastung wird ein positiver Messwert in der Mitte des Balkens angezeigt, welcher sich weiter nach rechts erhöht. Bei Belastung durch einen negativen Messwertes wird ein negativer Messwert in der Balkenmitte angezeigt und der Balken verkleinert sich. Jürgen Härig | Software 63 64 Dokumentation Projekt: Datenlogger, von Jürgen Härig ‐ Technikerarbeit 2008 Datenlog: Im Datenlog können die insgesamt empfangenen Datenübertragungen vom Datenlogger betrachtet werden. Diese Werte sind die roh‐Daten von der seriellen Schnittstelle und können von kundigen Personen zur Programmierung einer eigenen Darstellungssoftware genutzt werden. Über den Dialog „Log“ und „Log speichern“ können die in „Datenlog“ gespeicherten Messwerte als dlog‐ oder txt‐Datei gespeichert werden. Ebenso ist es Möglich ein vorhandenes Datenlog, welches entweder von der SD Karte , oder vom DataLog Programm stammt, fort zu setzen. Allerdings werden die zuvor aufgezeichneten Werte nicht in das Diagramm des DataLog Programmes übernommen. Diese Option folgt vielleicht in einer folgenden Version des DataLog Programmes. Wichtig ist, vor beenden des DataLog Programmes, die aufgezeichneten Daten zu sichern, da sie ansonsten verloren gehen! Jürgen Härig | Software 64 65 Dokumentation Projekt: Datenlogger, von Jürgen Härig ‐ Technikerarbeit 2008 Diagramm: Das Line‐Graph Diagramm hilft dem Benutzer die gesendeten Daten des Datenloggers graphisch im Bezug zur Zeit, darstellen zu lassen. Nun kann der Benutzer das DataLog Programm durch drücken des Maximieren‐Buttons am Programmfensterrand, oder durch doppelklicken auf den oberen Programmfensterrand, auf Bildschirmfüllenden Modus schalten. Diese Ansicht kann das Betrachten der Messwerte vereinfachen. Der Benutzer muss ca. 20 Sekunden warten, bis genügend Messwerte von Datenlogger geschickt wurden, dann kann er durch rechtsklick mit der Maus einen Dialog öffnen. Durch drücken des untersten Menüpunktes „Maßstab auf Standardwert setzen“, richtet sich die Ansicht auf die empfangenen Datenwerte aus. Über den rechtsklick‐Dialog: „Bild speichern als…“ kann ein Bild des Diagrammes gespeichert werden. Es stehen die Dateiformate: EMF, PNG, GIF, JPG, TIF und BMP zur Auswahl. Das angezeigte Diagramm läßt sich auch über einen lokalen oder Netzwerkdrucker ausdrucken. Jürgen Härig | Software 65 66 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.2.4 Schnittstellen für die Datenaufzeichnung Das DataLog Programm besitzt zwei Möglichkeiten, Daten aufzunehmen. Zum einen das empfangen von Daten über die serielle Schnittstelle, sowie das Fortsetzen von Datenlogger‐Dateien, welche auf einer SD‐Karte aufgezeichnet wurden. Die aufgezeichneten oder fortgesetzten Daten können dann als dlog‐ oder txt‐Datei gespeichert werden. Diese Dateien eignen sich, um mit Excel eingelesen zu werden, um umfangreiche Diagramme erstellen zu können. 8.2.4.1 RS232 Um über die serielle Schnittstelle, Daten vom Datenlogger empfangen zu können, muss der Computer über eine solche Schnittstelle oder ein „USB nach RS232“‐Gerät verfügen. Im Auswahlfenster neben der „Verbinden“ Schaltfläche, kann einer der verfügbaren COM‐Ports ausgewählt werden, über den der Datenlogger mit dem Computer verbunden ist. Durch drücken der „Verbinden“ Schaltfläche, lauscht das DataLog Programm auf dem gewählten COM‐Port. Wenn im Datenlogger, die serielle Ausgabe aktiviert wurde, erscheinen nach maximal 10 Sekunden, Datum und Uhrzeit sowie Datenmesswerte der aktiven Messkanäle. 8.2.4.2 SD Karte Die Aufzeichnungs‐Datei auf der SD‐Karte entspricht dem Format des DataLog Programmes und können damit fortgesetzt werden. Allerdings werden die schon bestehenden Daten der SD‐Karte nicht im Diagramm dargestellt! Jürgen Härig | Software 66 67 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 8.2.4.3 Struktur der aufgezeichneten Daten Die aufgezeichneten Daten des Datenloggers entsprechen immer dem selben Datenmuster, da die Daten‐Formatierungsstruktur der Senderoutine für RS232 Speicherung und SD‐Karten Übertragung die selbe ist. Das Protokol für die Datenanordnung ist ziemlich simpel gewählt, es wird Datum, Uhrzeit, AD‐ Wandler Wert als Digit‐Wert sowie die Spannung, ausgegeben. Alle 10 Sekunden wird ein Log‐Eintrag hinzugefügt. Dieser Eintrag ist ein Mittelwert aus den Messungen innerhalb der 10 Sekunden. Die Struktur der Daten ist wie folgend: AD 1 AD 2 AD 3 AD 4 AD 5 AD 6 AD 7 AD 8 Volt 01.01.2000 12:00:00 512 512 512 512 512 512 512 512 230 01.01.2000 12:00:10 512 512 512 512 512 512 512 512 230 Datum Uhrzeit Datum: Uhrzeit: Das Datum ist nach dd/mm/yyyy formatiert und für jeden Log‐Eintrag ausgegeben, um den Daten einen zeitlichen Bezug zu geben. Die Uhrzeit ist in hh/mm/ss formatiert. Strom‐Wandler 1 bis 8: Die Strom‐Wandler Werte werden als Digit’s ausgegeben und müssen mit dem Prescaler und dem Offset der AD‐Wandler verrechnet werden. Diese Werte können zwischen 0 und 1023 liegen Spannungswert: Der Spannungswert wird als Interger Wert übertragen. Alle einzelnen Werte sind durch Leerzeichen voneinander getrennt, zudem ist jede Zeile durch folgende Werte als String formatiert. ‐ carriage return ‐ new line ‐ \0 Diese Daten können von einem Excel Programm eingelesen und als Diagramm dargestellt werden. Beim öffnen einer DatenLog‐Datei, muss in Excel die Erkennung von Leerstellen angegeben werden, um die einzelnen Daten voneinander zu trennen. Jürgen Härig | Software 67 68 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 9 Bedienungsanleitung Um den Datenlogger in Betrieb nehmen zu können, müssen folgende Bedingungen eingehalten werden. 9.1 Hardware ‐ Spannungsversorgung: 12 Volt DC Spannungsquelle mit einer Leistung von mindestens 250mA. ‐ Betriebsumgebung: EMV‐Problematische Umgebungen können die Messungen verfälschen. DerDatenlogger muss in trockenen Bereichen mit Temperaturen um die 20° Celsius betrieben werden, um korrekt zu funktionieren. ‐ Zu messende Ströme: Die Strom‐Messanschlüsse des Datenloggers sind für Ströme bis maximal 16A bei 230Volt Wechselspannung ausgelegt. Um einen Verbraucher mit einer solchen Stromaufnahme messen zu können müssen Kabel ausgewählt werden, die für diese Belastung ausgelegt sind und vor Berührung gesichert wurden. Das anschließen und messen höhere Ströme oder Spannungen können den Bediener gefährden und den Datenlogger so wie das zu messende Gerät zerstören! Die geraden Ziffern der Anschlussleiste, 20 bis 34 müssen bei Gleichspannung zum Anschluss der Spannungsquelle genutzt. Wärend alle ungeraden Ziffern, 21 bis 35 an die Last angeschlossen werden müssen. Die Anschlüsse, 18 und 19 sind nicht belegt. Zueinander verbundene Messkanäle: Kanal 1 Kanal 2 Kanal 3 Kanal 4 Kanal 5 Kanal 6 Kanal 7 Kanal 8 20→21 22→23 24→25 26→27 28→29 30→31 32→33 34→35 Spannungs‐ quelle Verbraucher Jürgen Härig | Bedienungsanleitung 68 69 Dokumentation Projekt: Datenlogger, von Jürgen Härig ‐ Technikerarbeit 2008 RS232 Anschluss: Durch Anschluss eines Null‐Modem Kabels, kann der Datenlogger an einen Computer angeschlossen werden. Alternativ kann der Datenlogger auch über einzelne Kabel an eine RS232 Schnittstelle angeschlossen werden. Dafür werden zwei Kabel benötigt: Schema des Datenloggers: ‐ Anschluss 15, ist Masse vom Datenlogger und muss zur Masse des Computers verbunden werden. ‐ Anschluss 16, ist Tx des Datenloggers und muss zu Rx des Computers verbunden werden. 9.2 Windows Software Vorraussetzungen: ‐ ‐ ‐ Windows 2000 / XP / Vista mindestens .Net Framework 2.0 RS232 Schnittstelle (auch via USB‐>RS232 möglich) Installation der Software: 1. Die Datei: Setup_DataLog.exe, ausführen und die Installationsanweisungen befolgen. Sollten sie kein .Net Framework installiert haben, wird automatisch ein Installationsprogramm gestartet, welches die benötigten Daten von der Microsoft Webseite herunter läd und installiert. Für diesen Vorgang ist eine aktive Internetverbindung nötig. 2. Nach der Installation steht das DataLog Programm unter: Start → Programme → DataLog zur Verfügung und kann sofort gestartet werden. Jürgen Härig | Bedienungsanleitung 69 70 Dokumentation Projektt: Datenloggger, von Jürrgen Härig Tecchnikerarbeeit 2008 9.3 SD D Karte Es wird eempfohlen eeine 256 MB große SD‐Sp peicherkarte von Hama zu nutzen, grö ößere Speicherrkarten, oder Speicherkaarten von and deren Herste ellern könnteen vom Dateenlogger nich ht erkannt werden. Die Speiccherkarte muss FAT16 fo ormatiert seiin um von Datenloggger gelesen n werden zu können. Jü ürgen Härig | Bedienunggsanleitung 70 71 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 10 Inbetriebnahme Anleitung zur Inbetriebnahme des Datenloggers. 10.1 Hardware Um den Datenlogger in Betrieb zu nehmen, muss eine konstante 12V Gleichspannung zur Verfügung stehen. Diese Spannung muss dann an den Pin 1 (+) und Pin 2 (‐) angeschlossen und verschraubt werden. Es ist darauf zu achten, dass die Spannungsquelle beim anschließen ausgeschalten ist und vor dem einschalten der Spannungsquelle nochmals die Polarität überprüft wurde! 1. Beim einschalten der Versorgungsspannung ist auf dem LCD für 2 Sekunden die Software‐ Version und der Produkt‐Hersteller zu sehen. Danach ist das Hauptmenü zu sehen. 2. Um die später aufgezeichneten Daten einen korrekten zeitlichen Bezug zu geben, muss durch drücken der linken Taste in das Zeit Konfig. Menü gewechselt werden. 1.Mittlerer Tastendruck: 2.Mittlerer Tastendruck: 3.Mittlerer Tastendruck: 4.Mittlerer Tastendruck: 5.Mittlerer Tastendruck: 6.Mittlerer Tastendruck: Der Sekundenzähler bleibt stehen. Der Doppelpunkt zwischen Stunden und Minuten Zähler blinkt im Sekundentakt. Die Stunden sind nun mit der rechten Taste aufwärts und mit der linken Taste abwärts einstellbar. Der Doppelpunkt zwischen Minuten und Sekunden Zähler blinkt im Sekundentakt. Die Minuten sind nun mit der rechten Taste aufwärts und mit der linken Taste abwärts einstellbar. Der Punkt zwischen Tag und Monat Zähler blinkt im Sekundentakt. Der Tageszähler ist nun mit der rechten Taste aufwärts und mit der linken Taste abwärts einstellbar. Der Punkt zwischen Monat und Jahre Zähler blinkt im Sekundentakt. Der Monatszähler ist nun mit der rechten Taste aufwärts und mit der linken Taste abwärts einstellbar. Der Punkt hinter dem Jahre Zähler blinkt im Sekundentakt. Der Jahreszähler ist nun mit der rechten Taste aufwärts und mit der linken Taste abwärts einstellbar. Die Uhreneinstellung wurde verlassen und der Sekundenzähler zählt nun weiter. Jürgen Härig | Inbetriebnahme 71 72 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 3. Durch nochmaliges drücken der linken Taste gelangt man in das Datenausgabe‐Menü. 1.Mittlerer Tastendruck: Zwei Pfeile erscheinen neben den beiden Ausgabemöglichkeiten des Datenloggers. Mit der linken Taste kann nun die RS232 Ausgabe aktiviert werden und „connected“ erscheint unterhalb des RS232 Textes.Im Hauptmenü wird zusätzlich in der Zeile: Ausg.Status, „RS232“ ausgegeben. Nochmaliges drücken der linken Taste deaktiviert die RS232 Ausgabe und der Text „connected“ verschwindet wieder. Mit drücken der rechten Taste wird die SD‐Kartenausgabe aktiviert. Bei vorhandensein einer einwandfreien Speicherkarte erscheint „mounted“ unterhalb des SDCard Textes ansonst eine Fehlermeldung. Zusätzlich wird im Hauptmenü in der Zeile: Ausg.Status, „SDCard“ ausgegeben. Nochmaliges drücken der rechten Taste deaktiviert die Ausgabe auf der SD‐ Karte wieder. Sind beide Ausgaben gleichzeitig aktiv, wird im Hauptmenü hinter: Ausg.Status, „RS SD“ ausgegeben. Es ist unbedingt darauf zu achten, die SD‐Karte niemals vor Deaktivierung der Karte aus dem Datenlogger zu ziehen. Es kann zu Datenfehlern oder Zerstörung des Dateisystems kommen! 2.Mittlerer Tastendruck: Deaktiviert die Bearbeitung der Datenausgabe auswahl. Die bestehende Ausgabe auswahl wird übernommen. 4. Mit der linken Taste kann nun in das Spannungart‐Menü gewechselt werden. Dort kann die Spannungsart sowie die Spannung in Volt eingestellt werden. 1.Mittlerer Tastendruck: Ein Pfeil erscheint links neben der „Art“ Auswahl, die standardgemäß ein = Zeichen enthält, was Gleichstrommessung bedeutet.Durch drücken einer der Richtungstasten (linker oder rechter Taster) kann auf Wechselstrommessung, und wieder zurück geschaltet werden. 2.Mittlerer Tastendruck: Mit der rechten Taste kann die Spannungsstärke erhöht, mit der linken Taste vermindert werden. 3.Mittlerer Tastendruck: Beendet die Spannungsart auswahl. Jürgen Härig | Inbetriebnahme 72 73 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 5. Zuletzt kann man nun durch mehrmaliges drücken der rechten Taste in das Status‐Menü wechseln. Dort stehen 8 Messkanäle zur Verfügung. 1.Mittlerer Tastendruck: Aktiviert einen Inaktiven Messkanal, bzw. deaktiviert einen aktiven Messkanal. Es werden die Messwerte im Sekundentakt ausgegeben. Fehlermeldungen des Datenloggers: SD‐Kartenfehler: NO CARD: Wärend die Ausgabe auf die SD‐Karte aktiviert wurde, wurde keine Karte im SD‐ Kartenslot gefunden. UNKNOWN: Die eingesteckte SD‐Karte wurde nicht erkannt. Bitte einen anderen Datenträger, von anderem Hersteller oder größe wählen. LOCKED: Der Schreibschutz der SD‐Karte ist aktiv. Bitte deaktivieren. FULL: Die eingesteckte SD‐Karte ist voll und muss getauscht oder geleert werden. FATFAIL: Ein Fehler beim schreiben auf das Dateisystem wurde festgestellt. Bitte den Datenträger formatieren. Jede Fehlermeldung muss mit deaktivieren der SD‐Karte, im Datenausgabe‐Menü quitiert werden. 10.2 Software Die Windows Software, DataLog ist nach der Installation schon größtenteils konfiguriert und kann sofort gestartet werden. Der Datenlogger muss zur Kommunikation mit der DataLog Software auf RS232, in der Datenausgabe gestellt werden. Auch muss der Datenlogger über ein Null‐Modem Kabel oder ähnlichem mit dem Computer verbunden sein. Rechts neben der Schaltfläche: Verbinden, ist eine Drop‐Down Auswahl der COM‐Ports, die an diesem Computer vorhanden sind. Bitte dort den COM‐Port auswählen, an dem der Datenlogger angeschlossen wurde und die Schaltfläche „Verbinden“ anklicken. Sobald das Programm sich mit der Schnittstelle verbunden hat, ist die Drop‐Down Auswahl der COM‐ Ports ausgegraut und die „Verbinden“ Schaltfläche ist umbenannt worden in, „Trennen“. Jürgen Härig | Inbetriebnahme 73 74 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Umgang mit der Diagramm Ansicht: Mit der Ansichts‐Option, „Diagramm“ werden die aufgezeichneten Messwerte als Line‐Graph dargestellt. In diesem Modus kann die DataLog Software in den bildschirmfüllenden Modus gebracht werden. Um die Diagrammansicht auf die Messwerte zu setzen, muss mit der rechten Maustaste in das Diagrammfeld geklickt werden, dann erscheint ein Auswahlmenü. Mit der Auswahl: ‐ ‐ ‐ ‐ ‐ ‐ Maßstab auf Standardwert setzen: Werden die aktuellen Messwerte fokusiert. Kopieren: Wird das aktuelle Diagramm als Bild in die Zwischenablage kopiert. Bild speichern als…: Gibt eine Auswahl an Dateiformaten, in denen das Diagrammbild gespeichert werden kann. Seite einrichten: Einrichten der Seite für einen Druckauftrag. Drucken…: Das Diagramm wird direkt gedruckt. Punktwerte anzeigen: Zeigt dieMessdaten für die jeweiligen Messpunkte als Text an, wenn man mit der Maus auf einen Messwert zeigt. Jürgen Härig | Inbetriebnahme 74 75 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 11 Anhang 11.1.1 main.c //------------------------------------------------------------//Compiler Header //------------------------------------------------------------#include <stdlib.h> #include <stdio.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <stdbool.h> #include <avr/interrupt.h> #include <string.h> #include <math.h> //------------------------------------------------------------// Eigene Header //------------------------------------------------------------#include "lcd.h" #include "main.h" #include "rs232.h" #include "mem-check.h" #include "mydefs.h" #include "dos.h" //------------------------------------------------------------//------------------------------------------------------------// Prototypen //------------------------------------------------------------// Delay-Schleifen Ablaufberechnung für µSekunden #define delay(us) _delayFourCycles( ( ( 1*(XTAL/4000) )*us)/1000 ) static inline void _delayFourCycles(unsigned int __count); void onrelease(int); void onpress(int); void timer_init(void); void date(uint8_t *,uint8_t *,uint8_t *); uint16_t adc_read(int); void change_dir(void); //------------------------------------------------------------//SD Karten Variablen //------------------------------------------------------------char filename[] = "dlog.TXT"; // Dateiname, der Log-Datei char datadir[] = "DATENLOG"; // Standard-Verzeichnis der Log-Datei char fileid; // Datei-ID Register char data_temp[8]; // Daten-Zwischenspeicher char date_temp[20]; // Datum-Zwischenspeicher int len; // Ungenutzte snprintf-Variable int i,d; // Schleifen-Variablen char out_buffer[70]; // Ausgabe-Buffer unsigned int written;// SDCard-Ausgabe, wieviele Zeichen geschrieben wurden unsigned int write = 70; //------------------------------------------------------------//Datenausgabe //------------------------------------------------------------#define SD_OK 0 // SD: kein Fehler #define SD_EMPTY 1 // SD: Kartenslot leer #define SD_UNKNOWN 2 // SD: Medium nicht erkannt #define SD_FULL 3 // SD: Karte voll #define SD_FATFAIL 4 // SD: FAT Probleme #define SD_LOCKED 5 // SD: Karte verriegelt Jürgen Härig | Anhang 75 76 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 int device_sd_error = SD_OK; // SDCard Fehlerflag uint8_t device_select = 0; // Ausgabe Auswahl zeiger bool device_rs232 = false; // Ausgabe über RS232? bool device_sd_card = false; // Ausgabe über SDcard? bool device_sd_active = false; // SDcard Aktiv? bool device_rs_active = false; // RS232 Aktiv? bool device_rts = false; // Ready to Send, (alle 10 Sekunden getriggert) int device_buffer_delay = 0; // Zählt die 10 Sekunden für device_rts //------------------------------------------------------------// Timer Interrupts und Intervale //------------------------------------------------------------// 1 Sekunde für 256 Takte bei 32.768KHz externer zweiter Quarz #define START_1S 0 // 20ms für 50Hz AD Wandlung jedes Kanals #define START_20_MS 65248 // Toggelt bei jedem Sekunden-Takt. Blink-Quelle bool timertakt = false; //------------------------------------------------------------//Menü //------------------------------------------------------------unsigned char menu = 10; // Menü auswahl unsigned char hereiam = 0; // Menü positionsmerker unsigned char sd_mount = 0; // Mount merker für SD Karte // Startet die LCD Variablen Auffrischung bool data_update = true; //------------------------------------------------------------//Interne Uhr //------------------------------------------------------------uint8_t sec = 0; // Sekunden-Zähler uint8_t min = 0; // Minuten-Zähler uint8_t hour = 0; // Stunden-Zähler uint8_t day = 1; // Tages-Zähler uint8_t month = 1; // Monats-Zähler uint8_t year = 0; // Jahres-Zähler // Definiert welche Zeiteinstellung, im Edit-Modus ist. unsigned int change_time = 0; // Stunden-Änderungs-Flag, zeigt an, ob die Stunden verändert werden können bool change_time_hour = false; // Minuten-Änderungs-Flag, zeigt an, ob die Minuten verändert werden können bool change_time_min = false; // Tage-Änderungs-Flag, zeigt an, ob die Tage verändert werden können bool change_date_day = false; // Monate-Änderungs-Flag, zeigt an, ob die Monate verändert werden können bool change_date_month = false; // Jahre-Änderungs-Flag, zeigt an, ob die Jahre verändert werden können bool change_date_year = false; //------------------------------------------------------------//Taster Debounce //------------------------------------------------------------// Zählt die Dauer des Tasterdrucks int debounce_counter[] = {0,0,0}; // Sperrt die einzelnen Tasten bool taster_lock[] = {false,false,false}; //------------------------------------------------------------//AD Wandlung //------------------------------------------------------------unsigned int kanal = 1; // Kanal auswahl // AD Wandler Kanal für Leistungsaufzeichnung unsigned int readkanal = 0; Jürgen Härig | Anhang 76 77 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Strom-Wert-Buffer, speichert die AD-Messwerte der AD-Wandler double strom[] = {0,0,0,0,0,0,0,0}; // Speichert, welche AD-Kanäle auf 'aktiv' gesetzt sind bool status[] = {false,false,false,false,false,false,false,false}; // Speichert die Anzahl der aktiven Logs unsigned int status_count = 0; // Speichert die angelegte Spannung, unter der gemessen werden soll int spannung = 36; // Speichert den errechneten Strom-Mittelwert jedes Kanals bis zur Ausgabe int mittelwert[] = {0,0,0,0,0,0,0,0}; // Buffer für die Ausgabe des Strom-Wertes auf dem Display unsigned int stromstring[5]; // Offset bei Strommessmodulen die negativ messen können int AD_OFFSET = {511,511,511,511,511,511,511,511}; // 20ms Strommessungsflag. Wenn aktiv, wird per RMS der Strom gemessen bool ms = false; // Speichert den AD-Wert des gewählen AD-Kanals, bei aktiver RMS Messung double ms_strom; // zählt die anzahl der AD Messungen wärend der 20ms int ms_counter = 0; // Zwischenspeicher um den Strom-Spitzenwert herauszufinden double strom_vergleich = 0; // ms_edit Index, um die Editierung im Spannungsart-Menü zu überwachen int ms_edit = 0; bool ms_edit_art = false; // Spannungsart bearbeiten = false; // Spannungshöhe bearbeiten bool ms_edit_volt // Prescaler für Strommessmodul (bei LEM Modul 64) int AD_PRESCALE = 20; ////////////////////////////////////////////////////////Main-Start int main(void) { PORTA =0x00; DDRA = 0x00; //Eingänge AD Wandler taster_port = 0x00; // Pullup für Steuertaster aktivieren DDRB |= (1<<7); // AVR Board LED Ausgang setzen // Initialisierung verschiedener Module //------------------------------------------------------------lcd_init(LCD_DISP_ON); // LCD initialisieren und einschalten timer_init(); // Sekunden Timer initialisieren rs232_init(); // RS232 Schnittstelle initialisieren MMC_IO_Init(); // SPI für SD Karte initialisieren sei(); // Timer-Interrupt erlauben //------------------------------------------------------------// Bootscreen //------------------------------------------------------------int a; lcd_print(0,1,"Datenlogger %i.%i",version,subversion); lcd_print(0,2,"Dev. By JHaerig"); for (a=0;a<2000;a++) // 2 Sekunden warten { delay(1000); } Jürgen Härig | Anhang 77 78 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //------------------------------------------------------------// Niederspannungserkennung, Abschalt-Funktion ausführen (8,7V Versorgungsspannung) while(voltsense_pin&(1<<voltsense)) { //------------------------------------------------------------// Rechter Taster - Entprellung //------------------------------------------------------------if(debounce_counter[rechts]<=0&&taster_lock[rechts]==true) // On-Release-Event { debounce_counter[rechts]=0; taster_lock[rechts]=false; //onrelease(rechts); // On Release Aktion } if(debounce_counter[rechts]>=1&&taster_lock[rechts]==false) // On-Push-Event { taster_lock[rechts]=true; onpress(rechts); // On Push Aktion } if(taster_pin&(1<<taster_rechts)&&taster_lock[rechts]==false) debounce_counter[rechts]++; if(!(taster_pin&(1<<taster_rechts))&&taster_lock[rechts]==true) debounce_counter[rechts]--; //------------------------------------------------------------// Mittlerer Taster - Entprellung //------------------------------------------------------------if(debounce_counter[mitte]<=0&&taster_lock[mitte]==true) // On-Release-Event { debounce_counter[mitte]=0; taster_lock[mitte]=false; //onrelease(mitte); // On Release Aktion } if(debounce_counter[mitte]>=1&&taster_lock[mitte]==false) // On-Push-Event { taster_lock[mitte]=true; onpress(mitte); // On Push Aktion } if(taster_pin&(1<<taster_mitte)&&taster_lock[mitte]==false) debounce_counter[mitte]++; if(!(taster_pin&(1<<taster_mitte))&&taster_lock[mitte]==true) debounce_counter[mitte]--; //------------------------------------------------------------// Linker Taster - Entprellung //------------------------------------------------------------if(debounce_counter[links]<=0&&taster_lock[links]==true) // On-Release-Event { debounce_counter[links]=0; taster_lock[links]=false; //onrelease(links); // On Release Aktion Jürgen Härig | Anhang 78 79 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 } if(debounce_counter[links]>=1&&taster_lock[links]==false) // On-Push-Event { taster_lock[links]=true; onpress(links); // On Push Aktion } if(taster_pin&(1<<taster_links)&&taster_lock[links]==false) debounce_counter[links]++; if(!(taster_pin&(1<<taster_links))&&taster_lock[links]==true) debounce_counter[links]--; //------------------------------------------------------------// Statischer Menüinhalt, abhängig vom gewählten Menüpunk, zeichnen //------------------------------------------------------------//Wenn der übergebene Menüpunkt ungleich dem aktuellen Menüpunkt ist baue das gewünschte Menü auf. if(menu != hereiam) { //merke dir die aktuelle Menüposition hereiam = menu; lcd_clrscr(); //Clear Display switch(menu) //Zeichne das gewünschte Menü { case(7): lcd_print(0,0,"---=Spannungsart=---"); lcd_print(5,1,"Art:"); lcd_print(4,2,"Volt:"); lcd_print(12,3,"Ausgabe");lcd_putc(0x10); break; case(8): //------------------------------------------------------------// Datenausgabe //------------------------------------------------------------lcd_print(0,0,"---=Datenausgabe=---"); //Bearbeitbar mit "mittelere taste". Zeige an: "mounted" bei geladener SD Karte -> unmount. "unmounted" bei keiner geladenen SD Karte. Springt zu Menüpunkt 0 lcd_print(1,1,"RS232");lcd_print(13,1,"SDCard"); // Zeichen: Pfeil nach Links lcd_gotoxy(0,3);lcd_putc(0x11); lcd_print(1,3,"SArt"); lcd_print(15,3,"Zeit"); lcd_putc(0x10); // Zeichen: Pfeil nach Rechts break; //------------------------------------------------------------case(9): //------------------------------------------------------------// Zeit Einstellung //------------------------------------------------------------lcd_print(0,0,"---=Zeit Konfig.=---"); //Bearbeitbar mit "mittlere taste". Springe mit weiterem druck der Taste zur nächsten Stelle. lcd_print(0,1,"Datum:"); lcd_print(1,2,"Zeit:"); // Zeichen: Pfeil nach Links lcd_gotoxy(0,3);lcd_putc(0x11); //Sonderzeichen "ü" ausgeben lcd_print(1,3,"Ausgabe Men");lcd_putc(0x7E); // Zeichen: Pfeil nach Rechts lcd_putc(0x10); Jürgen Härig | Anhang 79 80 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 break; //------------------------------------------------------------case(10): //------------------------------------------------------------// Hauptmenü //------------------------------------------------------------lcd_print(0,0,"----=Datenlogger=---"); lcd_print(0,1,"Ausg.Status:"); lcd_print(0,2,"Aktive Logs:"); // Zeichen: Pfeil nach Links lcd_gotoxy(0,3);lcd_putc(0x11); lcd_print(1,3,"Konfig. Status"); // Zeichen: Pfeil nach Rechts lcd_putc(0x10); break; //------------------------------------------------------------case(11): //------------------------------------------------------------// Messkanal Status //------------------------------------------------------------lcd_print(2,0,"Status:"); lcd_print(3,1,"Strom:"); lcd_print(0,2,"Spannung:"); // Zeichen: Pfeil nach Links lcd_gotoxy(0,3);lcd_putc(0x11); lcd_print(2,3,"Kanal"); break; default: break; } } //------------------------------------------------------------// Variabler Menüinhalt, bei Änderung eines Wertes, neu zeichnen //------------------------------------------------------------//Wenn es neue variable Daten gibt. if(data_update == true) { //setze das Flag zurück data_update = false; switch(menu) //Zeichne variable Menüinhalte { case(7): // Wenn Wechselspannung ausgewertet werden soll if(ms==true) { // Zeichen: Tilde '~' lcd_gotoxy(10,1); lcd_putc(0xCE); }else{ lcd_print(10,1,"="); } lcd_print(10,2,"%i ",spannung); if(ms_edit_art) { // Zeichen: Pfeil nach Links lcd_gotoxy(14,1);lcd_putc(0x11); }else{ // Zeichen: Leerzeichen lcd_gotoxy(14,1);lcd_putc(0x20); } Jürgen Härig | Anhang 80 81 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 if(ms_edit_volt) { // Zeichen: Pfeil nach Links lcd_gotoxy(14,2);lcd_putc(0x11); }else{ lcd_gotoxy(14,2);lcd_putc(0x20); // Zeichen: Leerzeichen } break; case(8): //------------------------------------------------------------// Datenausgabe Konfig //------------------------------------------------------------// Anzeige der gewählten Ausgabe (Punkte an der Linken bzw. Rechten Seite der Auswahl) //------------------------------------------------------------// rs232 als ausgabe gewählt if(device_rs232==true) { // Zeichen: Punkt lcd_gotoxy(0,1);lcd_putc(0xBB); }else{ lcd_print(0,1," "); } // sd_card als ausgabe gewählt if(device_sd_card==true) { // Zeichen: Punkt lcd_gotoxy(19,1);lcd_putc(0xBB); }else{ lcd_print(19,1," "); } //------------------------------------------------------------// Auswahl Pfeile wärend der Bearbeitung des Datenausgabe Menüs //------------------------------------------------------------if(device_select!=0) { // Zeichen: Pfeil nach Rechts lcd_gotoxy(12,1);lcd_putc(0x10); // Zeichen: Pfeil nach Links lcd_gotoxy(6,1);lcd_putc(0x11); }else{ // Zeichen: Leerzeichen lcd_gotoxy(12,1);lcd_putc(0x20); // Zeichen: Leerzeichen lcd_gotoxy(6,1);lcd_putc(0x20); } //------------------------------------------------------------// Status- oder Fehlermeldung der SD Karte ausgeben //------------------------------------------------------------if(device_sd_card==true&&device_sd_active==true&&device_sd_error==SD_OK) { // gib "mounted" aus lcd_print(12,2,"mounted"); }else{ switch(device_sd_error) { case(SD_OK): // Kein Fehler lcd_print(12,2," "); break; Jürgen Härig | Anhang 81 82 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 case(SD_EMPTY): // Slot leer lcd_print(12,2,"NO CARD"); break; case(SD_UNKNOWN): // Medium unbekannt lcd_print(12,2,"UNKOWN "); break; case(SD_FULL): // Karte voll lcd_print(12,2,"FULL "); break; case(SD_FATFAIL): // FAT Fehler, beim erstellen des Verzeichnisses oder beim öffnen der Datei lcd_print(12,2,"FATFAIL"); break; case(SD_LOCKED): // Karte schreibgeschützt lcd_print(12,2,"LOCKED "); break; } } //------------------------------------------------------------// Status der RS232 ausgeben //------------------------------------------------------------if(device_rs232==true&&device_rs_active==true) { // gib "connected" aus lcd_print(1,2,"connected"); }else{ // ansonst nichts lcd_print(1,2," "); } break; //------------------------------------------------------------case(9): //------------------------------------------------------------// Zeit/Datum Konfig //------------------------------------------------------------// Zeit hh:mm:ss lcd_print(8,2,"%2i:%2i:%2i",hour,min,sec); // Stunde, blinkend if(change_time==1&&timertakt==false) lcd_print(10,2," "); // Minute, blinkend if(change_time==2&&timertakt==false) lcd_print(13,2," "); // Datum Ausgabe date(&day,&month,&year); // Tag, blinkend if(change_time==3&&timertakt==false) lcd_print(10,1," "); // Monat, blinkend if(change_time==4&&timertakt==false) lcd_print(13,1," "); // Jahr, blinkend if(change_time==5&&timertakt==false) lcd_print(18,1," "); break; case(10): Jürgen Härig | Anhang 82 83 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //------------------------------------------------------------//Hauptmenü //------------------------------------------------------------// Wenn RS232 aktiv und SD inaktiv if(device_rs_active&&!device_sd_active) lcd_print(13,1,"RS232"); // Wenn RS232 und SD aktiv if(device_rs_active&&device_sd_active) lcd_print(13,1,"RS SD"); // Wenn RS232 und SD aktiv if(!device_rs_active&&device_sd_active) lcd_print(13,1,"SDCard"); // Wenn RS232 und SD inaktiv if(!device_rs_active&&!device_sd_active) lcd_print(13,1,"N/A"); // Wenn es einen SD Fehler gibt if(device_sd_error!=0) lcd_print(13,1,"ERROR "); // Anzahl der aktiven Logs ausgeben lcd_print(13,2,"%i",status_count); break; //------------------------------------------------------------case(11): //------------------------------------------------------------// Mess-Kanäle //------------------------------------------------------------// Wenn Kanal-Flag auf true, gib Aktiv aus if(status[kanal-1]==true) { lcd_print(10,0," Aktiv"); }else{ lcd_print(10,0,"Inaktiv"); } lcd_gotoxy(10,1); int z; // Stromstring array löschen, sonst stehen noch alte Werte darin. Folge: Falsche ausgabe for(z=0;z<=5;z++) stromstring[z]=0; //------------------------------------------------------------// Wenn Wechselspannungsmessung aktiv //------------------------------------------------------------if(ms) { // Wenn Wechselspannungsmessung aktiv: Spitzenwertberechnung ausführen dtostrf((((strom[kanal-1] - AD_OFFSET) / AD_PRESCALE) * 0.707106), 5, 2, stromstring ); }else{ // Wenn Wechselspannungsmessung inaktiv: Normale Berechnung ausführen1 dtostrf(((strom[kanal-1] - AD_OFFSET) / AD_PRESCALE), 5, 2, stromstring ); } //------------------------------------------------------------// Strom-Wert ausgeben lcd_puts(stromstring); lcd_print(15,1," A"); // Spannungswert ausgaben lcd_print(10,2,"%2i V",spannung); // Gewählter Kanal ausgeben lcd_print(10,3,"%i",kanal); // Wenn kanal-menü kleiner 8 (kanäle von 1-8) if(kanal<8) Jürgen Härig | Anhang 83 84 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { lcd_gotoxy(19,3); // Zeichen: Pfeil nach Rechts lcd_putc(0x10); }else{ // Zeichen: Leerzeichen lcd_print(19,3," "); } break; //------------------------------------------------------------default: break; } } //------------------------------------------------------------// Bestimmt die Anzahl der aktiven Log-Kanäle //------------------------------------------------------------status_count = 8; unsigned int s; for(s=0;s<=7;s++) { if(status[s]==false) status_count--; } //------------------------------------------------------------// Liest einen AD Kanal aus und speichert den Wert, um beim nächsten Zyklus den nächsten Kanal aus zu lesen //------------------------------------------------------------// Wertet Aktive und Inaktive AD Wandler aus. if(status[readkanal]==true) { if(ms!=true) // Wenn RMS Messung nicht aktiv { //------------------------------------------------------------// normale Strommessung //------------------------------------------------------------// AD Wandler bei jedem zyklus einlesen strom[readkanal] = adc_read(readkanal); }else{ //------------------------------------------------------------// 20ms Strommessung //------------------------------------------------------------TCNT3 = START_20_MS; // ab hier läuft Timer los clk/128 TCCR3B = 0x05; do { //AD Wandler bei jedem zyklus einlesen ms_strom = adc_read(readkanal); // Peakwert ermitteln und speichern if(ms_strom>strom_vergleich) strom_vergleich=ms_strom; delay(200); } // warten, solange kein Überlauf while ((ETIFR & (1<<TOV3))==0); // Überlauf wieder löschen ETIFR = ETIFR | (1<<TOV3); // Timer wieder stoppen TCCR3B = 0x00; // Spitzenwert als AD-Wert nutzen Jürgen Härig | Anhang 84 85 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 strom[readkanal] = strom_vergleich; // ms-Zähler zurücksetzen ms_counter=0; // Vergleichs-Variable auf 0 strom_vergleich=0; } //------------------------------------------------------------}else{ //------------------------------------------------------------//OFFSET vom Strommessmodul als Nullwert ausgeben //------------------------------------------------------------// Offset in strom-Register schreiben strom[readkanal]=AD_OFFSET; } readkanal++; // nächsten zu lesenden auswählen if(readkanal==8)// bei Kanal 8 auf ersten Kanal zurückspringen readkanal=0; //------------------------------------------------------------// Verbindungsoption umsetzen, Menü Datenausgabe //------------------------------------------------------------// RS232 //------------------------------------------------------------if(device_rs232==true) // Wenn rs232 aktivert sein soll { device_rs_active=true; // Setzt die rs232 Ausgabe auf aktiv }else{ // Wenn die Funktion device_rs_active=false; deaktiviert wurde, setze das Flag zurück } //------------------------------------------------------------// SD Karte //------------------------------------------------------------// Überprüfe die SD Karte auf Vorhandensein und Schreibschutz //------------------------------------------------------------if(sd_detect_pin&(1<<sd_detect)) // Überprüfe, ob SD Karte vorhanden { // Überprüfe, SD Schreibschutz if(sd_detect_pin&(1<<sd_lock)) { //device_sd_error=SD_OK; // Wenn SD Karte nicht schreibgeschützt, Übernimm den vorherigen Fehler }else{ if(device_sd_error==0) device_sd_error=SD_LOCKED;// Ansonst SD_LOCKED } }else{ device_sd_error=SD_EMPTY; // Ansonst SD_EMPTY ausgeben // Wenn Karte wärend sie gemounted ist, nichtmehr vorhanden ist, wirf sie "sauber" aus. if(device_sd_active) { Fclose(fileid); } } //------------------------------------------------------------if(device_sd_card==true&&device_sd_active==false&&device_sd_error==SD_OK) // Wenn sd_card aktivert sein soll Jürgen Härig | Anhang 85 86 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { // Wenn die Karte nicht initialisiert werden kann if(GetDriveInformation()!=F_OK) { device_sd_active=false; // Setze Fehlermeldung, Karte nicht erkannt device_sd_error=SD_UNKNOWN; }else{ // Verzeichnis wechseln change_dir(); // Datei zum schreiben öffnen und Daten anhängen fileid=Fopen(filename,F_APPEND); if(fileid>=0) { // Setzt die sd_card Ausgabe auf aktiv device_sd_active=true; // Wenn das Mounten geglückt ist, übernimm den vorherigen fehler }else{ // Fileopen Fehler device_sd_error=SD_FATFAIL; device_sd_active=false; } } } // Wenn die Karte nicht als Ausgabe genutzt werden soll if(device_sd_card==false) { // Wenn die Funktion deaktiviert wurde, setze das Flag zurück device_sd_active=false; device_sd_error=SD_OK; Fclose(fileid); // Log File schließen } //------------------------------------------------------------// Datenausgabe, gewählt anhand der aktivierten Datenausgabe Optionen // Ausgabe alle 10 Sekunden //------------------------------------------------------------// Wenn 10 Sekunden vorbei sind und die Datenausgabe über RS232 erlaubt wurde if(device_rts) { // Datum Array vorbereiten strcpy(date_temp, "00000000"); // Array: date_temp löschen for(d=0;d<70;d++) // Array: out_buffer löschen out_buffer[d] = 0; // Ausgabe des Strings formatieren und in date_temp schreiben. len = snprintf(date_temp, 20, "%02i.%02i.%02i %02i:%02i:%02i ",day,month,year,hour,min,sec); // Überträgt den Inhalt von date_temp in out_buffer strcat(out_buffer,date_temp); // Daten Array vorbereiten strcpy(data_temp, "000000"); // Array: data_temp löschen for(i=0;i<8;i++) { //strom[i] mit 2 vor und nachkommastellen als String in data_temp speichern len = snprintf(data_temp, 8, "%4d ",mittelwert[i]); // data_temp-Array: Inhalt an out_buffer anhängen strcat(out_buffer,data_temp); } // Formatierter Spannungswert als String in data_temp schreiben Jürgen Härig | Anhang 86 87 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 snprintf(data_temp, 4, "%3i",spannung); // data_temp-Array: Inhalt an out_buffer anhängen strcat(out_buffer,data_temp); i = 0; while (out_buffer[i] != 0x00) // Suche in out_buffer nach 0x00 { i++; } // Folgende Reihenfolge ist WICHTIG, sonst erkennen die Text-Editoren die Zeilenumbrüche nicht! out_buffer[i] = 0x0D; // carriage return out_buffer[i+1] = 0x0A; // newline out_buffer[i+2] = 0x00; // "\0" macht aus einem Char-Array einen "echten" String! // Wenn die Ausgabe auf die RS232 aktiv ist if(device_rs_active) { // Gib out_buffer an der RS232 Schnittstelle aus rs232_puts(out_buffer); } //Überprüfe ob die SD Karte aktiv und fehlerfrei gemounted wurde. if(device_sd_active&&device_sd_error==SD_OK) { // Write to SD Card // Schreibe nur, wenn die Versorgungesspannung gewährleistet ist. if(voltsense_pin&(1<<voltsense)) { //Schreibe out_buffer mit "write"-Anzahl an Bytes written=Fwrite(out_buffer,i+2,fileid); if(written!=i+2) // Wenn die geschriebenen Bytes ungleich den übertragenen Bytes sind, ist die Karte voll. device_sd_error=SD_FULL; }else{ // Unmounte die Karte bei zuwenig Versorungsspannung Fclose(fileid); } }else{ // deaktiviere die Ausgabe auf die SDCard device_sd_active=false; } device_rts=false; //Daten gesendet. RTS-Flag zurücksetzen } //------------------------------------------------------------// Debug //------------------------------------------------------------/* if(timertakt) // Zeitdurchlauf Messung (Oszi) {PORTB |= (1<<7);}else {PORTB &= ~(1<<7);} */ //------------------------------------------------------------} // Ende While-Schleife // Abschaltfunktion bei Niederspannung //------------------------------------------------------------- Jürgen Härig | Anhang 87 88 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 cli(); // Interrupts abschalten lcd_clrscr(); // Display löschen if(device_sd_active) // Wenn SD Karte aktiv, unmounte die Karte Fclose(fileid); // geöffnete Datei schließen // Fehlermeldung auf LCD Ausgeben lcd_print(0,1,"Niederspannungs-"); lcd_print(0,2,"Abschaltung"); while(1); //------------------------------------------------------------} ////////////////////////////////////////////////////////Main-Ende //------------------------------------------------------------// Sekunden Timer Init Funktion //------------------------------------------------------------void timer_init(void) // Uhrzeit Counter Initialisieren { ASSR = (1<<AS0); // Externer Quarz Oszi an TOSC wählen TCCR0 = 0x05; // Prescale auf clk/128 setzen. TCNT0 = START_1S; // Startwert in Zählerregister laden TIMSK = (1<<TOIE0); // TimerOverflowInterruptEnable für Timer0 aktivieren. } //------------------------------------------------------------// Interrupt Sekundentimer //------------------------------------------------------------SIGNAL (SIG_OVERFLOW0) // Uhrzeit Interrupt 1mal pro Sekunde { cli(); // Interrupts deaktivieren TCNT0 = START_1S; // Timer Counter zurücksetzen data_update = true; // LCD Melden, dass sich Inhalt geändert hat 1mal pro Sekunde // Sekunden, Minuten, Stunden und Tag aufrechnen //------------------------------------------------------------if(change_time==0) sec++; // Sekunden hochzählen if (sec == 60) { min++; // Minuten hochzählen sec = 0; } if (min == 60) { hour++; // Stunden hochzählen min = 0; } if (hour == 24) { day++; // Tage hochzählen hour = 0; } // Timer-Takt (z.B. für blinken im Datumsmenü) timertakt = timertakt ^ true; //------------------------------------------------------------// Zählt 10 Sekunden für die Datenausgabe //------------------------------------------------------------- Jürgen Härig | Anhang 88 89 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 device_buffer_delay++; if(device_buffer_delay==10) { device_rts=true; //Daten senden FLAG setzen device_buffer_delay=0; //Zurücksetzen des Zählers } //------------------------------------------------------------// Mittelwertrechnung für Datenausgabe (nicht Live-Ausgabe!) //------------------------------------------------------------for(i=0;i<8;i++) { // Stromwert auf Mittelwert aufaddieren mittelwert[i] = mittelwert[i] + strom[i]; // Mittelwert durch zwei teilen mittelwert[i] = mittelwert[i] >> 1; } //------------------------------------------------------------// Interrupts aktivieren sei(); } //------------------------------------------------------------//------------------------------------------------------------// On-Button-Release Tastenauswertung //------------------------------------------------------------void onrelease(int richtung) //Bei fallender Schaltflanke eines Tasters { switch(richtung) { case(rechts): break; case(mitte): break; case(links): break; } } //------------------------------------------------------------// On-Button-Press Tastenauswertung //------------------------------------------------------------// Bei steigender Schaltflanke eines Tasters voidonpress(intrichtung) { // Variable Daten auf dem Display 1 mal erneuern data_update=true; switch(richtung) { case(rechts): //------------------------------------------------------------// Rechter Tastendruck //------------------------------------------------------------switch(menu) { case(7): //------------------------------------------------------// Wenn in Spannungsart Menü //------------------------------------------------------// ms_edit Flag auswerten switch(ms_edit) { case(0): menu++; // Menü aufwärts Jürgen Härig | Anhang 89 90 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 break; case(1): // Wenn die Spannungsart geändert werden soll if(ms) // Wenn ms-Flag (RMS-Flag) aktiv { ms=false; spannung=36; }else{ ms=true; spannung=230; } break; case(2): // Wenn Spannungswert angepasst werden soll spannung++; break; default: } break; break; case(8): //------------------------------------------------------// Wenn in Datenausgabe Menü //------------------------------------------------------// Wenn device_select aktiv switch(device_select) { case(1): // wenn device_sd_card aktiv ist if(device_sd_card) { // deaktiviere sd_card device_sd_card=false; }else{ // ansonst aktiviere sd_card device_sd_card=true; } break; default:menu++; break; } break; case(9): //------------------------------------------------------// Wenn in Zeit/Datum Menü //------------------------------------------------------// change_time Flag aktiv auswerten switch(change_time) { case(0): // Menü hochzählen menu++; break; case(1): // Stunde hochzählen hour++; // Stunden begrenzung if(hour>=24){hour=0;} break; case(2): Jürgen Härig | Anhang 90 91 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Minute hochzählen min++; // Minuten begrenzung if(min>=60){min=0;} break; case(3): // Tag hochzählen day++; break; case(4): // Monat hochzählen month++; break; case(5): // Jahr hochzählen year++; break; default: break; } break; case(11): //------------------------------------------------------// Wenn in Mess Kanäle Menü //------------------------------------------------------// Wenn kanal-menü kleiner 8 if(kanal<8) { //Zähle Kanal hoch kanal++; }else{} break; default: //------------------------------------------------------// Wenn in allen anderen Menü's //------------------------------------------------------// Menüpunkt aufaddieren menu++; break; } break; case(mitte): //------------------------------------------------------------// Mittlerer Tastendruck //------------------------------------------------------------switch(menu) { case(7): //------------------------------------------------------// Wenn in Spannungsart Menü //------------------------------------------------------// EditFlag für Spannungsart-Menü hochzählen ms_edit++; // ms_edit Flag auswerten switch(ms_edit) { case(0): // Nichts machen break; case(1): // Spannungsart Editier-Bereit ms_edit_art=true; Jürgen Härig | Anhang 91 92 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 break; case(2): // Volt Editier-Bereit ms_edit_art=false; ms_edit_volt=true; break; case(3): // Edit Beendet ms_edit_volt=false; ms_edit=0; break; } break; case(8): //------------------------------------------------------// Wenn in Datenausgabe Menü //------------------------------------------------------// device_select Flag hochzählen device_select++; if(device_select==2) //Device auswahl beenden device_select=0; break; case(9): //------------------------------------------------------// Wenn in Zeit/Datum Menü //------------------------------------------------------// change_time Flag hochzählen change_time++; switch(change_time) { case(1): Jürgen Härig | Anhang 92 93 Dokumentation Projekt: Datenlogger, von Jürgen Härig // Wenn Flag auf Stunde ändern change_time_hour=true; // Setze variable "stunde ändern" // Wenn Flag auf Minute ändern change_time_min=true; // Setze variable "minute ändern" change_time_hour=false; // Setze variable "stunde ändern" zurück // Wenn Flag auf Tag ändern change_time_min=false; // Setze variable "minute ändern" zurück change_date_day=true; // Setze changecount zurück // Wenn Flag auf Monat ändern change_date_day=false; // Setzt Variable "tag ändern" zurück change_date_month=true; // Setze Variable "monat" // Wenn Flag auf Jahr ändern change_date_month=false; // Setze Variable "monat ändern" zurück change_date_year=true; // Setze Variable "jahr" Technikerarbeit 2008 break; case(2): break; case(3): break; case(4): break; case(5): break; case(6): change_date_year=false; // Setze Variable "jahr ändern" zurück change_time=0; break; default: break; } break; case(10): //------------------------------------------------------// Wenn in Hauptmenü //------------------------------------------------------break; case(11): //------------------------------------------------------// Wenn in Mess Kanäle Menü //------------------------------------------------------//Wenn Kanal [kanal] "Aktiv" ist if(status[kanal-1]==true) { //Setze bei Tastendruck, kanal auf inaktiv status[kanal-1]=false; }else{ //Setze bei Tastendruck, kanal auf aktiv status[kanal-1]=true; } break; default: break; Jürgen Härig | Anhang 93 94 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 } break; case(links): //------------------------------------------------------------// Linker Tastendruck //------------------------------------------------------------switch(menu) { case(7): //------------------------------------------------------// Wenn in Spannungsart Menü //------------------------------------------------------// ms_edit Flag auswerten switch(ms_edit) { case(0): // Nichts machen break; case(1): // Wenn Editierung der Spannungsart aktiv // Wenn ms-Flag (RMS-Flag) gesetzt if(ms) { ms=false; spannung=36; }else{ ms=true; spannung=230; } break; case(2): // Wenn Spannungswert verändert werden soll // spannung Variable subtrahieren spannung--; break; default: break; } break; case(8): //------------------------------------------------------// Wenn in Datenausgabe Menü //------------------------------------------------------// Wenn device_select aktiv switch(device_select) { case(1): // wenn device_rs232 aktiv ist if(device_rs232) { // deaktiviere rs232 device_rs232=false; }else{ // ansonst aktiviere rs232 device_rs232=true; } break; default: // Menü abwärts menu--; Jürgen Härig | Anhang 94 95 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 break; } break; case(9): //------------------------------------------------------// Wenn in Zeit/Datum Menü //------------------------------------------------------// change_time Flag auswerten switch(change_time) { case(0): // Menü herabzählen menu--; break; case(1): // Wenn Flag auf Stunde ändern // Stunde herabzählen hour--; // Stunden begrenzung if(hour>23){hour=23;} break; case(2): //Wenn Flag auf Minute ändern // Minuten herabzählen min--; // Minuten begrenzung if(min>59){min=59;} break; case(3): // Wenn Flag auf Tag ändern // Tage herabzählen solange nicht gleich Null if(day!=0) day--; break; case(4): // Wenn Flag auf Monat ändern // Monat herabzählen solange nicht gleich Null if(month!=0) month--; break; case(5): // Wenn Flag auf Jahr ändern // Jahre herabzählen solange nicht gleich Null if(year!=0) year--; break; default: break; } break; case(11): //------------------------------------------------------// Wenn in Mess Kanäle //------------------------------------------------------// Ab Kanal 1 if(kanal<2) { // gehe im Menü zurück menu--; }else{ // ansonst gehe Kanal zurück kanal--; Jürgen Härig | Anhang 95 Dokumentation 96 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 } break; default: //------------------------------------------------------// Bei allen anderen Menü's //------------------------------------------------------// Menüzähler herabzählen menu--; break; } } break; } //------------------------------------------------------------// SD Karten Change & Check Dir //------------------------------------------------------------void change_dir(void) { // Wenn der Verzeichnis-Wechsel nicht geglückt ist if(Chdir(datadir)!=F_OK) { // Erstelle Verzeichnis, und prüfe, ob es erfolgreich war if(Mkdir(datadir)==F_OK) { // Wechsle in das Verzeichnis Chdir(datadir); }else{ // Wenn Probleme beim Verzeichnis erstellen auftreten // Fehlerflag setzen device_sd_error=SD_FATFAIL; } } } //------------------------------------------------------------// Delay Schleife 1µS //------------------------------------------------------------static inline void_delayFourCycles(unsigned int__count) { if(__count==0) __asm____volatile__("rjmp 1f\n 1:");// 2 cycles else __asm____volatile__( "1: sbiw %0,1""\n\t" "brne 1b"// 4 cycles/loop :"=w"(__count) :"0"(__count) ); } //------------------------------------------------------------- 11.1.2 main.h #include <avr/io.h> // PIN Belegung der Steuer-Taster //------------------------------------------------------------#define taster_port PORTE #define taster_pin PINE Jürgen Härig | Anhang 96 97 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #define taster_rechts 4 #define taster_mitte 3 #define taster_links 2 //------------------------------------------------------------//SD Kartenerkennungskontakte //------------------------------------------------------------#define sd_detect_pin PINE #define sd_lock 7 #define sd_detect 6 //------------------------------------------------------------// PIN Belegung für Niederspannungsabschaltung //------------------------------------------------------------#define voltsense_pin PINE #define voltsense 5 //------------------------------------------------------------//------------------------------------------------------------// Hilft bei der Umwandlung von "rechts/mitte/links" // in den Wert "0/1/2" für das array taster_lock und debounce_counter //------------------------------------------------------------#define rechts 0 #define mitte 1 #define links 2 //------------------------------------------------------------// Versions-Informationen über den Stand der Entwicklung seit Version 4 //------------------------------------------------------------#define version 5 //SD Karten Implementierung #define subversion 8 // Überprüfung der Versorungsspannung VOR dem schreiben auf die SD Karte implementiert. // 20ms Periodenmessung eingebaut. // Menü zur Konfigurierung der Messstromart (Gleich/Wechsel) und Spannungshöhe eingebaut. // Bug in Live-Ausgabe gefixt. // kleine Bugfixes an der Datumsausgabe, der Datenausgabe // AD_PRESCALE und AD_OFFSET als Variablen eingebaut // Bug gefixt, führende Nullen ausgeben, bei der Datenausgabefunktion %02i, anstatt %2i // Code sauber Kommentiert und ausgerichtet. Finale Version //#define subversion 7 // AD Wert Umrechnung auf Ampere integriert. // Problem mit stromstring behoben //#define subversion 6 // Fehlermeldungen für die SD Karte eingefügt. // Hardware Meldungen über SD Kartenslot // Automatisches Fclose() bei Abzug der Karte, wärend sie gemounted ist. // Code gesäubert und ausgerichtet //#define subversion 5 //Datenausgabemenü, Pfeile wärend der Bearbeitung sichtbar. //#define subversion 4 // Timer an TOSC (32.768kHz) wird als Sekunden-Takt-Quelle genommen //#define subversion 3 Jürgen Härig | Anhang 97 98 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Low Voltage Sense // Datenvorbereitung umgesetzt. RS232 Ausgabe nimmt nun die selbe Datenquelle (out_buffer) wie die SD Karte //#define subversion 2 // Schreibt die Textdatei SAUBER! auf die SD Karte. // mittelwert[]-Array liefert sich aufsummierenden Mittelwert und gibt ihn auf der SD Karte aus //#define subversion 1 //Aufsummierung der AD Werte zwischen daten Ausgaben // Funktioniert NOCH nicht! //#define subversion 0 // Fehler in der AD Ausgabe. double-Wert macht Probleme! //#define version 4 // Code neu geschrieben //#define subversion 91 // kleine Menüänderungen. Änderung an der Datumsfunktion (Jahr nicht größer als 2099) // Änderung an der Datenausgabe. //#define subversion 8 // 20ms AD Wandlung noch nicht gemacht! //#define subversion 7 //Aufspielung auf crum128 mit KS0073 LCD //#define subversion 6 //OCSR1 PWM Ausgang für LCD-Helligkeitsregelung implementieren! //#define subversion 5 // Menü Neuordnung //#define subversion 4 //UART Test //#define subversion 3 //ADC Implementierung Jürgen Härig | Anhang 98 Dokumentation 99 Projekt: Datenlogger, von Jürgen Härig 11.1.3 Technikerarbeit 2008 adc.c #include <inttypes.h> #include <avr/io.h> uint16_t adc_read(int channel) { // 16Bit große Variable definieren uint16_t x=0; // Aktiviert den ADC mit Prescaler 16 --> 14.745600Mhz/16 = 921.6kHz ADCSRA = _BV(ADEN) | _BV(ADPS2); // auszulesender Kanal über das ADMUX Register wählen // (Übergabewert des Funktionsaufrufs) ADMUX = channel; // Externe Referenzspannung verwenden (also 5 V) ADMUX |= (1<<REFS0); // AD Umwandlung starten ADCSRA |= _BV(ADSC); // Warte bis die Umwandlung fertig ist while (ADCSRA & _BV(ADSC) ) {} // gewandelten Wert auslesen x = ADCW; return x; } 11.1.4 #include #include #include #include date.c "lcd.h" <inttypes.h> <stdio.h> <stdbool.h> // Übergabe der Daten void date(uint8_t *day, uint8_t *month, uint8_t *year) { // Schaltjahr Auswertung bool schaltjahr = ((*year % 4 == 0 && *year % 100 != 0) 400 == 0); || *year % // Anzahl der Tage für einen Monat uint8_t monats_tage[] = { 31, // Januar // Februar (Schaltjahr Abwägung) 28, 31, // März 30, // April 31, // Mai 30, // Juni 31, // Juli 31, // August 30, // September 31, // Oktober 30, // November 31 // Dezember }; // Schaltjahr-Fall behandeln if(schaltjahr) Jürgen Härig | Anhang 99 Dokumentation 100 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 monats_tage[1]=29; // Tage pro Monat auswerten if(*day>monats_tage[*month-1]) { *day=1; *month = *month + 1; } // Monate pro Jahr auswerten if(*month>13) { *month=1; *year = *year + 1; } // Overflow des Jahr-Wertes abfangen. if(*year==100) { *year=0; } // Datum-Ausgabe auf dem LC-Display dd.mm.yy Ausgabe lcd_print(8,1,"%2i.%2i.20%2i.",*day,*month,*year); } 11.1.5 rs232.c #include <avr/io.h> #include <inttypes.h> #include <avr/pgmspace.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <stdarg.h> #define BAUD_RATE 9600 // RS232 Schnittstelle Initialisieren void rs232_init (void) { // Aktiviere Receiver und Transmitter UCSR0B = (1<<RXEN0)|(1<<TXEN0); // Setze Rahmen Format: 8data, 1stop bit, no-parity UCSR0C = (3<<UCSZ0); // Übertragungsgeschwindigkeit festlegen UBRR0H = (unsigned char)((F_CPU / (BAUD_RATE * 16L) - 1)>>8); UBRR0L = (unsigned char)(F_CPU / (BAUD_RATE * 16L) - 1); } // Char-Zeichen auf RS232 Schnittstelle ausgeben void rs232_putchar (char c) { while ( !( UCSR0A & (1<<UDRE0)) ); UDR0 = c; } // String auf RS232 Schnittstelle ausgeben void rs232_puts(const char *s) { while (*s) rs232_putchar(*s++); } // Formatierter String auf RS232 Schnittstelle ausgeben. Jürgen Härig | Anhang 100 101 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Option für Variablen-Ausgabe gegeben void rs232_print (char *Buffer,...) { va_list ap; va_start (ap, Buffer); int format_flag; char str_buffer[10]; char str_null_buffer[10]; char move = 0; char Base = 0; int tmp = 0; //Ausgabe der Zeichen while (*Buffer != 0) { if (*Buffer == '%') { *Buffer++; if (isdigit(*Buffer)>0) { str_null_buffer[0] = *Buffer++; str_null_buffer[1] = '\0'; move = atoi(str_null_buffer); } switch (format_flag = *Buffer++) { case 'b': // Bin Ausgabe Base = 2; goto ConversionLoop; case 'c': //Int to char format_flag = va_arg(ap,int); rs232_putchar (format_flag++); break; case 'i': // Int Ausgabe Base = 10; goto ConversionLoop; case 'o': // Oct Ausgabe Base = 8; goto ConversionLoop; case 'x': // Hex Ausgabe Base = 16; //**************************** ConversionLoop: //**************************** itoa(va_arg(ap,int),str_buffer,Base); int b=0; while (str_buffer[b++] != 0){}; b--; if (b<move) { move -=b; for (tmp = 0;tmp<move;tmp++) { str_null_buffer[tmp] = '0'; } Jürgen Härig | Anhang 101 Dokumentation 102 Projekt: Datenlogger, von Jürgen Härig } else Technikerarbeit 2008 //tmp ++; str_null_buffer[tmp] = '\0'; strcat(str_null_buffer,str_buffer); strcpy(str_buffer,str_null_buffer); } rs232_puts (str_buffer); move =0; break; } { rs232_putchar (*Buffer++); } } va_end(ap); } 11.1.6 rs232.h // Prototypen // Prototyp für die Initialisierung des UART Registers void rs232_init (void); // Einzelnes ASCII Zeichen über die UART (RS232) Schnittstelle senden void rs232_putchar (char c); // Char-Array (String) über die UART Schnittstelle senden void rs232_puts(const char *s); // Modifizierbares Char-Array senden. Durch das Steuerzeichen '%' kann die Funktion // variable Werte als Interger, Hexadezimal, Binär-Zahl wiedergeben // z.B. %i für Interger Ausgabe einer Variable. void rs232_print (char *Buffer,...); 11.2 Externe Quellcodes 11.2.1 compact.c //########################################################### // File: compact.c // // Read-/Writeroutines for CompactFlash in LBA mode. // CHS mode not supported. // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 09.05.2004 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### Jürgen Härig | Anhang 102 103 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Compiler: AVR-GCC 3.4.3 //######################################################################### //@{ #include <avr/io.h> #include "mydefs.h" #include "dos.h" #ifdef CF_DEBUG #include "printf.h" #endif #ifdef COMPACTFLASH_CARD U32 maxsect; // last sector on drive //###################################################### unsigned char CFReadSector(unsigned long lba, unsigned char *buf) //###################################################### { unsigned int i; unsigned char by; unsigned char *p; #ifdef CF_DEBUG printf("RS %lu\n",lba); #endif if(lba>=maxsect) return 1; //sectornumber too big if(CFWaitReady()) return 1; // CFWriteAdr(CF_FEATURES,0); CFWriteAdr(CF_SECCOUNT,1); //read one sector //This will work only up to 128GB, 2^28 * 512 by=(unsigned char)lba; CFWriteAdr(CF_LBA0,by); //D7..0 by=(unsigned char)(lba>>8); CFWriteAdr(CF_LBA1,by); //D15..8 by=(unsigned char)(lba>>16); CFWriteAdr(CF_LBA2,by); //D23..16 by=(unsigned char)(lba>>24); by&=0x0F; //only four LSBs used by|=0xE0; //LBA-Mode Drive0 CFWriteAdr(CF_LBA3,by); //D27..24 CFWriteAdr(CF_STACOM,CF_READ_SEC); if(CFWaitDrq()) return 2; p=buf; CF_DATA_DIR_IN(); CFSetAdr(CF_IO); CF_CS_OFF(); for(i=0; i<BYTE_PER_SEC; i++) { CF_RD_OFF(); NOP(); //Schon bei 8MHz muß man hier etwas warten NOP(); //Es könnte evtl. nötig sein hier noch mehr Jürgen Härig | Anhang 103 104 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //nop einzufügen, um möglichst viele CF zu lesen //Bei mir reichten bisher 2 nop //Sicherheitshalber noch einen mehr NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif *p++=CF_READ_DATA(); CF_RD_ON(); } CF_CS_ON(); return 0; } #ifdef DOS_WRITE //###################################################### unsigned char CFWriteSector(unsigned long lba, unsigned char *buf) //###################################################### { unsigned int i; unsigned char by; unsigned char *p; #ifdef CF_DEBUG printf("WS %lu\n",lba); #endif if(lba>=maxsect) return 1; //sectornumber too big if(CFWaitReady()) return 1; // CFWriteAdr(CF_FEATURES,0); CFWriteAdr(CF_SECCOUNT,1); //write one sector //This will work only up to 128GB, 2^28 * 512 by=(unsigned char)lba; CFWriteAdr(CF_LBA0,by); //D7..0 by=(unsigned char)(lba>>8); CFWriteAdr(CF_LBA1,by); //D15..8 by=(unsigned char)(lba>>16); CFWriteAdr(CF_LBA2,by); //D23..16 by=(unsigned char)(lba>>24); by&=0x0F; //only four LSBs used by|=0xE0; //LBA-Mode Drive0 CFWriteAdr(CF_LBA3,by); //D27..24 CFWriteAdr(CF_STACOM,CF_WRITE_SEC); if(CFWaitDrq()) return 2; p=buf; //using a pointer is much faster than indexing buf[i] CFSetAdr(CF_IO); CF_DATA_DIR_OUT(); CF_CS_OFF(); for(i=0; i<BYTE_PER_SEC; i++) { Jürgen Härig | Anhang 104 105 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // CF_WRITE_DATA(buf[i]); CF_WRITE_DATA(*p++); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif CF_WR_OFF(); NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif CF_WR_ON(); NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif } CF_CS_ON(); CF_DATA_DIR_IN(); return 0; } #endif //DOS_WRITE //###################################################### unsigned char CFIdentify(void) //###################################################### { // unsigned long maxtracks; // unsigned int heads,sectors_per_track; unsigned int i; union Convert *cv; //Alles Ausgänge CF_CONTROL_DDR=0xFF; CF_CONTROL_PORT=0xFF; if(CFWaitReady()) return 1; // cf_features=0; // cf_seccount=1; // cf_lba0=1; // cf_lba1=0; // cf_lba2=0; // cf_lba3=0xE0; CFWriteAdr(CF_STACOM,CF_IDENTIFY); if(CFWaitDrq()) return 2; for(i=0; i<BYTE_PER_SEC; i++) dirbuf[i]=CFReadAdr(CF_IO); // cv=(union Convert *)&dirbuf[2]; // maxtracks=cv->ul; //Tracks // cv=(union Convert *)&dirbuf[6]; // heads=cv->ui; //Heads // cv=(union Convert *)&dirbuf[12]; // sectors_per_track=cv->ui; //Sectors per Track // this is the only information we need cv=(union Convert *)&dirbuf[14]; Jürgen Härig | Anhang 105 106 Dokumentation Projekt: Datenlogger, von Jürgen Härig maxsect=(unsigned long)cv->ui << 16; cv=(union Convert *)&dirbuf[16]; maxsect+=cv->ui; Technikerarbeit 2008 // number of sectors return 0; } //###################################################### unsigned char CFWaitReady(void) //###################################################### { unsigned char by; by=CFReadAdr(CF_STACOM); if(by & 0x01) return 1; //Error ! do { by=CFReadAdr(CF_STACOM); by&=0xF0; }while(by!=0x50); //endless loop possible ! return 0; } //###################################################### unsigned char CFWaitDrq(void) //###################################################### { unsigned char by; by=CFReadAdr(CF_STACOM); if(by & 0x01) return 1; //Error ! do { by=CFReadAdr(CF_STACOM); by&=0xF8; }while(by!=0x58); //endless loop possible ! } return 0; //###################################################### unsigned char CFReadAdr(unsigned char adr) //###################################################### { unsigned char by; CF_DATA_DIR_IN(); CFSetAdr(adr); CF_CS_OFF(); CF_RD_OFF(); NOP(); //Schon bei 8MHz muß man hier etwas warten NOP(); //Es könnte evtl. nötig sein hier noch mehr //nop einzufügen, um möglichst viele CF zu lesen //Bei mir reichten bisher 2 nop //Sicherheitshalber noch einen mehr NOP(); Jürgen Härig | Anhang 106 107 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //Sonst meldet CFWaitReady() immer Fehler #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif by=CF_READ_DATA(); CF_RD_ON(); CF_CS_ON(); return by; } //###################################################### unsigned char CFRead(void) //###################################################### { unsigned char by; CF_RD_OFF(); NOP(); //Schon bei 8MHz muß man hier etwas warten NOP(); //Es könnte evtl. nötig sein hier noch mehr //nop einzufügen, um möglichst viele CF zu lesen //Bei mir reichten bisher 2 nop //Sicherheitshalber noch einen mehr NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif by=CF_READ_DATA(); CF_RD_ON(); return by; } //###################################################### void CFWriteAdr(unsigned char adr, unsigned char dat) //###################################################### { CF_DATA_DIR_OUT(); CFSetAdr(adr); CF_WRITE_DATA(dat); NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif CF_CS_OFF(); NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif CF_WR_OFF(); NOP(); #if F_CPU >=16000000 //>=16MHz processor clock NOP(); #endif CF_WR_ON(); CF_CS_ON(); } Jürgen Härig | Anhang 107 108 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //###################################################### //Ist so kurz das man statt der Routine auch direkt oben //per Macro einfügen könnte. Wird dann etwas schneller. void CFSetAdr(unsigned char adr) //###################################################### { unsigned char by; by=CF_ADR_PIN; //read port pins by&=0xF0; //set D0..D3 zero by|=adr; //adr reinodern CF_ADR_PORT=by; } #endif //COMPACTFLASH_CARD //@} 11.2.2 compact.h //######################################################################### // File: compact.h // // CompactFlash hardware definitions // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 29.07.2005 //######################################################################### // Compiler: AVR-GCC 3.4.3 //######################################################################### #ifndef __COMPACT_FLASH_H #define __COMPACT_FLASH_H //#define CF_DEBUG //activate debug output via printf() //CF register adresses #define CF_IO #define CF_FEATURES #define CF_SECCOUNT #define CF_LBA0 #define CF_LBA1 #define CF_LBA2 #define CF_LBA3 #define CF_STACOM 7 0 //IO-Port 1 //Errors Out / Features In 2 //Sectorcount 3 //LBA 0-7 4 //LBA 8-15 5 //LBA 16-23 6 //LBA 24-27 //Status Out / Command In //ATAPI #define #define #define (unsigned char)0x20 (unsigned char)0x30 (unsigned char)0xEC commands CF_READ_SEC CF_WRITE_SEC CF_IDENTIFY //port where data i/o has to be done //without pullups on PortA some CF do not work ! //maybe for some CF ATMegas internal pullups are to weak. //then use 10k external pullups on port and please use short //cables (max. 30cm) #if defined (__AVR_ATmega32__) || defined (__AVR_ATmega323__) || defined (__AVR_ATmega161__) || defined (__AVR_ATmega644__) Jürgen Härig | Anhang 108 109 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define CF_DATA_DIR_IN() inputs, Pullups #define CF_DATA_DIR_OUT() #define CF_READ_DATA() #define CF_WRITE_DATA(a) Technikerarbeit 2008 DDRA=0x00; PORTA=0xFF; // set io-pins to DDRA=0xFF; // set io-pins to outputs PINA // read PIN, ! NOT ! PORT PORTA=(a); // write to data port #define CF_CONTROL_DDR DDRB #define CF_CONTROL_PORT PORTB #define CF_ADR_PORT #define CF_ADR_PIN PORTB PINB //Port CF_Adresspins ( D0..3 ) #define CF_CS #define CF_CS_PORT 4 //Pin number for CF_CS PORTB //Port where CF_CS is located #define CF_RD #define CF_RD_PORT 5 //Pin number for CF_RD PORTB //Port where CF_RD is located #define CF_WR #define CF_WR_PORT 6 //Pin number for CF_WR PORTB //Port where CF_WR is located #elif defined (__AVR_ATmega128__) || defined (__AVR_ATmega64__) #define CF_DATA_DIR_IN() DDRA=0x00; PORTA=0xFF; // set io-pins to inputs, Pullups #define CF_DATA_DIR_OUT() DDRA=0xFF; // set io-pins to outputs #define CF_READ_DATA() PINA // read PIN, ! NOT ! PORT #define CF_WRITE_DATA(a) PORTA=(a); // write to data port #define CF_CONTROL_DDR DDRF #define CF_CONTROL_PORT PORTF #define CF_ADR_PORT #define CF_ADR_PIN PORTF PINF //Port CF_Adresspins ( D0..3 ) #define CF_CS #define CF_CS_PORT 4 //Pin number for CF_CS PORTF //Port where CF_CS is located #define CF_RD #define CF_RD_PORT 5 //Pin number for CF_RD PORTF //Port where CF_RD is located #define CF_WR #define CF_WR_PORT 6 //Pin number for CF_WR PORTF //Port where CF_WR is located #else # error "processor type not defined in compact.h" #endif #define CF_CS_ON() #define CF_CS_OFF() sbi(CF_CS_PORT,CF_CS); cbi(CF_CS_PORT,CF_CS); #define CF_RD_ON() #define CF_RD_OFF() sbi(CF_RD_PORT,CF_RD); cbi(CF_RD_PORT,CF_RD); #define CF_WR_ON() #define CF_WR_OFF() sbi(CF_WR_PORT,CF_WR); cbi(CF_WR_PORT,CF_WR); //prototypes extern unsigned long maxsect; extern extern extern extern unsigned unsigned unsigned unsigned char char char char // last sector on drive CFReadSector(unsigned long lba, unsigned char *buf); CFWriteSector(unsigned long lba, unsigned char *buf); CFIdentify(void); CFWaitReady(void); Jürgen Härig | Anhang 109 110 Dokumentation Projekt: Datenlogger, von Jürgen Härig extern extern extern extern extern Technikerarbeit 2008 unsigned char CFWaitDrq(void); unsigned char CFReadAdr(unsigned char adr); unsigned char CFRead(void); void CFWriteAdr(unsigned char adr, unsigned char dat); void CFSetAdr(unsigned char adr); #define ReadSector(a,b) #define WriteSector(a,b) #define IdentifyMedia() CFReadSector((a),(b)) CFWriteSector((a),(b)) CFIdentify() #endif 11.2.3 dir.c /*! \file dir.c \brief Directory-Functions */ //########################################################### /// \ingroup multifat /// \defgroup DIR DIR-Functions (dir.c) /// \code #include "dir.h" \endcode /// \code #include "dos.h" \endcode /// \par Uebersicht //########################################################### // Directory functions // // For FAT12, FAT16 and FAT32 // Only for first Partition // Only for drives with 512 bytes per sector (the most) // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // // 24.08.2007 Changed MakeFileName() to MakeDirEntryName(). // A lot of optimizations to make smaller code. // // 10.08.2007 A lot of changes in ScanOneDirectorySector(). // // 08.08.2007 Merged ScanRootDir() and ScanSubDir() in one function ScanDirectory() // // 20.11.2006 Bugfix. If WIN makes a directory for FAT32 and directory // is in Root directory, upperdir cluster is ZERO ! // //######################################################################### // Last change: 24.08.2007 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" U8 dirbuf[BYTE_PER_SEC]; //buffer for directory sectors Jürgen Härig | Anhang 110 111 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #ifdef USE_FAT32 U32 FirstDirCluster=0; #else U16 FirstDirCluster=0; #endif #ifdef DOS_WRITE //########################################################### /*!\brief Make DOS time from system time * \return time in DOS format * * If your system has an RTC get your time here and make a DOS time */ static inline U16 DOSTime(void) //########################################################### { U16 time; // while(seconds==59); // maybe we should do this !? // Get your system time here // fixed time 16:19:33 if no clock exists time = (U16)16 << 11; // hours time |= (U16)19 << 5; // minutes time |= 33 / 2; // seconds return time; } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Make DOS date from system date * \return date in DOS format * * If your system has an RTC get your date here and make a DOS date */ static inline U16 DOSDate(void) //########################################################### { U16 date; // Get your system date here // fixed date 18.02.2007 if no clock exists date = 2007 - 1980; // years since 1980 date <<= 9; date |= (U16)2 << 5; date |= (U16)18; // month // day return date; } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Search sub dir for free dir entry * \param fileid A fileid you got from Fopen() or other functions * \param startcluster first cluster number of the sub dir * \return Above 0 if successfull, 0 if not Jürgen Härig | Anhang 111 112 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 */ #ifdef USE_FAT32 static inline U8 SearchFreeDirentry(char fileid, U32 startcluster) #else static inline U8 SearchFreeDirentry(char fileid, U16 startcluster) #endif //########################################################### { U32 tmpsector; #ifdef USE_FAT32 U32 tmpcluster; #else U16 tmpcluster; #endif U8 i,result; result=0; if(startcluster<2) // We are in root dir for FAT12/16 { tmpsector = FirstRootSector; i = RootDirSectors; do { result=SearchDirSector(fileid,tmpsector++); if(result!=0) break; //break sector loop i--; }while(i); } else // We are in a subdir { tmpcluster=startcluster; while(tmpcluster < endofclusterchain) { tmpsector=GetFirstSectorOfCluster(tmpcluster); i = secPerCluster; do { result=SearchDirSector(fileid, tmpsector++); if(result!=0) break; //break sector loop i--; }while(i); if(result!=0) break; //break cluster loop tmpcluster=GetNextClusterNumber(tmpcluster); } } return result; } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Search dir sector for free dir entry * \param fileid A fileid you got from Fopen() or other Jürgen Härig | Anhang 112 113 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 functions * \param sector Sector number of the directory * \return Above 0 if successfull, 0 if not */ U8 SearchDirSector(char fileid, U32 sector) //########################################################### { U16 count; struct FileDesc *fdesc; fdesc=&FileDescriptors[(U16)fileid]; ReadSector(sector,dirbuf); //read one directory sector. count=0; do { if(dirbuf[count]==0 || dirbuf[count]==0xE5) { //keep some values in mind fdesc->FileDirSector=sector; fdesc->FileDirOffset=count/32; return 1; } count+=32; }while(count<BYTE_PER_SEC); return 0; } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Fill a cluster with zeros * \param startsector * \return nothing */ void ZeroCluster(U32 startsector) //########################################################### { U32 sector; U16 i; sector = startsector; //clean all sectors of a cluster (fill with 0x00) for(i=0; i<BYTE_PER_SEC; i++) dirbuf[i]=0; //Fill write buffer unsigned char j = secPerCluster; do { WriteSector(sector++,dirbuf); j--; }while(j); } #endif #ifdef DOS_WRITE #ifdef DOS_MKDIR //########################################################### /*!\brief Make a new directory * \param name 8.3 DOS name Jürgen Härig | Anhang 113 114 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 * \return F_OK if successfull, otherwise F_ERROR */ U8 Mkdir(char *name) //########################################################### { struct FileDesc *fdesc; char fileid=findfreefiledsc(); if(fileid==-1) return F_ERROR; // no free filedescriptor found fdesc=&FileDescriptors[(U16)fileid]; //Test if directoryname exists in current directory MakeDirEntryName(name,fileid); //split name into name and extension parts if(FindName(fileid)==FULL_MATCH) { if(fdesc->FileAttr == ATTR_DIRECTORY) { fdesc->FileFlag=0; // Close this filedescriptor return F_OK; // directory exists } fdesc->FileFlag=0; // Close this filedescriptor return F_ERROR; // There is a FILE named "name" ! } fdesc->FileAttr=ATTR_DIRECTORY; //want to make a directory ! if(MakeNewFileEntry(fileid)) { //MakeNewFileEntry() allocates first cluster for new directory ! //first cluster of new directory is returned in FileFirstCluster fdesc->FileDirSector=GetFirstSectorOfCluster(fdesc->FileFirstCluster); ZeroCluster(fdesc->FileDirSector); //insert "." my new dir entry with newdir firstcluster fdesc->FileDirOffset=0; //first direntry "." MakeDirEntryName(".",fileid); UpdateFileEntry(fileid); //insert ".." upper dir entry with upperdir firstcluster fdesc->FileDirOffset=1; //2nd direntry ".." MakeDirEntryName("..",fileid); fdesc->FileFirstCluster=FirstDirCluster; UpdateFileEntry(fileid); #ifdef USE_FATBUFFER WriteSector(FATCurrentSector,fatbuf); // write the FAT buffer FATStatus=0; #endif } else { fdesc->FileFlag=0; // Close this filedescriptor // fdesc->FileAttr=ATTR_FILE; //default return F_ERROR; //new dir could not be made } Jürgen Härig | Anhang 114 115 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 fdesc->FileFlag=0; // Close this filedescriptor // fdesc->FileAttr=ATTR_FILE; //default return F_OK; } #endif //DOS_MKDIR #endif //DOS_WRITE #ifdef DOS_CHDIR //########################################################### /*!\brief Change to a directory * \param name 8.3 DOS name * \return F_OK if successfull, otherwise F_ERROR */ U8 Chdir(char *name) //########################################################### { struct FileDesc *fdesc; char fileid=findfreefiledsc(); if(fileid==-1) return F_ERROR; // no open file allowed here fdesc=&FileDescriptors[(U16)fileid]; if(name[0]=='/') { #ifdef USE_FAT12 if(FATtype==FAT12) FirstDirCluster=0; #endif #ifdef USE_FAT16 if(FATtype==FAT16) FirstDirCluster=0; #endif #ifdef USE_FAT32 if(FATtype==FAT32) FirstDirCluster=FAT32RootCluster; #endif fdesc->FileFlag=0; // Close this filedescriptor return F_OK; } MakeDirEntryName(name,fileid); if(FindName(fileid)==FULL_MATCH) { if(fdesc->FileAttr == ATTR_DIRECTORY) // Is this a directory ? { //First cluster of directory is returned in FileFirstCluster FirstDirCluster = fdesc->FileFirstCluster; fdesc->FileFlag=0; // Close this filedescriptor return F_OK; } } fdesc->FileFlag=0; // Close this filedescriptor return F_ERROR; // Maybe there is a FILE named "name" ! } #endif //DOS_CHDIR #ifdef DOS_WRITE //########################################################### /*!\brief Make a new file/directory entry in current directory * \param fileid Number of a filedescriptor Jürgen Härig | Anhang 115 116 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 * \return F_OK if successfull, otherwise F_ERROR */ U8 MakeNewFileEntry(char fileid) //########################################################### { #ifdef USE_FAT32 U32 tmpcluster,lastdircluster; #else U16 tmpcluster,lastdircluster; #endif U8 result; struct FileDesc *fdesc; fdesc=&FileDescriptors[(U16)fileid]; result=SearchFreeDirentry(fileid,FirstDirCluster); if(result==0) // no free direntry found. lets try to alloc and add a new dircluster { //if dir is rootdir (FAT12/16 only) you have a problem ;) #if defined (USE_FAT12) || defined (USE_FAT16) if(FirstDirCluster<2) { #ifdef USE_FAT12 if(FATtype==FAT12) return F_ERROR; #endif #ifdef USE_FAT16 if(FATtype==FAT16) return F_ERROR; #endif } #endif //search the last cluster of directory lastdircluster=FirstDirCluster; do { tmpcluster=GetNextClusterNumber(lastdircluster); if(tmpcluster < endofclusterchain) lastdircluster=tmpcluster; }while(tmpcluster < endofclusterchain); tmpcluster=AllocCluster(lastdircluster); //if current directory is full alloc new cluster for dir if(tmpcluster==DISK_FULL) //no free clusters ? { return F_ERROR; } fdesc->FileDirSector=GetFirstSectorOfCluster(tmpcluster); ZeroCluster(fdesc->FileDirSector); fdesc->FileDirOffset=0; dirsector } //set offset for new direntry in tmpcluster=AllocCluster(0); //alloc first cluster for file if(tmpcluster==DISK_FULL) //no free clusters ? { return F_ERROR; } Jürgen Härig | Anhang 116 117 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 fdesc->FileFirstCluster=tmpcluster; fdesc->FileSize=0; UpdateFileEntry(fileid); //write new file entry return F_OK; //all ok } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Update directory entry of a file/dir * \param fileid A fileid you got from Fopen() * \return F_OK if successfull, F_ERROR if not */ U8 UpdateFileEntry(char fileid) //########################################################### { struct DirEntry *di; struct FileDesc *fdesc; fdesc=&FileDescriptors[(U16)fileid]; ReadSector(fdesc->FileDirSector,dirbuf); di=(struct DirEntry *)&dirbuf[fdesc->FileDirOffset * 32]; strncpy(di->DIR_Name,fdesc->FileName,11); di->DIR_Attr=fdesc->FileAttr; di->DIR_NTres=0; di->DIR_CrtTimeTenth=0; di->DIR_CrtTime= DOSTime(); di->DIR_WrtTime= di->DIR_CrtTime; di->DIR_CrtDate= DOSDate(); di->DIR_LastAccDate= di->DIR_CrtDate; di->DIR_WrtDate= di->DIR_CrtDate; //creation time //last write time //creation date //last access date //last write date #ifdef USE_FAT32 di->DIR_FstClusHI=(U16)(fdesc->FileFirstCluster>>16); //first cluster high word #else di->DIR_FstClusHI=0; //first cluster high word #endif di->DIR_FstClusLO=(U16)(fdesc->FileFirstCluster); //first cluster low word di->DIR_FileSize=fdesc->FileSize; WriteSector(fdesc->FileDirSector,dirbuf); return F_OK; } #endif //DOS_WRITE //##################################################################### /*!\brief Heart of the directory functions * \param fileid A fileid you got from Fopen() or other functions * \param sector Sector number of the directory * \return FULL_MATCH if successfull, NO_MATCH or END_DIR if not * Jürgen Härig | Anhang 117 118 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Following global variables will be set if dir or filename was found: For directories: FirstDirCluster First cluster of new current directory For files: FileName[fileid] 8 chars for filename FileExt[fileid] 3 chars for extension FileDirSector[fileid] Sector which keeps direntry of the file FileDirOffset[fileid] Offset to direntry of the file FileFirstCluster[fileid] First cluster of the dir/file in FAT FileSize[fileid] FileAttr[fileid] */ // U8 ScanOneDirectorySector(char fileid,U32 sector) //##################################################################### { U8 by; U16 count; struct DirEntry *di; struct DirEntryBuffer *dib; U8 match; struct FileDesc *fdesc; if(fileid>=MAX_OPEN_FILE) return NO_MATCH; if(fileid<(-1)) return NO_MATCH; if(fileid>=0) fdesc=&FileDescriptors[(U16)fileid]; else fdesc=NULL; by=ReadSector(sector,dirbuf); //read one directory sector. count=0; do { match=NO_MATCH; di=(struct DirEntry *)(&dirbuf[count]); //make a second pointer to dirbuf for easier access to long filename entrys dib=(struct DirEntryBuffer *)di; if(di->DIR_Name[0]==0) return END_DIR; //end of directory if((unsigned char)di->DIR_Name[0]!=0xE5) // Deleted entry ? { di->DIR_Attr&=0x3F; //smash upper two bits if(di->DIR_Attr==ATTR_LONG_NAME) //is this a long name entry ? { #ifdef USE_FINDFILE #ifdef USE_FINDLONG if(fileid==-1) //FIND_OPERATION Jürgen Härig | Anhang 118 119 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { // Build the long name from the 13 bytes long name direntrys. // The long name direntrys have to be in a block of direntrys. // Otherwise this will not work and you get strange results. if(ffblk.newposition >= ffblk.lastposition) //found a new file ? { U8 offset; //offset of the direntry in long name U8 ffcount; //loop counter U8 i; 1. offset=dib->longchars[0]; offset&=0x1F; //force upper bits D7..5 to zero. //max. value of 20 is allowed here, or even less //if _MAX_NAME is defined smaller than 255. //long filenames will then be cut at _MAX_NAME if(offset>20) offset=20; //force maximum value if too big ffcount=(offset-1) * 13; // calc start adress in long name array //We can not use strncpy() because a CHAR in the long name //direntry has two bytes ! 2nd byte is ignored here. for(i=1; i<=9; i+=2) { by=dib->longchars[i]; if(ffcount<_MAX_NAME) ffblk.ff_longname[ffcount]=by; ffcount++; } for(i=14; i<=24; i+=2) { by=dib->longchars[i]; if(ffcount<_MAX_NAME) ffblk.ff_longname[ffcount]=by; ffcount++; } for(i=28; i<=30; i+=2) { by=dib->longchars[i]; if(ffcount<_MAX_NAME) ffblk.ff_longname[ffcount]=by; ffcount++; } ffblk.ff_longname[_MAX_NAME-1]=0; //End of string to avoid buffer overruns }//if(ffblk.newposition >= ffblk.lastposition) }//if(fileid==-1) #endif //#ifdef USE_FINDLONG #endif //#ifdef USE_FINDFILE } else //no long name entry { if(fileid==-1) //List only { //place a listing function here } else Jürgen Härig | Anhang 119 120 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { if(strncmp(fdesc->FileName,di->DIR_Name,11)==0) match=FULL_MATCH; //does name match ? //match==FULL_MATCH if name found } if(di->DIR_Attr & ATTR_VOLUME_ID) //is this a volume label ? { //nothing to do here. volume id not supported } else //FILE/DIR/FIND operation { if(fileid==-1) //FIND_OPERATION { #ifdef USE_FINDFILE ffblk.newposition++; //one more entry found if(ffblk.newposition > ffblk.lastposition) //found a new file ? { //save new file ffblk.lastposition=ffblk.newposition; position // di->DIR_Name may look like this "0123 ext" // But we need this "0123.ext". If there is no extension // we also don't want a '.'. unsigned char pos,pos1; pos = 0; do { if(di->DIR_Name[pos] == ' ') break; else ffblk.ff_name[pos] = di->DIR_Name[pos]; pos++; }while(pos < 8); if(di->DIR_Name[8] != ' ') // we have an extension { ffblk.ff_name[pos++] = '.'; // insert '.' pos1 = 8; // position of extension do { if(di->DIR_Name[pos1] == ' ') break; else ffblk.ff_name[pos++] = di->DIR_Name[pos1]; pos1++; }while(pos1 < 11); } // // fileextension strncpy(ffblk.ff_name,di->DIR_Name,8); //copy filename strncpy(&ffblk.ff_name[9],&di->DIR_Name[8],3);//copy if(di->DIR_Attr & ATTR_DIRECTORY) { ffblk.ff_attr=ATTR_DIRECTORY; //file attribute ffblk.ff_fsize=0; //not a file, clear filesize } else { ffblk.ff_attr=ATTR_FILE; Jürgen Härig | Anhang 120 121 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 ffblk.ff_fsize=di->DIR_FileSize; } return FULL_MATCH; //found next entry, stop searching }//if(ffblk.newposition > ffblk.lastposition) #endif//#ifdef USE_FINDFILE }//if(fileid==-1) else //FILE/DIR operation { #ifdef USE_FAT32 fdesc->FileFirstCluster = di->DIR_FstClusHI; //Highword of first cluster number fdesc->FileFirstCluster <<= 16; fdesc->FileFirstCluster += di->DIR_FstClusLO; //Lowword of first cluster number #else fdesc->FileFirstCluster = di->DIR_FstClusLO; //Lowword of first cluster number #endif fdesc->FileDirSector=sector; //keep some values in mind fdesc->FileDirOffset=count/32; if(match==FULL_MATCH) { if(di->DIR_Attr & ATTR_DIRECTORY) //this is a directory { #ifdef USE_FAT32 // Special case for FAT32 and directories in ROOT directory MADE BY WIN. // Upper directory ".." first cluster for a subdirectory is ZERO here, and NOT FAT32RootCluster ! // Bug found by Andreas ??? if(FATtype==FAT32) { if(fdesc->FileFirstCluster < 2) fdesc->FileFirstCluster = FAT32RootCluster; // force to correct cluster } #endif //#ifdef USE_FAT32 fdesc->FileSize = 0; // Directorys have no size fdesc->FileAttr = ATTR_DIRECTORY; }//if(di->DIR_Attr & ATTR_DIRECTORY) else //is not a directory. this is a file { fdesc->FileSize = di->DIR_FileSize; fdesc->FileAttr = ATTR_FILE; }//if(di->DIR_Attr & ATTR_DIRECTORY) return FULL_MATCH; }//if(match==FULL_MATCH) }//if(fileid==-1) } } }//if(di->DIR_Name[0]!=0xE5) count+=32; Jürgen Härig | Anhang 121 122 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 }while(count<BYTE_PER_SEC); return NO_MATCH; } //########################################################### /*!\brief Scan directory for something * \param fileid A fileid you got from Fopen() or other functions * \param startcluster first cluster number of the sub dir * \return FULL_MATCH if successfull, NO_MATCH if not */ #ifdef USE_FAT32 U8 ScanDirectory(char fileid,U32 startcluster) #else U8 ScanDirectory(char fileid,U16 startcluster) #endif //########################################################### { U32 tmpsector; #ifdef USE_FAT32 U32 tmpcluster; #else U16 tmpcluster; #endif U8 i,result; result=NO_MATCH; if(startcluster < 2) // Is this a FAT12/FAT16 rootdirectory ? { tmpsector = FirstRootSector; i = RootDirSectors; do { result=ScanOneDirectorySector(fileid,tmpsector++); if(result!=NO_MATCH) break; //break sector loop i--; }while(i); } else // We are in a subdirectory { tmpcluster=startcluster; while(tmpcluster < endofclusterchain) { tmpsector=GetFirstSectorOfCluster(tmpcluster); i = secPerCluster; do { result=ScanOneDirectorySector(fileid,tmpsector++); if(result!=NO_MATCH) break; //break sector loop i--; }while(i); if(result!=NO_MATCH) break; //break cluster loop Jürgen Härig | Anhang 122 123 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 tmpcluster=GetNextClusterNumber(tmpcluster); } } return(result); } //########################################################### /*!\brief Change a 8.3 DOS name to a direntry formatted name * \param inname 8.3 DOS name as a string ("test.txt") * \param fileid A fileid you got from Fopen() or other functions * \return Nothing */ void MakeDirEntryName(char *inname, char fileid) //########################################################### { U8 by,i,j; char *po; struct FileDesc *fdesc; fdesc=&FileDescriptors[(U16)fileid]; po=fdesc->FileName; i=11; while(i--) *po++ = ' '; //fill filename buffer with spaces po=fdesc->FileName; if(inname[0]=='.') { *po++ ='.'; if(inname[1]=='.') *po } ='.'; //change to upper dir return; i = 0; j = 0; do { by=inname[i++]; if(by == 0) return; // end of filename reached if(by == '.') po = &fdesc->FileName[8]; // extension reached else { *po++ = toupper(by); j++; } }while(j<11); // for(i=0; i<11; i++) putchar(outname[i]); } //@} Jürgen Härig | Anhang 123 124 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.2.4 Technikerarbeit 2008 dir.h /*! \file "dir.h" \brief Directory-Definitions */ /// \ingroup DIR //######################################################################### // //######################################################################### // Last change: 24.08.2007 //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #ifndef __DIR_H #define __DIR_H extern U8 Mkdir(char *name); extern U8 Chdir(char *name); extern U8 MakeNewFileEntry(char fileid); extern U8 UpdateFileEntry(char fileid); //extern U16 DOSTime(void); //extern U16 DOSDate(void); extern U8 ScanOneDirectorySector(char fileid, U32 sector); #ifdef USE_FAT32 // extern U8 SearchFreeDirentry(char fileid, U32 cluster); extern U8 ScanDirectory(char fileid, U32 startcluster); #else // extern U8 SearchFreeDirentry(char fileid, U16 cluster); extern U8 ScanDirectory(char fileid, U16 startcluster); #endif extern U8 SearchDirSector(char fileid, U32 sector); extern void MakeDirEntryName(char *inname, char fileid); extern void ZeroCluster(U32 startsector); #define ls() FindName(-1) extern U8 dirbuf[]; //buffer for directory sectors #ifdef USE_FAT32 extern U32 FirstDirCluster; #else extern U16 FirstDirCluster; #endif #endif //__DIR_H //@} 11.2.5 dos.c /*! \file dos.c \brief DOS-Functions */ //########################################################### /// \ingroup multifat /// \defgroup DOS DOS-Functions (dos.c) Jürgen Härig | Anhang 124 125 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 /// \code #include "dos.h" \endcode /// \code #include "mmc_spi.h" \endcode /// \par Uebersicht //########################################################### // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // // If you like it fast compile with -O2 // If you like it small compile with -Os -mcall-prologues // // 29.09.2007 Started Fseek() for files open for writing. // Flag F_WRITE 'w' does not spool to end of file anymore ! // You have to use flag F_APPEND 'a' for this now. // // 01.10.2007 Changed FileCurrentSector to FileFirstClusterSector. // Add FileClusterSectorOffset every time we need to. // // 17.08.2007 Removed SMALL_FWRITE, SMALL_FREAD. So FAST_FWRITE, FAST_FREAD // is the only option now. Removed #ifdefs for this too. // Code looks much better now. // // 10.08.2007 Remove() can delete directorys now. // Take care that you only delete EMPTY directorys. // Remove() does not clean up for you ! // // 28.06.2007 Fseek() from Alex ???. // Seeking is only available for files open for reading ! // //######################################################################### // Last change: 29.09.2007 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: avr-gcc 4.1.1 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" #if defined (FAT_DEBUG_RW_HITS) #include "serial.h" //for testing only #include "printf.h" //for testing only #endif struct FileDesc FileDescriptors[MAX_OPEN_FILE]; //################################################################### /*!\brief Give back actual size of an open file * \param fileid A fileid you got from Fopen() * \return Filesize of the file */ U32 Filelength(char fileid) //################################################################### { struct FileDesc *fdesc; if(fileid<0 || fileid>=MAX_OPEN_FILE) return 0; //invalid filehandle fdesc=&FileDescriptors[(U16)fileid]; Jürgen Härig | Anhang 125 126 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // if(fdesc->FileFlag==0) return 0; //file is closed return fdesc->FileSize; } #ifdef DOS_WRITE #ifdef DOS_DELETE //########################################################### /*!\brief Delete a file/dir * \param name DOS 8.3 name of the file/dir * \return F_OK if file/dir could be deleted, F_ERROR if not */ U8 Remove(char *name) //########################################################### { #ifdef USE_FAT32 U32 tmp,tmp1; #else U16 tmp,tmp1; #endif struct FileDesc *fdesc; char fileid=findfreefiledsc(); if (fileid==-1) return F_ERROR ; //too many open files fdesc=&FileDescriptors[(U16)fileid]; //Test if directoryname exists in current directory MakeDirEntryName(name,fileid); if(FindName(fileid)!=FULL_MATCH) { fdesc->FileFlag=0; // Close this filedescriptor return F_OK; //file not found, so nothing to remove } if(fdesc->FileAttr != ATTR_FILE && fdesc->FileAttr != ATTR_DIRECTORY) { fdesc->FileFlag=0; // Close this filedescriptor return F_ERROR; // this was not a file/dir ! } #ifdef STRICT_FILESYSTEM_CHECKING // Bug found by Michele Ribaudo if(fdesc->FileFirstCluster>=2) // Zero length files made by Win have no cluster chain ! { #endif tmp=fdesc->FileFirstCluster; // free clusters in FAT cluster chain (make zero) do { tmp1=GetNextClusterNumber(tmp); // save next cluster number WriteClusterNumber(tmp,0); // free cluster tmp=tmp1; // restore next cluster number }while(tmp<endofclusterchain); Jürgen Härig | Anhang 126 127 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #ifdef STRICT_FILESYSTEM_CHECKING } #endif #ifdef USE_FATBUFFER if(FATStatus>0) { WriteSector(FATCurrentSector,fatbuf); // write the FAT buffer FATStatus=0; } #endif fdesc->FileName[0]=0xE5; filename entrys ! fdesc->FileSize=0; fdesc->FileFirstCluster=0; fdesc->FileAttr=0; //mark file as deleted. does not affect long //make filesize 0 //delete first cluster //is this necessary ? UpdateFileEntry(fileid); fdesc->FileFlag=0; // Close this filedescriptor return F_OK; } #endif //DOS_DELETE #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Write to a file * \param buf Buffer containing your data * \param count Number of bytes to be written * \param fileid A fileid you got from Fopen() * \return Number of bytes written to the file * * If number of bytes written is 0, your disk is full. Fwrite() does not write to CF until a sector is completely filled. You can force writing with Fflush() when file should keep open, or close the file with Fclose(). */ //########################################################### U16 Fwrite(U8 *buf, U16 count, char fileid) //########################################################### { struct FileDesc *fdesc; U8 secoffset; U32 tmp; U16 buffoffset; U16 tmp2; U16 remaining; U16 bytecount; if(fileid<0 || fileid>=MAX_OPEN_FILE) return 0; //invalid filehandle fdesc=&FileDescriptors[(U16)fileid]; if(fdesc->FileFlag!=F_WRITE) return 0; //don't write if file is closed Jürgen Härig | Anhang 127 128 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //or open for reading only ! bytecount=0; remaining = count; while(remaining) { // tmp=fdesc->FileSize; //next write position tmp = fdesc->FilePosition; //next write position tmp -= fdesc->FileClusterCount; //calc byte position in cluster if(tmp >= BytesPerCluster)//is position in new cluster ? { FlushWriteBuffer(fileid); // Write old sector if it is not saved til now ! if(fdesc->FilePosition == fdesc->FileSize) // we are at end of file, get a new cluster { fdesc->FileCurrentCluster = AllocCluster(fdesc->FileCurrentCluster); //alloc new cluster if(fdesc->FileCurrentCluster == DISK_FULL) { return bytecount; //return number of bytes written before disk full } } else // we are not at end of file (maybe fseek), so use next cluster { fdesc->FileCurrentCluster = GetNextClusterNumber(fdesc>FileCurrentCluster); } fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(fdesc>FileCurrentCluster);//set new 1st sector of cluster fdesc->FileClusterSectorOffset = 0; if(fdesc->FilePosition < fdesc->FileSize) // fseek !? { ReadSector(fdesc->FileFirstClusterSector,fdesc->iob); //read new sector used } fdesc->FileClusterCount += BytesPerCluster; //update cluster count tmp -= BytesPerCluster; //calc new byte position in cluster } buffoffset=(U16)tmp; //we loose upper bits here, but it does not matter ! // secoffset=(U8)(buffoffset / BYTE_PER_SEC); //calc offset from first sector in cluster secoffset=(U8)(buffoffset >> 9 ); //calc offset from first sector in cluster if(fdesc->FileClusterSectorOffset != secoffset) { FlushWriteBuffer(fileid); // Write old sector if it is not saved til now ! Jürgen Härig | Anhang 128 129 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 fdesc->FileClusterSectorOffset = secoffset; if(fdesc->FilePosition < fdesc->FileSize) // fseek !? { ReadSector(fdesc->FileFirstClusterSector + fdesc>FileClusterSectorOffset,fdesc->iob); //read new sector used } } // buffoffset=(U16)(tmp % BYTE_PER_SEC); //calc offset in sector buffoffset = buffoffset & (BYTE_PER_SEC - 1); // tmp % 512 => tmp & (512-1) if((buffoffset + remaining) <= BYTE_PER_SEC) // all bytes can be copied to current sector buffer { tmp2 = remaining; remaining = 0; // All bytes copied } else // All bytes can NOT be copied to current sector buffer. Copy as much as we can { // to fill the buffer to the end. Then do the loop again with remaining bytes. tmp2 = BYTE_PER_SEC-buffoffset; remaining -= tmp2; } bytecount += tmp2; fdesc->FilePosition += tmp2; //update Filesize if(fdesc->FilePosition > fdesc->FileSize) fdesc->FileSize = fdesc>FilePosition; U8 *p; p = &fdesc->iob[buffoffset]; // while(tmp2--) *p++ = *buf++; do // a little bit faster,smaller than while(tmp2--) { *p++ = *buf++; tmp2--; }while(tmp2); fdesc->FileWriteBufferDirty=1; // Write Buffer contains data to be written ! }// while(remaining) return bytecount; } #endif //DOS_WRITE #ifdef DOS_READ //########################################################### /*!\brief Read from a file * \param buf Buffer where your data should be stored * \param count Number of bytes to be read * \param fileid A fileid you got from Fopen() * \return Number of bytes read from the file * Jürgen Härig | Anhang 129 130 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 * If number of bytes read is smaller as count or equal 0, end of file is reached. */ U16 Fread(U8 *buf, U16 count, char fileid) //########################################################### { struct FileDesc *fdesc; U8 secoffset; U32 tmp; U16 buffoffset; U16 tmp2; U16 remaining; U16 bytecount; if(fileid<0 || fileid>=MAX_OPEN_FILE) return 0; //invalid filehandle fdesc=&FileDescriptors[(U16)fileid]; if(fdesc->FileFlag == 0) return 0; //don't read if file is closed bytecount=0; remaining=count; tmp = fdesc->FileSize - fdesc->FilePosition; // get bytes til end of file if(tmp < remaining) remaining = tmp; // smaller as we want to read, then read til end of file while(remaining) { tmp=fdesc->FilePosition; tmp-= fdesc->FileClusterCount; //calc byte position in cluster if(tmp >= BytesPerCluster)//is position in current cluster ? { #ifdef DOS_WRITE if(fdesc->FileFlag==F_WRITE) FlushWriteBuffer(fileid); // Write old sector if it is not saved til now ! #endif fdesc->FileCurrentCluster = GetNextClusterNumber(fdesc>FileCurrentCluster); //if not get next cluster fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(fdesc>FileCurrentCluster);//set new 1st sector of cluster ReadSector(fdesc->FileFirstClusterSector,fdesc->iob); //read new sector fdesc->FileClusterSectorOffset = 0; fdesc->FileClusterCount += BytesPerCluster; //update cluster count //calc new byte position in tmp -= BytesPerCluster; cluster } buffoffset=(U16)tmp; //we loose upper bits here, but it does not matter ! // secoffset=(U8)(buffoffset / BYTE_PER_SEC); //calc sector offset from first sector in cluster secoffset=(U8)(buffoffset >> 9); //calc sector offset from first Jürgen Härig | Anhang 130 131 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 sector in cluster if(fdesc->FileClusterSectorOffset != secoffset) //new sector ? { #ifdef DOS_WRITE if(fdesc->FileFlag==F_WRITE) FlushWriteBuffer(fileid); // Write old sector if it is not saved til now ! #endif fdesc->FileClusterSectorOffset = secoffset; ReadSector(fdesc->FileFirstClusterSector + fdesc>FileClusterSectorOffset,fdesc->iob); //read new sector } // buffoffset=(U16)(tmp % BYTE_PER_SEC); //calc offset in sector buffoffset = buffoffset & (BYTE_PER_SEC - 1); // tmp % 512 => tmp & (512-1) if((buffoffset + remaining) <= BYTE_PER_SEC) // All bytes can be copied from current sector buffer { tmp2 = remaining; remaining = 0; // All bytes copied } else // all bytes can NOT be copied from current sector buffer { tmp2 = BYTE_PER_SEC - buffoffset; remaining -= tmp2; } bytecount += tmp2; fdesc->FilePosition += tmp2; U8 *p; p = &fdesc->iob[buffoffset]; // while(tmp2--) *buf++ = *p++; do // a little bit faster,smaller than while(tmp2--) { *buf++ = *p++; tmp2--; }while(tmp2); }// while(remaining) return bytecount; } #endif //DOS_READ //########################################################### /*!\brief Find a free file descriptor * \return 0 to MAX_OPENFILE-1 if free descriptor is found, -1 if not. * * Don't call this function directly ! */ //look for a free file descriptor //returns -1 if not found //returns 0..(MAX_OPEN_FILE-1) if found char findfreefiledsc(void) //########################################################### Jürgen Härig | Anhang 131 132 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { U8 i; struct FileDesc *fdesc; for (i=0;i<MAX_OPEN_FILE;i++) { if (FileDescriptors[i].FileFlag==0) { fdesc=&FileDescriptors[i]; fdesc->FileFlag=0xFF; // make this filedescriptor ;-) open fdesc->FileDirOffset=0; // fdesc->FileCurrentSector=0; fdesc->FileFirstClusterSector=0; fdesc->FileClusterSectorOffset = 0; fdesc->FileDirSector=0; fdesc->FileSize=0; fdesc->FilePosition=0; fdesc->FileAttr=0; fdesc->FileFirstCluster=0; fdesc->FileCurrentCluster=0; fdesc->FileClusterCount=0; #ifdef DOS_WRITE fdesc->FileWriteBufferDirty=0; #endif return i; } } return (-1); } //########################################################### /*!\brief Open a file * \param name 8.3 DOS name * \param flag F_READ or F_WRITE * \return A fileid (0 to MAX_OPEN_FILE-1) if successfull, otherwise -1 * * Open a file for reading OR writing, NOT both ! */ char Fopen(char *name, U8 flag) //########################################################### { char fileid; struct FileDesc *fdesc; fileid=findfreefiledsc(); if (fileid==(-1)) return -1; fdesc=&FileDescriptors[(U16)fileid]; // fdesc->FileClusterCount=0; MakeDirEntryName(name,fileid); //Split into name and extension #ifdef DOS_READ if(flag==F_READ) { if((FindName(fileid))==FULL_MATCH) //file MUST exist for reading { Jürgen Härig | Anhang 132 Dokumentation 133 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 if(fdesc->FileAttr != ATTR_FILE) { fdesc->FileFlag=0; // Close this filedescriptor return -1; // this was not a file ! } #ifdef STRICT_FILESYSTEM_CHECKING // Bug found by Michele Ribaudo if(fdesc->FileFirstCluster<2) // no clusters allocated for the file ! { fdesc->FileFlag=0; // Close this filedescriptor return -1; // nothing to read from this file } #endif if(fdesc->FileSize==0) //nothing to read { fdesc->FileFlag=0; // Close this filedescriptor return -1; // nothing to read from this file } fdesc->FileFlag = F_READ; //needed for fclose #ifdef DOS_FSEEK Fseek(0,SEEK_SET,fileid); #else fdesc->FilePosition = 0; //actual read position fdesc->FileCurrentCluster = fdesc->FileFirstCluster; fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(fdesc>FileFirstCluster); fdesc->FileClusterSectorOffset = 0; ReadSector(fdesc->FileFirstClusterSector,fdesc->iob); //read first sector of file #endif return fileid; //give back filehandle number if successfull } } #endif //DOS_READ #ifdef DOS_WRITE if(flag==F_WRITE || flag==F_APPEND) { fdesc->FileWriteBufferDirty=0; //if file exists, open it if(FindName(fileid)==FULL_MATCH) { if(fdesc->FileAttr != ATTR_FILE) { fdesc->FileFlag=0; // Close this filedescriptor return -1; // this was not a file ! } #ifdef STRICT_FILESYSTEM_CHECKING // Bug found by Michele Ribaudo if(fdesc->FileFirstCluster<2) // no clusters allocated for the file ! { Jürgen Härig | Anhang 133 134 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // todo: allocate a cluster // You can open this file if you do the following: // Remove() the file ! Make a new one with Fopen() fdesc->FileFlag=0; // Close this filedescriptor return -1; } #endif fdesc->FileFlag=F_WRITE; //needed for fclose //FileFlag becomes F_WRITE, even when opened with F_APPEND #ifdef DOS_FSEEK if(flag==F_APPEND) Fseek(0,SEEK_END,fileid); // go to end of file if flag is append else Fseek(0,SEEK_SET,fileid); #else //#ifdef DOS_FSEEK U32 tmp; fdesc->FileCurrentCluster=fdesc->FileFirstCluster; //we need this if file is smaller as ONE cluster fdesc->FileClusterSectorOffset = 0; fdesc->FilePosition=0; //actual write position if(flag==F_APPEND) // go to end of file if flag is append { tmp=fdesc->FileFirstCluster; while(tmp<endofclusterchain) //go to end of cluster chain { tmp=GetNextClusterNumber(tmp); if(tmp<endofclusterchain) { fdesc->FileCurrentCluster=tmp; fdesc->FileClusterCount+=BytesPerCluster; } } tmp= fdesc->FileSize - fdesc->FileClusterCount; //get number of bytes in current cluster //fdesc->FileClusterSectorOffset = (U8)(tmp / BYTE_PER_SEC); fdesc->FileClusterSectorOffset = (U8)(tmp >> 9); fdesc->FilePosition = fdesc->FileSize; //actual write position }//if(flag==F_APPEND) fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(fdesc>FileCurrentCluster); ReadSector(fdesc->FileFirstClusterSector + fdesc>FileClusterSectorOffset,fdesc->iob); //read first sector of file #endif //#ifdef DOS_FSEEK } else //make a new file { fdesc->FileAttr=ATTR_FILE; //needed for MakeNewFileEntry() ! 18.10.2006 if(MakeNewFileEntry(fileid)) //file does not exist, try to make new file in current directory { Jürgen Härig | Anhang 134 135 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 fdesc->FileFlag=F_WRITE; //needed for fclose //FileFlag becomes F_WRITE, even when opened with F_APPEND #ifdef DOS_FSEEK Fseek(0,SEEK_SET,fileid); #else fdesc->FileCurrentCluster = fdesc->FileFirstCluster; fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(fdesc>FileFirstCluster); fdesc->FileClusterSectorOffset = 0; fdesc->FilePosition=0; //actual write position ReadSector(fdesc->FileFirstClusterSector,fdesc->iob); //read first sector of file #endif //#ifdef DOS_FSEEK } else { fdesc->FileFlag=0; // Close this filedescriptor return -1; //new file could not be made } } return fileid; }//if(flag==F_WRITE) #endif //DOS_WRITE //give back filehandle number if successfull fdesc->FileFlag=0; // Close this filedescriptor return -1; //something went wrong } //########################################################### /*!\brief Close a file * \param fileid A fileid you got from Fopen() * \return Nothing */ void Fclose(char fileid) //########################################################### { struct FileDesc *fdesc; if (fileid<0 || fileid>=MAX_OPEN_FILE) return; fdesc=&FileDescriptors[(U16)fileid]; #ifdef DOS_READ // if(fdesc->FileFlag==F_READ) // { // } #endif //DOS_READ #ifdef DOS_WRITE if(fdesc->FileFlag==F_WRITE) { Fflush(fileid); //write last sector used to CF and update filesize, filetime } #endif //DOS_WRITE } fdesc->FileFlag=0; //a second fclose should do nothing //reading and writing disabled Jürgen Härig | Anhang 135 136 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #ifdef DOS_WRITE //########################################################### /*!\brief Flush a file buffer * \param fileid A fileid you got from Fopen() * \return Nothing * Force writing last data written into sectorbuffer to be stored into CF without Fclose(). Direntry will also be updated. */ void Fflush(char fileid) //########################################################### { struct FileDesc *fdesc; if (fileid<0 || fileid>=MAX_OPEN_FILE) return; fdesc=&FileDescriptors[(U16)fileid]; // if(fdesc->FileFlag==0) return; //don't write if file is closed // if(fdesc->FileFlag==F_READ) return; //don't write if file is open for reading if(fdesc->FileFlag==F_WRITE) { #ifdef USE_FATBUFFER if(FATStatus>0) { WriteSector(FATCurrentSector,fatbuf); // write the FAT buffer FATStatus=0; } #endif FlushWriteBuffer(fileid); //update file entry filesize and date/time UpdateFileEntry(fileid); } } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Flush all file buffers * \return Nothing */ // force writing last data written into sectorbuffers to be // stored into CF without fclose(). direntrys for all open // files will also be updated. void fflush_all(void) //########################################################### { char i; for (i=0;i<MAX_OPEN_FILE;i++) Fflush(i); } #endif //DOS_WRITE //############################################################ /*!\brief Search for a filename in current directory * \param fileid A fileid you got from findfreefiledsc() * \return FULL_MATCH if file exists, NO_MATCH if not Jürgen Härig | Anhang 136 137 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 * */ U8 FindName(char fileid) //############################################################ { U8 result; // if (fileid<=(-1)) return NO_MATCH; //don't search if too many files open // no directory list function with the line above if (fileid<(-1)) return NO_MATCH; //don't search if too many files open if(fileid>=MAX_OPEN_FILE) return NO_MATCH; //no valid filehandle number result=ScanDirectory(fileid,FirstDirCluster); return result; } #ifdef DOS_FSEEK /************************************************************************** ***/ // First version by Alex ??? 28.06.2007 /*!\brief Seek in a file * \param offset position to seek to (forward,backward) * \param mode start position from where to seek * \param fileid you got from Fopen() * \return 0 if seeking successfull, >0 if not * You can not seek above FileSize. Parameters for mode: SEEK_SET Seek from beginning of file. offset has to be positive. SEEK_CUR Seek from current file pointer position. offset may be positive or negative. SEEK_END Seek from end of file. offset has to be negative. */ /************************************************************************** ***/ U8 Fseek(S32 offset, U8 mode, char fileid) { #ifdef USE_FAT32 U32 numClusters, curCluster; #else U16 numClusters, curCluster; #endif struct FileDesc *fdesc; if(fileid<0 || fileid>=MAX_OPEN_FILE) return 0; //invalid filehandle fdesc=&FileDescriptors[(U16)fileid]; #ifdef DOS_WRITE // Check if file write buffer is dirty and save it here before seeking if(fdesc->FileFlag == F_WRITE) FlushWriteBuffer(fileid); #endif Jürgen Härig | Anhang 137 138 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // calculate the new File Position switch (mode) { case SEEK_SET: if(offset<0) return 3; // don't seek below 0 if(offset > fdesc->FileSize) return 4; // don't seek above FileSize fdesc->FilePosition = offset; break; case SEEK_END: // offset has to be 0 or negative ! if(offset>0) return 5; // don't seek above FileSize // don't if((fdesc->FileSize + offset) < 0) return 6; seek below 0 fdesc->FilePosition = fdesc->FileSize + offset; // offset is negativ ! break; case SEEK_CUR: if((fdesc->FilePosition + offset) < 0) return 7; // don't seek below 0 if((fdesc->FilePosition + offset) > fdesc->FileSize) return 8; // don't seek above FileSize fdesc->FilePosition = fdesc->FilePosition + offset; break; default: return 9; } // test if new FilePosition exists // if(fdesc->FilePosition > fdesc->FileSize) return F_ERROR; // Notbremse // numSector = (fdesc->FilePosition / BYTE_PER_SEC) % secPerCluster; // numSector = (fdesc->FilePosition >> 9) % secPerCluster; // secPerCluster is always power of 2 and <=128 ! fdesc->FileClusterSectorOffset = ((U8)(fdesc->FilePosition >> 9) & (secPerCluster-1) ); // numClusters = fdesc->FilePosition / (BYTE_PER_SEC*secPerCluster); numClusters = (fdesc->FilePosition >> 9) / secPerCluster; // calculate the current file cluster curCluster=fdesc->FileFirstCluster; // calculate the cluster address of the new Position (verkettete pointerliste) fdesc->FileClusterCount=0; while (numClusters > 0) { curCluster=GetNextClusterNumber(curCluster); numClusters--; fdesc->FileClusterCount+=BytesPerCluster; //update cluster count } //set Current Cluster fdesc->FileCurrentCluster=curCluster; Jürgen Härig | Anhang 138 139 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // calculate the Sector address of the new Position fdesc->FileFirstClusterSector = GetFirstSectorOfCluster(curCluster); ReadSector(fdesc->FileFirstClusterSector + fdesc>FileClusterSectorOffset,fdesc->iob); //read current sector of file return 0; } #endif //DOS_FSEEK #ifdef DOS_WRITE /************************************************************************** ***/ /*!\brief Write the file buffer if necessary * \param fileid yove got from Fopen() * \return nothing * */ void FlushWriteBuffer(char fileid) /************************************************************************** ***/ { struct FileDesc *fdesc; fdesc=&FileDescriptors[(U16)fileid]; if(fdesc->FileWriteBufferDirty == 1) { WriteSector(fdesc->FileFirstClusterSector + fdesc>FileClusterSectorOffset,fdesc->iob); //write current sector of file fdesc->FileWriteBufferDirty=0; } } #endif //@} 11.2.6 dos.h /*! \file "dos.h" \brief DOS-Definitions */ /// \ingroup multifat /// \defgroup DOS DOS-Functions (dos.h) //######################################################################### // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // // 18.02.2007 Changed F_READ to 'r', F_WRITE to 'w' // Now you can do Fopen("mydata.txt",'w'); ;) // //######################################################################### // Last change: 23.08.2007 //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #ifndef __DOS_H #define __DOS_H #define MAX_OPEN_FILE ((char) 3) ///< Declare how many files you want to Jürgen Härig | Anhang 139 140 Dokumentation Projekt: Datenlogger, von Jürgen Härig open at the same time // every 1 declared open file // every 1 declared open file // // ATMega32 has only 2kB RAM. // have to switch off the FAT // Do it typedef typedef typedef typedef typedef typedef in dos.h unsigned char unsigned int unsigned long Technikerarbeit 2008 takes 558 Bytes for FAT12/16/32 takes 554 Bytes for FAT12/16 only If you want to use 2 open files, you buffer ! or make an avrtypes.h and include it here char U8; // one byte S8; // one byte signed int U16; // two bytes S16; // two bytes signed long U32; // four bytes S32; // four bytes signed // Some more defines for special filesystem handling #define STRICT_FILESYSTEM_CHECKING // If you define this, some very rare special cases will be // noticed. But this needs more code. // Special cases when files where made on a Win system and // copied to the flash card: // // Bug found by Michele Ribaudo // Files with zero filesize have no clusters allocated ! // Calling Remove() and Fopen() may hang up the system. // If you define STRICT_FILESYSTEM_CHECKING opening a // zero length file for reading or writing gives an error. // You can remove a zero length file ! // // You don't need this define if all files where made with // my FAT system. Even if filesize is zero. // If unsure keep it defined ! //fopen #define #define #define #define flags F_CLOSED 0 F_READ 'r' // read only F_WRITE 'w' // read/write, no automatic seek to end of file F_APPEND 'a' // read/write, automatic seek to end of file #define F_ERROR #define F_OK #ifndef SEEK_SET #define SEEK_SET #endif #ifndef SEEK_CUR #define SEEK_CUR #endif #ifndef SEEK_END #define SEEK_END #endif 0 // dir/file operation failed 1 // dir/file operation successfull 0 /* set file offset to offset */ 1 /* set file offset to current plus offset */ 2 /* set file offset to EOF plus offset */ // #undef defines below in // spare program memory by #define DOS_READ //define #define DOS_WRITE //define "dosdefs.h" deciding if this if you this if you if you don't need them we want to read, write or both want to read files want to write files Jürgen Härig | Anhang 140 141 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define DOS_DELETE #define DOS_READ_RAW ReadFileRaw() #define #define #define #define only) Technikerarbeit 2008 //define this if you want to delete files //define this if you want to read files with DOS_CHDIR //define this if you want to go into subdirectories DOS_MKDIR //define this if you want to make subdirectories DOS_RENAME //define this if you want to rename files DOS_FSEEK //define this if you want to seek in files (reading // spare program memory by deciding if we want to use FAT12, FAT16, FAT32. // don't touch if you don't know the FAT type of your drive ! #define USE_FAT12 //define this if you want to use FAT12 #define USE_FAT16 //define this if you want to use FAT16 #define USE_FAT32 //define this if you want to use FAT32 #define USE_FATBUFFER //define this if you want to use a FAT buffer //needs 517 Bytes of RAM ! #define USE_FINDFILE //define this if you want to use Findfirst(); Findnext(); #define USE_FINDLONG //define this if you want to get long filenames //from Findfirst(); Findnext(); #define USE_DRIVEFREE space of your drive #include "dosdefs.h" delete //define this if you want to get free and used // keep the line at this place ! don't move down or extern char Fopen(char *name, U8 flag); extern void Fclose(char fileid); extern U16 Fread(U8 *buf, U16 count, char fileid ); extern U16 Fwrite(U8 *buf, U16 count, char fileid); extern U8 Fseek(S32 offset, U8 mode, char fileid); extern void FlushWriteBuffer(char fileid); extern extern extern extern extern void Fflush(char fileid); void fflush_all(void); U8 Remove(char *name); U8 Rename(char *OldName, char *NewName); U32 Filelength(char fileid); extern U8 ReadFileRaw(char *name); extern U8 FindName(char fileid); extern char findfreefiledsc(void); // return -1 if too many open files #ifndef BYTE_PER_SEC #define BYTE_PER_SEC #endif (U16) 512 struct FileDesc { //FileDescriptor structure U8 iob[BYTE_PER_SEC]; //file i/o buffer // U32 FileCurrentSector; //number of sector with last data read/written U32 FileDirSector; //dir sector holding this fileentry U32 FileSize; Jürgen Härig | Anhang 141 142 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 U32 FilePosition; //file byte position U32 FileFirstClusterSector; // first sector of cluster with last data read/written U8 FileClusterSectorOffset; //sector of current cluster used U8 FileFlag; //open or closed U8 FileAttr; //file attribute. also used for directory functions U32 FileClusterCount; //this is NOT uint ! #ifdef USE_FAT32 U32 FileFirstCluster; //needed for UpdateFileEntry() ! U32 FileCurrentCluster; //number of cluster in use #else //#ifdef USE_FAT32 U16 FileFirstCluster; U16 FileCurrentCluster; #endif //#ifdef USE_FAT32 char FileName[11]; //file name U8 FileDirOffset; //dir entry offset in FileDirSector/32 #ifdef DOS_WRITE U8 FileWriteBufferDirty; #endif }; extern struct FileDesc FileDescriptors[]; //this is for easier and faster converting from byte arrays to UINT, ULONG //ui and ul share the same memory space union Convert { U16 ui; U32 ul; }; #ifdef COMPACTFLASH_CARD #include "compact.h" #endif #ifdef MMC_CARD_SPI #include "mmc_spi.h" #endif #include "fat.h" #include "dir.h" #ifdef USE_FINDFILE #include "find_x.h" #endif #ifdef USE_DRIVEFREE #include "drivefree.h" #endif #endif //__DOS_H //@} 11.2.7 dosdefs.h //######################################################################### Jürgen Härig | Anhang 142 143 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // File: DOSDEFS.H // // Central DOS configuration file for this project // //######################################################################### // Last change: 22.02.2007 //######################################################################### // Compiler: AVR-GCC 3.4.5 //######################################################################### #ifndef __DOSDEFS_H #define __DOSDEFS_H //define only ONE of these ! //#define COMPACTFLASH_CARD #define MMC_CARD_SPI //SD_CARD_SPI too ! //#define HD_DRIVE //maybe later ;)not used yet // spare program memory by deciding if we want to read, write or both //#undef DOS_READ //undefine this if you don't want to read files with Fread() //#undef DOS_WRITE //undefine this if you don't want to write files with Fwrite() //deleting files is also deactivated #undef DOS_DELETE //undefine this if you don't want to delete files #undef DOS_READ_RAW //undefine this if you don't want to read files with ReadFileRaw() //#undef DOS_MKDIR //undefine this if you don't want to make subdirectories //#undef DOS_CHDIR //undefine this if you don't want to go into subdirectories #undef DOS_RENAME //undefine this if you don't want to rename files #undef DOS_FSEEK //undefine this if you don't want to seek in files // spare program memory by deciding if we want to use FAT12, FAT16, FAT32. // comment out FAT types not used. NOT recommended if you don't know the // FAT type of your drive ! #undef USE_FAT12 //undefine this if you don't want to use FAT12 //#undef USE_FAT16 //undefine this if you don't want to use FAT16 #undef USE_FAT32 //undefine this if you don't want to use FAT32 //#undef USE_FATBUFFER //undefine this if you don't want to use a FAT buffer //needs 517 Bytes of RAM ! #undef USE_FINDFILE //undefine this if you don't want to use Findfirst(), Findnext() #undef USE_FINDLONG //undefine this if you don't want to get long filenames //from Findfirst(); Findnext(); #undef USE_DRIVEFREE //undefine this if you don't want to get used and free space of your drive // MMC/SD //#define printf() //#define //#define //#define //#define card debug options MMC_DEBUG_IDENTIFY //activate debug output for MMCIdentify() via MMC_DEBUG_SECTORS //activate debug output via printf() MMC_DEBUG_COMMANDS //activate debug output via printf() MMC_DEBUG_CMD0_TIMEOUT //activate debug output via printf() MMC_DEBUG_CMD1_TIMEOUT //activate debug output via printf() Jürgen Härig | Anhang 143 144 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // These defines are for FAT debugging only. You don't need them. // Activating all three options takes about 1.5kB of flash. // So be careful on devices with small flash memory ! //#define FAT_DEBUG_SHOW_FAT_INFO //activate FAT information output via printf() or puts() in GetDriveInformation() //#define FAT_DEBUG_CLUSTERS // show cluster numbers read/write access via printf() //#define FAT_DEBUG_RW_HITS // show FAT sectors read/write access summary when calling Fclose(), via printf() #endif //__DOSDEFS_H 11.2.8 drivefree.c //########################################################### // File: drivefree.c // // Get used and free memory of the drive. // // Counts only free and used CLUSTERS. If a file does not // use all bytes in a cluster, this free space is NOT // given back by this routines. This space can't be used // by another file because the cluster is reserved. // // Only the free space of the unused clusters will be given back. // So you get the MINIMUM free memory. This is the same way WIN // gives you free or used space. // // Don't use this functions without a FAT buffer ! // Or you will have to wait a LOOOOOONG time. // // With FAT buffer on a 256MB FAT16 CF and 62650 clusters: // 2 seconds for drivefree() or driveused() // ATMega128 at 16MHz // // On a 32GB harddrive with FAT32 and 2 Mio clusters: ?????????????????? // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 18.12.2005 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: AVR-GCC 3.4.3 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" #ifdef USE_DRIVEFREE //################################################ // Give back free memory of unused clusters in kB U32 drivefree(void) //################################################ Jürgen Härig | Anhang 144 145 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { #ifdef USE_FAT32 U32 tmpcluster, count; #else U16 tmpcluster, count; #endif count=0; tmpcluster=2; // search til end of FAT while(tmpcluster<maxcluster) { if(GetNextClusterNumber(tmpcluster)==0) count++; tmpcluster++; } return ((U32)count * secPerCluster) / 2; } //################################################ // Give back memory size of used clusters in kB U32 driveused(void) //################################################ { return drivesize() - drivefree(); } //################################################ // Give back memory size of the drive in kB U32 drivesize(void) //################################################ { return ((U32)(maxcluster-2) * secPerCluster) / 2; } #endif //#ifdef USE_DRIVEFREE //@} 11.2.9 drivefree.h //########################################################### // File: drivefree.h // //######################################################################### // Last change: 18.12.2005 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: AVR-GCC 3.4.3 //######################################################################### //@{ #ifndef __DRIVEFREE_H #define __DRIVEFREE_H extern U32 drivefree(void); extern U32 driveused(void); Jürgen Härig | Anhang 145 146 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 extern U32 drivesize(void); #endif // __DRIVEFREE_H //@} 11.2.10 dumpsect.c //########################################################### // File: dumpsect.c // // Gibt ein HEX und ASCII Listing aus. // Jede Zeile enthält 16 Bytes. // //######################################################################### // Last change: 06.03.2004 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### #include "rs232.h" //###################################################### void DumpSector(unsigned char *buf) //###################################################### { unsigned int i; unsigned char by,j; for(i=0; i<512; i+=16) { for(j=0; j<16; j++) { by=buf[i+j]; rs232_print("%x",by); } //HEX-Dump rs232_print(" "); for(j=0; j<16; j++) //ASCII-Dump { by=buf[i+j]; if(by<0x20) by='.'; rs232_print("%x",by); } rs232_putchar(0x0d); rs232_putchar(0x0a); } } 11.2.11 dumpsect.h // Datei: dumpsect.h // // 18.08.2005 #ifndef DUMPSECT_H #define DUMPSECT_H //Mehrfache includes vermeiden extern void DumpSector(unsigned char *buf); Jürgen Härig | Anhang 146 147 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #endif 11.2.12 fat.c /*! \file fat.c \brief FAT-Functions */ //########################################################### /// \ingroup multifat /// \defgroup FAT FAT-Functions (fat.c) /// \code #include "fat.h" \endcode /// \code #include "dos.h" \endcode /// \par Uebersicht //########################################################### // For FAT12, FAT16 and FAT32 // Only for first Partition // Only for drives with 512 bytes per sector (the most) // // Based on a White Paper from MS // FAT: General Overview of On-Disk Format // Version 1.03, December 6, 2000 // // MBR MasterBootRecord // PC intern 4 // M.Tischer // Data Becker // // 11.10.2006 Replaced "% BYTE_PER_SEC" with "& (BYTE_PER_SEC-1)". // Typecast variables from "unsigned long" to "unsigned int" before: // secoffset = (unsigned int)fatoffset & (BYTE_PER_SEC-1); // Use "unsigned int" for indexing arrays. Does not really speed up, // but reduces code size ;) // // 25.09.2006 Initialize all global variables to zero for better compatibility // with other compilers. // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 17.09.2007 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" #if defined (FAT_DEBUG_SHOW_FAT_INFO) || defined (FAT_DEBUG_RW_HITS) || defined (FAT_DEBUG_CLUSTERS) #include "rs232.h" //for testing only //#include "printf.h" //for testing only #endif Jürgen Härig | Anhang 147 148 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 U32 FirstDataSector=0; U32 FirstRootSector=0; U32 FATFirstSector=0; U8 FATtype=0; #ifdef USE_FAT32 U32 FAT32RootCluster=0; U32 endofclusterchain=0; //value for END_OF_CLUSTERCHAIN U32 maxcluster=0; // last usable cluster+1 #else U16 endofclusterchain=0; //value for END_OF_CLUSTERCHAIN U16 maxcluster=0; // last usable cluster+1 #endif #ifdef USE_FATBUFFER U8 fatbuf[BYTE_PER_SEC]; U32 FATCurrentSector=0; //buffer for FAT sectors #ifdef DOS_WRITE U8 FATStatus=0; // only for FAT write buffering #endif #endif U8 secPerCluster=0; #ifdef USE_64k_CLUSTERS U32 BytesPerCluster=0; #else U16 BytesPerCluster=0; #endif //bytes per cluster //bytes per cluster //U32 RootDirSectors=0; // to big ! //U16 RootDirSectors=0; // maybe U8 is enough. U8 RootDirSectors=0; // never saw more then 32 sectors #ifdef USE_FATBUFFER //############################################################ /*!\brief Decide if we have to write a used fat sector or read a new fat sector * \param newsector Actual sector number * \return Nothing */ void UpdateFATBuffer(U32 newsector) //############################################################ { if(newsector!=FATCurrentSector) // do we need to update the FAT buffer ? { #ifdef DOS_WRITE if(FATStatus>0) { WriteSector(FATCurrentSector,fatbuf); // write the old FAT buffer FATStatus=0; // flag FAT buffer is save } #endif ReadSector(newsector,fatbuf); //read FAT sector FATCurrentSector=newsector; } } #endif Jürgen Härig | Anhang 148 149 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //############################################################ /*!\brief Get back next cluster number from fat cluster chain * \param cluster Actual cluster number * \return Next cluster number */ #ifdef USE_FAT32 U32 GetNextClusterNumber(U32 cluster) #else U16 GetNextClusterNumber(U16 cluster) #endif //############################################################ { #ifdef USE_FAT12 U16 tmp, secoffset; U8 fatoffset; #endif union Convert *cv; #ifdef FAT_DEBUG_CLUSTERS #ifdef USE_FAT32 rs232_print("GNCN %lu\n",cluster); #else rs232_print("GNCN %u\n",cluster); #endif #endif if(cluster<maxcluster) //we need to check this ;-) { #ifdef USE_FAT12 if(FATtype==FAT12) { // FAT12 has 1.5 Bytes per FAT entry // FAT12 can only have 4085 clusters. So cluster * 3 is 16 bit tmp= ((U16)cluster * 3) >>1 ; //multiply by 1.5 (rounds down) secoffset = (U16)tmp & (BYTE_PER_SEC-1); //we need this for later // FAT12 4085 Cluster * 1.5Bytes = 6127.5 Bytes => max 12 FAT sectors // FAT sector offset is 8 Bit // fatoffset = (U8)(tmp / BYTE_PER_SEC); //sector offset from FATFirstSector fatoffset = (U8)(tmp >> 9); //sector offset from FATFirstSector #ifdef USE_FATBUFFER UpdateFATBuffer(FATFirstSector + fatoffset); if(secoffset == (BYTE_PER_SEC-1)) //if this is the case, cluster number is //on a sector boundary. read the next sector too { tmp=(U16)fatbuf[BYTE_PER_SEC-1]; //keep first part of cluster number UpdateFATBuffer(FATFirstSector + fatoffset +1 ); //read next FAT sector tmp+=(U16)fatbuf[0] << 8; //second part of cluster number } else { cv=(union Convert *)&fatbuf[secoffset]; tmp=cv->ui; } Jürgen Härig | Anhang 149 Dokumentation 150 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #else //#ifdef USE_FATBUFFER ReadSector(FATFirstSector + fatoffset,dirbuf); //read FAT sector if(secoffset == (BYTE_PER_SEC-1)) //if this is the case, cluster number is //on a sector boundary. read the next sector too { tmp=(U16)dirbuf[BYTE_PER_SEC-1]; //keep first part of cluster number ReadSector(FATFirstSector + fatoffset +1,dirbuf ); //read next FAT sector tmp+=(U16)dirbuf[0] << 8; //second part of cluster number } else { cv=(union Convert *)&dirbuf[secoffset]; tmp=cv->ui; } #endif //#ifdef USE_FATBUFFER if((U8)cluster & 0x01) tmp>>=4; //shift to right position else tmp&=0xFFF; //delete high nibble return (tmp); }//if(FATtype==FAT12) #endif //#ifdef USE_FAT12 #ifdef USE_FAT16 if(FATtype==FAT16) { //two bytes per FAT entry #ifdef USE_FATBUFFER // UpdateFATBuffer(FATFirstSector + ((U32)cluster * 2) / BYTE_PER_SEC); UpdateFATBuffer(FATFirstSector + (U8)((U16)cluster >> 8) ); // cv=(union Convert *)&fatbuf[((U32)cluster * 2) & (BYTE_PER_SEC-1)]; cv=(union Convert *)&fatbuf[((U16)cluster * 2) & (BYTE_PER_SEC-1)]; #else //#ifdef USE_FATBUFFER ReadSector(FATFirstSector + (U8)((U16)cluster >> 8), dirbuf); cv=(union Convert *)&dirbuf[((U16)cluster * 2) & (BYTE_PER_SEC-1)]; #endif //#ifdef USE_FATBUFFER return(cv->ui); }//if(FATtype==FAT16) #endif //#ifdef USE_FAT16 #ifdef USE_FAT32 if(FATtype==FAT32) { //four bytes per FAT entry #ifdef USE_FATBUFFER // UpdateFATBuffer(FATFirstSector + (cluster * 4) / BYTE_PER_SEC); // UpdateFATBuffer(FATFirstSector + cluster / (BYTE_PER_SEC/4)); UpdateFATBuffer(FATFirstSector + (cluster >> 7) ); // Buffer index is max 511. So in any case we loose upper bits. typecast cluster to U16 // cv=(union Convert *)&fatbuf[(cluster * 4) & (BYTE_PER_SEC-1)]; cv=(union Convert *)&fatbuf[((U16)cluster << 2) & (BYTE_PER_SEC-1)]; #else //#ifdef USE_FATBUFFER ReadSector(FATFirstSector + (cluster >> 7), dirbuf); Jürgen Härig | Anhang 150 151 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 cv=(union Convert *)&dirbuf[((U16)cluster << 2) & (BYTE_PER_SEC-1)]; #endif //#ifdef USE_FATBUFFER return( cv->ul & 0x0FFFFFFF ); }//if(FATtype==FAT32) #endif //#ifdef USE_FAT32 } return DISK_FULL; //return impossible cluster number } //########################################################### /*!\brief Get back number of first sector of cluster * \param cluster Actual cluster number * \return Sector number */ #ifdef USE_FAT32 U32 GetFirstSectorOfCluster(U32 cluster) #else U32 GetFirstSectorOfCluster(U16 cluster) #endif //########################################################### { // Komische Sache: Die Schieberei hier bringt bei ATmega32 ca. 200 Byte weniger Code. // Bei ATmega644 wird der Code dadurch ca. 20 Bytes größer ! U8 temp; U32 templong; templong = cluster-2; // secPerCluster is always power of two temp = secPerCluster>>1; // don't multiply with 1 ;) while(temp) { templong <<= 1; temp >>= 1; } return (templong + FirstDataSector); // return (((U32)(cluster - 2) * secPerCluster) + FirstDataSector); } #ifdef DOS_WRITE //########################################################### /*!\brief Allocate a new cluster in cluster chain * \param currentcluster Actual cluster number * \return New cluster number or DISK_FULL */ #ifdef USE_FAT32 U32 AllocCluster(U32 currentcluster) #else U16 AllocCluster(U16 currentcluster) #endif //########################################################### { #ifdef USE_FAT32 U32 cluster; #else Jürgen Härig | Anhang 151 152 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 U16 cluster; #endif // do this if you want to search from beginning of FAT // cluster=FindFreeCluster(0); //get next free cluster number cluster=FindFreeCluster(currentcluster); // get next free cluster number if(cluster!=DISK_FULL && cluster<=maxcluster) // disk full ? { // insert new cluster number into chain // currentcluster=0 means: this is a new cluster chain if(currentcluster>0) WriteClusterNumber(currentcluster,cluster); // mark end of cluster chain #ifdef USE_FAT12 if(FATtype==FAT12) WriteClusterNumber(cluster,0xFFF); #endif #ifdef USE_FAT16 if(FATtype==FAT16) WriteClusterNumber(cluster,0xFFFF); #endif #ifdef USE_FAT32 if(FATtype==FAT32) WriteClusterNumber(cluster,0x0FFFFFFF); #endif } return cluster; } #endif //DOS_WRITE #ifdef DOS_WRITE //########################################################### /*!\brief Find a free cluster in FAT * \param currentcluster Actual cluster number * \return Number of a free cluster or DISK_FULL */ #ifdef USE_FAT32 U32 FindFreeCluster(U32 currentcluster) #else U16 FindFreeCluster(U16 currentcluster) #endif //########################################################### { #ifdef USE_FAT32 U32 cluster; #else U16 cluster; #endif cluster=currentcluster+1; // its a good idea to look here first // maybe we do not need to search the whole FAT // and can speed up free cluster search // if you do not want this call FindFreeCluster(0) // search til end of FAT while(cluster<maxcluster) { if(GetNextClusterNumber(cluster)==0) break; cluster++; } // if we have not found a free cluster til end of FAT // lets start a new search at beginning of FAT if(cluster>=maxcluster) Jürgen Härig | Anhang 152 153 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { cluster=2; // first possible free cluster while(cluster<=currentcluster) // search til we come to where we have started { if(GetNextClusterNumber(cluster)==0) break; cluster++; } if(cluster>=currentcluster) return DISK_FULL; // no free cluster found } if(cluster>=maxcluster) return DISK_FULL; return cluster; } #endif //DOS_WRITE #ifdef DOS_WRITE //############################################################ /*!\brief Insert a new cluster number into cluster chain * \param cluster Actual cluster number * \param number Cluster number to append to cluster chain * \return Nothing til now (0) */ #ifdef USE_FAT32 U8 WriteClusterNumber(U32 cluster, U32 number) #else U8 WriteClusterNumber(U16 cluster, U16 number) #endif //############################################################ { #ifdef USE_FAT12 U16 tmp, secoffset; U8 fatoffset; U8 lo,hi; #endif U32 sector; U8 *p; #ifdef FAT_DEBUG_CLUSTERS #ifdef USE_FAT32 rs232_print("WCN %lu\n",cluster); #else rs232_print("WCN %u\n",cluster); #endif #endif if(cluster<maxcluster) //we need to check this ;-) { #ifdef USE_FAT12 if(FATtype==FAT12) { //FAT12 has 1.5 Bytes per FAT entry // FAT12 can only have 4085 clusters. So cluster * 3 is 16 bit tmp= ((U16)cluster * 3) >>1 ; //multiply by 1.5 (rounds down) secoffset = (U16)tmp & (BYTE_PER_SEC-1); //we need this for later // FAT12 4085 Cluster * 1.5Bytes = 6127.5 Bytes => max 12 FAT sectors // FAT sector offset is 8 Bit // fatoffset = (U8)(tmp / BYTE_PER_SEC); //sector offset from Jürgen Härig | Anhang 153 154 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 FATFirstSector fatoffset = (U8)(tmp >> 9); //sector offset from FATFirstSector sector=FATFirstSector + fatoffset; tmp=(U16)number; if((U8)cluster & 0x01) tmp<<=4; //shift to right position lo=(U8)tmp; hi=(U8)(tmp>>8); #ifdef USE_FATBUFFER UpdateFATBuffer(sector); //read FAT sector #else //#ifdef USE_FATBUFFER ReadSector(sector,dirbuf); //read FAT sector #endif //#ifdef USE_FATBUFFER if(secoffset == (BYTE_PER_SEC-1)) //if this is the case, cluster number is //on a sector boundary. read the next sector too { #ifdef USE_FATBUFFER p=&fatbuf[BYTE_PER_SEC-1]; //keep first part of cluster number #else //#ifdef USE_FATBUFFER p=&dirbuf[BYTE_PER_SEC-1]; //keep first part of cluster number #endif //#ifdef USE_FATBUFFER if((U8)cluster & 0x01) { *p&=0x0F; *p|=lo; } else *p=lo; #ifdef USE_FATBUFFER FATStatus=1; // we have made an entry, so write before next FAT sector read UpdateFATBuffer(sector+1); //read FAT sector p=&fatbuf[0]; //second part of cluster number #else //#ifdef USE_FATBUFFER WriteSector(sector,dirbuf); ReadSector(sector+1,dirbuf ); //read next FAT sector p=&dirbuf[0]; //second part of cluster number #endif //#ifdef USE_FATBUFFER if((U8)cluster & 0x01) *p=hi; else { *p&=0xF0; *p|=hi; } #ifdef USE_FATBUFFER FATStatus=1; // we have made an entry, so write before next FAT sector read #else //#ifdef USE_FATBUFFER WriteSector(sector+1,dirbuf); #endif //#ifdef USE_FATBUFFER } else { #ifdef USE_FATBUFFER p=&fatbuf[secoffset]; Jürgen Härig | Anhang 154 155 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #else //#ifdef USE_FATBUFFER p=&dirbuf[secoffset]; #endif //#ifdef USE_FATBUFFER if((U8)cluster & 0x01) { *p&=0x0F; *p++|=lo; *p=hi; } else { *p++=lo; *p&=0xF0; *p|=hi; } #ifdef USE_FATBUFFER FATStatus=1; // we have made an entry, so write before next FAT sector read #else //#ifdef USE_FATBUFFER WriteSector(sector,dirbuf); #endif //#ifdef USE_FATBUFFER } }//if(FATtype==FAT12) #endif #ifdef USE_FAT16 if(FATtype==FAT16) { //two bytes per FAT entry // sector=FATFirstSector + ((U32)cluster * 2) / BYTE_PER_SEC; // sector=FATFirstSector + ((unsigned int)cluster) / (BYTE_PER_SEC/2); sector=FATFirstSector + (U8)((U16)cluster >> 8); #ifdef USE_FATBUFFER UpdateFATBuffer(sector); //read FAT sector // Buffer index is max 511. So in any case we loose upper bits. typecast cluster to U16 p = &fatbuf[((U16)cluster << 1) & (BYTE_PER_SEC-1)]; #else //#ifdef USE_FATBUFFER ReadSector(sector, dirbuf); p = &dirbuf[((U16)cluster << 1) & (BYTE_PER_SEC-1)]; #endif //#ifdef USE_FATBUFFER *p++ = (U8)(number); *p = (U8)(number >> 8); #ifdef USE_FATBUFFER FATStatus=1; // we have made an entry, so write before next FAT sector read #else //#ifdef USE_FATBUFFER WriteSector(sector, dirbuf); #endif //#ifdef USE_FATBUFFER }// if(FATtype==FAT16) #endif //#ifdef USE_FAT16 #ifdef USE_FAT32 Jürgen Härig | Anhang 155 156 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 if(FATtype==FAT32) { //four bytes per FAT entry // sector=FATFirstSector + (cluster * 4) / BYTE_PER_SEC; // sector=FATFirstSector + cluster / (BYTE_PER_SEC/4); sector=FATFirstSector + (cluster >> 7); #ifdef USE_FATBUFFER UpdateFATBuffer(sector); //read FAT sector // Buffer index is max 511. So in any case we loose upper bits. typecast cluster to U16 // p=&fatbuf[(cluster * 4) & (BYTE_PER_SEC-1)]; p = &fatbuf[((U16)cluster << 2) & (BYTE_PER_SEC-1)]; #else //#ifdef USE_FATBUFFER ReadSector(sector, dirbuf); p = &dirbuf[((U16)cluster << 2) & (BYTE_PER_SEC-1)]; #endif //#ifdef USE_FATBUFFER number&=0x0FFFFFFF; *p++ *p++ *p++ *p = = = = (U8)( number); (U8)(number >> 8); (U8)(number >> 16); (U8)(number >> 24); #ifdef USE_FATBUFFER FATStatus=1; // we have made an entry, so write before next FAT sector read #else //#ifdef USE_FATBUFFER WriteSector(sector, dirbuf); #endif //#ifdef USE_FATBUFFER }// if(FATtype==FAT32) #endif //#ifdef USE_FAT32 } // if(cluster<maxcluster) //we need to check this ;-) return 0; } #endif //DOS_WRITE //########################################################### /*!\brief Get drive informations * \return F_OK if successfull, F_ERROR if not * * This function is most important for the FAT filesystem. * Following values will be read out: * Type of the FAT filesystem. * Number of sectors for the partition (if there is one). * Number of clusters of the drive. * Where is the rootdirectory. And many more. * When using MMC/SD cards, call MMC_IO_Init() BEFORE GetDriveInformation() ! */ U8 GetDriveInformation(void) //########################################################### { U8 by; U32 DataSec,TotSec; U32 bootSecOffset; U32 FATSz; // FATSize U8 loop; Jürgen Härig | Anhang 156 157 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #ifdef USE_FAT32 U32 CountofClusters; #else U16 CountofClusters; #endif U16 RootEntrys; struct MBR *mbr; struct BootSec *boot; struct FileDesc *fdesc; by=IdentifyMedia(); //LaufwerksInformationen holen if(by==0) { FATtype=0; //Unknown FAT type bootSecOffset=0; //erstmal by=ReadSector(0,dirbuf); //Lese den MBR. Erster Sektor auf der Platte //enthält max. 4 Partitionstabellen mit jeweils 16Bytes //Die erste fängt bei 0x01BE an, und nur die nehme ich ! //Erstmal checken ob wir nicht schon einen Bootsektor gelesen haben. boot=(struct BootSec *)dirbuf; FAT loop=0; do { // Jetzt checke ich doch den FAT-String im Bootsektor um den Typ der // zu bestimmen. Einen besseren Weg sehe ich im Moment nicht. if( boot->eb.rm.BS_FilSysType[0]=='F' // && boot->eb.rm.BS_FilSysType[1]=='A' // && boot->eb.rm.BS_FilSysType[2]=='T' && boot->eb.rm.BS_FilSysType[3]=='1' ) { //Wenn ich hier ankomme habe ich entweder FAT12 oder FAT16 #ifdef USE_FAT12 if(boot->eb.rm.BS_FilSysType[4]=='2') FATtype=FAT12; #endif #ifdef USE_FAT16 if(boot->eb.rm.BS_FilSysType[4]=='6') FATtype=FAT16; #endif } else { #ifdef USE_FAT32 if( boot->eb.rm32.BS_FilSysType[0]=='F' // && boot->eb.rm32.BS_FilSysType[1]=='A' // && boot->eb.rm32.BS_FilSysType[2]=='T' && boot->eb.rm32.BS_FilSysType[3]=='3' && boot->eb.rm32.BS_FilSysType[4]=='2' ) { FATtype=FAT32; } else //war kein Bootsektor, also feststellen wo der liegt #endif { Jürgen Härig | Anhang 157 158 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 mbr=(struct MBR *)dirbuf; //Pointer auf die Partitionstabelle bootSecOffset=mbr->part1.bootoffset; //Nur den brauche ich by=ReadSector(bootSecOffset,dirbuf); boot=(struct BootSec *)dirbuf; //read bootsector } } loop++; }while(loop<2 && FATtype==0); //Bis zu zwei Versuche den Bootsektor zu lesen if(FATtype==0) { #ifdef FAT_DEBUG_SHOW_FAT_INFO rs232_print("FAT unknown\n"); #endif return F_ERROR; // FAT-Typ nicht erkannt } secPerCluster=boot->BPB_SecPerClus; //Sectors per Cluster RootEntrys=boot->BPB_RootEntCnt; //32 Byte Root Directory Entrys RootDirSectors = (unsigned char)( ((RootEntrys * 32) + (BYTE_PER_SEC 1)) / BYTE_PER_SEC); //Number of sectors for FAT if(boot->BPB_FATSz16 != 0) FATSz = boot->BPB_FATSz16; else FATSz = boot->eb.rm32.BPB_FATSz32; //Für FAT32 if(boot->BPB_TotSec16 != 0) TotSec = boot->BPB_TotSec16; else TotSec = boot->BPB_TotSec32; FATFirstSector= bootSecOffset + boot->BPB_RsvdSecCnt; FirstRootSector = FATFirstSector + (boot->BPB_NumFATs * FATSz); //Number of data sectors DataSec = TotSec - (boot->BPB_RsvdSecCnt + (boot->BPB_NumFATs * FATSz) + RootDirSectors); FirstDataSector = FirstRootSector + RootDirSectors; //Number of valid clusters //CountofClusters = DataSec / secPerCluster; U8 temp; // secPerCluster is always power of two temp = secPerCluster>>1; // don't divide by 1 ;) CountofClusters = DataSec; while(temp) { CountofClusters >>= 1; temp >>= 1; } maxcluster = CountofClusters + 2; //Note also that the CountofClusters value is exactly that: the count of data clusters //starting at cluster 2. The maximum valid cluster number for the volume is Jürgen Härig | Anhang 158 159 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //CountofClusters + 1, and the "count of clusters including the two reserved clusters" // is CountofClusters + 2. FirstDirCluster=0; // for FAT12 and FAT16 #ifdef USE_FAT12 if(FATtype==FAT12) { endofclusterchain=EOC12; } #endif #ifdef USE_FAT16 if(FATtype==FAT16) { endofclusterchain=EOC16; } #endif #ifdef USE_FAT32 if(FATtype==FAT32) { endofclusterchain=EOC32; FAT32RootCluster=boot->eb.rm32.BPB_RootClus; FirstDirCluster=FAT32RootCluster; FirstRootSector=GetFirstSectorOfCluster(FAT32RootCluster); } #endif } else { return F_ERROR; // CF gives no answer } for (by=0;by<MAX_OPEN_FILE;by++) { fdesc=&FileDescriptors[(U16)by]; fdesc->FileFirstCluster=0; fdesc->FileSize=0; fdesc->FileFlag=0; } BytesPerCluster=BYTE_PER_SEC * secPerCluster; //bytes per cluster // for debugging only #ifdef FAT_DEBUG_SHOW_FAT_INFO //activate output via printf() or puts() if(FATtype==FAT12) rs232_print("FAT12\n"); if(FATtype==FAT16) rs232_print("FAT16\n"); if(FATtype==FAT32) rs232_print("FAT32\n"); rs232_print("bootSecOffset %lu\n",bootSecOffset); rs232_print("Reserved Sectors %u\n",boot->BPB_RsvdSecCnt); rs232_print("FAT Sectors %lu\n",FATSz); rs232_print("Num. of FAT's %u\n",(U16)boot->BPB_NumFATs); rs232_print("secPerCluster %u\n",(U16)secPerCluster); #ifdef USE_64k_CLUSTERS rs232_print("BytesPerCluster %lu\n",BytesPerCluster); #else rs232_print("BytesPerCluster %u\n",BytesPerCluster); #endif Jürgen Härig | Anhang 159 160 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 rs232_print("FATFirstSector %lu\n",FATFirstSector); rs232_print("FirstRootSector %lu\n",FirstRootSector); rs232_print("RootDirSectors %u\n",(U16)RootDirSectors); rs232_print("FirstDataSector %lu\n",FirstDataSector); rs232_print("maxsect %lu\n",maxsect); #ifdef USE_FAT32 rs232_print("FirstDirCluster %lu\n",FirstDirCluster); rs232_print("maxcluster %lu\n",maxcluster); #else rs232_print("FirstDirCluster %u\n",FirstDirCluster); rs232_print("maxcluster %u\n",maxcluster); #endif #endif #ifdef USE_FATBUFFER FATCurrentSector=FATFirstSector; ReadSector(FATCurrentSector,fatbuf); //read first FAT sector #ifdef DOS_WRITE FATStatus=0; // nothing to write til here #endif #endif return F_OK; } //@} 11.2.13 fat.h /*! \file "fat.h" \brief FAT-Definitions */ /// \ingroup multifat /// \defgroup FAT FAT-Functions (fat.h) //######################################################################### // // Benutzt nur die erste Partition // Nur für Laufwerke mit 512 Bytes pro Sektor // // Nach einem White Paper von MS // FAT: General Overview of On-Disk Format // Version 1.03, December 6, 2000 // // 28.12.2006 Removed "unsigned int" ! On ARM this is 4 bytes. Added typedefs for U16. // Added __attribute__((packed)) in FAT struct's for ARM. // Added defined(__AVR__) || defined(ATMEGA) for AVR. // // 24.09.2006 Typecast most constants for better compatibility with other compilers // like MCC18 for PIC18Fxxxx // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 17.09.2007 //######################################################################### Jürgen Härig | Anhang 160 161 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #ifndef __FAT_H #define __FAT_H // These defines are for FAT debugging only. You don't need them. // Activating all three options takes about 1.5kB of flash. // So be careful on devices with small flash memory ! //#define FAT_DEBUG_SHOW_FAT_INFO //activate FAT information output via printf() or puts() in GetDriveInformation() //#define FAT_DEBUG_CLUSTERS // show cluster numbers read/write access via printf() //#define USE_64k_CLUSTERS // This will use more code ! // You should not use 64kB Clusters if your card is smaller than 4GB //file operations #define END_DIR #define NO_MATCH 1 #define MATCH_NAME #define MATCH_EXT 3 #define FULL_MATCH 0 2 MATCH_NAME + MATCH_EXT #define PART1_TABLE_OFFSET (U16) 0x01BE //offset to first partitiontable in sector 0 //Using structures needs less memory than indexing in arrays like inbuff[] //partitiontable structure //most of it is not used in this program //bootsector offset is the only thing we need //because C/H/S values are not used. LBA ! struct PartInfo { U8 status; //Partition status, 0x80 = Active, 0x00 = inactive U8 firsthead; //First head used by partition U16 firstseccyl; //First sector and cylinder used by partition U8 type; //Partition type U8 lasthead; //Last head used by partition U16 lastseccyl; //Last sector and cylinder used by partition U32 bootoffset; //Location of boot sector. !!!!!!!!!!! U32 secofpart; //Number of sectors for partition } __attribute__((packed)); //first sector of disc is the master boot record //it contains four partitiontables //only the first partition is used in this program struct MBR { U8 dummy[PART1_TABLE_OFFSET]; //we don't need all these bytes struct PartInfo part1; struct PartInfo part2; struct PartInfo part3; struct PartInfo part4; //all bytes below are not necessary } __attribute__((packed)); //part of FAT12/16 bootsector different to FAT32 Jürgen Härig | Anhang 161 162 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 struct RemBoot //FAT12/16 defs beginning at offset 36 { U8 BS_DrvNum; U8 BS_Reserved1; U8 BS_BootSig; U8 BS_VolID[4]; char BS_VolLab[11]; char BS_FilSysType[8]; U8 remaining_part[450]; } __attribute__((packed)); //part of FAT32 bootsector different to FAT12/16 struct RemBoot32 //FAT32 defs beginning at offset 36 { U32 BPB_FATSz32; //4 bytes U16 BPB_ExtFlags; //2 bytes U16 BPB_FSVer; //2 bytes U32 BPB_RootClus; //4 bytes U16 BPB_FSInfo; //2 bytes U16 BPB_BkBootSec; //2 bytes U8 BPB_Reserved[12]; U8 BS_DrvNum; U8 BS_Reserved1; U8 BS_BootSig; U32 BS_VolID; //4 bytes char BS_VolLab[11]; char BS_FilSysType[8]; U8 remaining_part[422]; } __attribute__((packed)); union endboot { struct RemBoot rm; struct RemBoot32 rm32; } __attribute__((packed)); struct BootSec { U8 BS_jmpBoot[3]; char BS_OEMName[8]; U16 BPB_BytesPerSec; //2 bytes U8 BPB_SecPerClus; U16 BPB_RsvdSecCnt; //2 bytes U8 BPB_NumFATs; U16 BPB_RootEntCnt; //2 bytes U16 BPB_TotSec16; //2 bytes U8 BPB_Media; U16 BPB_FATSz16; //2 bytes U16 BPB_SecPerTrk; //2 bytes U16 BPB_NumHeads; //2 bytes U32 BPB_HiddSec; //4 bytes U32 BPB_TotSec32; //4 bytes union endboot eb; //remaining part of bootsector } __attribute__((packed)); #ifndef BYTE_PER_SEC #define BYTE_PER_SEC (U16) 512 Jürgen Härig | Anhang 162 163 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #endif #define FAT12 #define FAT16 #define FAT32 (U8) 12 (U8) 16 (U8) 32 //defines for special cluster values //free cluster has value 0 //for fat32 don't use upper four bits ! ignore them //cluster value of 0x10000000 is a FREE cluster in FAT32 //values for end of cluster chain //ranges for example for FAT12 from 0xFF8 to 0xFFF #define EOC12 (U16) 0xFF8 #define EOC16 (U16) 0xFFF8 #define EOC32 (U32) 0x0FFFFFF8 //values for bad #define BADC12 #define BADC16 #define BADC32 marked (U16) (U16) (U32) clusters 0xFF7 0xFFF7 0x0FFFFFF7 //values for reserved clusters //ranges for example for FAT12 from 0xFF0 to 0xFF6 #define RESC12 (U16) 0xFF0 #define RESC16 (U16) 0xFFF0 #define RESC32 (U32) 0x0FFFFFF0 #ifdef USE_FAT32 #define DISK_FULL (U32) 0xFFFFFFFF #else #define DISK_FULL (U16) 0xFFFF #endif //File/Dir Attributes #define ATTR_FILE (U8) #define ATTR_READ_ONLY #define ATTR_HIDDEN #define ATTR_SYSTEM #define ATTR_VOLUME_ID #define ATTR_DIRECTORY #define ATTR_ARCHIVE #define ATTR_LONG_NAME #define ATTR_NO_ATTR 0x00 //not defined by MS ! I did it (U8) 0x01 (U8) 0x02 (U8) 0x04 (U8) 0x08 (U8) 0x10 (U8) 0x20 (U8) 0x0F (U8) 0xFF //not defined by MS ! I did it //Char codes not allowed in a filename //NOT checked yet //0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, and 0x7C. struct DirEntry { char DIR_Name[11]; //8 chars filename 3 chars extension U8 DIR_Attr; //file attributes RSHA U8 DIR_NTres; //set to zero U8 DIR_CrtTimeTenth; //creation time part in milliseconds U16 DIR_CrtTime; //creation time U16 DIR_CrtDate; //creation date U16 DIR_LastAccDate; //last access date (no time for this !) U16 U16 U16 DIR_FstClusHI; DIR_WrtTime; DIR_WrtDate; //first cluster high word //last write time //last write date Jürgen Härig | Anhang 163 164 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 U16 DIR_FstClusLO; //first cluster low word U32 DIR_FileSize; } __attribute__((packed)); //do a little trick for getting long name characters from a DirEntry //DirEntryBuffer later gets the same adress as DirEntry struct DirEntryBuffer { U8 longchars[sizeof(struct DirEntry)]; } __attribute__((packed)); //Prototypes extern U8 GetDriveInformation(void); extern void UpdateFATBuffer(U32 newsector); #ifdef USE_FAT32 extern U32 GetFirstSectorOfCluster(U32 n); extern U32 GetNextClusterNumber(U32 cluster); extern U8 WriteClusterNumber(U32 cluster, U32 number); extern U32 AllocCluster(U32 currentcluster); extern U32 FindFreeCluster(U32 currentcluster); #else extern U32 GetFirstSectorOfCluster(U16 n); extern U16 GetNextClusterNumber(U16 cluster); extern U8 WriteClusterNumber(U16 cluster, U16 number); extern U16 AllocCluster(U16 currentcluster); extern U16 FindFreeCluster(U16 currentcluster); #endif #ifdef USE_FAT32 extern U32 endofclusterchain; extern U32 maxcluster; // last usable cluster+1 extern U32 FAT32RootCluster; #else extern U16 endofclusterchain; extern U16 maxcluster; // last usable cluster+1 #endif extern U8 secPerCluster; #ifdef USE_64k_CLUSTERS extern U32 BytesPerCluster; #else extern U16 BytesPerCluster; #endif extern U8 fatbuf[]; //buffer for FAT sectors //extern U32 FATHits; // count FAT write cycles. you don't really need this ;) extern U32 FATFirstSector; extern U32 FATCurrentSector; extern U8 FATtype; extern U8 FATStatus; // only for FAT write buffering extern U32 FirstRootSector; extern U32 FirstDataSector; //extern U32 RootDirSectors; extern U8 RootDirSectors; #endif //FAT_H Jürgen Härig | Anhang 164 165 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 //@} 11.2.14 find_x.c /*! \file find_x.c \brief Directory Find-Functions */ //########################################################### /// \ingroup multifat /// \defgroup find Directory Find-Functions (find_x.c) /// \code #include "dos.h" \endcode /// \par Uebersicht //########################################################### // Find first file in a directory and give back filename as // a NULL terminated string. Also fileattr and filesize. // // ffblk.ff_name[] 8.3 DOS name with '.' in it and \0 at the end // ffblk.ff_longname[] Long filename with \0 at the end // ffblk.ff_attr ATTR_FILE or ATTR_DIRECTORY // ffblk.ff_fsize Filesize, 0 if directory // // Use this data to find next file, next file, ... in a directory. // // Necessary to make a "dir" or "ls" command. Or opening files // with Fopen() without knowing the filename of the first,next,next... file. // // This doesn't work without initialized global variable FirstDirCluster // which points to the current directory. // // 11.08.2007 Most code simply merged into dir.c // // 26.12.2005 Found a bug in constructing the long name. If it had exactly // 13 Bytes, parts of the last Findnext() are in ffblk.ff_longname. // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 11.08.2007 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" #ifdef USE_FINDFILE struct FindFile ffblk; //make this global to avoid parameter passing //########################################################### // finds the first file or dir entry in current directory // returns 0 if no file is found // returns 1 if a file is found Jürgen Härig | Anhang 165 166 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // /*!\brief Find first directory entry (file or directory) * \return 0 if nothing found, 1 if something found * * check ffblk.ff_attr if you have a file or a dir entry */ U8 Findfirst(void) //########################################################### { ffblk.lastposition=0; //no position return Findnext(); } //########################################################### // finds the next file or dir entry in current directory // /*!\brief Find next directory entry (file or directory) * \return 0 if nothing found, 1 if something found * * Check ffblk.f_attr if you have a file or a dir entry. * Always starts to search from beginning of a directory. * NEVER call this before calling Findfirst() ! * Your program crashes and your hardware will be destroyed. * * Findfirst(), Findnext(), Findnext().... * * If you change to another directory you have to call Findfirst() first again ! */ U8 Findnext(void) //########################################################### { U8 i; #ifdef USE_FINDLONG for(i=0; i<_MAX_NAME; i++) ffblk.ff_longname[i]=0; filename. #endif // clean the long //delete last data for(i=0; i<13; i++) ffblk.ff_name[i]=0; //clean last filename ffblk.ff_attr=ATTR_NO_ATTR; //no file attr ffblk.ff_fsize=0; //no filesize ffblk.newposition=0; //no position for next search if(ScanDirectory(-1, FirstDirCluster) == FULL_MATCH) return 1; } return 0; #endif //USE_FINDFILE //@} 11.2.15 find_x.h //######################################################################### // File: find_x.h // // Benutzung auf eigene Gefahr ! // Jürgen Härig | Anhang 166 167 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Use at your own risk ! // //######################################################################### // Last change: 26.09.2006 //######################################################################### // Compiler: AVR-GCC 3.4.5 //######################################################################### //@{ #ifndef __FINDX_H #define __FINDX_H #define _MAX_NAME 255 struct FindFile { U8 ff_attr; // // // // // // // Max. length of long filenames + 1. This should be 256, but i dont want to use an unsigned int. Maybe 128 or 64 Bytes are also enough for a microcontroller DOS. Change it here if you have problems with free RAM. // file attributes like file, dir // long name ,hidden,system and readonly flags are ignored U32 ff_fsize; // filesize of the file ( not directory ! ) char ff_name[13]; // 8.3 DOS filename with '.' in it and \0 at the end for fopen() #ifdef USE_FINDLONG char ff_longname[_MAX_NAME]; // The very long filename. #endif #ifdef USE_FAT32 U32 newposition; // position of this file entry in current directory (1 means first file) U32 lastposition; // position of last file entry found in current directory (1 means first file) #else U16 newposition; // position of this file entry in current directory (1 means first file) U16 lastposition; // position of last file entry found in current directory (1 means first file) // does also count ".", ".." entrys ! // does not count long filename entrys and volume id #endif }; extern struct FindFile ffblk; extern U8 Findfirst(void); extern U8 Findnext(void); #endif //__FINDX_H //@} 11.2.16 lcd.c /************************************************************************** ** Title : HD44780U LCD library Author: Peter Fleury <[email protected]> http://jump.to/fleury Jürgen Härig | Anhang 167 168 Dokumentation Projekt: Datenlogger, von Jürgen Härig File: Software: Target: Technikerarbeit 2008 $Id: lcd.c,v 1.14.2.1 2006/01/29 12:16:41 peter Exp $ AVR-GCC 3.3 any AVR device, memory mapped mode only for AT90S4414/8515/Mega DESCRIPTION Basic routines for interfacing a HD44780U-based text lcd display Originally based on Volker Oth's lcd library, changed lcd_init(), added additional constants for lcd_command(), added 4-bit I/O mode, improved and optimized code. Library can be operated in memory mapped mode (LCD_IO_MODE=0) or in 4-bit IO port mode (LCD_IO_MODE=1). 8-bit IO port mode not supported. Memory mapped mode compatible with Kanda STK200, but supports also generation of R/W signal through A8 address line. USAGE See the C include lcd.h file for a description of each function *************************************************************************** **/ #include <inttypes.h> #include <avr/io.h> #include <avr/pgmspace.h> #include "lcd.h" /* ** constants/macros */ #define DDR(x) (*(&x - 1)) /* address of data direction register of port x */ #if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) /* on ATmega64/128 PINF is on port 0x00 and not 0x60 */ #define PIN(x) ( &PORTF==&(x) ? _SFR_IO8(0x00) : (*(&x - 2)) ) #else #define PIN(x) (*(&x - 2)) /* address of input register of port x */ #endif #if LCD_IO_MODE #define lcd_e_delay() #define lcd_e_high() #define lcd_e_low() #define lcd_e_toggle() #define lcd_rw_high() #define lcd_rw_low() #define lcd_rs_high() #define lcd_rs_low() #endif __asm__ __volatile__( "rjmp 1f\n 1:" ); LCD_E_PORT |= _BV(LCD_E_PIN); LCD_E_PORT &= ~_BV(LCD_E_PIN); toggle_e() LCD_RW_PORT |= _BV(LCD_RW_PIN) LCD_RW_PORT &= ~_BV(LCD_RW_PIN) LCD_RS_PORT |= _BV(LCD_RS_PIN) LCD_RS_PORT &= ~_BV(LCD_RS_PIN) #if LCD_IO_MODE #if LCD_LINES==1 #define LCD_FUNCTION_DEFAULT #else #define LCD_FUNCTION_DEFAULT #endif #else #if LCD_LINES==1 #define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_1LINE LCD_FUNCTION_4BIT_2LINES LCD_FUNCTION_8BIT_1LINE Jürgen Härig | Anhang 168 169 Dokumentation Projekt: Datenlogger, von Jürgen Härig #else #define LCD_FUNCTION_DEFAULT #endif #endif Technikerarbeit 2008 LCD_FUNCTION_8BIT_2LINES #if LCD_CONTROLLER_KS0073 #if LCD_LINES==4 #define KS0073_EXTENDED_FUNCTION_REGISTER_ON 0x24 mode extension-bit RE = 1 */ #define KS0073_EXTENDED_FUNCTION_REGISTER_OFF 0x20 mode */ #define KS0073_4LINES_MODE 0x09 mode, extension-bit RE = 0 */ /* |0|010|0100 4-bit /* |0|000|1001 4 lines /* |0|001|0000 4-bit #endif #endif /* ** function prototypes */ #if LCD_IO_MODE static void toggle_e(void); #endif /* ** local functions */ /************************************************************************* delay loop for small accurate delays: 16-bit counter, 4 cycles/loop *************************************************************************/ static inline void _delayFourCycles(unsigned int __count) { if ( __count == 0 ) __asm__ __volatile__( "rjmp 1f\n 1:" ); // 2 cycles else __asm__ __volatile__ ( "1: sbiw %0,1" "\n\t" // 4 cycles/loop "brne 1b" : "=w" (__count) : "0" (__count) ); } /************************************************************************* delay for a minimum of <us> microseconds the number of loops is calculated at compile-time from MCU clock frequency *************************************************************************/ #define delay(us) _delayFourCycles( ( ( 1*(XTAL/4000) )*us)/1000 ) #if LCD_IO_MODE /* toggle Enable Pin to initiate write */ static void toggle_e(void) { lcd_e_high(); lcd_e_delay(); lcd_e_low(); } Jürgen Härig | Anhang 169 Dokumentation 170 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #endif /************************************************************************* Low-level function to write byte to LCD controller Input: data byte to write to LCD rs 1: write data 0: write instruction Returns: none *************************************************************************/ #if LCD_IO_MODE static void lcd_write(uint8_t data,uint8_t rs) { unsigned char dataBits ; if (rs) { /* write data (RS=1, RW=0) */ lcd_rs_high(); } else { /* write instruction (RS=0, RW=0) */ lcd_rs_low(); } lcd_rw_low(); if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT ) && (LCD_DATA0_PIN == 0) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) ) { /* configure data pins as output */ DDR(LCD_DATA0_PORT) |= 0x0F; /* output high nibble first */ dataBits = LCD_DATA0_PORT & 0xF0; LCD_DATA0_PORT = dataBits |((data>>4)&0x0F); lcd_e_toggle(); /* output low nibble */ LCD_DATA0_PORT = dataBits | (data&0x0F); lcd_e_toggle(); /* all data pins high (inactive) */ LCD_DATA0_PORT = dataBits | 0x0F; } else { /* configure data pins DDR(LCD_DATA0_PORT) |= DDR(LCD_DATA1_PORT) |= DDR(LCD_DATA2_PORT) |= DDR(LCD_DATA3_PORT) |= as output */ _BV(LCD_DATA0_PIN); _BV(LCD_DATA1_PIN); _BV(LCD_DATA2_PIN); _BV(LCD_DATA3_PIN); /* output high nibble first */ LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN); LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN); LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN); LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN); if(data & 0x80) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN); if(data & 0x40) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN); if(data & 0x20) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); if(data & 0x10) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); Jürgen Härig | Anhang 170 171 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 lcd_e_toggle(); /* output low nibble */ LCD_DATA3_PORT &= ~_BV(LCD_DATA3_PIN); LCD_DATA2_PORT &= ~_BV(LCD_DATA2_PIN); LCD_DATA1_PORT &= ~_BV(LCD_DATA1_PIN); LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN); if(data & 0x08) LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN); if(data & 0x04) LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN); if(data & 0x02) LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); if(data & 0x01) LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); lcd_e_toggle(); /* all data pins high (inactive) */ LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); LCD_DATA2_PORT |= _BV(LCD_DATA2_PIN); LCD_DATA3_PORT |= _BV(LCD_DATA3_PIN); } } #else #define lcd_write(d,rs) if (rs) *(volatile uint8_t*)(LCD_IO_DATA) = d; else *(volatile uint8_t*)(LCD_IO_FUNCTION) = d; /* rs==0 -> write instruction to LCD_IO_FUNCTION */ /* rs==1 -> write data to LCD_IO_DATA */ #endif /************************************************************************* Low-level function to read byte from LCD controller Input: rs 1: read data 0: read busy flag / address counter Returns: byte read from LCD controller *************************************************************************/ #if LCD_IO_MODE static uint8_t lcd_read(uint8_t rs) { uint8_t data; if (rs) lcd_rs_high(); else lcd_rs_low(); lcd_rw_high(); /* RS=1: read data */ /* RS=0: read busy flag */ /* RW=1 read mode */ if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT ) && ( LCD_DATA0_PIN == 0 )&& (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) ) { DDR(LCD_DATA0_PORT) &= 0xF0; /* configure data pins as input */ lcd_e_high(); lcd_e_delay(); data = PIN(LCD_DATA0_PORT) << 4; lcd_e_low(); lcd_e_delay(); /* read high nibble first */ /* Enable 500ns low */ Jürgen Härig | Anhang 171 172 Dokumentation Projekt: Datenlogger, von Jürgen Härig lcd_e_high(); lcd_e_delay(); data |= PIN(LCD_DATA0_PORT)&0x0F; lcd_e_low(); } else { /* configure data pins DDR(LCD_DATA0_PORT) &= DDR(LCD_DATA1_PORT) &= DDR(LCD_DATA2_PORT) &= DDR(LCD_DATA3_PORT) &= Technikerarbeit 2008 /* read low nibble */ as input */ ~_BV(LCD_DATA0_PIN); ~_BV(LCD_DATA1_PIN); ~_BV(LCD_DATA2_PIN); ~_BV(LCD_DATA3_PIN); /* read high nibble first */ lcd_e_high(); lcd_e_delay(); data = 0; if ( PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN) if ( PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN) if ( PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN) if ( PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN) lcd_e_low(); data data data data |= |= |= |= 0x10; 0x20; 0x40; 0x80; /* Enable 500ns low lcd_e_delay(); /* read low nibble */ lcd_e_high(); lcd_e_delay(); if ( PIN(LCD_DATA0_PORT) if ( PIN(LCD_DATA1_PORT) if ( PIN(LCD_DATA2_PORT) if ( PIN(LCD_DATA3_PORT) lcd_e_low(); ) ) ) ) & & & & _BV(LCD_DATA0_PIN) _BV(LCD_DATA1_PIN) _BV(LCD_DATA2_PIN) _BV(LCD_DATA3_PIN) ) ) ) ) data data data data |= |= |= |= */ 0x01; 0x02; 0x04; 0x08; } return data; } #else #define lcd_read(rs) (rs) ? *(volatile uint8_t*)(LCD_IO_DATA+LCD_IO_READ) : *(volatile uint8_t*)(LCD_IO_FUNCTION+LCD_IO_READ) /* rs==0 -> read instruction from LCD_IO_FUNCTION */ /* rs==1 -> read data from LCD_IO_DATA */ #endif /************************************************************************* loops while lcd is busy, returns address counter *************************************************************************/ static uint8_t lcd_waitbusy(void) { register uint8_t c; /* wait until busy flag is cleared */ while ( (c=lcd_read(0)) & (1<<LCD_BUSY)) {} /* the address counter is updated 4us after the busy flag is cleared */ delay(2); /* now read the address counter */ Jürgen Härig | Anhang 172 173 Dokumentation Projekt: Datenlogger, von Jürgen Härig return (lcd_read(0)); Technikerarbeit 2008 // return address counter }/* lcd_waitbusy */ /************************************************************************* Move cursor to the start of next line or to the first line if the cursor is already on the last line. *************************************************************************/ static inline void lcd_newline(uint8_t pos) { register uint8_t addressCounter; #if LCD_LINES==1 addressCounter = 0; #endif #if LCD_LINES==2 if ( pos < (LCD_START_LINE2) ) addressCounter = LCD_START_LINE2; else addressCounter = LCD_START_LINE1; #endif #if LCD_LINES==4 #if KS0073_4LINES_MODE if ( pos < LCD_START_LINE2 ) addressCounter = LCD_START_LINE2; else if ( (pos >= LCD_START_LINE2) && (pos < addressCounter = LCD_START_LINE3; else if ( (pos >= LCD_START_LINE3) && (pos < addressCounter = LCD_START_LINE4; else addressCounter = LCD_START_LINE1; #else if ( pos < LCD_START_LINE3 ) addressCounter = LCD_START_LINE2; else if ( (pos >= LCD_START_LINE2) && (pos < addressCounter = LCD_START_LINE3; else if ( (pos >= LCD_START_LINE3) && (pos < addressCounter = LCD_START_LINE4; else addressCounter = LCD_START_LINE1; #endif #endif lcd_command((1<<LCD_DDRAM)+addressCounter); LCD_START_LINE3) ) LCD_START_LINE4) ) LCD_START_LINE4) ) LCD_START_LINE2) ) }/* lcd_newline */ /* ** PUBLIC FUNCTIONS */ /************************************************************************* Send LCD controller instruction command Input: instruction to send to LCD controller, see HD44780 data sheet Returns: none *************************************************************************/ void lcd_command(uint8_t cmd) { lcd_waitbusy(); Jürgen Härig | Anhang 173 174 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 lcd_write(cmd,0); } /************************************************************************* Send data byte to LCD controller Input: data to send to LCD controller, see HD44780 data sheet Returns: none *************************************************************************/ void lcd_data(uint8_t data) { lcd_waitbusy(); lcd_write(data,1); } /************************************************************************* Set cursor to specified position Input: x horizontal position (0: left most position) y vertical position (0: first line) Returns: none *************************************************************************/ void lcd_gotoxy(uint8_t x, uint8_t y) { #if LCD_LINES==1 lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x); #endif #if LCD_LINES==2 if ( y==0 ) lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x); else lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x); #endif #if LCD_LINES==4 if ( y==0 ) lcd_command((1<<LCD_DDRAM)+LCD_START_LINE1+x); else if ( y==1) lcd_command((1<<LCD_DDRAM)+LCD_START_LINE2+x); else if ( y==2) lcd_command((1<<LCD_DDRAM)+LCD_START_LINE3+x); else /* y==3 */ lcd_command((1<<LCD_DDRAM)+LCD_START_LINE4+x); #endif }/* lcd_gotoxy */ /************************************************************************* *************************************************************************/ int lcd_getxy(void) { return lcd_waitbusy(); } /************************************************************************* Clear display and set cursor to home position *************************************************************************/ void lcd_clrscr(void) { lcd_command(1<<LCD_CLR); Jürgen Härig | Anhang 174 175 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 } /************************************************************************* Set cursor to home position *************************************************************************/ void lcd_home(void) { lcd_command(1<<LCD_HOME); } /************************************************************************* Display character at current cursor position Input: character to be displayed Returns: none *************************************************************************/ void lcd_putc(char c) { uint8_t pos; pos = lcd_waitbusy(); // read busy-flag and address if (c=='\n') { lcd_newline(pos); } else { #if LCD_WRAP_LINES==1 #if LCD_LINES==1 if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) { lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0); } #elif LCD_LINES==2 if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) { lcd_write((1<<LCD_DDRAM)+LCD_START_LINE2,0); }else if ( pos == LCD_START_LINE2+LCD_DISP_LENGTH lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0); } #elif LCD_LINES==4 if ( pos == LCD_START_LINE1+LCD_DISP_LENGTH ) { lcd_write((1<<LCD_DDRAM)+LCD_START_LINE2,0); }else if ( pos == LCD_START_LINE2+LCD_DISP_LENGTH lcd_write((1<<LCD_DDRAM)+LCD_START_LINE3,0); }else if ( pos == LCD_START_LINE3+LCD_DISP_LENGTH lcd_write((1<<LCD_DDRAM)+LCD_START_LINE4,0); }else if ( pos == LCD_START_LINE4+LCD_DISP_LENGTH lcd_write((1<<LCD_DDRAM)+LCD_START_LINE1,0); } #endif lcd_waitbusy(); #endif lcd_write(c, 1); } counter ){ ) { ) { ) { }/* lcd_putc */ /************************************************************************* Display string without auto linefeed Jürgen Härig | Anhang 175 176 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Input: string to be displayed Returns: none *************************************************************************/ void lcd_puts(const char *s) /* print string on lcd (no auto linefeed) */ { register char c; while ( (c = *s++) ) { lcd_putc(c); } }/* lcd_puts */ /************************************************************************* Display string from program memory without auto linefeed Input: string from program memory be be displayed Returns: none *************************************************************************/ void lcd_puts_p(const char *progmem_s) /* print string from program memory on lcd (no auto linefeed) */ { register char c; while ( (c = pgm_read_byte(progmem_s++)) ) { lcd_putc(c); } }/* lcd_puts_p */ /************************************************************************* Initialize display and select type of cursor Input: dispAttr LCD_DISP_OFF display off LCD_DISP_ON display on, cursor off LCD_DISP_ON_CURSOR display on, cursor on LCD_DISP_CURSOR_BLINK display on, cursor on flashing Returns: none *************************************************************************/ void lcd_init(uint8_t dispAttr) { #if LCD_IO_MODE /* * Initialize LCD to 4 bit I/O mode */ if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT ) && ( &LCD_RS_PORT == &LCD_DATA0_PORT) && ( &LCD_RW_PORT == &LCD_DATA0_PORT) && (&LCD_E_PORT == &LCD_DATA0_PORT) && (LCD_DATA0_PIN == 0 ) && (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) && (LCD_RS_PIN == 4 ) && (LCD_RW_PIN == 5) && (LCD_E_PIN == 6 ) ) { /* configure all port bits as output (all LCD lines on same port) */ DDR(LCD_DATA0_PORT) |= 0x7F; } else if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT ) && (LCD_DATA0_PIN == 0 ) && (LCD_DATA1_PIN == 1) && Jürgen Härig | Anhang 176 177 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) ) { /* configure all port bits as output (all LCD data lines on same port, but control lines on different ports) */ DDR(LCD_DATA0_PORT) |= 0x0F; DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN); DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN); DDR(LCD_E_PORT) |= _BV(LCD_E_PIN); } else { /* configure all port bits as output (LCD data and control lines on different ports */ DDR(LCD_RS_PORT) |= _BV(LCD_RS_PIN); DDR(LCD_RW_PORT) |= _BV(LCD_RW_PIN); DDR(LCD_E_PORT) |= _BV(LCD_E_PIN); DDR(LCD_DATA0_PORT) |= _BV(LCD_DATA0_PIN); DDR(LCD_DATA1_PORT) |= _BV(LCD_DATA1_PIN); DDR(LCD_DATA2_PORT) |= _BV(LCD_DATA2_PIN); DDR(LCD_DATA3_PORT) |= _BV(LCD_DATA3_PIN); } delay(16000); /* wait 16ms or more after power-on */ /* initial write to lcd is 8bit */ LCD_DATA1_PORT |= _BV(LCD_DATA1_PIN); // _BV(LCD_FUNCTION)>>4; LCD_DATA0_PORT |= _BV(LCD_DATA0_PIN); // _BV(LCD_FUNCTION_8BIT)>>4; lcd_e_toggle(); delay(4992); /* delay, busy flag can't be checked here */ /* repeat last command */ lcd_e_toggle(); delay(64); /* delay, busy flag can't be checked here */ /* repeat last command a third time */ lcd_e_toggle(); delay(64); /* delay, busy flag can't be checked here */ /* now configure for 4bit mode */ LCD_DATA0_PORT &= ~_BV(LCD_DATA0_PIN); // LCD_FUNCTION_4BIT_1LINE>>4 lcd_e_toggle(); delay(64); /* some displays need this additional delay */ /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */ #else /* * Initialize LCD to 8 bit memory mapped mode */ /* enable external SRAM (memory mapped lcd) and one wait state */ MCUCR = _BV(SRE) | _BV(SRW); /* reset LCD */ delay(16000); /* wait 16ms after power-on lcd_write(LCD_FUNCTION_8BIT_1LINE,0); /* function set: 8bit interface delay(4992); /* wait 5ms lcd_write(LCD_FUNCTION_8BIT_1LINE,0); /* function set: 8bit interface */ */ */ */ Jürgen Härig | Anhang 177 178 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 delay(64); /* wait 64us lcd_write(LCD_FUNCTION_8BIT_1LINE,0); /* function set: 8bit interface */ */ delay(64); */ #endif /* wait 64us #if KS0073_4LINES_MODE /* Display with KS0073 controller requires special commands for enabling 4 line mode */ lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_ON); lcd_command(KS0073_4LINES_MODE); lcd_command(KS0073_EXTENDED_FUNCTION_REGISTER_OFF); #else lcd_command(LCD_FUNCTION_DEFAULT); /* function set: display lines */ #endif lcd_command(LCD_DISP_OFF); /* display off */ lcd_clrscr(); /* display clear */ lcd_command(LCD_MODE_DEFAULT); /* set entry mode */ lcd_command(dispAttr); /* display/cursor control */ //************************************************************************* / }/* lcd_init */ //************************************************************************* / // lcd_print-Funktion. // Diese Funktion ermöglicht es Strings, ähnlich wie mit Printf() // auf dem LC-Display zu positionieren und auszugeben. // Zudem können Variablen in den Ausgabeformaten: Int, Hex, Bin, Char // in dem Text ausgegeben werden. void lcd_print (uint8_t zeile,uint8_t spalte,char *Buffer,...) { va_list ap; va_start (ap, Buffer); int format_flag; char str_buffer[10]; char str_null_buffer[10]; char move = 0; char Base = 0; int tmp = 0; // Zeichenausgabe-Cursor an Zeile und Spalte schicken. lcd_gotoxy(zeile, spalte); // Ausgabe der Zeichen while (*Buffer != 0) { if (*Buffer == '%') { *Buffer++; if (isdigit(*Buffer)>0) { str_null_buffer[0] = *Buffer++; Jürgen Härig | Anhang 178 179 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 str_null_buffer[1] = '\0'; move = atoi(str_null_buffer); } switch (format_flag = *Buffer++) { case 'b': // Binär-Ausgabe Base = 2; goto ConversionLoop; case 'c': //Int to char format_flag = va_arg(ap,int); lcd_write (format_flag++,1); break; case 'i': // Int-Ausgabe Base = 10; goto ConversionLoop; case 'o': // Octal-Ausgabe Base = 8; goto ConversionLoop; case 'x': // Hex-Ausgabe Base = 16; //**************************** ConversionLoop: //**************************** itoa(va_arg(ap,int),str_buffer,Base); int b=0; while (str_buffer[b++] != 0){}; b--; if (b<move) { move -=b; for (tmp = 0;tmp<move;tmp++) { str_null_buffer[tmp] = '0'; } //tmp ++; str_null_buffer[tmp] = '\0'; strcat(str_null_buffer,str_buffer); strcpy(str_buffer,str_null_buffer); } lcd_print_str (str_buffer); move =0; break; } } else { lcd_putc (*Buffer++); } } va_end(ap); } //************************************************************************* / Jürgen Härig | Anhang 179 180 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // String ausgabe // Gibt einen String auf dem Display aus. //************************************************************************* / void lcd_print_str (char *Buffer) { while (*Buffer != 0) { lcd_putc (*Buffer++); }; }; //************************************************************************* / 11.2.17 lcd.h #ifndef LCD_H #define LCD_H /************************************************************************* Title : C include file for the HD44780U LCD library (lcd.c) Author: Peter Fleury <[email protected]> http://jump.to/fleury File: $Id: lcd.h,v 1.13.2.2 2006/01/30 19:51:33 peter Exp $ Software: AVR-GCC 3.3 Hardware: any AVR device, memory mapped mode only for AT90S4414/8515/Mega *************************************************************************** / /** @defgroup pfleury_lcd LCD library @code #include <lcd.h> @endcode @brief Basic routines for interfacing a HD44780U-based text LCD display Originally based on Volker Oth's LCD library, changed lcd_init(), added additional constants for lcd_command(), added 4-bit I/O mode, improved and optimized code. Library can be operated in memory mapped mode (LCD_IO_MODE=0) or in 4-bit IO port mode (LCD_IO_MODE=1). 8-bit IO port mode not supported. Memory mapped mode compatible with Kanda STK200, but supports also generation of R/W signal through A8 address line. @author Peter Fleury [email protected] http://jump.to/fleury @see The chapter <a href="http://homepage.sunrise.ch/mysunrise/peterfleury/avr-lcd44780.html" target="_blank">Interfacing a HD44780 Based LCD to an AVR</a> on my home page. */ /*@{*/ #if (__GNUC__ * 100 + __GNUC_MINOR__) < 303 #error "This library requires AVR-GCC 3.3 or later, update to newer AVR-GCC compiler !" #endif #include <inttypes.h> #include <avr/pgmspace.h> Jürgen Härig | Anhang 180 181 Dokumentation Projekt: Datenlogger, von Jürgen Härig #include #include #include #include Technikerarbeit 2008 <stdlib.h> <ctype.h> <string.h> <stdarg.h> /** * @name Definitions for MCU Clock Frequency * Adapt the MCU clock frequency in Hz to your target. */ #define XTAL 14745600UL /**< clock frequency in Hz, used to calculate delay timer */ /** * @name Definition for LCD controller type * Use 0 for HD44780 controller, change to 1 for displays with KS0073 controller. */ #define LCD_CONTROLLER_KS0073 1 /**< Use 0 for HD44780 controller, 1 for KS0073 controller */ /** * @name Definitions for Display Size * Change these definitions to adapt setting to your display */ #define LCD_LINES 4 /**< number of visible lines of the display */ #define LCD_DISP_LENGTH 13 /**< visibles characters per line of the display */ #define LCD_LINE_LENGTH 0x40 /**< internal line length of the display */ #define LCD_START_LINE1 0x00 /**< DDRAM address of first char of line 1 */ #define LCD_START_LINE2 0x20 /**< DDRAM address of first char of line 2 */ #define LCD_START_LINE3 0x40 /**< DDRAM address of first char of line 3 */ #define LCD_START_LINE4 0x60 /**< DDRAM address of first char of line 4 */ #define LCD_WRAP_LINES 0 /**< 0: no wrap, 1: wrap at end of visibile line */ #define LCD_IO_MODE 1 /**< 0: memory mapped mode, 1: IO port mode */ #if LCD_IO_MODE /** * @name Definitions for 4-bit IO mode * Change LCD_PORT if you want to use a different port for the LCD pins. * * The four LCD data lines and the three control lines RS, RW, E can be on the * same port or on different ports. * Change LCD_RS_PORT, LCD_RW_PORT, LCD_E_PORT if you want the control lines on * different ports. * * Normally the four data lines should be mapped to bit 0..3 on one port, but it * is possible to connect these data lines in different order or even on different * ports by adapting the LCD_DATAx_PORT and LCD_DATAx_PIN definitions. * */ Jürgen Härig | Anhang 181 182 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< /**< LCD_PORT LCD_DATA0_PORT LCD_DATA1_PORT LCD_DATA2_PORT LCD_DATA3_PORT LCD_DATA0_PIN LCD_DATA1_PIN LCD_DATA2_PIN LCD_DATA3_PIN LCD_RS_PORT LCD_RS_PIN LCD_RW_PORT LCD_RW_PIN LCD_E_PORT LCD_E_PIN PORTC LCD_PORT LCD_PORT LCD_PORT LCD_PORT 0 1 2 3 LCD_PORT 4 LCD_PORT 5 LCD_PORT 6 Technikerarbeit 2008 port for the LCD lines port for 4bit data bit 0 port for 4bit data bit 1 port for 4bit data bit 2 port for 4bit data bit 3 pin for 4bit data bit 0 pin for 4bit data bit 1 pin for 4bit data bit 2 pin for 4bit data bit 3 port for RS line pin for RS line port for RW line pin for RW line port for Enable line pin for Enable line */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ #elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || defined(__AVR_ATmega64__) || \ defined(__AVR_ATmega8515__)|| defined(__AVR_ATmega103__) || defined(__AVR_ATmega128__) || \ defined(__AVR_ATmega161__) || defined(__AVR_ATmega162__) /* * memory mapped mode is only supported when the device has an external data memory interface */ #define LCD_IO_DATA 0xC000 /* A15=E=1, A14=RS=1 */ #define LCD_IO_FUNCTION 0x8000 /* A15=E=1, A14=RS=0 */ #define LCD_IO_READ 0x0100 /* A8 =R/W=1 (R/W: 1=Read, 0=Write */ #else #error "external data memory interface not available for this device, use 4-bit IO port mode" #endif /** * @name Definitions for LCD command instructions * The constants define the various LCD controller instructions which can be passed to the * function lcd_command(), see HD44780 data sheet for a complete description. */ /* instruction register bit positions, see HD44780U data sheet */ #define LCD_CLR 0 /* DB0: clear display */ #define LCD_HOME 1 /* DB1: return to home position */ #define LCD_ENTRY_MODE 2 /* DB2: set entry mode */ #define LCD_ENTRY_INC 1 /* DB1: 1=increment, 0=decrement */ #define LCD_ENTRY_SHIFT 0 /* DB2: 1=display shift on */ #define LCD_ON 3 /* DB3: turn lcd/cursor on */ #define LCD_ON_DISPLAY 2 /* DB2: turn display on */ #define LCD_ON_CURSOR 1 /* DB1: turn cursor on */ #define LCD_ON_BLINK 0 /* DB0: blinking cursor ? */ #define LCD_MOVE 4 /* DB4: move cursor/display */ Jürgen Härig | Anhang 182 183 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define */ #define */ #define */ #define */ #define */ #define */ #define */ #define */ #define */ Technikerarbeit 2008 LCD_MOVE_DISP 3 /* DB3: move display (0-> cursor) ? LCD_MOVE_RIGHT 2 /* DB2: move right (0-> left) ? LCD_FUNCTION 5 /* DB5: function set LCD_FUNCTION_8BIT 4 /* DB4: set 8BIT mode (0->4BIT mode) LCD_FUNCTION_2LINES 3 /* DB3: two lines (0->one line) LCD_FUNCTION_10DOTS 2 /* DB2: 5x10 font (0->5x7 font) LCD_CGRAM 6 /* DB6: set CG RAM address LCD_DDRAM 7 /* DB7: set DD RAM address LCD_BUSY 7 /* DB7: LCD is busy /* set entry mode: display shift #define LCD_ENTRY_DEC move dir */ #define LCD_ENTRY_DEC_SHIFT move dir */ #define LCD_ENTRY_INC_ move dir */ #define LCD_ENTRY_INC_SHIFT move dir */ on/off, dec/inc cursor move direction */ 0x04 /* display shift off, dec cursor 0x05 /* display shift on, 0x06 /* display shift off, inc cursor 0x07 /* display shift on, /* display on/off, cursor on/off, blinking #define LCD_DISP_OFF 0x08 /* */ #define LCD_DISP_ON 0x0C /* */ #define LCD_DISP_ON_BLINK 0x0D /* char */ #define LCD_DISP_ON_CURSOR 0x0E /* */ #define LCD_DISP_ON_CURSOR_BLINK 0x0F /* char */ /* move #define */ #define */ #define */ #define */ dec cursor inc cursor char at cursor position */ display off display on, cursor off display on, cursor off, blink display on, cursor on display on, cursor on, blink cursor/shift display */ LCD_MOVE_CURSOR_LEFT 0x10 /* move cursor left LCD_MOVE_CURSOR_RIGHT 0x14 /* move cursor right (increment) LCD_MOVE_DISP_LEFT 0x18 /* shift display left LCD_MOVE_DISP_RIGHT 0x1C /* shift display right /* function set: set interface data length #define LCD_FUNCTION_4BIT_1LINE 0x20 /* 5x7 dots */ #define LCD_FUNCTION_4BIT_2LINES 0x28 /* 5x7 dots */ #define LCD_FUNCTION_8BIT_1LINE 0x30 /* 5x7 dots */ #define LCD_FUNCTION_8BIT_2LINES 0x38 /* 5x7 dots */ #define LCD_MODE_DEFAULT (decrement) and number of display lines */ 4-bit interface, single line, 4-bit interface, dual line, 8-bit interface, single line, 8-bit interface, dual line, ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC) ) Jürgen Härig | Anhang 183 184 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 /** * @name Functions */ /** @brief @param Initialize display and select type of cursor dispAttr \b LCD_DISP_OFF display off\n \b LCD_DISP_ON display on, cursor off\n \b LCD_DISP_ON_CURSOR display on, cursor on\n \b LCD_DISP_ON_CURSOR_BLINK display on, cursor on flashing @return none */ extern void lcd_init(uint8_t dispAttr); /** @brief Clear display and set cursor to home position @param void @return none */ extern void lcd_clrscr(void); /** @brief Set cursor to home position @param void @return none */ extern void lcd_home(void); /** @brief Set cursor to specified position @param x horizontal position\n (0: left most position) @param y vertical position\n (0: first line) @return none */ extern void lcd_gotoxy(uint8_t x, uint8_t y); /** @brief Display character at current cursor position @param c character to be displayed @return none */ extern void lcd_putc(char c); /** @brief Display string without auto linefeed @param s string to be displayed @return none */ extern void lcd_puts(const char *s); /** @brief Display string from program memory without auto linefeed Jürgen Härig | Anhang 184 185 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 @param s string from program memory be be displayed @return none @see lcd_puts_P */ extern void lcd_puts_p(const char *progmem_s); /** @brief Send LCD controller instruction command @param cmd instruction to send to LCD controller, see HD44780 data sheet @return none */ extern void lcd_command(uint8_t cmd); /** @brief Send data byte to LCD controller Similar to lcd_putc(), but without interpreting LF @param data byte to send to LCD controller, see HD44780 data sheet @return none */ extern void lcd_data(uint8_t data); /** @brief macros for automatically storing string constant in program memory */ #define lcd_puts_P(__s) lcd_puts_p(PSTR(__s)) /** @brief Send data String to LCD controller Similar to lcd_putc(), but without interpreting LF @param data byte to send to LCD controller, see HD44780 data sheet @return none */ extern void lcd_print(uint8_t,uint8_t,char *Buffer,...); /** @brief Send data Buffer to LCD controller Similar to lcd_putc(), but without interpreting LF @param data byte to send to LCD controller, see HD44780 data sheet @return none */ extern void lcd_print_str (char *Buffer); /*@}*/ #endif //LCD_H 11.2.18 mem­check.c //http://www.roboternetz.de/wissen/index.php/Speicherverbrauch_bestimmen_mi t_avr-gcc #include <avr/io.h> // RAMEND #include "mem-check.h" // This does not work if you use malloc(), or fdevopen() for printf() of avr-libc ! // Use FDEV_SETUP_STREAM() for printf() instead. Jürgen Härig | Anhang 185 Dokumentation 186 Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // Mask to init SRAM and check against #define MASK 0xaa unsigned short get_mem_unused (void) { unsigned short unused = 0; unsigned char *p = &__heap_start; do { if (*p++ != MASK) break; unused++; } while (p <= (unsigned char*) RAMEND); return unused; } void init_mem(void) __attribute__((naked)) __attribute__((section (".init3"))); /* !!! never call this function !!! */ void init_mem (void) { __asm volatile ( "ldi r30, lo8 (__heap_start)" "\n\t" "ldi r31, hi8 (__heap_start)" "\n\t" "ldi r24, %0" "\n\t" "ldi r25, hi8 (%1)" "\n" "0:" "\n\t" "st Z+, r24" "\n\t" "cpi r30, lo8 (%1)" "\n\t" "cpc r31, r25" "\n\t" "brlo 0b" : : "i" (MASK), "i" (RAMEND+1) ); } 11.2.19 mem­check.h #ifndef _MEM_CHECK_H_ #define _MEM_CHECK_H_ // From linker script // Holger Klabunde: Wieso unsigned char ? extern unsigned char __heap_start; extern unsigned short get_mem_unused (void); #endif /* _MEM_CHECK_H_ */ 11.2.20 mmc_spi.c /*! \file mmc_spi.c \brief MMC/SD-Functions */ //########################################################### /// \defgroup MMC MMC/SD-Functions (mmc_spi.c) Jürgen Härig | Anhang 186 187 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 /// \ingroup MMC /// \code #include "mmc_spi.h" \endcode /// \code #include "dos.h" \endcode /// \par Uebersicht //########################################################### // File: mmc_spi.c // // Read-/Writeroutines for MMC MultiMedia cards and // SD SecureDigital cards in SPI mode. // // This will work only for cards with 512 bytes block length ! // Cards with 4GB that are not SDHC cards may not work ! // // 04.07.2007 2GB card works // // 28.06.2007 Started Support for 2GB and non SDHC 4GB cards // // 26.04.2007 Toshiba 4GB SDHC works ! // // 01.04.2007 Started SDHC support. // // 04.09.2006 Made new MMC_IO_Init(). Removed IO pin settings from // MMCIdentify(). Why ? See comments above MMC_IO_Init(). // // 20.06.2006 Store value for SPI speed and switch to double speed // for Read/WriteSector(). Restore SPI speed at end of // routines. // // 29.09.2004 split SPI_WRITE() into SPI_WRITE() and SPI_WAIT() // speeds up program because CPU can do something else // while SPI hardware module is shifting in/out data // see MMCReadSector() and MMCWriteSector() // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 18.09.2007 //######################################################################### // [email protected] // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: WinAVR 3.4.6 // Compiler: WinAVR 4.1.1 //######################################################################### //@{ #include <avr/io.h> #include <avr/interrupt.h> #include "dos.h" #ifdef MMC_CARD_SPI #include "mmc_spi.h" #if defined (MMC_DEBUG_IDENTIFY) || defined (MMC_DEBUG_SECTORS) || defined (MMC_DEBUG_COMMANDS) || defined (MMC_DEBUG_CMD1_TIMEOUT) || defined (MMC_DEBUG_CMD0_TIMEOUT) #include "serial.h" // For debug only #include "printf.h" // For debug only #endif Jürgen Härig | Anhang 187 188 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 unsigned char card_capacity; // Standard for MMC/SD or High for SDHC U32 maxsect; // last sector on drive #ifdef BUSY_BEFORE_CMD U8 card_type; #endif //###################################################### /*!\brief Send commands to MMC/SD * \param command Command code * \param adress Sector adress * \return Response of MMC/SD * */ U8 MMCCommand(U8 command, U32 adress) //###################################################### { #ifdef BUSY_BEFORE_CMD U16 timeout; #else U8 timeout; #endif U8 by; // MMC_CS_ON(); // Write the dummy byte with CS high. My cards don't need it. SPI_WRITE(DUMMY_WRITE); // This dummy write is necessary for most SD cards ! SPI_WAIT(); MMC_CS_OFF(); #ifdef BUSY_BEFORE_CMD // Some SD Cards don't like this :( AARG, writing MMC was so fast :::( // This is the busy check after writing a sector ! // Some SD cards send 0x00 if not being initialised before, so check card type if(card_type == MMC_CARD) { #ifdef MMC_DEBUG_COMMANDS printf("Busy"); #endif timeout=0xFFFF; //min. 65ms at 8MHz SPI, this should be enough time ! do // a busy check { SPI_WRITE(DUMMY_WRITE); timeout--; // dec timeout while SPI module shifts out SPI_WAIT(); by=SPI_DATA_REGISTER; #ifdef MMC_DEBUG_COMMANDS printf(" 0x%02X",(U16)by); #endif if(timeout == 0) { MMC_CS_ON(); return 0xFF; } Jürgen Härig | Anhang 188 189 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 }while(by != 0xFF); // wait til busy has gone #ifdef MMC_DEBUG_COMMANDS printf("\n"); #endif } #endif //#ifdef BUSY_BEFORE_CMD SPI_WRITE(command); SPI_WAIT(); SPI_WRITE((unsigned SPI_WAIT(); SPI_WRITE((unsigned SPI_WAIT(); SPI_WRITE((unsigned SPI_WAIT(); SPI_WRITE((unsigned SPI_WAIT(); SPI_WRITE(0x95); // other commands SPI_WAIT(); char)((adress & 0xFF000000)>>24)); // MSB of adress char)((adress & 0x00FF0000)>>16)); char)((adress & 0x0000FF00)>>8)); char)(adress & 0x000000FF)); // LSB of adress Checksum for CMD0 GO_IDLE_STATE and dummy checksum for timeout=255; #ifdef MMC_DEBUG_COMMANDS printf("Cmd %02u:",(U16)command-0x40); #endif //wait for response do { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); by=SPI_DATA_REGISTER; #ifdef MMC_DEBUG_COMMANDS printf(" 0x%02X",(U16)by); #endif timeout--; if(timeout==0) break; // no response } while(by==DUMMY_WRITE); #ifdef MMC_DEBUG_COMMANDS puts("\n"); #endif return by; } //###################################################### /*!\brief Read a sector from MMC/SD * \param sector Actual sector number * \param buf Buffer for data * \return 0 if successfull */ U8 MMCReadSector(U32 sector, U8 *buf) //###################################################### { U16 i; Jürgen Härig | Anhang 189 190 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 U8 by; U8 tmpSPSR; // U8 tmpSPCR; #ifdef MMC_DEBUG_SECTORS printf("RS %lu 0x%08lX\n",sector,sector); #endif #ifdef CHECK_MAXSECTOR if(sector>=maxsect) return 1; //sectornumber too big #endif //tmpSPCR = SPI_CTRL_REGISTER; tmpSPSR = SPI_STATUS_REGISTER; //save old value SPI_STATUS_REGISTER = (1<<SPI2X); //give me double speed // MMC_CS_OFF(); // moved to MMCCommand() //calculate startadress of the sector // startadr=sector; // this is the blockadress for SDHC // if(card_capacity==STANDARD_CAPACITY) sector *= BYTE_PER_SEC; // SD/MMC This will work only up to 4GB if(card_capacity==STANDARD_CAPACITY) sector <<= 9; // SD/MMC This will work only up to 4GB by=MMCCommand(MMC_READ_BLOCK,sector); while(by!=START_BLOCK_TOKEN) { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); by=SPI_DATA_REGISTER; // wait for card response #ifdef MMC_DEBUG_COMMANDS // no way to come out of this :( skip this sector !? if(by==0x01) // ERROR ! { // One of my SD cards sends this error. My cardreader has also // problems to read (NOT write !) the card completely. Trash. printf("\nRead error 0x01 at sector %lu !\n",sector); MMC_CS_ON(); // SPI_CTRL_REGISTER = tmpSPCR; //restore old value SPI_STATUS_REGISTER = tmpSPSR; //restore old value // data buffer is not valid at this point ! return 1; } #endif } //---------------------------------------------------------------#ifdef STANDARD_SPI_READ i=BYTE_PER_SEC; // This routine is a little bit faster as for() while(i) { SPI_WRITE(DUMMY_WRITE); // start shift in next byte i--; // dec loop counter while SPI shifts in Jürgen Härig | Anhang 190 191 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 SPI_WAIT(); // wait for next byte *buf++ = SPI_DATA_REGISTER; // store byte in buffer } SPI_WRITE(DUMMY_WRITE); // shift in crc part1 SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); // shift in crc part2 SPI_WAIT(); #endif // STANDARD_SPI_READ //---------------------------------------------------------------//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #ifdef FAST_SPI_READ // The following code looks very strange ! // The idea is not to stop the cpu while SPI module transfers data. // You have some cpu cycles until transmission has finished ! // You can use this time to do something like storing your last data // or get your next data out of memory, doing some loop overhead..... // Don't wait for end of transmission until you have done something better ;) SPI_WRITE(DUMMY_WRITE); // shift in first byte SPI_WAIT(); // we have to wait for the first byte, but ONLY for the first byte by=SPI_DATA_REGISTER; // get first byte, but store later ! SPI_WRITE(DUMMY_WRITE); // start shift in next byte // Diese Schleife läßt sich nicht mehr verbessern :( // Die Zeit zwischen SPI_WRITE() und SPI_WAIT() wird optimal ausgenutzt. // Selbst ein 8 Bit Schleifenzähler bringt nichts. Ganz im Gegenteil. // Man könnte noch INTs verbieten, aber dann kann man die Zeit nicht mehr messen ;) i=BYTE_PER_SEC-1; while(i) //execute the loop while transmission is running in background { *buf++ = by; // store last byte in buffer while SPI module shifts in new data i--; SPI_WAIT(); // wait for next byte by=SPI_DATA_REGISTER; // get next byte, but store later ! SPI_WRITE(DUMMY_WRITE); // start shift in next byte // do the loop overhead at this point while SPI module shifts in new data // i--; // Code wird kleiner, aber dauert länger } // last SPI_WRITE(DUMMY_WRITE); is shifting in crc part1 at this point *buf++ = by; // store last byte in buffer while SPI module shifts in crc part1 SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); // shift in crc part2 SPI_WAIT(); #endif //FAST_SPI_READ //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //################################################################ #ifdef HYPER_SPI_READ // Use this with AVR/ATMEGA only ! // Hand optimized, dangerous routine ! Jürgen Härig | Anhang 191 192 Dokumentation Projekt: Datenlogger, von Jürgen Härig // // // // Technikerarbeit 2008 SPI clock has to be F_CPU/2. THIS IS FOR TESTING ONLY ! Don't use for development ! Next compiler version may give you a kick in the ass. // This should be done in a real assembly routine or in inline assembly. // Does anyone have some for me ? SPI_WRITE(DUMMY_WRITE); // shift in first byte SPI_WAIT(); // we have to wait for the first byte, but ONLY for the first byte by=SPI_DATA_REGISTER; // get first byte, but store later ! SPI_WRITE(DUMMY_WRITE); // start shift in next byte NOP(); i=BYTE_PER_SEC-1; // don't change anything here ! while(i) //execute the loop while transmission is running in background { *buf++ =by; // store last byte in buffer while SPI module shifts in new data i--; // Dec loop counter while SPI shifts in NOP(); // Don't try to remove even a single NOP() here ! NOP(); NOP(); NOP(); NOP(); NOP(); //GCC 4.11 only NOP(); //GCC 4.11 only cli(); // We have to disable Interrupts here ! SPI_WRITE(DUMMY_WRITE); // start shift in next byte by=SPI_DATA_REGISTER; // get next byte, but store later sei(); // Enable Interrupts // do the loop overhead at this point while SPI module shifts in new data } // last SPI_WRITE(DUMMY_WRITE); is shifting in crc part1 at this point *buf++ =by; // store last byte in buffer while SPI module shifts in crc part1 SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); // shift in crc part2 SPI_WAIT(); #endif //HYPER_SPI_READ //################################################################ MMC_CS_ON(); // SPI_CTRL_REGISTER = tmpSPCR; SPI_STATUS_REGISTER = tmpSPSR; //restore old value //restore old value return 0; } #ifdef DOS_WRITE //###################################################### /*!\brief Write a sector to MMC/SD Jürgen Härig | Anhang 192 193 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 * \param sector Actual sector number * \param buf Buffer for data * \return 0 if successfull */ U8 MMCWriteSector(U32 sector, U8 *buf) //###################################################### { U16 i; U8 by; U8 tmpSPSR; // U8 tmpSPCR; #ifdef MMC_DEBUG_SECTORS printf("WS %lu 0x%08X\n",sector,sector); #endif #ifdef CHECK_MAXSECTOR if(sector>=maxsect) return 1; //sectornumber too big #endif //tmpSPCR = SPI_CTRL_REGISTER; //we should save this also ! tmpSPSR = SPI_STATUS_REGISTER; //save old value SPI_STATUS_REGISTER = (1<<SPI2X); //give me double speed // MMC_CS_OFF(); // moved to MMCCommand() //calculate startadress // startadr=sector; // this is the blockadress for SDHC // if(card_capacity==STANDARD_CAPACITY) sector *= BYTE_PER_SEC; // SD/MMC This will work only up to 4GB if(card_capacity==STANDARD_CAPACITY) sector <<= 9; // SD/MMC This will work only up to 4GB MMCCommand(MMC_WRITE_BLOCK,sector); //---------------------------------------------------------------#ifdef STANDARD_SPI_WRITE SPI_WRITE(START_BLOCK_TOKEN); // start block token for next sector i=BYTE_PER_SEC; SPI_WAIT(); while(i) { SPI_WRITE(*buf++); // shift out next byte i--; // dec loop counter while SPI shifts out SPI_WAIT(); // wait for end of transmission } #endif //STANDARD_SPI_WRITE //---------------------------------------------------------------//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #ifdef FAST_SPI_WRITE SPI_WRITE(START_BLOCK_TOKEN); // start block token for next sector i=BYTE_PER_SEC; while(i) // execute the loop while transmission is running in background { // do the loop overhead at this point while SPI module shifts out new data Jürgen Härig | Anhang 193 194 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 by = *buf++; // get next data from memory while SPI module shifts out new data i--; SPI_WAIT(); // wait for end of transmission SPI_WRITE(by); // start shift out next byte } SPI_WAIT(); // wait til last byte is written to MMC #endif //FAST_SPI_WRITE //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //################################################################ #ifdef HYPER_SPI_WRITE // Use this with AVR/ATMEGA only ! // Hand optimized, dangerous routine ! // SPI clock has to be F_CPU/2. // THIS IS FOR TESTING ONLY ! // Don't use for development ! // Next compiler version may give you a kick in the ass. // This should be done in a real assembly routine or in inline assembly. // Does anyone have some for me ? SPI_WRITE(START_BLOCK_TOKEN); // start block token for next sector SPI_WAIT(); i=BYTE_PER_SEC; while(i) { SPI_WRITE(*buf++); // Shift out next byte i--; // Dec loop counter while SPI NOP(); // Don't check SPI busy flag, NOP(); // Loop overhead plus NOP()'s cycles NOP(); // Don't try to remove even a NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); //GCC 4.11 only NOP(); //GCC 4.11 only } SPI_WAIT(); shifts out but simply wait ! have to be min. 16 CPU single NOP() here ! // wait for end of LAST transmission #endif //HYPER_SPI_WRITE //################################################################ SPI_WRITE(DUMMY_WRITE); // 16 bit crc follows data SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); // read response SPI_WAIT(); by=SPI_DATA_REGISTER & 0x1F; if(by != 0x05) // data block accepted ? { #ifdef MMC_DEBUG_COMMANDS Jürgen Härig | Anhang 194 195 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 printf("\nWrite error at sector %lu !\n",sector); #endif MMC_CS_ON(); // SPI_CTRL_REGISTER = tmpSPCR; SPI_STATUS_REGISTER = tmpSPSR; //restore old value //restore old value return 1; } //Note: // // // Moving this to MMCCommand may be dangerous. Don't know if the card needs this clock to finish programming. This may cause problems if after last sector write no MMCCommand is called. MMC cards like this, SD cards NOT ! #ifdef BUSY_BEFORE_CMD if(card_type == SD_CARD || card_type == SDHC_CARD) { #endif do // a busy check { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); }while(SPI_DATA_REGISTER != 0xFF); // wait til busy has gone #ifdef BUSY_BEFORE_CMD } #endif MMC_CS_ON(); // SPI_CTRL_REGISTER = tmpSPCR; SPI_STATUS_REGISTER = tmpSPSR; //restore old value //restore old value return 0; } #endif //DOS_WRITE //###################################################### /*!\brief Initialise io pin settings for MMC/SD * \return Nothing * * Removed this from MMCIdentify(). * Maybe some other devices are connected to SPI bus. * All chip select pins of these devices should have * high level before starting SPI transmissions ! * So first call MMC_IO_Init(), after that for example * VS1001_IO_Init(), SPILCD_IO_Init() and AFTER that MMCIdentify(). */ void MMC_IO_Init(void) //###################################################### { // MMC_CS_ON(); sbi(MMC_CS_PORT,MMC_CS_BIT); //MMC_CS set 1 sbi(MMC_CS_DDR,MMC_CS_BIT); //MMC_CS output cbi(MMC_SCK_PORT,MMC_SCK_BIT); //SCK set 0 sbi(MMC_SCK_DDR,MMC_SCK_BIT); //SCK output cbi(MMC_MISO_DDR,MMC_MISO_BIT); //MISO input Jürgen Härig | Anhang 195 196 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 cbi(MMC_MOSI_PORT,MMC_MOSI_BIT); //MOSI set 0 sbi(MMC_MOSI_DDR,MMC_MOSI_BIT); //MOSI output } //###################################################### /*!\brief Get most important informations from MMC/SD * \return Nothing til now * * This function enables SPI transfers and determines * size of MMC/SD Card */ U8 MMCIdentify(void) //###################################################### { U8 by; #ifndef BUSY_BEFORE_CMD U8 card_type; #endif U16 i; U16 c_size_mult; U32 c_size; // now has 22 bits U16 read_bl_len; U8 csd_version; U8 response[16]; // Init SPI with a very slow transfer rate first ! // // // // // // // // // SPCR SPI Controlregister SPIE=0; //No SPI Interrupt SPE=1; //SPI Enable DORD=0; //Send MSB first MSTR=1; //I am the master ! CPOL=0; //SCK low if IDLE CPHA=0; //SPI Mode 0 SPR1=1; //SPI Clock = f/128 = 125kHz @16MHz Clock SPR0=1; //or f/64 if SPI2X = 1 in SPSR register SPI_CTRL_REGISTER = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0); // SPSR SPI Statusregister // SPI2X=0; // No double speed SPI_STATUS_REGISTER = 0x00; // give min 74 SPI clock pulses before sending commands i=10; do { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); i--; }while(i); // MMC_CS_OFF(); // moved to MMCCommand() #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD0\n"); #endif // Some of my cards give 0x01 response only after a few retrys of CMD0 Jürgen Härig | Anhang 196 197 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 for(i=0; i<CMD0_RETRYS; i++) { //send CMD0 for RESET by=MMCCommand(MMC_CMD0,0); if(by == IN_IDLE_STATE) break; } #ifdef MMC_DEBUG_CMD0_TIMEOUT printf("CMD0 retrys %u\n", (U16)i); #endif if(by != IN_IDLE_STATE) { MMC_CS_ON(); return CMD0_TIMEOUT; } // no response from card #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD8\n"); #endif card_capacity = STANDARD_CAPACITY; // assume standard capacity card_type = UNKNOWN_CARD; // CMD8 has to be send for SDHC cards to expand ACMD41 functions // Check pattern = 0x22 Bits 7..0 (calculated for crc=0x95 !) // VHS Voltage supplied 2.7-3.6V 0b0001 Bits 11..8 c_size = (unsigned long)0x00000122 ; // reuse c_size here ;) // SEND_IF_COND if((MMCCommand(SD_CMD8,c_size) & ILLEGAL_COMMAND) == 0 ) // SD card version 2.00 { GetResponse(response,4); //geht four more bytes of response3 card_type = SD_CARD; // MMC don't know CMD8 ! if(response[2] != 0x01) // test voltage range failed { #ifdef MMC_DEBUG_IDENTIFY printf("Test Voltage range failed 0x%02X\n",(U16)response[2]); #endif MMC_CS_ON(); return TEST_VOLTAGE_ERROR; } if(response[3] != 0x22) // test check pattern failed { #ifdef MMC_DEBUG_IDENTIFY printf("Test pattern failed 0x%02X\n",(U16)response[3]); #endif MMC_CS_ON(); return TEST_PATTERN_ERROR; } // begin: Think we don't need this ! #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD58\n"); #endif MMCCommand(SD_CMD58,0); // READ_OCR GetResponse(response,4); //geht four more bytes of response3 // end: Think we don't need this ! i = 0; while(1) // repeat ACMD41 until card is ready Jürgen Härig | Anhang 197 198 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 { #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD55\n"); #endif MMCCommand(SD_CMD55,0); // SEND_APP_CMD #ifdef MMC_DEBUG_IDENTIFY puts("Send ACMD41\n"); #endif if( MMCCommand(SD_ACMD41,0x40000000) == 0 ) break; // send with HCS bit set if(i++ > 1024) { MMC_CS_ON(); return ACMD41_TIMEOUT; } // no response from card } #ifdef MMC_DEBUG_CMD1_TIMEOUT printf("ACMD41 retrys %u\n", (U16)i); #endif #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD58\n"); #endif // check for high capacity card now by=MMCCommand(SD_CMD58,0); // READ OCR GetResponse(response,4); //geht four more bytes of response3 if(response[0] & 0x40) { card_capacity=HIGH_CAPACITY; // high capacity card if bit 30 of OCR is set card_type = SDHC_CARD; #ifdef MMC_DEBUG_IDENTIFY puts("High capacity card\n"); #endif } } else // SD card V1.xx or MMC { #ifdef MMC_DEBUG_IDENTIFY puts("CMD8 illegal command\n"); #endif // Note: CMD1 is not supported by all SD cards ? // Thin 1,4mm cards don't accept CMD1 before sending ACMD41 // Try ACMD41 first here ! #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD55\n"); #endif // SEND_APP_CMD if((MMCCommand(SD_CMD55,0) & ILLEGAL_COMMAND) == 0 ) // SD card V1.xx { card_type = SD_CARD; // MMC don't know CMD55 ! i=0; while(1) // repeat ACMD41 until card is ready { #ifdef MMC_DEBUG_IDENTIFY puts("Send ACMD41\n"); #endif if( MMCCommand(SD_ACMD41,0) == 0 ) break; Jürgen Härig | Anhang 198 199 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 if(i++ >1024) { MMC_CS_ON(); return ACMD41_TIMEOUT; } // no response from card #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD55\n"); #endif MMCCommand(SD_CMD55,0); // Repeat SEND_APP_CMD } #ifdef MMC_DEBUG_CMD1_TIMEOUT printf("ACMD41 retrys %u\n", (U16)i); #endif } else // MMC card { #ifdef MMC_DEBUG_IDENTIFY puts("CMD55 illegal command\n"); #endif card_type = MMC_CARD; // Repeat CMD1 til result=0 i=0; while(1) { #ifdef MMC_DEBUG_IDENTIFY puts("Send CMD1\n"); #endif if( MMCCommand(MMC_CMD1,0) == 0 ) break; if(i++ > 1024) { MMC_CS_ON(); return CMD1_TIMEOUT; } // no response from card } #ifdef MMC_DEBUG_CMD1_TIMEOUT printf("CMD1 retrys %u\n", (U16)i); #endif }// if((MMCCommand(SD_CMD55,0) & ILLEGAL_COMMAND) == 0 ) // SD card version 1.00 }// if((MMCCommand(SD_CMD8,c_size) & ILLEGAL_COMMAND) == 0 ) // SD card version 2.00 // Read CID // MMCCommand(MMC_READ_CID,0); // nothing really interesting here #ifdef MMC_DEBUG_IDENTIFY puts("Read CSD\n"); #endif // Read CSD Card Specific Data by=MMCCommand(MMC_READ_CSD,0); while(by!=START_BLOCK_TOKEN) { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); by=SPI_DATA_REGISTER; // Wait for card response } GetResponse(response,16); //CSD has 128 bits -> 16 bytes Jürgen Härig | Anhang 199 200 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 SPI_WRITE(DUMMY_WRITE); // 16 bit crc follows data SPI_WAIT(); SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); MMC_CS_ON(); #ifdef MMC_DEBUG_IDENTIFY puts("CSD done\n"); #endif // Here comes the hard stuff ! // Calculate disk size and number of last sector // that can be used on your mmc/sd card // Be aware ! Bit 127 of CSD is shifted in FIRST. by= response[5] & 0x0F; read_bl_len=1; read_bl_len <<= by; #ifdef MMC_DEBUG_IDENTIFY if(read_bl_len > 512) { printf("read_bl_len is %u !\n",read_bl_len); // // // // // // // /* MMC_SET_BLOCKLEN always fails on my 2GB memorex card. Response is 0xFF. But it does work without this. All my other cards work too WITHOUT it. So: FORGET IT !! Auf deutsch: MMC_SET_BLOCKLEN braucht niemand ! Geht komplett ohne bei allen meinen Karten. // Set blocklen to 512 bytes if >512 bytes by=MMCCommand(MMC_SET_BLOCKLEN,512); if(by != 0) { #ifdef MMC_DEBUG_IDENTIFY printf("SetBlocklen failed ! Resp=0x%02X\n",(int)by); #endif return SET_BLOCKLEN_ERROR; // Set blocklen failed } */ } #endif // CSDVERSION = 0, MMC and Version 1.0/2.0 SD Standard Capacity Cards // CSDVERSION = 1, SDHC and Version 2.0 SD Cards if(card_type == MMC_CARD) { csd_version = 0; } else // SD, SDHC { csd_version = response[0] >> 6; } #ifdef MMC_DEBUG_IDENTIFY by = response[0] >> 6; printf("CSD_STRUCT %u\n",(U16)by); by = (response[0] >> 2) & 0x0F; Jürgen Härig | Anhang 200 201 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 printf("SPEC_VERSION %u\n",(U16)by); if(card_type == MMC_CARD) { puts("MMC card\n"); } else // SD / SDHC card { if(csd_version==0) puts("SD card\n"); if(csd_version==1) puts("SDHC card\n"); } #endif if(csd_version==2 || csd_version==3) { #ifdef MMC_DEBUG_IDENTIFY puts("Unknown card\n"); #endif return CSD_ERROR; // error } if(csd_version==0) { //c_size has 12 bits c_size = response[6] & 0x03; //bits 1..0 c_size <<= 10; c_size += (U16)response[7]<<2; c_size += response[8]>>6; by = response[9] & 0x03; by <<= 1; by += response[10] >> 7; c_size_mult=1; c_size_mult<<=(2+by); // drive_size=(unsigned long)(c_size+1) * (unsigned long)c_size_mult * (unsigned long)read_bl_len; // maxsect= drive_size / BYTE_PER_SEC; // Calculate this CAREFULLY ! maxsect has to be <= 2^32 maxsect = read_bl_len / BYTE_PER_SEC; maxsect *= (unsigned long)(c_size+1) * (unsigned long)c_size_mult; } else // if(csd_version==1) { //c_size has 22 bits c_size = response[7] & 0x3F; // 6 lower bits from here c_size <<= 16; c_size += (U16)response[8]<<8; c_size += response[9]; maxsect=1024; // c_size is a multiple of 512kB -> 1024 sectors maxsect *= (c_size+1); c_size_mult=0; // to remove compiler warnings only } #ifdef MMC_DEBUG_IDENTIFY printf("c_size %lu , c_size_mult %u\n",c_size, c_size_mult); printf("DriveSize %lu kB , maxsect %lu\n",(maxsect>>1), maxsect); Jürgen Härig | Anhang 201 202 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #endif // Switch to high speed SPI // SPR1=0; //SPI Clock = f/4 = 4MHz @16MHz Clock // SPR0=0; //or f/2 if SPI2X = 1 in SPSR register SPI_CTRL_REGISTER = (1<<SPE) | (1<<MSTR); // SPSR SPI Statusregister // SPI2X=1; //Double speed for SPI = 8MHz @16MHz Clock SPI_STATUS_REGISTER = (1<<SPI2X); //give me double speed #ifdef MMC_DEBUG_IDENTIFY puts("mmc_init() ok\n"); #endif return MMC_OK; } //###################################################### void GetResponse(U8 *buf, U8 numbytes) //###################################################### { #ifdef MMC_DEBUG_IDENTIFY unsigned char by; #endif unsigned char i; i = numbytes; do { SPI_WRITE(DUMMY_WRITE); SPI_WAIT(); #ifdef MMC_DEBUG_IDENTIFY by = SPI_DATA_REGISTER; *buf++ = by; // printf(" 0x%02X",(U16)SPI_DATA_REGISTER); // don't read this twice if SPI has a FIFO ! printf(" 0x%02X",(U16)by); #else *buf++ = SPI_DATA_REGISTER; #endif i--; }while(i); #ifdef MMC_DEBUG_IDENTIFY puts("\n"); #endif } #endif //MMC_CARD_SPI //@} 11.2.21 mmc_spi.h /*! \file "mmc_spi.h" \brief MMC/SD-Definitions */ /// \ingroup MMC Jürgen Härig | Anhang 202 203 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 /// \defgroup MMC MMC/SD-Functions (mmc_spi.h) //######################################################################### // File: mmc_spi.h // // MMC MultiMediaCard and SD SecureDigital definitions for SPI protocol // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 18.09.2007 //######################################################################### // Compiler: AVR-GCC 4.1.1 //######################################################################### //@{ #ifndef __MMC_CARD_SPI_H #define __MMC_CARD_SPI_H #define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) /* Test for GCC > 4.1.2 */ // #if GCC_VERSION > 40102 // Use ONE of these definitions only ! //#define STANDARD_SPI_READ // No optimizations #define FAST_SPI_READ // Optimizations //#define HYPER_SPI_READ // Very fast. Use this with AVR/ATMEGA only ! // It's very dangerous to use this ! // SPI clock has to be F_CPU/2. // THIS IS FOR TESTING ONLY ! // Don't use for development ! // Next compiler version may give you a kick in the ass. // Use ONE of these definitions only ! //#define STANDARD_SPI_WRITE // No optimizations #define FAST_SPI_WRITE // Optimizations //#define HYPER_SPI_WRITE // Very fast. Use this with AVR/ATMEGA only ! // It's very dangerous to use this ! // SPI clock has to be F_CPU/2. // THIS IS FOR TESTING ONLY ! // Don't use for development ! // Next compiler version may give you a kick in the ass. //#define CHECK_MAXSECTOR WriteSector() // Maximum sector number check in ReadSector(), // Not needed if YOU check it yourself before calling these routines ;) //#define BUSY_BEFORE_CMD before sending a new command. // Busy check after writing is done // So write buffer can be filled while card is busy. This gives // maximum speed ! // Does only work for my MMC cards. SD cards don't like it ! define this. // If you use SD cards only, you should not Jürgen Härig | Anhang 203 204 Dokumentation Projekt: Datenlogger, von Jürgen Härig //#define printf() //#define //#define //#define //#define Technikerarbeit 2008 MMC_DEBUG_IDENTIFY //activate debug output for MMCIdentify() via MMC_DEBUG_SECTORS //activate debug output via printf() MMC_DEBUG_COMMANDS //activate debug output via printf() MMC_DEBUG_CMD0_TIMEOUT //activate debug output via printf() MMC_DEBUG_CMD1_TIMEOUT //activate debug output via printf() // Use only ONE of these ! Not ready yet. Don't use SPI1. #define USE_SPI0 //#define USE_SPI1 // Some usefull defines #define NOP() asm volatile ("nop" ::) #define sbi(portn, bitn) ((portn)|=(1<<(bitn))) #define cbi(portn, bitn) ((portn)&=~(1<<(bitn))) //makes it easier to switch to another hardware #define SPI_CTRL_REGISTER SPCR #define SPI_STATUS_REGISTER SPSR #define SPI_DATA_REGISTER SPDR #define SPI_STATUS_IF_BIT SPIF #define SPI_WRITE(a) { SPI_DATA_REGISTER=(a); } #define SPI_WAIT() { while(! ( SPI_STATUS_REGISTER & (1<<SPI_STATUS_IF_BIT) ) ); } #if defined (__AVR_ATmega32__) || defined (__AVR_ATmega323__) || defined (__AVR_ATmega161__) || defined (__AVR_ATmega163__) #define MMC_CS_BIT #define MMC_CS_PORT #define MMC_CS_DDR located #define MMC_SCK_BIT #define MMC_SCK_PORT #define MMC_SCK_DDR 4 //Pin number for MMC_CS PORTB //Port where MMC_CS is located DDRB //Port direction register where MMC_CS is 7 PORTB DDRB #define MMC_MISO_BIT 6 #define MMC_MISO_PORT PORTB #define MMC_MISO_DDR DDRB #define MMC_MOSI_BIT 5 #define MMC_MOSI_PORT PORTB #define MMC_MOSI_DDR DDRB #elif defined (__AVR_ATmega128__) || defined (__AVR_ATmega64__) #define MMC_CS_BIT #define MMC_CS_PORT #define MMC_CS_DDR located #define MMC_SCK_BIT #define MMC_SCK_PORT #define MMC_SCK_DDR 0 //Pin number for MMC_CS PORTB //Port where MMC_CS is located DDRB //Port direction register where MMC_CS is 1 PORTB DDRB #define MMC_MISO_BIT 3 #define MMC_MISO_PORT PORTB #define MMC_MISO_DDR DDRB #define MMC_MOSI_BIT 2 Jürgen Härig | Anhang 204 205 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define MMC_MOSI_PORT #define MMC_MOSI_DDR Technikerarbeit 2008 PORTB DDRB #elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega8__) || defined (__AVR_ATmega88__) #define MMC_CS_BIT 2 //Pin number for MMC_CS #define MMC_CS_PORT PORTB //Port where MMC_CS is located #define MMC_CS_DDR DDRB //Port direction register where MMC_CS is located #define MMC_SCK_BIT #define MMC_SCK_PORT #define MMC_SCK_DDR 5 PORTB DDRB #define MMC_MISO_BIT 4 #define MMC_MISO_PORT PORTB #define MMC_MISO_DDR DDRB #define MMC_MOSI_BIT 3 #define MMC_MOSI_PORT PORTB #define MMC_MOSI_DDR DDRB #elif defined (__AVR_ATmega644__) #ifdef USE_SPI0 #if GCC_VERSION < 40102 #define SPCR SPCR0 #define SPIE SPIE0 #define SPE SPE0 #define DORD DORD0 #define MSTR MSTR0 #define CPOL CPOL0 #define CPHA CPHA0 #define SPR1 SPR01 #define SPR0 SPR00 #define #define #define #define SPSR SPIF WCOL SPI2X #define SPDR #endif SPSR0 SPIF0 WCOL0 SPI2X0 SPDR0 #define MMC_CS_BIT #define MMC_CS_PORT #define MMC_CS_DDR located 4 //Pin number for MMC_CS PORTB //Port where MMC_CS is located DDRB //Port direction register where MMC_CS is #define MMC_SCK_BIT 7 #define MMC_SCK_PORT PORTB #define MMC_SCK_DDR DDRB #define MMC_MISO_BIT 6 #define MMC_MISO_PORT PORTB #define MMC_MISO_DDR DDRB #define MMC_MOSI_BIT 5 #define MMC_MOSI_PORT PORTB #define MMC_MOSI_DDR DDRB #endif /* Jürgen Härig | Anhang 205 206 Dokumentation Projekt: Datenlogger, von Jürgen Härig #ifdef USE_SPI1 #define SPCR #define SPIE #define SPE #define DORD #define MSTR #define CPOL #define CPHA #define SPR1 #define SPR0 #define #define #define #define SPSR SPIF WCOL SPI2X #define SPDR Technikerarbeit 2008 SPCR1 SPIE1 SPE1 DORD1 MSTR1 CPOL1 CPHA1 SPR11 SPR10 SPSR1 SPIF1 WCOL1 SPI2X1 SPDR1 # error "SPI1 not ready yet in mmc_spi.h" #endif */ #else # error "processor type not defined in mmc_spi.h" #endif #define MMC_CS_ON() #define MMC_CS_OFF() sbi(MMC_CS_PORT,MMC_CS_BIT); cbi(MMC_CS_PORT,MMC_CS_BIT); // MMC/SD commands #define MMC_CMD0 (U8)(0x40 + 0) #define MMC_CMD1 (U8)(0x40 + 1) #define SD_CMD8 (U8)(0x40 #define MMC_READ_CSD (U8)(0x40 #define MMC_READ_CID (U8)(0x40 #define MMC_STOP_TRANSMISSION (U8)(0x40 #define MMC_SEND_STATUS (U8)(0x40 #define MMC_SET_BLOCKLEN (U8)(0x40 #define MMC_READ_BLOCK (U8)(0x40 #define MMC_READ_MULTI_BLOCK (U8)(0x40 #define MMC_WRITE_BLOCK (U8)(0x40 #define MMC_WRITE_MULTI_BLOCK (U8)(0x40 #define SD_CMD55 (U8)(0x40 + 55) #define SD_CMD58 (U8)(0x40 + 58) #define SD_ACMD41 (U8)(0x40 + 41) #define DUMMY_WRITE #define START_BLOCK_TOKEN + + + + + + + + + + 8) 9) 10) 12) 13) 16) 17) 18) 24) 25) (U8)(0xFF) (U8)(0xFE) #ifndef BYTE_PER_SEC #define BYTE_PER_SEC (U16) 512 #endif #define CMD0_RETRYS // SPI Response Flags #define IN_IDLE_STATE #define ERASE_RESET #define ILLEGAL_COMMAND #define COM_CRC_ERROR #define ERASE_ERROR #define ADRESS_ERROR #define PARAMETER_ERROR 20 (U8)(0x01) (U8)(0x02) (U8)(0x04) (U8)(0x08) (U8)(0x10) (U8)(0x20) (U8)(0x40) Jürgen Härig | Anhang 206 207 Dokumentation Projekt: Datenlogger, von Jürgen Härig #define STANDARD_CAPACITY #define HIGH_CAPACITY #define #define #define #define Technikerarbeit 2008 (U8)0 (U8)1 UNKNOWN_CARD (U8)0 MMC_CARD (U8)1 SD_CARD (U8)2 SDHC_CARD (U8)3 //Returncodes for MMCIdentify() #define MMC_OK (U8)0 #define CMD0_TIMEOUT (U8)1 #define CMD1_TIMEOUT (U8)2 #define ACMD41_TIMEOUT (U8)3 #define CSD_ERROR (U8)4 #define TEST_PATTERN_ERROR (U8)5 #define TEST_VOLTAGE_ERROR (U8)6 #define SET_BLOCKLEN_ERROR (U8)7 //prototypes extern U32 maxsect; extern extern extern extern extern extern // last sector on drive U8 MMCCommand(U8 command, U32 adress); U8 MMCReadSector(U32 sector, U8 *buf); U8 MMCWriteSector(U32 sector, U8 *buf); U8 MMCIdentify(void); void MMC_IO_Init(void); void GetResponse(U8 *buf, U8 numbytes); #define ReadSector(a,b) #define WriteSector(a,b) #define IdentifyMedia() MMCReadSector((a),(b)) MMCWriteSector((a),(b)) MMCIdentify() // security checks ! #if defined (STANDARD_SPI_WRITE) && defined (FAST_SPI_WRITE) #error "Define STANDARD_SPI_WRITE or FAST_SPI_WRITE only in mmc_spi.h, NOT both !" #endif #if defined (STANDARD_SPI_WRITE) && defined (HYPER_SPI_WRITE) #error "Define STANDARD_SPI_WRITE or HYPER_SPI_WRITE only in mmc_spi.h, NOT both !" #endif #if defined (FAST_SPI_WRITE) && defined (HYPER_SPI_WRITE) #error "Define FAST_SPI_WRITE or HYPER_SPI_WRITE only in mmc_spi.h, NOT both !" #endif #if !defined (STANDARD_SPI_WRITE) && !defined (FAST_SPI_WRITE) && !defined (HYPER_SPI_WRITE) #error "Define at least STANDARD_SPI_WRITE, FAST_SPI_WRITE or HYPER_SPI_WRITE in mmc_spi.h !" #endif #if defined (STANDARD_SPI_READ) && defined (FAST_SPI_READ) #error "Define STANDARD_SPI_READ or FAST_SPI_READ only in mmc_spi.h, NOT both !" #endif #if defined (STANDARD_SPI_READ) && defined (HYPER_SPI_READ) #error "Define STANDARD_SPI_READ or HYPER_SPI_READ only in mmc_spi.h, NOT both !" Jürgen Härig | Anhang 207 208 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 #endif #if defined (FAST_SPI_READ) && defined (HYPER_SPI_READ) #error "Define FAST_SPI_READ or HYPER_SPI_READ only in mmc_spi.h, NOT both !" #endif #if !defined (STANDARD_SPI_READ) && !defined (FAST_SPI_READ) && !defined (HYPER_SPI_READ) #error "Define at least STANDARD_SPI_READ, FAST_SPI_READ or HYPER_SPI_READ in mmc_spi.h !" #endif #if defined (USE_SPI0) && defined (USE_SPI1) #error "Define USE_SPI0 or USE_SPI1 only in mmc_spi.h, NOT both !" #endif #if !defined (USE_SPI0) && !defined (USE_SPI1) #error "Define at least USE_SPI0 or USE_SPI1 in mmc_spi.h !" #endif #endif //@} 11.2.22 mydefs.h //######################################################################### // File: MYDEFS.H // // //######################################################################### // Last change: 18.10.2006 //######################################################################### // Compiler: AVR-GCC 3.4.5 //######################################################################### #ifndef __MYDEFS_H #define __MYDEFS_H #define T1_PRESCALER #define T1_TICKS #define T1_ONESECOND 8 F_CPU / ( T1_PRESCALER * 65536 ) T1_TICKS #define NOP() asm volatile ("nop" ::) #endif //__MYDEFS_H 11.2.23 readraw.c //########################################################### // File: readraw.c // // Benutzung auf eigene Gefahr ! // // Use at your own risk ! // //######################################################################### // Last change: 15.10.2006 //######################################################################### // [email protected] Jürgen Härig | Anhang 208 209 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // http://www.holger-klabunde.de/index.html //######################################################################### // Compiler: avr-gcc 3.4.5 //######################################################################### //@{ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "dos.h" #ifdef DOS_READ_RAW //########################################################### // reads all clusters/sectors of the file. // you have to check FileSize from direntry to see how // many bytes of last cluster are valid for the file. // much faster than fread() unsigned char ReadFileRaw(char *name) //########################################################### { U32 tmpsector; #ifdef USE_FAT32 U32 tmp; #else U16 tmp; #endif unsigned char *p; unsigned char by,secloop; U16 i; char fileid; struct FileDesc *fdesc; unsigned char end_of_file_is_near; fileid=findfreefiledsc(); if(fileid==-1) return F_ERROR; // there are too many open files ! fdesc=&FileDescriptors[(U16)fileid]; MakeDirEntryName(name,fileid); if(FindName(fileid)==FULL_MATCH) { tmp=fdesc->FileFirstCluster; fdesc->FilePosition=0; //actual read position while(tmp < endofclusterchain) // read all clusters of file { tmpsector=GetFirstSectorOfCluster(tmp); // read all sectors of this cluster for(secloop=0; secloop<secPerCluster; secloop++) { ReadSector(tmpsector,fdesc->iob); p=fdesc->iob; //pointer to io buffer if((fdesc->FileSize-fdesc->FilePosition) > BYTE_PER_SEC) end_of_file_is_near = 0; else end_of_file_is_near=1; Jürgen Härig | Anhang 209 210 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 // read data from iob now for(i=0; i<BYTE_PER_SEC; i++) // read all bytes of a sector { by=*p++; // get a byte from iob // 'by' holds your data now. use it as you want // put your function to use 'by' below //**************************** // load your MP3-Chip here ;) //**************************** // end of your function if(end_of_file_is_near) { fdesc->FilePosition++; // count bytes read if(fdesc->FilePosition>=fdesc->FileSize) { fdesc->FileFlag=0; return (fileid+1); // if F_ERROR is defined with value 0 // then we can use this trick (+1) to tell the caller that is F_OK // and where to find a filesize (dosreadraw1.c) } // end of file reached ? } } tmpsector++; // next sector of cluster }// for(secloop=0; secloop<secPerCluster; secloop++) fdesc->FilePosition += BYTE_PER_SEC; tmp=GetNextClusterNumber(tmp); }// while(tmp < endofclusterchain) }//if(FindName(name)==FULL_MATCH) else { fdesc->FileFlag=0; return F_ERROR; // something went wrong } fdesc->FileFlag=0; return (fileid+1); } #endif //@} Jürgen Härig | Anhang 210 211 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 11.3 Datenblätter Genutzte Bauteile oder Anschlussarten wurden mit einem roten Kreis gekennzeichnet. 11.3.1 Datenblatt – Crumb128 Jürgen Härig | Anhang 211 212 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 212 213 Dokumentation Projektt: Datenloggger, von Jürrgen Härig 11.3.2 Tecchnikerarbeeit 2008 Dattenblatt ­ 7805 Fesstspannun ngsreglerr 5V Jürgen Härigg | Anhang 213 214 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.3 Technikerarbeit 2008 Datenblatt ­ ACS0712 Strommess Modul Jürgen Härig | Anhang 214 215 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 215 216 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 216 217 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 217 218 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 218 219 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 219 220 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.4 Technikerarbeit 2008 Datenblatt ­ SD Karten Slot Jürgen Härig | Anhang 220 221 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 221 222 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.5 Technikerarbeit 2008 Datenblatt ­ Festspannungsregler 3.3V Jürgen Härig | Anhang 222 223 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 223 224 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 224 225 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 225 226 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.6 Technikerarbeit 2008 Datenblatt ­ ES204 LC­Display Jürgen Härig | Anhang 226 227 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 227 228 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 228 229 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 229 230 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.7 Technikerarbeit 2008 Datenblatt ­ HUT Schienen Gehäuse Jürgen Härig | Anhang 230 231 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 231 232 Dokumentation Projekt: Datenlogger, von Jürgen Härig 11.3.8 Technikerarbeit 2008 Datenblatt ­ Steuertaster Jürgen Härig | Anhang 232 233 Dokumentation Projekt: Datenlogger, von Jürgen Härig Technikerarbeit 2008 Jürgen Härig | Anhang 233