Krumeltee

-> Elektronik, Mikrocontroller und Retro-Computing <-

Archive for the ‘C’ Category

Cross-Compiling für Android – Toolchain

Posted by krumeltee - 11. Februar 2014

Um Programme, wie wir sie von Linux oder anderen Betriebssystemen her kennen auf einem Android-Gerät laufen zu lassen, brauchen wir einen Cross-Compiler. Der Cross-Compiler compiliert auf dem PC Programme für die ARM-Architektur des jeweiligen Android-Gerätes.

Android gibt es auch noch für andere Plattformen als ARM, aber die sind recht selten und ich gehe daher nicht darauf ein. Das erstellen der Cross-Toolchain (Compiler, Binutils, …) ist für andere Archtitekturen teilweise sehr unterschiedlich, das Compilieren an sich, bis auf die anderen Target-Optionen nicht.

Neben einem Cross-Compiler benötigt man noch „adb“, die „Android Debug Bridge“. Dieses Programm lässt einen recht einfach seine Binaries und weitere Dateien auf das Handy schieben, ausführen und eine Shell öffnen. Zwinged ist dies nicht, man kann auch den Umweg über Massenspeicher, MTP, WLAN, … gehen und mit einem Terminal-Programm auf dem Handy arbeiten, mit der adb ist es aber um einiges komfortabler.

  • Der Cross-Compiler

Ich verwende die „Sourcery CodeBench Lite Edition„-Toolchain welche einen C/C++-Compiler, Assembler, Linker und Debugger enthält. Ebenso enthalten ist die Standard-C-Library inkl. der Header- und Development-Dateien.

Die Toolchain alleine braucht etwa 350MB Platz. Compiliert man aber z.B. den Kernel hat man schonmal 600MB-1000MB nur an Quelldateien + den Tarball oder die git/svn-Informationen, beim Compilieren für jede benutzte C-Datei noch eine Objekt-Datei…. Da sammelt sich schnell viel Datenmüll an. Man braucht zum sauber Arbeiten mindestens 5-8GB freien Speicher, ich empfehle aber mindestens 15-20GB.

Ich setze vorraus, dass man hierfür mit Linux arbeitet. Auf Windows gestaltet sich das ganze ein wenig anders, was aber hauptsächlich nur an anderen Verzeichnis-Namen und einer anderen Shell liegt. Theorethisch klappt es sowohl mit Linux als auch mit Windows mit dieser Toolchain. Getestet habe ich dies hier mit Gentoo und Debian, auf anderen (aktuellen) Distributionen sollte es genauso sein.

Ich verwende die „G++ Lite 2010.09-110“ Toolchain für Linux. Bei der Auswahl der Pakete wähle ich den Installer, nicht das blosse tar-Paket.

Installationziel der Toolchain wird /opt/arm-linux-gcc/ sein, daher braucht man root-Rechte beim Ausführen des Installers.

$ chmod +x apm-2010.09-110-powerpc-apm-linux-gnu.bin
$ su -c './apm-2010.09-110-powerpc-apm-linux-gnu.bin -i console'

oder für die, die sudo verwenden:

$ sudo ./apm-2010.09-110-powerpc-apm-linux-gnu.bin -i console

Nun wird man durch die Lizenzbedingungen geführt, die man mit „Y“ bestätigt und wählt „1“ für „Typical“, bestätigt.

Jetzt gibt man den Zielpfad an: „/opt/arm-linux-gcc“. Die Frage, ob Links (symlinks) angelegt werden sollen, beantwortet man mit „nein“, da man sich sonst das System damit „zumüllt“. Es reicht vollkommen aus, den Cross-Compiler später mit in den Pfad aufzunehmen.

Hat man nun den Cross Compiler installiert, muss man ihn in seinen Pfad mit aufnehmen. Will man dauerhaft Zugriff auf diesen ARM-Cross-Compiler haben, schreibt man in die ~/.bashrc folgendes:

export PATH=$PATH:/opt/arm-linux-gcc/bin/

Wenn man den Zugriff nicht dauerhaft haben will, sondern jedesmal, bevor man loslegt den Pfad von Hand angeben will, man man genau dieses auf der Konsole. Jetzt einmal die Shell neu öffnen oder kurz von Hand „export ….“ eingeben und der Cross-Compiler steht.

