Archiv verlassen und diese Seite im Standarddesign anzeigen : Portierung von Software-UART auf ATTiny26
vklaffehn
11.02.2007, 22:08
Moin, nach langer Zeit hab ich mal wieder selbige, um mich meinem Robot zu widmen. Ich bin umgestiegen auf die Atmel-Serie und programmiere in C. Nun habe ich ein kleines Problem und hoffe, dass mich hier jemand erleuchten kann. Ich habe eine kleine Schaltung mit dem Tiny26 gebaut und wollte nun über den letzten freien Pin (PB1) seriell ein paar Daten an meinen PC senden. Auf der Suche nach einer Software-UART-Implementierung bin ich dann hierauf gestossen : http://temp.ph0rkeh.com/avr/projects/swusart.html
Ich hab auch schon so einiges rausgefunden, allerdings find ich nix über dieses ominöse WGM01....
Wenn ich probiere, das Beispiel so zu kompilieren, meckert der an allen Ecken und Enden, das legt sich aber, wenn ich z.B. aus TCCR0A TCCR1A bzw ...0B zu 1B mache, dabei hat der Tiny26 doch auch einen Timer0 oder nicht??? ICh bin völlig verwirrt.
Kann mir da mal einer 'nen Tipp geben oder verraten, wie ich meinen ATTiny26 an meinen PC kriege? Vielen Dank schonmal!!
void uart_init(void)
{
cli();
TCCR0A = (1 << WGM01); //CTC
TCCR0B = (1 << CS01); // perscaler of 8
OCR0A = OSCRA;
OCR0B = OSCRA;
TIFR = (1 << OCF0A); // set interrupt Output Compare Flag 1A
SUART_TXD_DDR |= (1 << SUART_TXD_BIT);
SUART_TXD_PORT |= (1 << SUART_TXD_BIT);
outframe = 0;
}[/b][/code]
vklaffehn
11.02.2007, 22:33
oh, ich bin vielleicht blind... kommando zurück, ich führe mir erstmal den Artikel vom RN-Wissen zu Gemüte, bei weiteren Fragen meld ich mich wieder....
vklaffehn
11.02.2007, 22:44
Okeee.... Also irgendwie scheint das mit dem T26 nicht so gut zu gehen, die Timer sind da wohl irgendwie anders.... Da werd ich wohl aus lauter Verzweiflung doch den ATMega8 nehmen oder die Daten über den Parallelport reinschaufeln mit 'nem zusätzlichen clocksignal oder so.
Warum nimmst du nicht das Appnote AVR305:
http://www.atmel.com/dyn/resources/prod_documents/doc0952.pdf
Hab ich zwar nur mit dem Tiny2313 getestet sollte aber mit dem Tiny26 auch funktionieren.
Gruß,
SIGINT
P.S.: Bei kleinen Übertragungsraten hat das bei mir ohne Probleme mit dem internen Oszillator funktioniert.
SprinterSB
12.02.2007, 17:41
Okeee.... Also irgendwie scheint das mit dem T26 nicht so gut zu gehen, die Timer sind da wohl irgendwie anders...
Jepp. Das Wiki-Beispiel (https://www.roboternetz.de/wissen/index.php/Software-UART_mit_avr-gcc) hatte ich gemacht für einen ATmega8. Mit dem Handbuch an der Seite sollte es aber nich allzu schwierig sein, das Beispiel für einen ATtiny zu portieren.
Falls Fragen zur M8-Implementierung sind, kannst du ja konkret nachfragen.
Das Beispiel sieht die Verwendung einer FIFO vor und Implementiert für Sender und Empfänger; wenn du also nur einen simplen Sender brauchst wird der Code deutlich schrumpfen und übersichtlicher werden.
Mit dem T26 bin ich nicht vertraut... anzupassen ist der Timer, der ja die Zeiten für die Bits erzeugt. Beim T26 ist Timer1 geeignet, konfiguriert über den OC1C im CTC-Mode. T26-Timer1 ist zwar nur ein 8-Bit-Timer, zusammen mit dem feinergranulierten Prescaler sollte er aber für nen Soft-UART taugen. Ob die PLL gewinnbringend eingesetzt werden kann überblick ich momentan nicht, bin wie gesagt nicht vertraut mit dem T26.
vklaffehn
12.02.2007, 19:39
Moin!
Danke für die Tipps, die Appnote hatte ich doch glatt übersehen, hatte nur die 307 entdeckt. Jetzt werde ich mal versuchen, das von Assembler nach C zu übersetzen, ich bitte schonmal, blöde Fragen meinerseits und geistige Inkompetenz zu entschuldigen, ich kämpfe zur Zeit mit einer zum Glück vollständig heilbaren Krebsart, allerdings nehme ich Schmerzmittel, also schwankt mein IQ zur Zeit etwas ;-) Gleich nochmal eine Frage : Wenn ich die UART_delay Funktion aus der Appnote in C umsetze, muß ich da was beachten, damit der Compiler die nicht einfach wegoptimiert? Oder kann ich die aus der delay.h vom AVR-GCC nehmen?
Setz das Appnote doch als Inline-ASM um... Atmel hat irgendwo den fertigen ASM Source.
Gruss,
SIGINT
vklaffehn
12.02.2007, 23:33
Hallo!
Ich bin jetzt mal dabei, die Appnote per Inline ASM umzubauen, jetzt würde ich gern mal sehen, was WinAVR da für ein Asemblerlisting draus erzeugt, kann man irgendwo einstellen, wo er das speichern soll??
Danke schonmal!!
SprinterSB
13.02.2007, 09:38
https://www.roboternetz.de/wissen/index.php/Assembler-Dump_erstellen_mit_avr-gcc
:-k Warum denn inline asm? Damit vergrößerst du die Hardwareabhänigkeit noch weiter....
vklaffehn
13.02.2007, 13:30
Ah, danke!!
Naja, ich werde wohl nur die Verzögerung umbauen, scheint zwar dasselbe zu sein wie in der delay.h vom AVR-GCC, aber ich machs wegen dem Lerneffekt. Und die Verzögerungsschleife müßte eigentlich auf allen AVR's laufen, ist ja nur 'lade register mit wert und zähl runter bis 0'. Und Assembler deswegen, weil das Timing ja ziemlich kritisch ist.
SprinterSB
13.02.2007, 14:04
Und Assembler deswegen, weil das Timing ja ziemlich kritisch ist.
Genau dafür gibt es Timer. Wenn (andere) IRQs aktiv sind kannst du mit ner Zählschleife eh nix anfangen...
vklaffehn
13.02.2007, 14:51
Hallo!
Die Timer sind leider schon anderweitig belegt, und die USI-Schnittstelle auch, sonst wäre das ganze ja viiiel zu einfach :-k Die Routine soll ja auch nur gelegentlich ein einzelnes Byte rüberschicken, da kann ich ja kurz meine IRQ's stoppen, das ist bei denen nicht ganz so kritisch. Ich werd jetzt mal ein wenig rumprogrammieren, meld mich bei bedarf wieder!
vklaffehn
14.02.2007, 14:58
Moin, schönen Valentinstag!
Es geht!!! Nach viel grübeln ist mir aufgefallen, daß ich, wenn ich keinen RS232-Treiber nehme, ich die Signale schon Softwaremäßig invertieren muß.... (Jaa, ich weiß, man sollte nicht direkt mit TTL an RS232, aber meine kleinen Picaxe machen das auch alle und es funktioniert) Die Routine funktioniert, selbst wenn ich den ATTiny26 mit dem internen PLL auf 16MHz takte (lt. Datenblatt S.32), sauber bei 115200 baud. Mit Treiber muß man halt wieder 'sbi' und 'cbi' vertauschen. Im Moment ist der Port,Pin und die Verzögerung (siehe AppNote AVR305) hardcoded, da werd ich noch dran feilen. Außerdem bin ich mir nicht sicher, ob das mit dem Inline-ASM-Operanden so richtig ist, auch wenn's funktioniert. Vielleicht kann da mal jemand drübergucken, der sich mit sowas auskennt.
Hier mal mein Code:
#include <avr/io.h>
#include <util/delay.h>
void init_uart()
{
DDRA |= (1<<0);
PORTA = 0;
}
void txd(uint8_t data)
{
uint8_t bitcnt;
uint8_t delay;
__asm__(
" ldi %A0,0x0a\n\t" //;1+8+sb (sb is # of stop bits)
" com %A1\n\t" //;Inverte everything
" sec\n\t" //;Start bit
"0: brcc 1f\n\t" //;If carry set
" sbi 0x1b,0\n\t" //;send a '0'
" rjmp 2f\n\t" //;else
"1: cbi 0x1b,0\n\t" //;send a '1'
" nop\n\t"
"2: rcall 3f\n\t" //;One bit delay
" rcall 3f\n\t"
" lsr %A1\n\t" //;Get next bit
" dec %A0\n\t" //;If not all bit sent
" brne 0b\n\t" //;send next
//;else
" ret\n\t" //; return
"3: ldi %C0,19\n\t"
"4: dec %C0\n\t"
" brne 4b\n\t"
" ret\n\t"
::"r"(bitcnt),"r"(data),"r"(delay));
}
int main(void)
{
uint8_t x,y;
init_uart();
{
for (y=0;y<124;y++)
{
for (x=0;x<128;x++)
{
txd(y);
//_delay_ms(4000);
}
}
}
return 0;
}
SprinterSB
14.02.2007, 15:31
Man sollte asm volatile (anstatt asm) nehmen, damit gcc nicht auf die Idee kommt, was wegzuoptimieren. Das inline asm hat ja keine output-Operanden, also aus gcc-Sicht keine Wirkung auf die Welt.
Sollte das %C0 nicht ein %A2 sein?
Zur Sicherheit sollte dem gcc mitgeteilt werden, daß sich Regsiterinhalte ändern. Momentan gibt es wohl keine Probleme, aber wenn du txd zum Beispiel als static deklarierst, kann gcc es inlinen und das asm verändert Register, ohne daß gcc was davon merkt. Das Problem könnte IMHO auch auftauchen ohne inlining, weil nur nichtinitialisierte Variablen als Input(!)operanden verwendet werden. Die Operanden sind hier also besser Output-Operanden.
vklaffehn
15.02.2007, 23:43
Hallo!!
Hier mal die Funktion, wie ich sie jetzt verwende :
void txd(uint8_t data)
{
uint8_t bitcnt;
uint8_t delay;
__asm__ __volatile__(
" cli\n\t"
" ldi %A0,0x0a\n\t" //;1+8+sb (sb is # of stop bits)
" com %A2\n\t" //;Inverte everything
" sec\n\t" //;Start bit
"0: brcc 1f\n\t" //;If carry set
" sbi 0x1b,6\n\t" //;send a '0'
" rjmp 2f\n\t" //;else
"1: cbi 0x1b,6\n\t" //;send a '1'
" nop\n\t"
"2: rcall 3f\n\t" //;One bit delay
" rcall 3f\n\t"
" lsr %A2\n\t" //;Get next bit
" dec %A0\n\t" //;If not all bit sent
" brne 0b\n\t" //;send next
" sei\n\t" //;else
" ret\n\t" //; return
"3: ldi %A1,19\n\t"
"4: dec %A1\n\t"
" brne 4b\n\t"
" ret\n\t"
:"=r"(bitcnt),"=r"(delay),"=r"(data));
}
Wenn ich die Operanden so übergebe, brauche ich dem Compiler nichts über veränderte Register sagen, der entscheidet ja eh selbst, welche er nimmt, richtig?
So, und das ganze läuft zur Zeit wunderbar zusammen mit meiner GBCAM, der T26 auf 16MHz, der ADC Prescaler auf 2 (!!), schalte ich die serielle Übertragung ab und lese nur die Kamera aus, dann komme ich auf ca. 10 fps, getestet mit einer LED, die nach jedem Bild kurz blinkt.
SprinterSB
16.02.2007, 13:21
data ist IN *und* out
RET ist nicht in Ordnung, weil du nicht wissen kannst, wie der Epilog der Funktion aussieht (hier besteht er wirklich nur aus einem RET, aber was, wenn es einen Framepointer gibt oder Register vom Stack gepoppt werden müssen?)
In deinem Code steht 0x1b, das ist DDRB !?. Treibst du den Ausgang als Open Collector? Falls nicht (push-pull), muss da PORTB hin.
#include <avr/io.h>
#include <avr/interrupt.h>
#define CR_TAB "\n\t"
#define STOP_BITS 1
void txd (uint8_t data)
{
uint8_t bitcnt = 1+8+STOP_BITS;
uint8_t delay = 19;
data = ~data;
cli();
__asm__ __volatile__(
" sec ; Start bit " CR_TAB
"0: brcc 1f ; If carry set " CR_TAB
" sbi %[port],%[pad] ; send a '0' " CR_TAB
" rjmp 2f ; else " CR_TAB
"1: cbi %[port],%[pad] ; send a '1' " CR_TAB
" nop" CR_TAB
"2: %~call bit_delay_%= ; One bit delay " CR_TAB
" %~call bit_delay_%=" CR_TAB
" lsr %[data] ; Get next bit " CR_TAB
" dec %[bitcnt] ; If not all bit sent " CR_TAB
" brne 0b ; send next " CR_TAB
" rjmp 5f ; else: done " CR_TAB
CR_TAB
"bit_delay_%=:" CR_TAB
" mov __zero_reg__, %[delay]" CR_TAB
"4: dec __zero_reg__" CR_TAB
" brne 4b" CR_TAB
" ret" CR_TAB
"5:" CR_TAB
: [bitcnt] "=r" (bitcnt), [data] "=r" (data)
: "1" (data), "0" (bitcnt), [delay] "r" (delay), [port] "M" (_SFR_IO_ADDR(PORTB)), [pad] "M" (6)
);
sei();
}
vklaffehn
16.02.2007, 13:33
Hallo!!
SUPER!! Danke, genau das, worauf ich hinauswollte, auch wenn 0x1b lt. Datenblatt PORTA ist ;-) Jetzt raff ich glaube ich so langsam, wie diese Operanden funktionieren, und das [port] "M" (_SFR_IO_ADDR(PORTB)) hatte ich schonmal gesehen, aber danach nicht mehr wiedergefunden!!
Nochmal vielen Dank für die Mühe! Jetzt kommt erstmal der nächste Teil, nämlich mein Fahrwerk mit Odometrie nachzurüsten. Da tauchen bestimmt noch weitere Fragen auf.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.