Hallo,
ich hab hier einen Attiny85 den ich für Debuggingzwecke um einen
minimalen UART erweitern möchte.
-Es reicht völlig wenn er nur senden kann, Empfang wird nicht benötigt.
-Beide Timer sind schon in Benutzung, Timer 1 läuft mit CTC und entweder
OCR1A oder OCR1B wäre noch frei. Notfalls hätte der Teiler auch noch
etwas Spiel.
Ich möchte natürlich, dass es den Rest des Programms möglichst wenig
behindert.
Meine erste Idee war es mit der freien Leitung vom Timer das USI zu
takten. Da mein Problem ja eigentlich nicht sonderlich selten ist, hat
es mich überrascht, dass dazu kaum etwas zu finden war. Das meiste mit
Senden und Empfangen und gleich ne Nummer größer als ich gehofft hab...
Sofern das mit dem USI überhaupt sinnvoll machbar ist, sollte das Timing
softwareseitig kein Problem sein. Das wäre bei einem reinen software
UART ja nicht unbedingt gegeben.
Wenn deine Timer schon einen OF Interrupt auslösen, kannst du einen
davon 'huckepack' als seriellen Ausgang mitnutzen. Dazu brauchst du
eigentlich nur einen 1-byte Buffer, indem dein zu sendendes Zeichen
steht, einen Bitzähler und evtl. 2 Flags für 'neues Zeichen im Buffer'
und 'Senden aktiv'.
Peters Code nutze ich auch ztw.
Danke dir nochmals ;)
Manchmal muss ich das Bit invertieren wenn MAX232 dranhängt.
Ich habe mit dem atiny85 mal ein paar Test mit internen 8Mhz gemacht.
eventuell klappts bei dir ja auch.
Hoffe es ist kein Fehler bei C&C passiert...
1
#include<util/delay.h>
2
#define TX_PORT PORTB
3
#define TX_PIN PB0
4
#define TX_INVERT // without MAX232
5
#define BAUD 9600UL
6
//Bitbanging
7
#define BB
8
9
// Das geht mit BB bei 8 Mhz
10
//#define BAUD 200UL
11
//#define BAUD 300UL
12
//#define BAUD 1200UL
13
#define BAUD 9600UL
14
//#define BAUD 4800UL
15
//#define BAUD 9600UL
16
//#define BAUD 19200UL
17
//#define BAUD 28800UL
18
//#define BAUD 38400UL
19
//#define BAUD 57600UL
20
21
// Ab hier nur mit Timer bei 8Mhz
22
//#define BAUD 76800UL
23
//#define BAUD 115200UL
24
25
// Ab hier nur mit externem Quartz
26
//#define BAUD 230400UL
27
//#define BAUD 256000UL
28
//#define BAUD 921600UL //C³ BT Modul
29
30
31
voidtx(uint8_tc)
32
{
33
#ifdef BB
34
c=~c;
35
#ifndef TX_INVERT
36
TX_PORT&=~(1<<TX_PIN);// start bit
37
#else
38
TX_PORT|=(1<<TX_PIN);
39
#endif
40
for(uint8_ti=10;i;i--){// 10 bits
41
_delay_us(1e6/BAUD);
42
#ifndef TX_INVERT
43
if(c&1)
44
TX_PORT&=~(1<<TX_PIN);// data bit 0
45
else
46
TX_PORT|=(1<<TX_PIN);// data bit 1 or stop bit
47
#else
48
if(c&1)
49
TX_PORT|=(1<<TX_PIN);// data bit 0
50
else
51
TX_PORT&=~(1<<TX_PIN);// data bit 1 or stop bit
52
c>>=1;
53
#endif
54
}
55
#else
56
uint8_ti=tx_in;
57
58
ROLLOVER(i,TX0_SIZE);
59
tx_buff[tx_in]=~c;// complement for stop bit after data
60
while(i==(*(volatileuint8_t*)&(tx_out)));// until at least one byte free
eric schrieb:> -Es reicht völlig wenn er nur senden kann, Empfang wird nicht benötigt.> -Beide Timer sind schon in Benutzung, Timer 1 läuft mit CTC und entweder> OCR1A oder OCR1B wäre noch frei. Notfalls hätte der Teiler auch noch> etwas Spiel.>> Ich möchte natürlich, dass es den Rest des Programms möglichst wenig> behindert.> Meine erste Idee war es mit der freien Leitung vom Timer das USI zu> takten.
Was für eine "freie Leitung" brauchst du denn? Das USI kann doch direkt
von Timer0 getaktet werden. Nur völlig idiotische Timer-Nutzungsarten,
die einen manuellen Reload des Zählregister beinhalten, können das
verhindern...
> Sofern das mit dem USI überhaupt sinnvoll machbar ist, sollte das Timing> softwareseitig kein Problem sein. Das wäre bei einem reinen software> UART ja nicht unbedingt gegeben.
Wenn dein Timer0 mit einer "brauchbaren" Zyklusfrequenz läuft und du
dich obendrein für die Debugausgaben auf einen 64 Zeichen großen
Zeichensatz beschränkst (sinnvollerweise: ASCII32..96), dann hast du die
optimale, minimalinvasive Debugausgabe.
Format 6N1. Die Ausgabe eines Zeichens (inklusive Prüfung, ob
Schieberegister frei) kostet nur 8 MCU-Takte. Und die eigentliche
Ausgabe läuft dann komplett mittels Hardware, ohne weitere Eingriffe
durch die Software zu erfordern.
Besser geht's auf einem Tiny nicht (außer auf einem mit echter USART
natürlich...)
Peter: Als reine Verständnisfrage: der Code erzeugt 2 stop bits, oder?
c-hater schrieb:> Was für eine "freie Leitung" brauchst du denn? Das USI kann doch direkt> von Timer0 getaktet werden. Nur völlig idiotische Timer-Nutzungsarten,> die einen manuellen Reload des Zählregister beinhalten, können das> verhindern...
Damit meinte ich Timer/Counter0 Compare Match, oder geht das noch
'direkter'?
c-hater schrieb:> Format 6N1. Die Ausgabe eines Zeichens (inklusive Prüfung, ob> Schieberegister frei) kostet nur 8 MCU-Takte. Und die eigentliche> Ausgabe läuft dann komplett mittels Hardware, ohne weitere Eingriffe> durch die Software zu erfordern.>> Besser geht's auf einem Tiny nicht (außer auf einem mit echter USART> natürlich...)
Das klingt schon mal gut, mal gucken was mein Terminalprogramm bei 6N1
ausspuckt.
Es sollte doch eigentlich auch möglich sein das mit dem Counter Overflow
Interrupt auf 8 bit zu verlängern, aber 6 bit reichen.
eric schrieb:> Das klingt schon mal gut, mal gucken was mein Terminalprogramm bei 6N1> ausspuckt.
Sichtbar? Nur Müll natürlich. Man muß zu jedem empfangenen Zeichen 32
addieren, um wieder auf darstellbare ASCII-Zeichen zu kommen.
> Es sollte doch eigentlich auch möglich sein das mit dem Counter Overflow> Interrupt auf 8 bit zu verlängern
Häh? Das Problem ist einfach, daß das verfügbare Schiebereister der
Hardware einfach mal nur 8 Bit breit ist. Da man für einen UART-Betrieb
Start- und Stopbit braucht, bleiben nur 6 Bit im Register für den
Payload.
Die üblichen UART-Implementierungen für USI machen das anders, die
erzeugen das Startbit i.d.R. komplett mittels Software und für das
Stopbit nutzen sie entweder eine Eigenart des USI, die erstens nicht
dokumentiert ist und die zweitens z.B. bei invertierter Ausgabe direkt
für eine PC-UART auch nicht in dieser Form nutzbar ist oder sie erzeugen
auch das Stopbit in Software. Damit ergeben sich in beiden Fällen
Probleme beim Timing, die zwar natürlich lösbar sind, aber dem Ziel
einer möglichst unabhängigen Debug-Ausgabe zuwieder laufen.
eric schrieb:> ich hab hier einen Attiny85 den ich für Debuggingzwecke um einen> minimalen UART erweitern möchte.
Such mal nach tinydebugserial.
LG, Sebastian
c-hater schrieb:> Häh? Das Problem ist einfach, daß das verfügbare Schiebereister der> Hardware einfach mal nur 8 Bit breit ist. Da man für einen UART-Betrieb> Start- und Stopbit braucht, bleiben nur 6 Bit im Register für den> Payload.
Er löst ja (wenn aktiviert) alle 8 bit einen Interrupt aus. Ob das
reicht um die restlichen bits nahtlos anzufügen ist natürlich die andere
frage.
Aber 6N1 reicht mir wie gesagt in dem fall, Und da ich sowieso fast
ausschließlich Zahlen ausgebe, ist das mit dem Zeichensatz auch kein
Ding.
Hallo,
hier ist so ein UART, das ich mal gebastelt habe. Es läuft über den
timer (war, glaube ich, für AT90USB647 geschrieben, aber könnte beim
Anpassen auch bei ATtinys funktionieren)...
MfG
Petr
1
/*
2
* (c) 2013 Petr Tomasek <petr.tomasek@evangnet.cz>
3
4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation, either version 3 of the License, or
7
(at your option) any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
*/
18
19
#include<avr/pgmspace.h>
20
#include<avr/interrupt.h>
21
#include<avr/sleep.h>
22
23
#include"utils.h"
24
25
volatileunsignedchardbg_serial_buffer[256];
26
volatileunsignedchardbg_serial_start;// interrupt reads from here...
27
volatileunsignedchardbg_serial_end;// user writes here...
Ich hab mich mal an dem USI versucht, leider ohne wirklichen Erfolg.
Die Dokumentation zum USI ist stellenweise leider ungenau oder
unvollständig, sodass einiges nur durch Rumprobieren rauszufinden war.
Mit dem angefügten Code klappt es immerhin schon mal, den Pin konstant
auf High zu halten...
Sobald ich versuche etwas anderes zu senden, kommt zwar etwas an, aber
er scheint Probleme zu haben Anfang und Ende richtig zu erkennen. Das
delay in der ISR sollte jetzt sicherstellen, dass auch für das Stopbit
das Timing eingehalten wird, hat aber nicht den gewünschten Erfolg und
ist hässlich.
F_CPU ist 1MHz und der Code von Peter funktioniert soweit. Ein
Oszilloskop um mir das Signal mal genauer anzugucken hab ich leider
nicht...
USI ist jedenfalls ein ziemlicher Krampf, da bin ich vermutlich besser
dran im Timer Interrupt selber am Pin zu wackeln...
Ich häng hier einfach mal meine kleine bit banging Variante dran. Das
ist im Grunde das Gleiche wie von Peter oben, nur etwas mehr als Library
aufgemacht.
header:
Peter Dannegger schrieb:> eric schrieb:>> USI ist jedenfalls ein ziemlicher Krampf
Das ja.
> Ja, was sich Atmel dabei gedacht hat, ist mir ein Rätsel. Es ist> wirklich zu nichts nütze.
Das ist allerdings Unsinn. Ja klar, ein Stück Hardware, was wirklich das
Akronym "USI" verdient, hätte schon gern ein ganzes Stück universeller
sein dürfen als das USI tatsächlich ist, soweit stimme ich dir da
vollkommen zu.
Das heißt aber andererseits nun auch ganz sicher nicht, daß das USI
wirklich zu garnix nützlichem zu gebrauchen wäre. Das ist Quatsch.
Insbesondere ist es Quatsch im Bezug auf das konkrete Thema dieses
Threads.
Man kann damit eine "minimalinvasive" Debugausgabe realisieren, die
praktisch unabhängig von jeglichem Timing der eigentlichen Anwendung ist
(und auch umgekehrt die Anwendung nur minimal tangiert). Das kostet USI
und leider auch zumindest Restriktionen für die Benutzung von Timer0
durch die Anwendung. Aber das war ja von vornherein klar.
Ergänzend: Ich war in meinen Postings in diesem Thread bisher davon
ausgegangen, daß die Timing-Unabhängigkeit der Ausgabe nur mit 6N1
möglich wäre.
Inzwischen habe ich mir die Sache nochmal genauer angeschaut und
festgestellt, daß auch 7N1 rein in Hardware geht. Das macht die
Debug-Ausgabe auf diesem Weg praktisch vollwertig (weil dann mit jedem
normalen Terminalprogramm nutzbar). Ein weiterer Vorteil bei der
7N1-Betriebsart ist, daß die eigentliche Debug-Ausgabe hier nicht 8,
sondern nur 6 MCU-Takte kostet, der Einfluß der Debug-ausgabe auf die
Anwendung als noch geringer ist.
Der Nachteil: zusätzlich zu dem naturgemäß immer benutzten DO fällt dann
auch noch der mit DI verknispelte µC-Pin für die Benutzung durch die
eigentliche Anwendung aus, was insbesondere bei den Tinys mit ganz
wenigen Pins durchaus eine herbe Einschränkung sein kann.
Naja, und ganz zuletzt ist mir auch noch aufgefallen, daß auch 8N1 rein
in Hardware möglich wäre, das allerdings nur, wenn Timer0 wirklich
exklusiv für die Debugausgabe genutzt wird (6N1 und 7N1 lassen vielfach
eine Mitnutzung durch die Anwendung zu, die wäre zwar auch bei 8N1 nicht
völlig ausgeschlossen, würde aber das Timing der Anwendung beeinflussen,
weil an TCNT geschraubt werden muß, um das Startbit jederzeit passend
generieren zu können)
Für einen 1 Mhz Attiny hatte ich auch mal das Problem mit der
Debuggausgabe und hab dann Peters Idee aufgegriffen und bin erfolgreich
bis 38400 Baud gekommen.
Der komplette Code ist auf Github
https://github.com/ArminJo/AttinySendSerial_1Mhz_38400Bd
1
/*
2
* Only multiple of 4 cycles are possible. Last loop is only 3 cycles.
>> USI ist jedenfalls ein ziemlicher Krampf> Ja, was sich Atmel dabei gedacht hat, ist mir ein Rätsel. Es ist> wirklich zu nichts nütze.
Ich habe damit mal eine I2C Slave implementiert und dabei soviel Frust,
dass ich beinahe auf eine reine Softwarelösung umgestiegen wäre.
#warning SOFTSERIAL_BITRATE is to high for this CPU clock frequency
18
#endif
19
20
// Initialize the serial port
21
voidinitSerialConsole(void)
22
{
23
SOFTSERIAL_TXD_INIT;
24
SOFTSERIAL_TXD_HIGH;
25
// Bind stdout to the serial port
26
stdout=&serialPort;
27
}
28
29
30
// Write a character to the serial port
31
staticintserial_write(charc,FILE*f)
32
{
33
cli();
34
// Send start bit
35
SOFTSERIAL_TXD_LOW;
36
_delay_us(US_PER_BIT);
37
// Send 8 data bits
38
for(uint8_ti=0;i<8;i++)
39
{
40
if(c&1)
41
{
42
SOFTSERIAL_TXD_HIGH;
43
}
44
else
45
{
46
SOFTSERIAL_TXD_LOW;
47
}
48
_delay_us(US_PER_BIT);
49
c=c>>1;
50
}
51
SOFTSERIAL_TXD_HIGH;
52
sei();
53
_delay_us(2*US_PER_BIT);
54
return0;
55
}
Du musst zuerst initSerialConsole() aufrufen. Danach kannst du mit den
Funktionen der Standard C Library Texte senden, zum Beispiel puts() und
printf().
@Stefan
Sieht man ja im Code danach in meinen Kommentaren.
Hatte ich mal mit rumprobiert.
Aber jeder Tiny ohne Quarz war halt ab 57600 anders bzw. "fehlerhafter"
Is halt die Frage ob ich für Debugging so schnell sein muss, wenns mal
nen Variable is.