Posted in Android, ARM, C, Linux | Leave a Comment »

Binär-Images in C Programme einbinden – bin2header

Posted by krumeltee - 27. April 2013

Immer wieder steht man vor dem Problem, dass man Daten in einem Programm haben will, diese aber nicht in zig einzelnen Dateien mit dem Programm verteilen will.

Eine Möglichkeit, die man dann hat, ist, die Daten in ein Array zu konvertieren. Zum Beispiel ein Bild, was als Fensterlogo angezeigt werden soll.

Das Programm muss die Bilddatei sowieso öffnen und in eine Struktur, einen Datentyp oder ein Array laden. Also gleich ein Array vom Typ byte nehmen und die Daten zur compile-Zeit fest ins Programm einbinden.

Dazu habe ich mir ein kleines Tool geschrieben, hier zu finden:
https://github.com/spacerace/bin2header

Posted in C, Codeschnipsel, Linux | Leave a Comment »

Kontrast für HD44780 LCD Displays per Software (PWM) regeln

Posted by krumeltee - 27. August 2011

Oft muss man im Betrieb den Kontrast oder auch die Hintergrundbeleuchtung für Displays ändern. Für den Kontrast ist normalerweise ein Poti vorgesehen, für die Hintergrundbleuchtung gibt es zig verschiedene Varianten, jenachdem wie die Hintergrundbeleuchtung gemacht ist. Die Steuerung der Hintergrundbeleuchtung beschreibe ich in einem anderen Artikel.

Hier zeige ich zwei Methoden zur Regelung des Kontrasts und eine, ganz einfache, für die Hintergrundbeleuchtung.

Für den Kontrast gibt es die Möglichkeiten per:

  • DA-Wandler + Operationsverstärker (einfach, teuer)
  • PWM + RC-Glied (sehr einfach, billig)

Die Methode mit dem DA-Wandler ist nur dann sinnvoll, wenn entweder der Mikrocontroller einen DA-Wandler eingebaut hat oder sowieso schon einer im „System“ vorhanden ist. Für meine Versuche habe ich einen MAX520 und einen LM358 als Spannungsfolger beschaltet verwendet. Der Ausgang vom DA-Wandler auf den Eingang vom OP, der Ausgang vom OP direkt auf den Kontrast-Pin vom LCD. Mehr ist nicht notwendig, keine Widerstände, keine Kondensatoren, nichts.

Da mir diese Methode aber etwas zu teuer und wenig sinnvoll ist, habe ich mich für PWM + RC-Glied entschieden.

Diese Methode lässt sich für alle Displays einsetzen, welche nur über ein Poti von GND nach VCC im Kontrast geregelt werden, also nicht nur die HD44780, sondern auch viele T6963, KS0107/KS0108 Grafik-LCDs, …

Hier der Schaltplan:

Die Schaltung funktioniert so:

C1 und R2 bilden einen Tiefpass, der die PWM-Spannung in eine (annähernd) Gleichspannung umwandelt. R1 und R3 bilden einen Spannungsteiler, der einen „Mindestpegel auf den Kontrastpin legt.

Die meisten LCDs sind bei einer Kontrastspannung von ca. 0,8V bis 1,2V am besten lesbar. Mit dieser Schaltung lässt sich der Pegel des Kontrastpins von ca. 0,125V bis 2,8V in 256 Schritten einstellen.

Der Tiefpass+Spannungsteiler ist nötig, da ein reines PWM-Signal das Display sehr stark flackern lässt. Die PWM-Frequenz in meinem Beispiel beträgt ca. 31khz, ab 25khz wurde es schön lesbar, ohne Flackern ausmachen zu können. Weiter unten habe ich dazu noch eine kurze Erklärung.

Folgender Beispielcode ist gemeinsam für Hintergrundbeleuchtung und Kontrast gemacht. Er läuft auf einem ATMega16/ATMega32 bei 16Mhz und benutzt die Pins OC1A und OC1B. Die Helligkeit und der Kontrast lassen sich regeln, in dem man einfach die Werte von 0 bis 255 in die Register OCR1A und OCR1B schreibt:

