Skip to content

Instantly share code, notes, and snippets.

@TG9541
Last active January 6, 2019 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TG9541/7f340dea90538f654eb6faa0f32093ac to your computer and use it in GitHub Desktop.
Save TG9541/7f340dea90538f654eb6faa0f32093ac to your computer and use it in GitHub Desktop.
STM8S buffered UART implementation with support for RS485 flow shared medium
#require WIPE
NVM
20 CONSTANT RXLEN
VARIABLE rxbuf RXLEN 2- ALLOT
VARIABLE rxp \ receive xfer pointer in ISR
VARIABLE tstamp \ receive timestamp
20 CONSTANT TXLEN
VARIABLE txbuf TXLEN 2- ALLOT
VARIABLE txp \ transmit xfer pointer in ISR
VARIABLE tbp \ transmit buffer pointer
RAM WIPE
\res MCU: STM8S103
\res export INT_UARTRX INT_UARTTX
\res export UART1_SR UART1_DR UART1_CR2
#require RS485DE
#require PINDEBUG
#require WIPE
#require :NVM
#require ALIAS
#require ]B!
#require ]B?
5 CONSTANT #RIEN
6 CONSTANT #TC
6 CONSTANT #TCIEN
7 CONSTANT #TXE
7 CONSTANT #TIEN
NVM
\ Start UART TX ISR chain
: start-tx ( -- )
RS485tx \ enable TX driver
txbuf txp ! \ next char: buffer start
[ 1 UART1_CR2 #TIEN ]B! \ start ISR chain (TXE is active)
;
\ reset TX buffer pointer
: txres ( -- )
txbuf tbp !
;
\ TX ISR handler
:NVM
SAVEC
\ P2H
txp DUP @ ( va a1 ) DUP tbp @ < IF
( va a1 ) C@ UART1_DR C!
( va ) 1 SWAP +!
ELSE
( va a1 ) 2DROP
[ 0 UART1_CR2 #TIEN ]B! \ spin down ISR chain
[ 1 UART1_CR2 #TCIEN ]B! \ next ISR call: transfer complete
\ test and clear TC ISR
[ UART1_SR #TC ]B? IF
\ terminate ISR chain and release bus
[ 0 UART1_CR2 #TCIEN ]B!
txres RS485rx
THEN
THEN
\ P2L
IRET
[ OVERT INT_UARTTX !
\ headerless: test for enough free space in txbuf for putting n bytes
:NVM ( n -- f )
tbp @ txbuf - + 1- TXLEN <
;RAM ALIAS test-tbp
NVM
\ add c to TX buffer
: txc+ ( c -- )
1 test-tbp IF
tbp @ C! 1 tbp +!
THEN
;
\ add n to TX buffer
: tx+ ( n -- )
2 test-tbp IF
tbp @ ! 2 tbp +!
THEN
;
\ RX ISR handler
:NVM
SAVEC
P1H
UART1_DR C@
( c ) rxp @ ( c a ) DUP rxbuf - ( c a len ) RXLEN < IF
( c a ) SWAP ( a c ) OVER ( a c a ) C!
( a ) 1+ rxp !
THEN
TIM tstamp !
P1L
IRET
[ OVERT INT_UARTRX !
\ reset RX buffer and initialize RX ISR handler
: rxres ( -- )
rxbuf rxp !
[ 1 UART1_CR2 #RIEN ]B!
;
\ show contents the RX and TX buffers
: bufdump ( -- )
CR ." rxbuf:"
rxbuf rxp @ OVER - DUP . DUMP
CR ." txbuf:"
txbuf tbp @ OVER - DUP . DUMP
;
WIPE RAM
\res export UART1_CR2 UART1_BRR1
#require ]C!
#require OSCFREQ
#require UART_DIV
: BR ( br -- )
OSCFREQ UART_DIV
;
NVM
HERE \ pass table address on at compile time
240 BR , 480 BR , 960 BR , 1920 BR , 5760 BR , 11520 BR , 23040 BR ,
\ initilization of buffered UART handler (call this once)
: UARTISR ( n -- )
2* ( BR table ) LITERAL + @ UART1_BRR1 !
[ $0C UART1_CR2 ]C! \ enable TX and RX
PINDEBUG
RS485DE
txres rxres
;
WIPE RAM
\\ Example, run e.g. in SWIMCOM
#include UARTISR
UARTISR
txbuf TXLEN 66 FILL
65 txbuf C!
10 txbuf TXLEN 1- + C!
txlen tbp +!
start-tx
@TG9541
Copy link
Author

TG9541 commented Jan 1, 2019

ISRs in Forth are quite capable - 30µs processing time max for the code above. Here is the timing of a 115200 baud loop-back:

timing

A discussion is here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment