hallo, Habe mich soweit es für mich möglich ist über SPI mit Codevision informiert, da ich einen LTC1257 in einem Schulprojekt verwenden sollte. Die DAC0808 Versuche sind leider alle gescheitert. Da ich noch absoluter AVR Anfänger bin, bitte ich um eure Hilfe. Hardware Verwendung: AT90S8535, 3,6864MHz Codevision Code Generator ========================= Der AVR müßte in dem Fall der Master sein, da er auch den Takt für den Transfer erzeugt --> SPI Type: Master SPI Clock Rate: 921,600kHz Clock Phase: Cycle Half Clock Polarity: Low Data Order: MSB First SPI Interrupt: NO Anschlussbelegung ================= AVR8535 LTC1257 ============================== PB7 (SCK) CLK PB5 (MOSI) Din PB4 (SS) Load Mit jedem vom AVR (Master) erzeugten Taktimpuls schiebt er ein Datenbit in den Eingang (MOSI) des Slaves (LTC1257). Der LTC1257 nimmt nur dann Daten auf, wenn er vom Master über die SS (Slave Select) Leitung aktiviert wird. Nach Beendigung vom Transfer wird das Flag SPIF-Bit gesetzt und löst eine Interruptanforderung aus, wenn das SPIE-Bit gesetzt ist. Der Code Generator erzeugt folgenden Code -> siehe Dateianhang. Die spi.h sieht wie folgt aus: /* CodeVisionAVR C Compiler (C) 1998-2000 Pavel Haiduc, HP InfoTech S.R.L. Prototypes for SPI access functions */ #ifndef SPI_INCLUDED #define SPI_INCLUDED unsigned char spi(unsigned char data); #endif Im Main Programm hätte ich einen Funktionsaufruf gemacht -> zB.: write_dac(parmeter) und eine Funktion die dann den Wert an den DAC übergibt. function write_dac(parameter) { ....... ????????????? } Ab hier stehe ich an ... Ich bitte um Eure Hilfe, da ich das Teil am Montag präsentieren sollte. thx CHris
Du machst dich ja selbst verückt und mit dem Hinweis auf den Abgabetermin machst du dir auch keine Freunde. Ich kann dir leider auch kaum helfen (keine Zeit und kein Code Vision). Ich möchte dir nur 2 Tips geben. 1 Ich glaube der 1257 hat eine 2,048 Volt Ref. Das heist um auf 5 Volt zu kommen brauchst du auch wieder ne OP Schaltung (simpel). 2. der 1257 braucht 12 bit. Ich bin mir nicht 100 % sicher aber er kann warscheinlich auch ein Word (16 bit) vertragen der Daten Out Pin spricht dafür. Du must im dann mit 4 Dummy Datenbit ( 1 oder 0 egal ) füttern. Wenn der uC SPI mit Word kann könnte es klappen.Mit 1 Byte (8 bit) kommt der 1257 definitiv nicht zurecht. Ich hab mal vor Jahren einen ähnlichen Wandler mit nem PIC ( ohne SPI) in asm geprogt, das war super einfach: Schleife (0-11) Datenbit reinschieben >> und Clock toogln, Wenn Schleife durch dann Load das ist es schon. Vieleicht solltest du es auch erstmal ohne SPI versuchen. Bringt dir vieleicht etwas Sicherheit ( Wie gesagt du machst dich gerade selber Fertig). Gruß Bernhard
das serielle Interface des LTC1257 ist kein SPI-Interface und kann simpel mit 3 IO Ports bedient werden A = Data B = Shift Clock Übernahme bei L/H Flanke C = Load Clock Übernahme bei H/L Flanke
danke mal ...könnte mir jemand erklären wie? oder hätte jemand nen source? thx Chris
Hallo Christian Dein Problem liegt doch erstmal darin, eine 12 Bit Variable seriell über eine Pin auszugeben. Wenn du das Problem gelöst hast, ist der Rest ein Leichtes. Der erste Anfang. Du hast eine Variable von 12 Bit in einer Word Variable. Jetzt mußt du diese auf eine Pin seriell ausgeben. Dazu mußt du nun immer eine Stelle (immer die gleiche) der Variablen testen, ob eine 1 oder eine 0 anliegt und danach deinen PIN HIGH oder LOW setzen. Danach wird die Variable um eine Stelle geschoben. Das ganze in einer Schleife mit 12 Durchläufen. Schleife Start Bit x in Variable gesetzt ja = dann PIN high nein = dann PIN low variable schieben. Richtung ???? Brainstorming next Setzt dich da mal ran und wenn man sieht, das du dir Gedanken machst, dann bekommst du hier auch alle Hilfe die du brauchst. MFG Dieter
Hab mmertens asm-Makro mal in C umgesetzt... http://www.mikrocontroller.net/forum/read-4-66518.html Gruß, Patrick...
Nachtrag: Für eine Variable vom Typ Word (16 bit) wird in C meist ein unsigned int verwendetet. Bsp.: Die Daten die ich vom uC zum AD Wandler schicken will nenne ich: zumAD. unsigned int zumAD; und schau dich mal in der Hilfe deines Compilers um, ( Bit level access to the I/O Registers )
Wow OldBug, wo du schon dabei bist (so nett zu sein) kannst morge auch Brötchen holen Wagen waschen etc. ;-)
hallo dieter, erstmal DANKE!!! hätte das so gelöst: zB.: 0xb3e void main(void) { int wert = 0xb3e; // = 2878 (1011 0011 1110) int bit_position_0 = 1; for (int i=0; i<12; i++) { if (wert & bit_position_0) { //Bit gesetzt } else { //Bit nicht gesetzt } wert = wert >> 1; } } bitte um weitere hilfe thx Chris
sorry, ihr habt in der zwischenzeit gepostet, hab das nicht gesehen als ich mein posting abgeschickt habe .... danke mal, schaue mir den code mal an ... thx Chris
hallo, habe das ganze mal in ein projekt eingefügt und probiert den source zu compilen ... bekomme jedoch immer Error: must be LValue zB.: DACPORT & DACCLK = LEV_LOW; geht das überhaupt? als linker operand kann dem operator doch kein linkswert übergeben werden ... thx Chris
hi, thx mal OldBug, ich habe für Montag sicherheitshalber mal eine simples AVR Projekt fertig gebastelt. sitz grade wieder dabei und versuche dein source file zu verstehen, ich glaube es hätte nicht viel sinn wenn ich dem prof. das teil vor die nase halte und sage es funzt ... und ich nicht oder nur ein wenig erklären kann wieso ... würde mich nur selbst belügen ... zumindest sehe ich das so ... .... ich probiere mal zu verstehen ... bitte um korrektur wenn ich falsch liege ... // Low level initialisation routine for LTC1257. extern inline void ltc1257_ll_init(void) { /* Initial port/pin state */ DACCLK(LEV_LOW); /* clock pin low -> idle */ DACLOAD(LEV_HIGH); /* load pin high -> idle */ } extern inline -> funzt mit codevision scheinbar nicht ... aber das ist ja das geringere problem ... hätte die routinen in ein c-file gepackt, ein h-file mit prototype declaration gemacht und das dann included, aber da gibts ja einige möglichkeiten ... DACCLK(LEV_LOW); ich nehme an, dass die CLK auf low liegen sollte da bei steigender Flanke ins shift register vom lt1257 geschrieben wird DACLOAD(LEV_HIGH); ... wenn 12bit im register sind und die LOAD auf LOW geht werden die bits vom shift register ins dac register übernommen ... extern inline void ltc1257_ll_write(unsigned int data) { volatile unsigned int bitctr = 0; for(bitctr = 0; bitctr < 0x0C; bitctr++) { DACDATA(data & 0x8000); /* output MSB (bits [15..4]!) */ data <<= data; /* shift next bit to MSB */ DACCLK(LEV_HIGH); /* rising edge -> load bit */ DACCLK(LEV_LOW); /* -> await rising edge */ } DACCLK(LEV_LOW); /* clock pin low -> idle */ DACLOAD(LEV_LOW); /* load pulled low -> output */ for (bitctr = 0; bitctr < LATCHTIMING; bitctr++) ; DACLOAD(LEV_HIGH); /* load pulled high -> idle */ } -> der übergebene wert data (12 bits left-justified) ... wenn ich also den tatsächlichen wert hernehme und zB.: data = data << 4 mache müßte das passen ... DACDATA(data & 0x8000); data verundest du mit 1000 0000 0000 0000 somit stellst du fest ob das 12bit gesetzt oder nicht gesetzt ist sorry BITx Macros verstehe ich nicht ... daher weiß ich leider auch nicht was DACDATA(....) macht ... data <<= data; ???? ist mir ein rätsel ... du shiftest data um den wert von data nach links ... DACCLK(LEV_HIGH) -> du ladest da bit bei steigender flanke DACCLK(LEV_LOW) -> abwarten bis flanke wie auf 0 ist das ganze läuft in ner schleife 12mal for (bitctr = 0; bitctr < LATCHTIMING; bitctr++) ; ok, ... die schleife ist mir klar, läuft solange bis bitctr kleiner als der wert von latchtiming ist .... nur wieso ..???? der wert müßte doch je nach Taktfrequenz vom µC unterschiedlich sein ... im datasheet vom lt1297 steht max. clock rate ist 1.4MHz wie weiß man jetzt, dass die clock rate diesen wert hat? in assembler sollte fast jeder "befehl" in 1 Taktzyklus ausgeführt werden ... nur in C ?????? DACLOAD(LEV_HIGH); LOAD wird wieder auf high gesetzt nachdem die daten vom SHIFT Register ins DAC Register übernommen worden sind ... wie gesagt den PART hab ich überhaupt nicht verstanden ... /* * Set the "BITx" makros to fit your needs! */ #define DACCLK(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT0):(DACIN | BIT0) #define DACDATA(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT1):(DACIN | BIT1) #define DACLOAD(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT2):(DACIN | BIT2) #define LATCHTIMING 0xFF /* fit this to your needs! */ #define DACOUT P1OUT #define DACIN P1IN P1OUT ... irgendein PORT PIN für OUT ... zB.: PINA P1IN ... irgendein PORT PIN für IN ... zB.: PINB müßte man dann doch eigentlich zB.: PORTA.A und PORTA.B schreiben können ... ... so das wars mal denk ich .... fragen über fragen ... ich hoffe ich verärgere euch nicht damit ....... thx Chris
Hi Chris! Also BIT0...7 sind die Port Pins Deines Prozessors. Wie Du schon richtig sagst, könnte man das auch mit PORTA.A schreiben, aber das ist wohl nicht ganz ANSI Konform. DACOUT ist der Port, an dem Deine drei Pins angeschlossen werden. DACIN muss der gleiche Port sein, damit Du immer nur die Bits manipulierst, die Du gerade verwendest (nicht den ganzen Port). DACDATA(data & 0x8000); Hier wird das MSB (most sufficient bit) des unsigned int maskiert und an den Port-Pin übergeben. data <<= data; Da hab ich wohl noch nen Denkfehler gehabt, muss natürlich data = data << 1; heissen. Damit wird das nächste Bit des unsigned integers nach ganz links geschoben (MSB). Beim nächsten DACDATA(data & 0x8000) wird dieses Bit dann übertragen. Die for-Schleife mit dem LATCHTIMING ist aufgrund eines Hinweises von mmerten da drin. Der LTC1257 benötigt wohl ein paar Zyklen um die Daten ins Latch zu übernehmen. Ums halt Portabel zu halten, habe ich diese Schelife mit einem Makro aufgebaut. Du könntest LATCHTIMING wohl auf 0x01 setzen, ich denke, das sollte auch Funktionieren (bei mmerten waren 2 NOP). Ich hoffe Dir geholfen zu haben... Gruß, Patrick...
hi patrick, THX mal! was ich noch immer nicht verstehe: #define DACOUT P1OUT #define DACIN P1IN #define DACCLK(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT0):(DACIN | BIT0) #define DACDATA(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT1):(DACIN | BIT1) #define DACLOAD(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT2):(DACIN | BIT2) der "? :" Operator ist mir klar ... .... warum brauch ich den DACIN ..?? versteh ich nicht ... hat das was mit dem DACIN Port vom LT1297 zu tun ..??? bitte dich mir die makros zu erklären ...?? BIG THX Chris
Hallo Christian Den DACIN brauchst du, um die alten Zustand des Ports einzulesen, damit nicht der ganze Port verändert wird, sondern nur die PINs, die du verändern willst. Deine Bits werden dann dann mit den alten Daten verknüpft und ausgegeben. Kann ja sein, das du von dem Port andere Pins für andere Aufgaben verwenden willst und die möchtest du ja dann nicht verändern, wenn du einen DA Wert ausgibst. MFG Dieter
hi, thx dieter, #define DACOUT P1OUT #define DACIN P1IN also ist P1OUT und P1IN der selbe PORT zB.: PORTA vom µC und DACIN hat dann nichts mit dem DACIN Port vom LT1257 zu tun. BIT0, BIT1, BIT2 sind PORT PINS ... in dem Fall dann vom PORTA zB.: BIT0 -> PIN0, BIT1 -> PIN1, BIT2 -> PIN2 connection vom LT1257 dann CLK -> BIT0 -> PORTA PIN0 DATA -> BIT1 -> PORTA PIN1 LOAD -> BIT2 -> PORTA PIN2 BITMAKRO: #define DACCLK(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~BIT0):(DACIN | BIT0) DACOUT = (LEVEL) ... ist doch eine Zuweisung und kein Vergleich ... könnte mir das bitte noch wer erklären .. BIG THX Chris
Hallo Chris! Z A B C DACOUT = (LEVEL) ? (DACIN & ~BIT1):(DACIN | BIT1) Z = Zuweisung A = Bedingung B = Wird ausgeführt, wenn A wahr C = Wird ausgeführt, wenn A nicht wahr Verständlich so?
hi, ahh, jetzt hab ichs geschnallt ... werde das ganze jetzt mal testen ... bin gespannt .. :=) BIG THX Chris
hi, so habe das ganze mal in ein c file gepackt und wollte testen ... compilen funkt auch ... hab mir mitm osci mal angeguckt was da so raus kommt ... NIX! anbei c-source .. (verwende den CAVR -> Codevision) THX Chris
... hier sicherheitshalber die 90s8535 definitions ... auch noch mit dazu ... Chris
guten morgen, hat mir keine ruhe gelassen ... irgendwas stimmt mit PORTA und PORTA.0 nicht ... da kommt nur müll raus ... hmm ...?? Chris
hi, was hab ich hier falsch gemacht ... habe noch einiges rumprobiert ... aber auf den ausgängen kommt nix daher ... bitte um hilfe .. thx Chris
bestimmt AVcc und AGnd nicht angeschlossen, muss mit Vcc bzw Gnd verbunden werden, wenn du Port A nutzen willst.
hi, funkt auch auf einem anderen PORT nicht ... wer weiß RAT? thx Chris
Hallo Ist die Hardware den Ordnung ? Hast du schonmal ein kleines Programm getestet, das z.B. die Ports einfach von LOW auf HIGH schaltet in einer Endlosschleife. Damit man die Hardware ausschliessen kann. Oder mal die Schaltung aufzeichnen und posten. Takt in Ordnung ?? MFG Dieter
Hallo Dieter, ich verwende ein STK200 und einen ICE200. Ein simples Programm zB.: LED´s aus ein ... #include <90s8515.h> #include <delay.h> #define LEV_LOW 0 #define LEV_HIGH 1 void main(void) { PORTB=0xFF; DDRB=0xFF; while (1) { PORTB.0 = LEV_HIGH; delay_ms(300); PORTB.0 = LEV_LOW; delay_ms(300); }; } das funktioniert ... sehe hier optisch an den LED´s das es paßt .. und mit OSCI hab ich mir angeschaut ob die 300ms auch wirklich 300ms sind. Funkt wunderbar .... Also schließe ich mal die Hardware aus. Ich vermute der CAVR Compiler checkt folgendes nicht: #define DACCLK(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~PORTA.0):(DACIN | PORTA.0) ... ist aber nur ne Vermutung ... beim compilen vom code werden keine Errors gemeldet ... was nun? thx Chris
hi, habe den ? : Operator aufgelöst und in function´s gepackt ... selbiges in grün ... source im anhang .. Chris
hi, was im obigen source fehlt ist die links ausrichtung des wertes der an LTC1257_LL_WRITE übergeben wird, ... habe ich aber hier gemacht .... bin jetzt die ltc1257_ll_init() schrittweise durchgegangen ... da stimmt doch was nicht, oder?!? bei mir kommt da immer 0 raus!?! #define DACOUT PORTB // P1OUT #define DACIN PORTB // P1IN #define DACCLK(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~PORTB.0):(DACIN | PORTB.0) #define DACDATA(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~PORTB.1):(DACIN | PORTB.1) #define DACLOAD(LEVEL) DACOUT = (LEVEL) ? (DACIN & ~PORTB.2):(DACIN | PORTB.2) #define LEV_LOW 0 #define LEV_HIGH 1 void ltc1257_ll_init(void) { /* Initial port/pin state */ DACCLK(LEV_LOW); /* clock pin low -> idle */ DACLOAD(LEV_HIGH); /* load pin high -> idle */ } DACCLK(0) LEVEL ist FALSE, daher (DACIN | PORTB.0) DACIN: 0000 0000 PORTB.0: 0 OR =================== DACOUT 0000 0000 -> liegt am PORTB jetzt an DACLOAD(1) LEVEL ist TRUE, daher (DACIN & ~PORTB.2) DACIN: 0000 0000 ~PORTB.1: 1 AND ==================== DACOUT 0000 0000 -> PORTB sollte doch jetzt 0000 0100 sein? ... weiter ... teil der write function ... for(bitctr = 0; bitctr < 0x0C; bitctr++) { DACDATA(data & 0x8000); /* output MSB (bits [15..4]!) */ data = data << 1; /* shift next bit to MSB */ DACCLK(LEV_HIGH); /* rising edge -> load bit */ DACCLK(LEV_LOW); /* -> await rising edge */ } 1. Schleifendurchgang ===================== DACDATA(data & 0x8000) ich setze für data mal 0xBB8 (1011 1011 1000) ein, dann ein << 4 (-> 12 bits left-justified) ergibt 1011 1011 1000 0000 1000 0000 0000 0000 AND =================== 1000 0000 0000 0000 -> ergibt 0x8000 DACDATA(0x8000) LEVEL ist TRUE, daher (DACIN & ~PORTB.1) DACIN (sollte meiner Meinung nach nach dem init auf 0000 0100 sein, wenn ich 0000 0000 einsetze kommt das selbe raus) 0000 0100 0000 0010 AND ========= 0000 0000 -> 0 bits am PORTB gesetzt! 2. Schleifendurchgang ... ich lasse mal das DACCLK(LEV_HIGH) und DACCLK(LEV_LOW) weg ... ===================== data = data << 1; data : 1011 1011 1000 0000 data << 1: 0111 0111 0000 0000 DACDATA(data & 0x8000) 0111 0111 0000 0000 1000 0000 0000 0000 AND =================== 0000 0000 0000 0000 DACDATA(0) LEVEL ist FALSE, daher (DACIN | PORTB.1) DACIN : 0000 0000 PORTB.1: 0 OR ========= 0000 0000 3. Schleifendurchgang ... ich lasse mal das DACCLK(LEV_HIGH) und DACCLK(LEV_LOW) weg ... ===================== data = data << 1; data : 0111 0111 0000 0000 data << 1: 1110 1110 0000 0000 DACDATA(data & 0x8000) 1110 1110 0000 0000 1000 0000 0000 0000 AND =================== 1000 0000 0000 0000 DACDATA(0x8000) LEVEL ist TRUE, daher (DACIN & ~PORTB.1) 0000 0000 0000 0010 AND ========= 0000 0000 -> 0 bits am PORTB gesetzt! ...... im Prinzip kommt bei allen Schleifendurchgängen 0 raus .... -> liege ich da richtig? bitte um Hilfe thx Chris
hi, ich habe ein bischen rumexperimentiert .... ich bitte euch den beigefügten code anzugucken .... das timing wird wahrscheinlich sicher nicht stimmen ... die max. clockrate vom LT1297 ist laut datenblatt 1.4MHz ... da wäre ich weit drunter.... wie ich ein küreres delay mache weiß ich leider nicht... cavr hat delay_ms und delay_µs ... schauts euch bitte mal an ... thx Chris
hi, obiger code erzeugt folgendes ... siehe osci foto ... 1: CLK 2: DATA thx Chris
hi, könnte mir bitte jemand mit dem timing helfen... thx Chris
Hallo Du wechselest die Daten auf dem Port, wenn CLK High ist. Weiß nicht, ob das erlaubt ist. Laut Datenblatt ist ein Wechsel der Daten nur bei CLK low erlaubt. for(i = 0; i < 12; i++) { if (data & 0x8000) // output MSB (bits [15..4]!) PORTB.1 = 1; // if BIT is set -> PORT PIN is high else PORTB.1 = 0; // if BIT is not set -> PORT PIN is low data <<= 1; //shift next bit to MSB delay_us(1); //wait 1µs Hier low PORTB.0 = 0; //trigger the clock delay_us(4); //wait 4µs Hier High, bleibt aber High bis zum nächsten Durchlauf mit Datenwechsel. PORTB.0 = 1; //reset the clock delay_us(4); //wait 4µs } Ändere das mal. Also erst Daten auf Port legen, CLK High, warten dann CLK low und nächsten Daten auf Port legen. MFG Dieter
Also ich habe hier mal die logischen Zustände meiner Routinen in ein Textfile gepackt (möglicherweise musst Du die Reihenfolge der Konstrukte mit ternary operator auf Deinen Prozessor anpassen). Am PC sieht das dann so aus, wie im Textfile... Logisch Funktioniert das ganze also, ist nur die Frage, obs auch mit Deinem Prozessor läuft! Gruß, Patrick...
Hi, DANKE AN ALLE! habs hinbekommen ... mein Problem lag an den BITVariablen .. thx Chris
Da sieht mans wieder: nicht blind alles was CodeVision und Co. ausspucken kopieren! Glückwunsch... Gruß, Patrick...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.