int main(void) {
    char buffer[25];
    unsigned char contrast = 0;

    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    lcd_puts("hello world");

    DDRD |= (1<<PD5)|(1<<PD4);    // OC1A, OC1B for contrast and backlight

    DDRB &= ~((1<<PB0)|(1<<PB1));    // buttons
    PORTB |= (1<<PB0)|(1<<PB1);    // pullups for buttons

    TCCR1A = (1<<WGM10) | (1<<COM1A1) | (1<<COM1B1);
    TCCR1B = (1<<CS10);
    OCR1A = 0xff;
    OCR1B = 0x60;

    for (;;) {
        if(!(PINB&(1<<PB0))) { OCR1A++; _delay_ms(25); }
        if(!(PINB&(1<<PB1))) { OCR1B++;  _delay_ms(25); }
    }
}

An PB0 und PB1 sind zwei Taster angeschlossen, die, wenn man sie drückt den Wert in den Registern für Kontrast und Beleuchtung einfach hochzählen. Sobald der Wert grösser als 255 wäre, hüpft er automatisch auf 0 herunter und geht dann wieder weiter nach oben.

Hier mal ein paar Fotos von einem Weiss-Schwarzen Display mit obigem Code:

Erklärung zur PWM Frequenz:

Die PWM-Frequenz für den Kontrast muss im Vergleich zum Dimmen von LEDs, Lampen usw. sehr hoch sein, da sich das Display ja stetig aktualisiert. Wenn nun in dem Moment, wo das Display seinen Inhalt anzeigt, der Kontrast für kurze Zeit fehlt und gleich wieder da ist, dann flimmert alles. Das sieht dann ungefähr so aus, wie wenn man mit einer einfachen Kamera einen Fernseher/Monitor filmt und dort dann die Streifen quer durchs Bild rollen.

Erklärung zum Tiefpass:

Die PWM-Spannung wird vom Tiefpass in eine nahezu glatte Gleichspannung geglättet. Das Thema Tiefpass ist hierfür zu Umgangreich und wird schon auf zig anderen Seiten Disskutiert. Hier aber noch zwei Oszilloskop-Bilder, das eine vor dem Tiefpass, direkt an der PWM, das andere nach dem Tiefpass.

  Man sieht hier genau die PWM mit 5V und einer Frequenz von etwa 31khz.

Hier sieht man die geglättete Kontrastspannung von etwa 1,2V für das Display. Die Spannung „schwankt im Dreieck“ um etwa +-50mV (0,05V), was schon sehr gut ist. Das liesse sich durch einen 1µF Kondensator nach GND nahezu komplett auf ca. -+10mV ausbügeln, was aber völlig unnötig ist.

Jeder 7805 und ähnliche haben einen grösseren Fehler als das 😉

Posted in AVR, C | 7 Comments »

SAA1064 I²C 7-Segment Treiber (4 Digits) am AVR32

Posted by krumeltee - 22. August 2011

Heute habe ich einen alten „Humax“ Receiver geschlachtet und gesehen, dass der Chip, welcher die 7 Segment Anzeigen im Bedienteil ansteuert ein SAA1064 ist.

Der SAA1064 kann bis zu vier  7-Segment Anzeigen mit bis zu 21mA pro Segment treiben, dazu sind nur zwei Transistoren, zwei Widerstände und zwei Kondensatoren extern nötig.

Der Chip ist zwar auch schon etwas älter, aber es gibt ihn in DIP und SMD noch bei Reichelt und anderen für ca. 2 Euro zu kaufen. Also hab ich mich kurz rangesetzt und ein bisschen Code für das Grasshopper Board und den Chip geschrieben. Sollte natürlich genauso auf dem NGW100, STK1000 usw. laufen.

Auf diesem Bild kommt die Zahl vom Grasshopper Board 🙂

Der Code ist relativ einfach und sollte anhand der Aufrufe der SAA1064-Funktionen in der main() selbsterklärend sein, hier findet ihr ihn.

Posted in ARM, AVR32, C, Dil NET PC 486, Embedded Linux, Hardware Hacks, I2C, mini2440, MIPS | Leave a Comment »

