Do Pr oku roje me ekt: nta Dat tion tenl n ogg ger.

Werbung
 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 
Herunterladen