\documentclass[11pt,a4paper]{article} % 2006-07-07 EW Adventures-1.tex % 2006-07-22 EW Adventures-2.tex % 2006-08-14 EW Adventures-2a.tex % 2007-01-10 EW Adventures-3.tex % 2007-01-13 EW Adventures-4.tex % 2008-11-14 EW Adventures-5.tex % 2011-01-07 EW Adventures-6.tex: rs485+mpc % % Aufgebessert nachdem ich 'ne Reihe Optimierungen gemacht hatte % Und dann nochmal komplett umgeräumt! % % language support \usepackage[german]{babel} %\usepackage{german} %\usepackage[latin1]{inputenc} % can use Umlauts now "u instead of "u %\usepackage{lmodern} % better fonts %\usepackage{pslatex} % use native PostScript fonts \usepackage{cm-super} \usepackage{url} % \url{} \path{} with correct "hyphenation" %\usepackage{fancyvrb} % for code examples % \voffset-10mm % \pagestyle{empty} % \pagestyle{plain} % \pagestyle{headings} % \renewcommand{\baselinestretch}{1.08} %\usepackage{xspace} \parindent=0pt \newcommand{\amforth}{\texttt{amforth}} \newcommand{\zB}{z.\,B.\ } \begin{document} %\title{Adventures in Forth 6: rs485 half duplex, mpc und andere, seltsame Tiere} \title{amforth: rs485 half duplex, mpc und andere, seltsame Tiere} \ifx\shorttitle\undefined\else \shorttitle{Adventures in Forth 6: amforth} \fi \author{Erich W"alde} \maketitle %\begin{multicols}{2} % -------------------------------------------------------------------- %\section{Intro} %\label{sec:intro} \begin{abstract} Auf dem Linuxtag 2010 habe ich (am Stand der Forth--Gesellschaft) ein paar Platinen mit atmega--32--Kontrollern gezeigt, die zusammen diverse Daten erfassten: Temperatur, Luftfeuchte, Luftdruck und den Stromverbrauch am Stand. Jeder Kontroller fragt selbstst"andig \zB alle 10 Sekunden die Werte der angeschlossenen Sensoren ab. Die Werte werden in physikalische Einheiten umgerechnet und dann so lange gesammelt (genauer aufaddiert), bis \textit{jemand} die Daten abholt. Dann wird der Mittelwert gebildet, und zusammen mit Minimum, Maximum und der Anzahl der Messwerte ausgegeben. "Ahnlich wird mit den aufgesammelten Z"ahlwerten des Stromverbrauchs verfahren. Das wirft nat"urlich Fragen auf: Wer ist dieser \textit{jemand}, der \textit{Einsammler}, wie kommuniziert der mit dem Kontroller? H"angen alle Kontroller an einem Kabel? Und wenn ja, wie geschieht es, dass nicht alle Kontroller wild durcheinander quasseln? Dieser Artikel zeigt, wie man Ordnung auf das Verbindungskabel bringt. Mein Dank gilt Matthias Trute, Lubo\v{s} P\v{e}kn\'{y}, Bernd Paysan, Michael Kalus, Martin Bitter und sicher noch anderen f"ur ihre Hilfe. \end{abstract} \begin{multicols}{2} % -------------------------------------------------------------------- \section{Assembler oder Forth?} \label{sec:asm-o-forth} %% Welche W"orter m"ussen ge"andert werden?? %% \begin{itemize} %% \item asm: rs485\_rw\_port, pin (constants) %% \item asm: tx (rw pin bedienen) %% \item asm: tx-complete-isr %% \item +emit, -emit (' drop|rs485.tx is emit) %% \item mpc (value StationID, variable mpc\_ID) %% \item +mpc7, -mpc7 %% \item asm: rx (bin ich in mpc? bin ich gemeint?) %% \item \~{}end, \~{}id, \~{}call %% \item asm: stationID in prompt %% \item init-turnkey (rw pin output, low; -emit; stationID mpc\_ID ! +mpc7) %% \item run-turnkey (init-turnkey, run starttasker) %% \end{itemize} %% } \textit{Afterwards everything is \textbf{obvious}.} Auf die Frage von Bernd, warum denn das alles in Assembler und nicht in Forth geschrieben sei, wusste ich zun"achst keine Antwort. Es gibt folgende Stellen, die zu \amforth{} geh"oren, in Assembler geschrieben sind, und die sich nicht so ganz einfach verbiegen lassen: \begin{itemize} \item \verb+tx+ muss den Lesen/Schreiben--Pin bedienen, w"ahrend es den Ausgabepuffer abarbeitet. \item Die Adresse dieses Pins wollte ich konfigurierbar haben. Au"serdem wird diese Information in den Assembler--Worten \verb+tx+ und \verb+txc-isr+ benutzt. \item (\verb+txc-isr+) am Ende einer "Ubertragung, die asynchron abgearbeitet wird, muss dieser Pin wieder auf Lesen zur"uckgesetzt werden. Der usart--transmit--complete interrupt bietet sich daf"ur an. Das Bit zu l"oschen, war in Assembler viel zu einfach. \item in \verb+rx+ wird die Stationadresse ben"otigt, wenn sich der Kontroller im mpc--Modus befindet. Die muss also unverlierbar und von Assembler aus zug"anglich sein. \item Die Prompts sind nicht \textit{deferred}, d.h.\ auch an dieser Stelle ist es einfacher, den Assembler--Code zu "andern. \end{itemize} Das Initialisieren des Lesen/Schreiben--Pins, das gute Benehmen am Bus (schweigsam soll der Kontroller sein und nur nach Aufforderung sprechen) und weitere Worte w"aren direkt in Forth schneller machbar. Teilweise habe ich sogar \verb+g4+ von Michael Kalus benutzt, um Assembler zu erzeugen --- teilweise dann aber mit manueller Nacharbeit verziert. Ich wollte ein System, welches sich am Bus sofort korrekt benimmt. Es kann auch "uber den Bus in Forth programmiert werden. Das funktioniert allerdings nur, wenn die Bedienung des Lesen/Schreiben--Pins schon direkt nach dem Flashen l"uckenlos klappt. Und ich habe ganz nebenbei eine Menge "uber das Innenleben von \amforth{} gelernt. % -------------------------------------------------------------------- \section{RS485: ein Bus f"ur viele Zwecke} \label{sec:RS485} Der Einsammler ist ein (perl--)Programm auf einem (Linux--)Rechner, welches "uber einen Bus, den RS485--Bus [\ref{RS485}], mit den Kontrollern redet. Das Signal wird differentiell "uber ein Adernpaar "ubertragen. Um ein Bit zu "ubertragen, wird die Spannungsdifferenz zwischen den Adern A und B entweder positiv (0) oder negativ (1). Die Anordnung der Bits und der zeitliche Ablauf sind gleich, wie bei der normalen seriellen Verbindung (RS232). Will man gleichzeitig Daten lesen und schreiben, dann braucht man zwei Adernpaare, entsprechende Bausteine zur Anschaltung und man redet von einer \textit{full duplex} Konfiguration. Es geht aber auch mit einem Adernpaar. Dann kann ein Busteilnehmer zu einer bestimmten Zeit nur lesen oder schreiben, nicht beides. Diese Konfiguration hei"st \textit{half duplex} und ben"otigt eine Steuerung, die "uber Lesen oder Schreiben entscheidet. Viel mehr ist f"ur RS485 nicht definiert. Insbesondere ist kein Protokoll definiert, welches zum Einsatz kommen muss. Der Bus kann betr"achtliche L"angen "uberbr"ucken, bis mindestens 1200\,m bei 100 kBit/s. Man kann damit also bequem, falls vorhanden, die Hacienda verkabeln und muss nicht t"aglich zum Briefkasten reiten. Um die Anbindung in \amforth\ zu machen, sind folgende Aufgaben zu l"osen: \begin{itemize} \item Die Unterst"utzung f"ur RS485 soll beim Assemblieren wahlweise eingebunden werden k"onnen (WANT\_RS485) \item Der RS485 W/R Pin soll konfiguierbar sein (\zB PortD.7) \item applturnkey muss den W/R Pin initialisieren (output, low) \item \texttt{tx} muss den Pin vor dem Senden auf \textit{Schreiben} setzen \item Nach der "Ubertragung muss der Pin wieder auf \textit{Lesen} gesetzt werden (Usart Transmit Complete Interrupt) \end{itemize} % -------------------------------------------------------------------- \section{RS485: Ansteuerung einbauen} \subsection{main.asm: WANT\_RS485} Um alle Quelltextstellen, die der Unterst"utzung des RS485 half duplex Modus dienen, selektiv ein-- oder auszuschalten, wird in der Datei \path{main.asm} eine Assembler--Variable definiert: {\small \begin{verbatim} .set WANT_RS485 = 1 ; RS485 half duplex \end{verbatim} } Im Assembler--Quelltext finden sich dann Bl"ocke der Bauart: {\small \begin{verbatim} .if WANT_RS485 == 1 ... .else ... .endif \end{verbatim} } \subsection{main.asm: den W/R Pin konfigurieren} Es war mir wichtig, dass der Pin, welcher den Lesen/Schreiben--Zustand am Transceiver einstellt, beim Erstellen von \amforth\ konfiguriert werden kann. Zwei weitere Assembler--Variablen werden daf"ur ben"otigt: {\small \begin{verbatim} ; file: main.asm ... ; rs485_rw_pin: D.7==W/R, low==read .if WANT_RS485 == 1 ; PORTx: use io location, not mem mapped .set RS485_RW_PORT = PORTD .set RS485_RW_PIN = 7 .endif \end{verbatim} } \texttt{PORTD} wird in den Tiefen der von device.asm eingeschlossenen Dateien auf den Wert \texttt{\$12} gesetzt. Um diese Definitionen aus \amforth\ heraus benutzen zu k"onnen, wurden sie entsprechend in \texttt{constants} verfrachtet: {\small \begin{verbatim} ; file: words/rs485.asm ; asm(RS485_RW_PORT) ; PORTD constant rs485_rw_port VE_RS485_RW_PORT: .dw $FF0D .db "rs485_rw_port",0 .dw VE_HEAD .set VE_HEAD = VE_RS485_RW_PORT XT_RS485_RW_PORT: .dw PFA_DOVARIABLE PFA_RS485_RW_PORT: ; use mem mapped location! .dw (RS485_RW_PORT+$20) ; asm(RS485_RW_PIN) ; $07 constant rs485_rw_pin VE_RS485_RW_PIN: .dw $FF0C .db "rs485_rw_pin" .dw VE_HEAD .set VE_HEAD = VE_RS485_RW_PIN XT_RS485_RW_PIN: .dw PFA_DOVARIABLE PFA_RS485_RW_PIN: .dw RS485_RW_PIN \end{verbatim} } Das dient nur der Bequemlichkeit. \subsection{applturnkey.asm: den W/R Pin initialisieren} Der RS485 W/R Pin soll beim Starten von \amforth\ als Ausgang geschaltet werden und auf null gestellt (nicht auf eins). Dazu wird ein entsprechendes Wort in Assembler definiert: {\small \begin{verbatim} ; file: words/rs485-init.asm ; : rs485.init ( -- ) ; rs485_rw_port rs485_rw_pin portpin: rs485_rw ; rs485_rw low ; rs485_rw pin_output ; ; VE_RS485_INIT: .dw $ff0a .db "rs485.init" .dw VE_HEAD .set VE_HEAD = VE_RS485_INIT XT_RS485_INIT: .dw PFA_RS485_INIT PFA_RS485_INIT: ; set DDR bit (pin_output) sbi (RS485_RW_PORT-1),RS485_RW_PIN ; clear PORT bit (receive) cbi RS485_RW_PORT,RS485_RW_PIN rjmp DO_NEXT \end{verbatim} } Das Wort \texttt{rs485.init} wird in \texttt{applturnkey} aufgerufen: {\small \begin{verbatim} ; file: words/applturnkey.asm ... .if WANT_RS485 == 1 .dw XT_RS485_INIT ; rs485.init .endif \end{verbatim} } \subsection{usart-isr-tx.asm: Schreibpin bedienen} Bis jetzt sind das alles Vorarbeiten. Der W/R Pin muss in der Funktion \texttt{tx} bedient werden. \texttt{tx} versendet ein Byte "uber die USART, die serielle Schnittstelle. Vorher muss der W/R Pin auf Schreiben (1) gesetzt werden: Ich habe auf meinen Kontrollern das Schreiben auf die serielle Schnittstelle mittels Interrupt--Service--Routine eingeschaltet (\verb@.set WANT_ISR_TX = 1@), so dass die hier gezeigten "Anderungen eben in der Interrupt--Service--Routine gemacht werden. Das ist reine Geschmacksache. {\small \begin{verbatim} ; file: drivers/usart-isr-tx.asm ... usart_udre_isr: ... usart_udre_next: .if WANT_RS485 == 1 sbi RS485_RW_PORT,RS485_RW_PIN .endif inc xh andi xh,usart_tx_mask sts usart_tx_out,xh ldi zl,low(usart_tx_data) ldi zh,high(usart_tx_data) add zl,xh adc zh,zeroh usart_udre_send: ld xl,z sts USART_DATA,xl ... \end{verbatim} } Die Funktion \texttt{usart\_udre\_isr} schreibt das n"achste zu versendende Byte in das Datenregister der seriellen Schnittstelle, r"aumt auf und kehrt dann zur"uck --- \textbf{ohne} das Verschicken des Bytes \textbf{abzuwarten}. Die Frage ist also, wer wann den W/R Pin zur"uck auf Lesen setzt. Gl"ucklicherweise gibt es daf"ur einen Interrupt, den \textit{usart transfer complete interrupt}. Den aktivieren wir und bringen der zugeh"origen Servicefunktion bei, den W/R Pin auf null zu setzen: {\small \begin{verbatim} ; file: drivers/usart-isr-tx.asm ... .if WANT_RS485 == 1 .set pc_ = pc .org UTXCaddr jmp_ usart_utx_isr .org pc_ ; : txc-isr ( -- ) ; rs485_rw low ; ; ; $1E constant USART_TXCAddr ; ' txc-isr USART_TXCAddr int! ; serial transfer complete interrupt ; clear rs485_rw_pin usart_utx_isr: cbi RS485_RW_PORT,RS485_RW_PIN reti .endif \end{verbatim} } Um den Interrupt zu aktivieren, setzen wir in der Datei \path{main.asm} ein zus"atzliches Bit {\small \begin{verbatim} .set USART_B_VALUE = ... | (1< |\\ zu bgr"u"sen. Mein erster Versuch, \texttt{ver} aus \texttt{applturnkey} zu eliminieren, war nat"urlich zu kurz gedacht: der Prompt kommt immer noch. Schade. Nun w"ar's denkbar, die Prompt--Worte entsprechend umzubiegen. Stellt sich aber raus, dass die nicht \textit{deferred} sind. Die drei Prompt--Worte zu vektorisieren ist nat"urlich m"oglich. Allerdings muss man sich dann die Adressen der urspr"unglichen Worte merken, das im \texttt{user}--Bereich ablegen und immer sch"on buchf"uhren. Die "Anderungen betreffen mehrere Stellen im \amforth\ Quelltext. Es geht aber auch einfacher. Der Prompt muss ja schlie"slich auch ausgegeben werden. Das bewerkstelligt am Ende die Funktion \texttt{emit}. Die ist schon \textit{deferred} definiert und kann einfach umgebogen werden: {\small \begin{verbatim} : -emit ['] drop is emit ; : +emit ['] tx is emit ; \end{verbatim} } Das ist etwas simplistisch gedacht, denn \texttt{emit} k"onnte ja auch auf etwas anderes als \texttt{tx} zeigen. Will man diesen Fall korrekt behandeln, dann etwa so: {\small \begin{verbatim} variable tmpemit : -emit ['] emit defer@ tmpemit ! ['] drop is emit ; : +emit tmpemit @ is emit ; \end{verbatim} } Ich habe mich hier f"ur den simplen Ansatz entschieden. Um \texttt{-emit} schon in \texttt{applturnkey} zur Verf"ugung zu haben, habe ich die beiden Worte mit Hilfe von Michael Kalus' Prorgamm \texttt{g4.fs} in Assembler umgewandelt und etwas von Hand editiert. Die Datei wird dann via \texttt{dict\_appl.inc} geladen. {\small \begin{verbatim} ; words/emit-on-off.asm ; : +emit ['] tx is emit ; VE_EMIT_ON: .dw $FF05 .db "+emit",0 .dw VE_HEAD .set VE_HEAD = VE_EMIT_ON XT_EMIT_ON: .dw DO_COLON PFA_EMIT_ON: .dw XT_DOLITERAL .dw XT_TX .dw XT_DOLITERAL .dw XT_EMIT .dw XT_DEFERSTORE .dw XT_EXIT ; : -emit ['] drop is emit ; VE_EMIT_OFF: .dw $FF05 .db "-emit",0 .dw VE_HEAD .set VE_HEAD = VE_EMIT_OFF XT_EMIT_OFF: .dw DO_COLON PFA_EMIT_OFF: .dw XT_DOLITERAL .dw XT_DROP .dw XT_DOLITERAL .dw XT_EMIT .dw XT_DEFERSTORE .dw XT_EXIT \end{verbatim} } Dann l"asst sich \texttt{applturnkey} entsprechend "andern: {\small \begin{verbatim} ; words/applturnkey.asm ; ( -- ) System ; R( -- ) ; application specific turnkey action VE_APPLTURNKEY: .dw $ff0b .db "applturnkey",0 .dw VE_HEAD .set VE_HEAD = VE_APPLTURNKEY XT_APPLTURNKEY: .dw DO_COLON PFA_APPLTURNKEY: .dw XT_INITUSER ; init-user .dw XT_USART ; +usart .dw XT_INTON ; +int .if WANT_RS485 == 1 .dw XT_RS485_INIT ; rs485.init .dw XT_EMIT_OFF ; -emit .endif .dw XT_VER ; ver .dw XT_EXIT ; : init-turnkey ; init-user ; +usart +int ; rs485.init -emit ; ; \end{verbatim} } Jetzt ist Funkstille, wenn man den Kontroller einschaltet. Allerdings muss man sich daran auch gew"ohnen, und erst mal blind \texttt{+emit} eingeben, bevor man beschlie"st, dass mehr Funkstille herrscht als gew"unscht. % -------------------------------------------------------------------- \section{Funkstille 2: mpc} Verbindet man den Rechner mit zwei oder mehr Kontrollern an einem RS485--Bus, dann m"ochte man auch gerne mit denen reden. Aber bitte einzeln und nicht mit allen gleichzeitig. Der bisherige Stand erzeugt hemmungslose echo--Schleifen, die nicht selten darin enden, dass mindestens einer der Kontroller richtig beleidigt ist und auch nach einem Reset gar nicht mehr reden will. Die Atmel--Kontroller sind f"ur diesen Fall vorbereitet. Es gibt eine Einrichtung mit dem Namen \textit{multi processor communication (mpc)}. Schaltet man mpc ein, dann werden alle vom USART empfangenen Bytes verworfen und keine Interrupts ausgel"ost, bis ein Byte empfangen wird, bei dem das h"ochste Bit gesetzt ist. Dieses Byte wird als Adressbyte behandelt. Die Interrupt--Service--Routine inspiziert dieses Byte und vergleicht es mit der gespeicherten Stationsadresse. Sind sie gleich, wird der mpc--Modus beendet und die Schnittstelle funktioniert ganz normal. An einem Bus mit mehreren Kontrollern (Stationen) werten alle Kontroller das Adressbyte aus. Aber nur der angesprochene beendet den mpc--Modus. Am Ende der Kommunikation muss er den mpc--Modus wieder aktivieren. Solange man nur ASCII Zeichen "ubertr"agt und keine bin"aren Daten, muss man keine weiteren Vorkehrungen treffen. Alle Messwerte, die die Stationen aufgesammelt haben, werden ganz normal als ASCII--Zeichenketten ausgegeben. Diese Zeichen haben das h"ochste Bit nicht gesetzt und animieren daher auch die Stationen nicht, dieses als Busadresse zu behandeln. Mit Umlauten oder bin"aren Daten geht das so nicht. Au"serdem hat das den Vorteil, dass man auf dem Bus einfach so mitlesen kann. Um den mpc--Modus zu nutzen, sind folgende Aufgaben zu l"osen: \begin{itemize} \item Die Unterst"utzung f"ur den mpc--Modus soll beim Assemblieren wahlweise eingebunden werden k"onnen (WANT\_MPC) \item Die Stationsadresse muss gesetzt werden (mpc\_ID) \item mpc--Modus einschalten (\texttt{+mpc}: \textit{7 Datenbits, kein Parit"atsbit, 2 Stopbits}) \item mpc--Modus beenden (\texttt{-mpc}: \textit{8 Datenbits, kein Parit"atsbit, 1 Stopbit}) \item Die Assemblerfunktion \texttt{usart\_rx\_isr} muss pr"ufen, ob der mpc--Modus an ist, und wenn ja, ob diese Station gemeint ist. \item Die Stationsadresse muss persistent gespeichert und beim Starten von \amforth\ wieder gesetzt werden % \$FE value stationID, \$60 to stationID, stationID mpc\_ID ! \item Im \texttt{amforth}--Programm braucht es ein paar Worte zur Bedienung, z.B \verb|~id|, \verb|~call|, \verb|~end| \end{itemize} % -------------------------------------------------------------------- \section{mpc--Modus einbauen} \subsection{main.asm: WANT\_MPC} Wie vorher wird eine Assembler--Variable definiert, die zum Aktivieren des entsprechenden Quelltexts benutzt wird: {\small \begin{verbatim} ; file: main.asm .set WANT_MPC = 1 ; mpc mode (7N2, 8N1) \end{verbatim} } \subsection{Stationsadresse in RAM} Die Stationsadresse wird zum Vergleich mit einer empfangenen Adresse gebraucht. Sie soll gleichzeitig als normale \amforth--Variable benutzbar sein. Daf"ur wird die Adresse der Speicherstelle gleichzeitig in der Assembler--Variablen \texttt{var\_mpcid} abgelegt. {\small \begin{verbatim} ; file: drivers/usart-isr-rx.asm ... .if WANT_MPC == 1 ; ( -- addr ) variable mpc_ID, RS485 ; module address VE_MPCID: .dw $ff06 .db "mpc_ID" .dw VE_HEAD .set VE_HEAD = VE_MPCID XT_MPCID: .dw PFA_DOVARIABLE PFA_MPCID: .dw here .set var_mpcid = here .set here = here + CELLSIZE .endif \end{verbatim} } \subsection{mpc--Modus einschalten} Um den mpc--Modus einzuschalten, muss das Bit \texttt{MPCM} im Register \texttt{UCSRA} gesetzt, sowie die serielle Schnittstelle auf 7 Datenbits, kein Parit"atsbit, 2 Stopbits konfiguriert werden (Register \texttt{UCSRC}). Dabei ist zu beachten, dass beim Schreiben von \texttt{UCSRC} das h"ochste Bit \texttt{URSEL} gesetzt werden muss, weil sonst das Register \texttt{UBRRH} beschrieben wird. An dieser Stelle habe ich l"anglich gegr"ubelt, was dem aufmerksamen Leser erspart werden kann. {\small \begin{verbatim} ; file: ewlib/mpc.fs : +mpc7 ( -- ) txc \ wait for tx complete UCSRA c@ $01 or UCSRA c! \ set MPCM $8C UCSRC c! \ 7N2 ; \end{verbatim} } \subsection{mpc--Modus beenden} Entsprechend wird zum Beenden des mpc--Modus das Bit \texttt{MPCM} gel"oscht und die serielle Schnittstelle auf 8 Datenbits, kein Parit"atsbit, 1 Stopbit gesetzt. {\small \begin{verbatim} ; file: ewlib/mpc.fs : -mpc7 ( -- ) UCSRA c@ $FE and UCSRA c! \ clear MPCM $86 UCSRC c! \ 8N1 ; \end{verbatim} } \subsection{Empfangenes Adressbyte auswerten} Zun"achst wird \texttt{variable mpc\_ID} in Assembler definiert. {\small \begin{verbatim} : file: drivers/usart-isr-rx.asm ... .if WANT_MPC == 1 ; ( -- addr ) variable mpc_ID, RS485 ; module address VE_MPCID: .dw $ff06 .db "mpc_ID" .dw VE_HEAD .set VE_HEAD = VE_MPCID XT_MPCID: .dw PFA_DOVARIABLE PFA_MPCID: .dw here .set var_mpcid = here .set here = here + CELLSIZE .endif \end{verbatim} } Dann wird die Interrupt--Service--Routine f"ur den Empfang von Daten auf der seriellen Schnittstelle aufgebessert. {\small \begin{verbatim} ; file: drivers/usart-isr-rx.asm ... . if WANT_MPC == 0 ; original version ... .else ; MPC version usart_rx_isr: push xl ; save registers in xl, SREG push xl push xh push zl push zh push temp0 in_ xh, UCSRA mov temp0, xh ; temp0 = UCSRA andi xh, (1< ver amforth 4.2 ATmega32 ok > ~id hex . 60 ok > $61 ~call unsichtbar: +emit ok > ~id hex . 61 ok > \end{verbatim} } %$ Bei meinem ersten Versuch mit RS485 und mpc hatte ich das Versenden des Prompts nicht unterbunden. Ich hatte nur festgestellt, dass auf dem Bus nach einem \verb|~end| ein paar krause Zeichen erscheinen (nicht druckbar). Inzwischen wei"s ich, dass das die Zeichen des ok-- und des ready--Prompts sind, wenn man sie durch \textit{7 Datenbits, kein Parit"atsbit, 2 Stopbits} verst"ummelt, d.\,h., das h"ochste Bit setzt. Das hat im Dateneinsammler zu Mehraufwand gef"uhrt, um ggf.\ unbrauchbare Zeichen zu lesen und zu filtern. Seit \texttt{-emit} ist Ruhe. Die Stationsadresse h"alt sieben signifikante Bits, man kann also Adressen von Null bis 127 (\$7F) vergeben. %$ % -------------------------------------------------------------------- \section{mpc: prompt} Nach einer Weile hat es mich gest"ort, dass ich immer erst \verb|~id .| eingeben muss, um zu sehen, auf welcher Station ich gerade bin. Und da ich sowieso schon einen Blick in die Prompts geworfen hatte, habe ich beschlossen, den ready--Prompt zu "andern. Ich wollte die Stationsnummer in hexadezimal sehen, etwa so: \verb|~61>|. Also erweitert man das Wort \verb|p_rd| sinngem"a"s um diese Zeilen: {\small \begin{verbatim} ... [char] ~ emit base @ $10 base ! mpc_ID @ 2 u0.r base ! ... \end{verbatim} } {\small \begin{verbatim} ; file: words/prompts.asm ... ; : p_rd ; [char] ~ emit ; base @ $10 base ! ; mpc_ID @ 2 u0.r ; base ! ; [char] > emit space ; ; ; send the READY prompt to the command line ;VE_PROMPTRDY: ; .dw $ff04 ; .db "p_rd" ; .dw VE_HEAD ; .set VE_HEAD = VE_PROMPTRDY XT_PROMPTRDY: .dw DO_COLON PFA_PROMPTRDY: .dw XT_CR .if WANT_MPC == 1 .dw XT_DOLITERAL .dw $7e ; '~' .dw XT_EMIT ; emit .dw XT_BASE ; base @ $10 base ! .dw XT_FETCH .dw XT_DOLITERAL .dw $10 .dw XT_BASE .dw XT_STORE .dw XT_MPCID ; mpc_ID @ 2 u0.r .dw XT_FETCH .dw XT_DOLITERAL .dw $2 .dw XT_UZERODOTR .dw XT_BASE ; base ! .dw XT_STORE .endif .dw XT_SLITERAL .dw 2 .db "> " .dw XT_ITYPE .dw XT_EXIT \end{verbatim} } %$ Die Unterhaltung mit den Stationen wird jetzt \textit{intuitiver}: {\small \begin{verbatim} ~60> ver amforth 4.2 ATmega32 ok ~60> ~id hex . 60 ok ~60> $62 ~call unsichtbar: +emit ok ~62> $61 ~call unsichtbar: +emit ok ~61> \end{verbatim} } So ist das Ganze recht nett geworden, jedenfalls nach meinen Ma"sst"aben f"ur nett. % -------------------------------------------------------------------- \section{Ausblick} Der Artikel ist mehr eine "Ubung in Assembler als in Forth geworden. Aber ich habe dazugelernt. Die eigentliche Aufgabe der Stationen (Messdatenerfassung) ist hier nicht weiter gezeigt. Ich benutze den Multitasker und baue zwei Tasks. Der eine bedient den prompt an der seriellen Schnittstelle. Der andere z"ahlt die Zeit, sammelt die Sensordaten ein und bereitet sie auf, bis der Dateneinsammler sie abholt. Der Dateneinsammler schickt den Befehl \verb+~data+, und der Kontroller versendet daraufhin seine gesammelten Erkenntnisse --- alles streng als Text, etwa so: {\small \begin{verbatim} > ~data __Q 41:0001 1:12,+25.50,+25.54,+25.56 C-- ok \end{verbatim} } % -------------------------------------------------------------------- \section{Referenzen} \begin{enumerate} %\item \label{Adv1} E. W"alde, Adventures in Forth, Die 4. Dimension 3/2006, Jahrgang 22 % gforth-ec r8c, i2c von Hand, Thermometer %\item \label{Adv2} E. W"alde, Adventures in Forth 2, Die 4. Dimension 4/2006, Jahrgang 22 % timeup Uhr und Kalender, twotask %\item \label{Adv3} E. W"alde, Adventures in Forth 3, Die 4. Dimension 1/2007, Jahrgang 23 % usart0, redirection %\item \label{Adv4} E. W"alde, Adventures in Forth 4, Die 4. Dimension 1/2007, Jahrgang 23 % dcf-77 Uhr %\item \label{Adv5} E. W"alde, Adventures in Forth 5, Die 4. Dimension 4/2008, Jahrgang 24 % Umstieg auf amforth, bit flags, sensor:, filter_mean: %\item \label{r8c-ds} Renesas R8C/13 datasheet auf \url{www.renesas.com} %\item \label{Koenig} A.\ K"onig und M.\ K"onig, Das PICmicro Profi Buch, Franzis % Verlag 1999, ISBN 3-7723-4284-1 %\item \label{SPelc} Stephen Pelc, Programming Forth, \\ \url{http://www.mpeforth.com/arena/ProgramForth.pdf} %\item \label{Deliano} \url{http://www.embeddedforth.de/emb3.pdf} S.\ 9 %\item \label{dcf} \url{http://de.wikipedia.org/wiki/DCF77} und Verweise darin %\item \label{willem} ByteForth von Willem Ouwerkerk,\\ \url{http://www.forth.hccnet.nl/byteforth.htm} \item \label{amforth} \amforth--Seite: \url{amforth.sourceforge.net/} %\item \label{ron} Ron Minke, Forth von der Pike auf Die 4. Dimension 3+4/2005 \ldots 4/2006, sowie Sonderheft AVR und 3+4/2007 %\item \label{namedbits} M. Kalus, M. Trute, Named Bits, Die 4. Dimension 2/2007, Jahrgang 23 \item \label{lubos} Lubo\v{s} P\v{e}kn\'{y}s Seite: \url{http://www.forth.cz} \item \label{RS485} Wikipedia (RS485) \url{de.wikipedia.org/wiki/RS485} \item \label{RS485a} \url{www.mikrocontroller.net/articles/RS-485} \item \label{g4.fs} Michael Kalus: g4.fs \url{www.forth-ev.de/repos/g4/g4.fs} \item \label{atmega32} atmega32--Datenblatt (doc2503.pdf) \url{www.atmel.com} \end{enumerate} \end{multicols} \vfill \begin{center} \includegraphics[height=10cm]{2011-01/Tobias}\\ \emph{Tobias, ich denke, Du hast die schrittweise Verfeinerung dieses Moduls weit genug getrieben.}\\ {\tiny Quelle: Thinking Forth} \end{center} \vfill % -------------------------------------------------------------------- \section{Listings} Die Listings des kompletten Projekts sind etwa 700 Zeilen lang, m.\,E.\ viel zu lang f"ur die Druckausgabe. Die Dateien stehen unter \url{www.forth-ev.de} zum Herunterladen zur Verf"ugung. Lediglich die Datei \path{main.asm}, eine ge"anderte Kopie der Datei \path{amforth/releases/4.2/appl/template/template.asm}, sei hier gezeigt, weil die "altere Generation der atmega--Kontroller ein paar mehr Informationen im Assemblerquelltext verlangt. \begin{quote} \begin{small} %\begin{multicols}{2} \listinginput[1]{1}{2011-01/main.asm} %%\listinginput[1]{1}{2011-01/dict_appl.inc} %%\listinginput[1]{1}{2011-01/dict_appl_core.inc} %%% %%% startup %%\listinginput[1]{1}{2011-01/applturnkey.asm} %%% %%% rs485 module %%\listinginput[1]{1}{2011-01/rs485-init.asm} %%\listinginput[1]{1}{2011-01/rs485.asm} %%\listinginput[1]{1}{2011-01/usart-isr-tx.asm} %%% %%% -emit %%\listinginput[1]{1}{2011-01/emit-on-off.asm} %%% %%% mpc module %%\listinginput[1]{1}{2011-01/mpc.asm} %%\listinginput[1]{1}{2011-01/mpc.fs} %%\listinginput[1]{1}{2011-01/usart-isr-rx.asm} %%% %%% application %%\listinginput[1]{1}{2011-01/main-61.fs} %%% %%% ready prompt %%\listinginput[1]{1}{2011-01/prompts.asm} %\end{multicols} \end{small} \end{quote} \section{Schaltung} \bigskip \begin{figure} \centerline{\includegraphics[scale=0.5,angle=0]{2011-01/p_rs485_bus.eps}} \caption{Anschluss des RS485 Transceivers an den Kontroller. R1 ist ein pull--down Widerstand, so dass diese Leitung auch dann low ist, wenn der D.7 Pin noch auf Eingang steht. Die Steckbr"ucken sind manchmal praktisch, um den Transceiver vor"ubergehend vom Bus und/oder Kontroller abzuh"angen. Die Widerst"ande R4, R5 und R6 werden nur unter bestimmten Umst"anden ben"otigt (Definition der Pegel und Busabschluss).} \end{figure} \end{document}