TLC540 8Bit, 11/12 Channel, AD-Wandler am AVR

Posted by krumeltee - 20. August 2011

Der TLC540 hat 12Analog- Kanäle, wovon 11 Kanäle auf Pins herausgeführt sind. Der Zwölfte Kanal ist für eine interne Referenzspannung benutzt worden um den Controller eine Art „Selbsttest“ machen lassen zu können.

Die 11 Kanäle werden mit einer Auflösung von  8 Bit gewandelt, die Geschwindigkeit pro Wandlung ist laut Datenblatt in etwa 17µS, dort ist allerdings noch nicht die Übertragung usw mit eingerechnet, nur die Wandlung im Chip an sich.

Mit meiner Software komme ich für eine AD-Wandlung auf etwa 45µS, was wohl so ziemlich an der Grenze des Machbaren liegt. Der TLC540 besitzt zwei unabhängige Clocks. Einen Systemtakt und einen IO-Takt. Der Systemtakt treibt die Wandlungen voran, der IO-Takt startet die Wandlungen und schiebt die Daten raus.

System-Clock ist laut Datenblatt mit 4,1Mhz angeben, IO-Clock mit 2Mhz. Meine Software liefert einen Systemtakt von 4,22Mhz und einen IO-Clock von 2Mhz. Der Systemtakt ist also mit etwa 0,12Mhz „übertaktet“. Der Grund dafür ist, dass die Software so etwas einfacher ist und stören tuts auch nicht. Ich habe mir das sehr genau angesehen und ausprobiert. Der ADC funktioniert so ohne irgendwelche Probleme.

Insgesamt habe ich so 12 verschiedene TLC540 probiert, keine Probleme aufgetreten.

Die Software von mir für den TLC540 ist ganz einfach für beliebig viele TLCs anpassbar. In meinem Beispiel hier verwende ich zwei, es funktioniert allerdings genauso mit einem, ohne eine Zeile Code ändern zu müssen. Man darf einfach nur die Channels 0-11 abfragen, 12-23 sind am zweiten AD-Wandler.

Mein Beispielprogramm ist für einen ATMega16 geschrieben, welcher mit 8Mhz läuft. Die 8Mhz können problemlos vom internen Taktgeber ezeugt werden, es ist also nicht unbedingt ein Quarz nötig.

