Hi!
Ist es irgendwie möglich in C eine kleine Routine zu schreiben die mir
an einem Pin ein paar Bytes über Uart senden kann? Das ganze sollte ganz
einfach ohne Timer funktionieren. D.h. es müssen Warteschleifen ran was
natürlich am besten in Assembler ginge. Aber es ist doch sicher möglich
sich kompilierte Programm anzugucken und daraus die Wartezeit bzw. die
Anzahl der zu wartenden Takte zu bestimmen und dann mit den Funktionen
_delay_loop_1 und 2 umzusetzen.
Ist das möglich, oder kann man nicht sicher stellen dass der gcc den
Code immer gleich umsetzt, gleich wie andere Programmteile des AVRs
aussehen oder in welchem Programmteil die Sendefuktion gerade ausgeführt
wird.
9600 Baud oder so reichen übrigens locker.
lg PoWl
Die Daten der Shift-Variable müssen ja nun am TX-Pin ausgegeben werden.
Wie shifte ich die das zu sendende Byte am besten und gebe das erste
jeweils am TX-Pin aus?
In einer for-Schleife shiften und dann per if(bit_is_set(...)) ... das
Bit am Port entweder setzen oder nicht, kann man das nicht auch
irgendwie kopieren? Aus asm-Zeiten fällt mir da das Transfer-Bit ein.
lg PoWl
Du willst also ein Software-UART machen und dabei ohne Timer-Interrupts
arbeiten, d.h. deine Bitzeiten per Wartezyklen in Software machen, wenn
ich dich richtig verstehe.
Man könnte dazu die Application Notes von Atmel befragen. Aber ich
vermute, dass dort der Timer benutzt wird.
Hier eine Idee, wie es gehen könnte:
1
// Status: Hingeschrieben, nicht simuliert, nicht auf Hardware testet
2
3
// #define F_CPU 8000000L
4
#include<avr/io.h>
5
#include<util/delay.h>
6
7
// #1
8
#define BAUD 9600
9
#define SOFTUART_DDR DDRD
10
#define SOFTUART_PORT PORTD
11
#define SOFTUART_TX 0 // Pin 0
12
#define SOFTUART_RX 1 // Pin 1
13
14
//
15
// #3
16
// Zeitdauer für ein Bit bei 8N1, Baudrate BAUD
17
// (1s) / (9600 Bit/s) = 104.17 µs
18
//
19
#define SOFTUART_DELAY() delay_us(104.17)
20
21
voidsoftuart_putc(unsignedchardata)
22
{
23
unsignedchari;
24
25
// #2
26
// 1 Startbit LOW
27
SOFTUART_PORT&=~(1<<SOFTUART_TX);
28
SOFTUART_DELAY();
29
30
// 8 Datenbits
31
for(i=0;i<8;i++)
32
{
33
if(data&1)
34
SOFTUART_PORT|=(1<<SOFTUART_TX);
35
else
36
SOFTUART_PORT&=~(1<<SOFTUART_TX);
37
SOFTUART_DELAY();
38
data=data>>1;
39
}
40
41
// 1 Stopbit HIGH
42
SOFTUART_PORT|=(1<<SOFTUART_TX);
43
SOFTUART_DELAY();
44
45
// Idle TTL HIGH
46
SOFTUART_PORT|=(1<<SOFTUART_TX);
47
}
48
49
intmain(void)
50
{
51
// #1
52
SOFTUART_DDR=(1<<SOFTUART_TX);// TX ist Ausgang, Rest Eingang
Hi, danke, den Code und die Links schau ich mir morgen an. Folgendes
habe ich nun rausgebracht. Mein PC kann das Zeug sogar empfangen, es
funktioniert?!
Leider sendet er jedes Byte doppelt?! Die Funktion wird ja pro Sekunde 2
mal aufgerufen und immer zweimal sendet er das gleiche (auf meinem
Oszilloskop auch zu beobachten). Ausserdem sendet er nur bis 127 bzw.
mein PC erkennt nicht mehr. Ob der wirklich 8 Bit sendet guck ich mir
morgen an aber es sieht schon danach aus.
1
#include<avr/io.h>
2
#include<stdint.h>
3
#include<util/delay.h>
4
5
#define UART_DDR DDRA
6
#define UART_PORT PORTA
7
#define UART_TX PA1
8
9
#define UART_Baud 9600
10
11
voidrs232_init(void)
12
{
13
UART_DDR|=(1<<UART_TX);// TX Pin als Ausgang schalten
Übrigens kriege ich da eine Warnung:
../Laminatorsteuerung.c:48: warning: 'counter' may be used uninitialized
in this function
damit ist unsigned char counter; gemeint. Wenn ich unsigned char counter
= 0; hinschreibe geht es, der hexcode wird etwas länger. Aber
funktionieren tut es genau gleich.
lg PoWl
>warning: 'counter' may be used uninitialized in this function
in C werden Variablen nicht mit 0 initialisiert, sondern einfach so
benutzt, wie sie im Speicher vorhanden sind.
Die Initialisierung muß der Programmierer übernehmen.
>Ausserdem sendet er nur bis 127 bzw. mein PC erkennt nicht mehr.
Das lässt irgendwie vermuten, dass du nur 7 Bits verschickst.
> Man könnte dazu die Application Notes von Atmel befragen. Aber ich> vermute, dass dort der Timer benutzt wird.
Es gibt mehrere, darunter auch eine, die außer dem I/O-Port keine
Peripherie braucht.
> in C werden Variablen nicht mit 0 initialisiert, sondern einfach so> benutzt, wie sie im Speicher vorhanden sind.
Das gilt aber nur für nicht-statische lokale Variablen.
Die Initialisierung muß der Programmierer übernehmen.
>> Ausserdem sendet er nur bis 127 bzw. mein PC erkennt nicht mehr.>> Das lässt irgendwie vermuten, dass du nur 7 Bits verschickst.
Er sendet alles um ein Bit verschoben, da:
ok, aber die for-schleife läuft doch 8 mal durch? X beginnt bei 0, dann
läuft die schleife durch, x wird hochgezählt. D.h. wenn x bei der
nächsten Prüfung der schleifenbedingung 1 ist ist auch die schleife
bereits einmal durchgelaufen. D.h. wenn x irgendwann 8 ist und die
schleifenbedingung die schleife beendet ist die schleife auch schon 8
mal durchgelaufen und es wurden 8 bit versendet, nicht?
Desweiteren kappier ich nicht wieso er die counter-variable nur alle
zwei 500ms hochzählt?!
Rolf Magnus wrote:
>> Man könnte dazu die Application Notes von Atmel befragen. Aber ich>> vermute, dass dort der Timer benutzt wird.>> Es gibt mehrere, darunter auch eine, die außer dem I/O-Port keine> Peripherie braucht.
Die ist aber wie zu erwarten in asm geschrieben wo die Dauer der Befehle
nicht vom Compiler abhängig ist, aber wenn gcc da immer den gleichen
code liefert kann man das ja durchaus auch in C machen. Zufälligerweise
funktioniert es ja auch so schon. Muss das ganze mal näher anschauen und
abstimmen.
>> Er sendet alles um ein Bit verschoben, da:>>
1
>if(bit_is_set(byte,1))
2
>
>> Das sollte besser heißen:>>
1
>if(bit_is_set(byte,0))
2
>
Thx, ist mir garnicht aufgefallen :-) Probier ich nachher. Jetzt darf
ich erstmal ne Englisch-Klausur schreiben -.-
lg PoWl
Ja. Da du allerdings nicht das unterste, sondern das zweite Bit von
unten abfragst, ist innerhalb des Bytes alles um 1 Bit nach rechts
verschoben. Dadurch kommt als oberstes Bit immer die 0, die
nachgeschoben wird, heraus. Außerdem wird dadurch für zwei
aufeinanderfolgende Bytes immer derselbe Wert gesendet, da das untere
Bit ja rausfällt. Das erklärt auch, warum du jeden Wert zweimal siehst.
Ja, richtig, ist mir eben auch gekommen. So ich hab das ganze jetzt doch
mal schnell geflasht. Funktioniert :-) Der PC empfängt alles richtig.
Heut Mittag beschäftige ich mich mal mit dem Timing, um näher ans
Optimum ranzukommen. Vll läuft das ganze dann noch mit dem internen
RC-Oszillator. Im Moment ist es erstmal wichtig damit ich einen
Tempsensor kalibrieren kann :-)
lg PoWl
Paul Hamacher wrote:
> Hi, danke, den Code und die Links schau ich mir morgen an.
Wenn das überhaupt noch nötig ist. Dann aber in meinem Posting noch
einen Fehler korrigieren. In deinem Code ist das bereits richtig drin.
1
// Idle TTL HIGH
2
SOFTUART_PORT|=(1<<SOFTUART_TX);
3
}
4
5
intmain(void)
6
{
7
// #1
8
SOFTUART_DDR=(1<<SOFTUART_TX);// TX ist Ausgang, Rest Eingang
So, das scheint ja nun schon zufällig ganz gut zu funktionieren. Gibt es
aber noch eine Möglichkeit das ganze etwas genauer abzustimmen?
Dazu wäre erstmal die Vorraussetzung sicherzustellen, dass der GCC den
Softuartcode immer gleich übersetzt und immer Register und nie
irgendwann einfach SRAM für die Operationen darin nutzt, denn das würde
ja viel länger dauern. Ansonsten wäre es ja notwendig das ganze direkt
über ASM einzubinden.
Nicht, dass der Code hier nun zwar funktioniert, aber sich ein
komplizierteres Programm dann auf einmal auf die kompilierung auswirkt.
lg PoWl
Paul nicht böse sein, aber IMHO willst du eine Garantie auf etwas, bei
dem dir keiner eine Garantie geben kann bzw. eine Garantie von GCC bzw.
der dortigen Lizenz explizit ausgeschlossen ist.
Jeder mit etwas Background im Programmieren wird dir sagen:
Selbstverständlich ist es möglich, dass eine Codeänderung oder eine
Weiterentwicklung der Toolchain zu Nebeneffekten führen kann.
Wenn du der Sache misstraust und es auf Leben und Tod darauf ankommt,
darfst du halt (GC)C nicht einsetzen.
Dann machst du den Code selber in ASM und hoffst, dass die Hardware
keinen Bug hat.
Oder du machst es mit einer Toolchain, bei der jemand den jeweiligen
Produktionscode auf Herz und Nieren prüft und/oder den Kopf hinhält,
wenn es schief geht.
Stefan "stefb" B. wrote:
> Paul nicht böse sein, aber IMHO willst du eine Garantie auf etwas, bei> dem dir keiner eine Garantie geben kann bzw. eine Garantie von GCC bzw.> der dortigen Lizenz explizit ausgeschlossen ist.
"Garantie", vor allem im Zusammenhang mit "Lizenz" sind viel zu harte
Worte für so ein kleines Anliegen eines noch relativ unerfahrenen
Hobbyprogrammierers der niemals im entferntesten an irgendeinen Anspruch
auf eine Garantie gedacht hat ;-)
> Jeder mit etwas Background im Programmieren wird dir sagen:>> Selbstverständlich ist es möglich, dass eine Codeänderung oder eine> Weiterentwicklung der Toolchain zu Nebeneffekten führen kann.
Na also, ich wollte nur freundlich fragen und das ist nun die passende
Antwort darauf :-)
> Wenn du der Sache misstraust und es auf Leben und Tod darauf ankommt,> darfst du halt (GC)C nicht einsetzen.
Keine Angst, es gibt keine Toten oder Verletzten.
> Dann machst du den Code selber in ASM und hoffst, dass die Hardware> keinen Bug hat.
Wird wohl dann eventuell notwendig sein.
> Oder du machst es mit einer Toolchain, bei der jemand den jeweiligen> Produktionscode auf Herz und Nieren prüft und/oder den Kopf hinhält,> wenn es schief geht.
Wie gesagt befinden wir uns im absolut nicht-kommerziellen Bereich also
besteht dafür auch keine Notwendigkeit, dass mir jemand dafür den Kopf
hinhält ;-)
Danke für die Infos!
Neue Frage: Ich möchte den ADC auslesen. Wenn ich das über die
ADC-Register mache
1
rs232_send_byte(ADCH);
2
rs232_send_byte(ADCL);
krig ich immernur 00000011 11111111 gesendet?
Wenn ich es folgendermaßen auslese funktionierts
> Wenn ich das über die ADC-Register mache>> rs232_send_byte(ADCH);> rs232_send_byte(ADCL);>> krig ich immernur 00000011 11111111 gesendet?
Weil du falschrum liest => Datenblatt konsultieren.
Paul Hamacher wrote:
> Ist es irgendwie möglich in C eine kleine Routine zu schreiben die mir> an einem Pin ein paar Bytes über Uart senden kann? Das ganze sollte ganz> einfach ohne Timer funktionieren.
Woher kommt diese unnötige Angst vor Timern?
Wenn Du Delays benutzt, bist Du stark Compilereabhängig.
Außerdem werden Dir auch Interrupts das Timing versauen.
Mit einem Timer hast Du beide Nachteile nicht, wenn die Interrupts kurz
gehalten sind.
Am einfachsten gehts mit einem Timerinterupt:
http://www.mikrocontroller.net/attachment/32137/sw_tx.zip
Peter