/* tlc540.c 
* driver for the tlc540 ad converter
* tlc540:
* 8bits resolution
* 17µS/conversion
* 11 channels + 1 internal test channel, connected to a test reference
* serial interface
* independent SYSTEM and SERIAL clocks
* SYSTEM clock up to 4mhz
* SERIAL clock up to 2,1mhz
* 
* (c) 2011 krumeltee, email: <nils.stec@googlemail.com>
* 
* this code is free software. you can use it and do with it whatever you like.
* if you redistribute this code or parts of it, please let there be a note
* with a link to the source of the code and the author.
* 
* this driver was written for a AVR ATmega16 running with internal clock source
* at 8mhz.
* a ctc timer generates a ~4.22mhz system clock for the TLC540. This slightly
* overclocks the chip (~0.12mhz = 122khz). It works great with 12 chips i have
* tested so far.
* the i/o clock is generated by the spi master hardware interface of the avr,
* with a frequency of 2mhz. 
* 
* this driver supports as many converters connected to your microcontroller
* as you have pins for Chip Select on each AD-Converter.
* i've tested 12 chips on my mega16, it worked.
* 
* the code is well commented and you should easily find out how to add more chips.
* now the code is configured for 2 chips, connected this way:
* 
* AVR | Chip1 | Chip2
* ------------|----------------|--------------
* SCK (PB7) | IO Clock | IO Clock
* MISO (PB6) | DATA OUT | DATA OUT
* MOSI (PB5) | ADR IN | ADR IN
* OC1A (PD5) | SYS CLOCK | SYS CLOCK
* SS (PB4) | CHIP SELECT | 
* PB3 | | CHIP SELECT
* 
* The speed of my tlc540() function is about 45µS for 1 conversion with the speed 
* settings written above. This results in about 22222.2 conversions per second.
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "lcd.h"

#ifndef F_CPU
#define F_CPU 8000000UL
#endif

#include <util/delay.h>

unsigned char tlc540(unsigned char channel) {
    unsigned char config_byte, i;

    PORTB |= (1<<PB4);    // SET CS, disable chip 0
    PORTB |= (1<<PB3);    // SET CS, disable chip 1

    if(channel <= 11) {
        PORTB &= ~(1<<PB3);        // CLR_CS enable chip 0 
    } else if(channel <= 23) {
        PORTB &= ~(1<<PB4);        // CLR_CS enable chip 1
        channel -= 12;
    }

    config_byte  = channel<<4;        // AD channel Y (YYYYxxxx) MSB .... LSB

    _delay_us(6);          // after enabling a chip wait at least 2 rising + the second falling edges of system clock

    SPDR = config_byte;        // set + get data
    while(!(SPSR & (1<<SPIF)));    

    for(i = 0; i < 36; i++) {    // 36 clock cycles to do the next conversion
        PORTB |=(1<<PB7);
        PORTB &=~(1<<PB7);
    }

    // make sure all chips are disabled, so we simply disable ALL cs
    PORTB |= (1<<PB3);
    PORTB |= (1<<PB4);

    return SPDR;    // our AD-value 
}

int main (void) {
    DDRB |= (1<<PB4);    // CS ad chip 0
    DDRB |= (1<<PB3);    // CS ad chip 1
    DDRB |= (1<<PB2);    // CS sr 0

    DDRB &= ~(1<<PB6);    // DATA (data from adc to controller)
    PORTB |= (1<<PB6);    // pullup

    DDRB |=(1<<PB7);    // Serial Clock

    DDRB |= (1<<PB5);    // ADRIN (adress input of adc)

    DDRD |= (1<<PD5);    // SYSTEM Clock via OC1A (CTC timer1)
    TCCR1A |=(1<<COM1A0);   // OC1A toggle on compare match
    TCCR1B |=(1<<WGM12);    // CTC mode 4
    TCCR1B |=(1<<CS10);     // no prescaling
    OCR1A=0x0;        // ~4,22mhz system clock @8mhz avr
                // use OCR1A = 0x01 for 16mhz

    SPCR = (1<<SPE)|(1<<MSTR);    // spi enable, master
    SPSR = 0;            // spi freq f_cpu/4 ~2mhz @ 8mhz avr
                    // use f_cpu/8 prescaler for 16mhz

    lcd_init(LCD_DISP_ON);
    lcd_clrscr();    
    sei();

    char buffer[17];
    unsigned char ad_result;
    int i;

    for(;;) {
        for(i = 0; i < 24; i++) {
            ad_result = tlc540(i);
            sprintf(buffer, "channel %02d: $%02x", i, ad_result);
            lcd_gotoxy(0,0);
            lcd_puts(buffer);
            //_delay_ms(1000);
        }
    };
}

Posted in AVR, C | Leave a Comment »

LM75 am AVR32 unter Linux

Posted by krumeltee - 15. August 2011

Hier eine kleine C-Lib, welche einen LM75 unter Linux auf dem AVR32 auslies.

Hier findet ihr die Quellcodes.

Der Code sollte auch mit meinem „RAM-I2C-Interface“ an jedem PC laufen.

Posted in ARM, AVR32, C, Dil NET PC 486, Embedded Linux, I2C, mini2440 | Leave a Comment »

PCF8574 am AVR32 unter Linux

Posted by krumeltee - 15. August 2011

Hier eine kleine C-Lib, welche einen MAX127 unter Linux auf dem AVR32 auslies.

Hier findet ihr die Quellcodes.

Der Code sollte auch mit meinem „RAM-I2C-Interface“ an jedem PC laufen.

Posted in ARM, AVR32, C, Dil NET PC 486, Embedded Linux, I2C, mini2440, MIPS | Leave a Comment »

MAX127 am AVR32 unter Linux

Posted by krumeltee - 15. August 2011

Hier eine kleine C-Lib, welche einen MAX127 unter Linux auf dem AVR32 auslies.

Der Code ist mehr oder weniger mein AVR-Code für den MAX127, einfach nur auf Linux angepasst.

Hier findet ihr die Quellcodes.

Der Code sollte auch mit meinem „RAM-I2C-Interface“ an jedem PC laufen.

Posted in AVR32, C, Embedded Linux, I2C, Linux, mini2440 | Leave a Comment »

ADS7841 12Bit AD-Wandler am AVR

Posted by krumeltee - 1. August 2011

Der ADS7841 von Texas Instruments / Burr Brown ist ein 12Bit AD-Wandler, welcher sich per SPI ansteuern lässt.

Er verfügt über 4 Single Ended oder 2 Differential Eingänge. Die Versorgungsspannung liegt zwischen 2,7V und 5V.

Hier habe ich einen Beispielcode, welcher die 4 Kanäle im Single Ended Modus ausliest. Die 4 Pins des AD-Wandler, welche nötig sind um ihn anzusprechen lassen sich auf beliebige Ports und Pins des AVRs legen.

Hier die Standard-Beispiel-Schaltung aus dem Datenblatt:

Im Code werden die Ports/Pins per #define angegeben.

Hier der Code:

#define CLK PB0
#define DIN PB2
#define CS PB1
#define DOUT PB4
#define PORT_CLK PORTB
#define PORT_DIN PORTB
#define PORT_CS PORTB
#define PORT_DOUT PORTB
#define DDR_CLK DDRB
#define DDR_DIN DDRB
#define DDR_CS DDRB
#define DDR_DOUT DDRB
#define PIN_DOUT PINB
#define SET_CS PORT_CS |=(1<<CS)
#define CLR_CS PORT_CS &=~(1<<CS)
#define SET_CLK PORT_CLK |=(1<<CLK)
#define CLR_CLK PORT_CLK &=~(1<<CLK)
#define SET_DIN PORT_DIN |=(1<<DIN)
#define CLR_DIN PORT_DIN &=~(1<<DIN)
#define GET_DOUT (PIN_DOUT &(1<<DOUT))

DDR_DIN &= ~(1<<DIN);
PORT_DIN |= (1<<DIN);
DDR_DIN  |= (1<<DIN);
DDR_CLK  |= (1<<CLK);
DDR_CS   |= (1<<CS);

get_ads7841(0);

unsigned short get_ads7841(char channel) {
    unsigned char i, control_byte;    
    unsigned short dat = 0;            

    control_byte = 0x97;    // default channel 0
    switch(channel) {        // if a channel between 1 and 3 is given, change the control byte
        case 1: control_byte = 0xD7; break;
        case 2: control_byte = 0xA7; break;
        case 3: control_byte = 0xE7; break;
    }

    CLR_CLK;
    CLR_DIN;
    CLR_CS;

    for(i=0; i<8; i++) {                    // send control byte to ADS7841
        if(control_byte & 0x80) SET_DIN;
        else CLR_DIN;
        CLR_CLK;
        SET_CLK;
        control_byte = control_byte<<1;      /* Shift bit once next time */
    }
    CLR_CLK;
    SET_CLK;

    for(i=0; i<12; i++)    {                    // read data from ADS7841 (12bits)
        CLR_CLK;
        SET_CLK;
        dat = dat<<1;            
        dat = dat | GET_DOUT;     
    }

    for(i=0;i<4;i++) {                         // we have to do four clock transitions, to because we only need 12bits
        CLR_CLK;
        SET_CLK;
    }

    SET_CS; 

    return dat>>4;
}

Posted in AVR, C | Leave a Comment »

MAX127 I²C-AD-Wandler am AVR

Posted by krumeltee - 27. Juli 2011

Ich bin vor kurzem durch Zufall an zwei MAX127 im DIP-Gehäuse gekommen, sehr praktische ADCs.

Hier ein kurzer Überblick:

  • I²C Interface
  • 8 Channels
  • 12Bit Auflösung
  • einzelne 5V Versorgungsspannung, ausreichend genaue Referenz mit 2 Kondesatoren möglich
  • 0-5V, 0-10V, -5V – +5V, -10V – +10V Input Range
  • -16,5V – +16,5V Überspannungsschutz auf den Eingängen
  • Bis zu 9 Chips am Bus verwendbar (72 Analoge Eingänge max.)

Durch die sehr starke Input Protection von +-16,5V ist dieser ADC nahezu Idioten- und Steckbrettsicher. Die Kommunikation ist extrem einfach gehalten, auch die Einstellmöglichkeiten sind nicht allzu aufgebläht, sodass man das Datenblatt in 10 Minuten auswendig kann.

Die Auflösung von 12Bit ist auch sehr gut, das reicht für Basteleien, Roboter, Akkuladegeräte, als Eingangs-ADC für analog gesteuerte Lichttechnik, …, … dicke aus.

Die Kommunikation benötigt für eine AD-Wandlung 4 Byte, also lasst uns mal rechnen:

100kHz Bus: 1/(32bit/100.000bit pro sekunde) = 3125 Messungen pro Sekunde

400kHz Bus: 1/(32bit/400.000bt pro sekunde)=12500 Messungen pro Sekunde

Soviel zur Theorie, die Start- und Stopbits, der Protokolloverhead, noch nicht mitgerechnet. Dies macht allerdings nicht allzuviel aus. Das Datenblatt gibt 8000 Samples Per Second an, das entspricht 125µS pro Messung. Um nun alle 8 Channels einmal abzufragen benötigt man also eine Millisekunde.

Nun genug der Rechnerei, hier zur Hardware, welche SEHR SEHR einfach ist.

Bei mir hing der ADC zum Testen an einem atmega32, sollte aber mit jedem AVR mit I²C funktionieren, wahrscheinlich schon auf einem attiny2313 oder ähnliche mit 2kb Flash.

Die Adresse des Chips lässt sich über die Pins A0, A1, A2 einstellen. Sind alle drei Pins auf GND, so ist die Adresse 0x50. Will man eine andere Adresse, sollte man sich das Datenblatt durchlesen.

Die Software ist ebenso einfach wie die Hardware. Mein Beispielcode für den ADC baut auf der Fleury I²C-Library auf. Die Display-Ausgaben in den weiteren Beispielen bauen auf der Fleury LCD-Library für ein HD44780 Display auf.

Hier erstmal der Quellcode:

/* get_max127()
*
* address - I2C Bus Addresse des Chips
* channel - Der Kanal, von 0 bis 7
* mode - 0 = 0-5V messung, 1 = 0-10V messung
* 2 = +-5V messung, 3 = +-10V messung
*
*/ 
unsigned short get_max127(unsigned char address, unsigned char channel,unsigned char mode) {
    unsigned char high, low, ret, config_byte;
    unsigned short value;
    if(channel > 7) return 0;

     ret = i2c_start(address);                                      // start communication
    if(ret == 0) {                                                // chip found
        if(mode == 3) config_byte = (0x8c | (channel<<4));        // configuration byte, for information see datasheet
        else if(mode == 2) config_byte = (0x84 | (channel<<4));
        else if(mode == 1) config_byte = (0x88 | (channel<<4)); 
        else config_byte = (0x80 | (channel<<4));
         i2c_write(config_byte);
        i2c_stop();

        i2c_start(address+1);
        high = i2c_readAck();
        low = i2c_readNak();
        i2c_stop();
        value = ((high<<8)|low);
        value >>= 4;
    } else {
        return 0;
    }
    return value;
}

Hier dann noch ein paar Beispiele zum verwenden der Funktion:

/* Channel 0, Wert in Hex, Dezimal, Volt von 0-5V Messung per RS232 rausschicken */
char buffer[64];
unsigned short ad_value = get_max127(0x50, 0, 0);
sprintf(buffer, "hex:%03x dez:%d volt:%1.4fV", ad_value, ad_value, ((5.0/0xfff)*ad_value));
uart_puts(buffer);

/* Channel 3, Wert in Volt auf HD44780 LCD mit Fleury LCD Lib ausgeben */
char buffer[17];    // buffer ist 17 bytes gross, fuer 16 zeichen LCD + ''
unsigned short ad_value = get_max127(0x50, 3, 1);
sprintf(buffer, "%1.4fV", ((10.0/0xfff)*ad_value));
rs232_send(buffer);

 

Sollten noch Fragen sein, könnt ihr mir schreiben oder aber auch das Datenblatt lesen. Zum Schluss natürlich noch ein Foto:

 

Posted in AVR, C, I2C | 2 Comments »