Reorg: replace_constants, resolve_includes.

This commit is contained in:
flabbergast 2019-07-31 23:45:31 +01:00
parent 02ac1e2e4f
commit 97c0fb8e4a
32 changed files with 520 additions and 105 deletions

6
.gitignore vendored
View file

@ -1,3 +1,3 @@
across
stellaris
msp430/mecrisp
/across
/stellaris
/msp430/mecrisp

View file

@ -63,8 +63,27 @@ Layout
- `msp430`: this code is for [mecrisp], 16bit forth running on TI's
MSP430G2553 and MSP430FR2433 MCUs. The space is very tight on these
(the G2553 with mecrisp has 5kB flash and 160 bytes RAM available).
- `scripts`: see below
scripts
-------
### `resolve_includes.py`
Pretty much all the forth sources here use the convention of using `include`
to 'insert the given file here'. [folie] resolves these automatically
when sending words to a MCU, but since it is not a forth word, anyone _not_
using [folie] is buggered (there are usually many of `include`s and they can
be nested).
So this script produces a single self-contained forth source by resolving
all the `include`s (and optionally also `\ on_top_of <file>`). If you are
using `*vim`, the included sources should be nicely folded when you open the
resulting file.
Example: `python3 scripts/resolve_includes.py deploy/sbo-sht31/deploy.fs > sbo-all.txt`
[jeenode zero]: https://www.digitalsmarties.net/products/jeenode-zero
[embello]: https://git.jeelabs.org/jcw/embello

View file

@ -5,5 +5,7 @@ include g6s-base/board.fs
include g6s-base/core.fs
compiletoflash
include ina219.fs
compiletoram

View file

@ -1 +0,0 @@
../../sbo-l041/board.fs

68
deploy/sbo-sht31/board.fs Normal file
View file

@ -0,0 +1,68 @@
\ on_top_of always.fs
\ board definitions
eraseflash
compiletoflash
( board start: ) here dup hex.
include ../../flib/mecrisp/hexdump-min.fs
include ../../flib/stm32l0/io-min.fs
include ../../flib/pkg/pins20.fs
include ../../flib/stm32l0/hal-min.fs
include ../../flib/stm32l0/sleep-min.fs
include ../../flib/stm32l0/adc-min.fs
PA1 constant LED \ sbo active low
\ PB3 constant LED \ nucleo32 active high
PB1 constant BTN \ -1 pressed, 0 not
: led-off LED ios! ;
: led-on LED ioc! ;
PA4 variable ssel
PA5 constant SCLK
PA6 constant MISO
PA7 constant MOSI
include ../../flib/stm32l0/spi-min.fs
\ rf69 rf-rssi needs LED
include ../../flib/spi/rf69-min.fs
PA9 constant SCL
PA10 constant SDA
\ potentially messes up PA0-PA5 as well
: i2c-pafs $110 PA9 io-base $24 + ! ; \ (GPIO.AFRH)
include ../../flib/stm32l0/i2c-min.fs
: sleep ( -- ) [ $BF30 h, ] inline ; \ WFI Opcode, enters sleep mode (systick wakes)
: hello ( -- ) flash-kb . ." KB <n041> " hwid hex.
$8000 compiletoflash here - flashvar-here compiletoram here -
." ram/flash: " . . ." free " ;
: init ( -- ) \ board initialisation
init \ uses new uart init convention
%10001100 $4002102C bic! \ disable ioports C D H (RCC-IOPENR)
OMODE-PP LED io-mode!
IMODE-FLOAT BTN io-mode!
\ 16MHz ( set by Mecrisp on startup to get an accurate USART baud rate )
8 $4002104C ! \ set USART2 clock to HSI16, independent of sysclk (sbo uses USART2!!) (RCC-CCIPR)
1000 systick-hz
led-on
hello ." ok." cr
;
: rx-connected? ( -- f ) \ true if RX is connected (and idle)
IMODE-LOW PA3 io-mode! sleep PA3 io@ 0<> OMODE-AF-PP PA3 io-mode!
dup if sleep serial-key? if serial-key drop then then \ flush any input noise
;
: fake-key? ( -- f ) \ check for RX pin being pulled high
rx-connected? if reset then false ;
: unattended
rx-connected? if quit then \ return to command prompt
['] fake-key? hook-key? ! ;
( board end, size: ) here dup hex. swap - .
cornerstone <<<board>>>
compiletoram

View file

@ -0,0 +1,3 @@
include always.fs
include board.fs
include main.fs

View file

@ -1,7 +1,7 @@
\ sensor board deployed with SHT31-D
\ when reset with UART connected, it should drop into prompt
\ otherwise init devices, deinit USART, slow down and periodically send readings
\ needs board.fs
\ on_top_of board.fs
<<<board>>>
compiletoflash
@ -94,7 +94,7 @@ PKTLEN 1+ buffer: packet \ 1+ to make the word-sized params aligned (starts at
\ sensor driver
include ../flib/i2c/sht31-d.fs
include ../../flib/i2c/sht31-d.fs
: packet-prepare ( -- ) \ get all measurements (separated for testing)
adc-vcc \ get 1000*vdd

View file

@ -1,5 +1,5 @@
\ on_top_of always.fs
\ board definitions
\ needs always.fs
eraseflash
compiletoflash

View file

@ -1,3 +1,4 @@
\ on_top_of board.fs
\ core libraries
<<<board>>>

View file

@ -13,7 +13,7 @@ My main concern (because sensors) is power consumption. So some pros and cons.
This is an amazing one. There is a DIP version of this chip, and with
Matthias Koch's venerated `lowpower` version of [mecrisp] it runs _prompt_
on about 2uA (requires external 32768 crystal but no caps). The biggest
on less than 2uA (requires external 32768 crystal but no caps). The biggest
downside is space: it's got only 16kB of flash (of which 11kB takes mecrisp),
and 512 bytes of RAM (of which about 200 is available for the program). It is
mostly enough for a deployed sensor though (just), but development is a bit
@ -38,7 +38,8 @@ it is quite a pain to make the MCU use it and switch off REFO. Possible though.
This makes `lpm3` pretty nice, just like on g2553. However I couldn't quite
make the prompt lowpower (like on g2553), because I didn't find a way to
make the UART use the external crystal (XT1) for timing. So it gets shut down
on `lpm3`, and while it does wake up on RX, the data is garbled.
on `lpm3`, and while it does wake up on RX, the data is garbled. Maybe
with lower baud rate?
### msp430fr2476
@ -46,7 +47,7 @@ Same as fr2433, except 64kB FRAM (only 32kB is below `$FFFF` though, so
mecrisp only actively uses that), and 8 kB RAM. Also REFO only uses 1uA here,
so one can get reasonably low power in `lpm3` without any components - about 3uA.
Comes also in QFP-48 package, so hand solderable.
The main downside here is _price_.
The main downside here is the _price_.
## Structure

View file

@ -1,7 +0,0 @@
<<basis>>
compiletoflash
include g2553/spi.fs
include drivers/rf69.fs
include g2553/i2c-bb-base.fs
include drivers/i2c-bb.fs
reset

View file

@ -1,3 +1,5 @@
The 'lowpower' version needs an external 32768Hz crystal between P2.6 and P2.7 (pins 19 and 18 on MSP430G2553) - because the delays in this one are done with low power sleep, and waking up needs a timer, which needs this external source.
The 'xt1' versions also need an external 32768Hz crystal on the correct pins; this time with loading caps as well.
The current `.hex` files are all with mecrisp-2.0.6a.

View file

@ -1,12 +0,0 @@
: i2c. ( -- ) \ scan and report all I2C devices on the bus
128 0 do
cr i h.2 ." :"
16 0 do space
i j +
dup $08 < over $77 > or if drop 2 spaces else
dup i2c-addr 0 i2c-xfer if drop ." --" else h.2 then
then
loop
16 +loop ;

View file

@ -1,9 +1,12 @@
\ rf69 driver; this file contains both tx and rx parts (splittable)
\ - slightly modified version of a pretty smart jcw's driver: https://git.jeelabs.org/jcw/embello
\ (mainly some 32 vs 16 bit fixes)
\ - slightly modified version of a pretty smart jcw's driver:
\ https://git.jeelabs.org/jcw/embello
\ - mainly some 32 vs 16 bit fixes
\ - frequency is hardcoded and non-correctable
\ - for the names of registers and bits see the fancy driver
\ needs spi
\ generate constant-free source with:
\ python replace-constants.py rf69.fs rf69-constants.fs
\ = 1762 bytes in flash
\ TX part
@ -51,18 +54,18 @@ decimal align
: rf!mode ( b -- ) \ set the radio mode, and store a copy in a variable
dup rf.mode !
$01 rf@ $E3 and or $01 rf! \ (RF:OP)
begin $27 rf@ %10000000 and until ; \ (RF:IRQ1_MRDY @ RF:IRQ1)
$01 rf@ $E3 and or $01 rf! \ ($01:RF:OP)
begin $27 rf@ %10000000 and until ; \ (%10000000:RF:IRQ1_MRDY) \ ($27:RF:IRQ1)
: rf-config! ( addr -- ) \ load many registers from <reg,value> array, zero-terminated
%00100 rf!mode \ some regs don't program in sleep mode, go figure... \ (RF:M_STDBY)
%00100 rf!mode \ some regs don't program in sleep mode, go figure... \ (%00100:RF:M_STDBY)
begin dup @ ?dup while rf-h! 2+ repeat drop
;
: rf-group ( u -- ) $31 rf! ; \ set the net group (1..250) \ (RF:SYN3)
: rf-group ( u -- ) $31 rf! ; \ set the net group (1..250) \ ($31:RF:SYN3)
: rf-check ( b -- ) \ check that the register can be accessed over SPI
begin dup $2F rf! $2F rf@ over = until \ (RF:SYN1)
begin dup $2F rf! $2F rf@ over = until \ ($2F:RF:SYN1)
drop ;
: rf-ini ( group -- ) \ internal init of the RFM69 radio module
@ -72,41 +75,41 @@ decimal align
rf-group ;
: rf-parity ( -- u ) \ calculate group parity bits
$31 rf@ dup 4 lshift xor dup 2 lshift xor $C0 and ; \ (RF:SYN3)
$31 rf@ dup 4 lshift xor dup 2 lshift xor $C0 and ; \ ($31:RF:SYN3)
: rf-n@spi ( addr len -- ) \ read N bytes from the FIFO
0 do $00 rf@ over c! 1+ loop drop ; \ (RF:FIFO)
0 do $00 rf@ over c! 1+ loop drop ; \ ($00:RF:FIFO)
: rf-n!spi ( addr len -- ) \ write N bytes to the FIFO
0 do dup c@ $00 rf! 1+ loop drop ; \ (RF:FIFO)
0 do dup c@ $00 rf! 1+ loop drop ; \ ($00:RF:FIFO)
\ this is the intended public API for the RF69 driver
: rf-power ( n -- ) \ change TX power level (0..31)
$11 rf@ $E0 and or $11 rf! ; \ (RF:PA)
$11 rf@ $E0 and or $11 rf! ; \ ($11:RF:PA)
: rf-sleep ( -- ) %00000 rf!mode ; \ put radio module to sleep \ (RF:M_SLEEP)
: rf-sleep ( -- ) %00000 rf!mode ; \ put radio module to sleep \ (%00000:RF:M_SLEEP)
: rf-encrypt ( addr -- ) \ load 16 bytes as AES password, enable encryption
$3E 16 + $3E do \ loop by register addr \ (RF:AES)
$3E 16 + $3E do \ loop by register addr \ ($3E:RF:AES)
dup c@ dup i rf! \ write one, leave ( addr b )
if 1+ then \ if b <> 0, advance addr
loop drop
$3D rf@ 1 or $3D rf! ; \ (RF:PCONF2)
$3D rf@ 1 or $3D rf! ; \ ($3D:RF:PCONF2)
: rf-deencrypt ( -- ) \ clear encryption
$3D rf@ $FE and $3D rf! ; \ (RF:PCONF2)
$3D rf@ $FE and $3D rf! ; \ ($3D:RF:PCONF2)
: rf-send ( addr count hdr -- ) \ send out one packet
%00100 rf!mode \ (RF:M_STDBY)
over 2+ $00 rf! \ (RF:FIFO)
dup rf-parity or $00 rf! \ (RF:FIFO)
$C0 and rf.nodeid @ or $00 rf! \ (RF:FIFO)
%00100 rf!mode \ (%00100:RF:M_STDBY)
over 2+ $00 rf! \ ($00:RF:FIFO)
dup rf-parity or $00 rf! \ ($00:RF:FIFO)
$C0 and rf.nodeid @ or $00 rf! \ ($00:RF:FIFO)
( addr count ) rf-n!spi
%01100 rf!mode \ (RF:M_TX)
begin $28 rf@ %1000 and until \ (RF:IRQ2_SENT @ RF:IRQ2)
%00100 rf!mode ; \ (RF:M_STDBY)
%01100 rf!mode \ (%01100:RF:M_TX)
begin $28 rf@ %00001000 and until \ (%00001000:RF:IRQ2_SENT) \ ($28:RF:IRQ2)
%00100 rf!mode ; \ (%00100:RF:M_STDBY)
: rf-init ( -- ) \ init RFM69 with current rf.group and rf.freq values
: rf-init ( -- ) \ init RFM69 with current rf.group
rf.group @ rf-ini ;
: rf-info ( -- ) \ display reception parameters as hex string
@ -117,43 +120,43 @@ decimal align
\ rf-timeout checks whether there is an rssi timeout and restarts the receiver if so.
: rf-timeout ( -- )
$27 rf@ %100 and if \ (RF:IRQ1_TIMEOUT @ RF:IRQ1)
%01000 rf!mode \ (RF:M_FS)
$27 rf@ %00000100 and if \ (%00000100:RF:IRQ1_TIMEOUT) \ ($27:RF:IRQ1)
%01000 rf!mode \ (%01000:RF:M_FS)
then ;
\ rf-status fetches the IRQ1 reg, checks whether rx_sync is set and was not set
\ in rf.last. If so, it saves rssi value; and then updates rf.last.
\ rf.last ensures that the info is grabbed only once per packet.
: rf-status ( -- ) \ update status values on sync match
$27 rf@ %1 and rf.last @ <> if \ (RF:IRQ1_SYNC @ RF:IRQ1)
rf.last %1 over xor! @ if \ (RF:IRQ1_SYNC)
$24 rf@ rf.rssi ! \ (RF:RSSI)
$27 rf@ %00000001 and rf.last @ <> if \ (%00000001:RF:IRQ1_SYNC) \ ($27:RF:IRQ1)
rf.last %00000001 over xor! @ if \ (%00000001:RF:IRQ1_SYNC)
$24 rf@ rf.rssi ! \ ($24:RF:RSSI)
then
then ;
\ this is the intended public API for the RF69 driver
: rf-recv ( -- b ) \ check whether a packet has been received, return #bytes
rf.mode @ %10000 <> if \ (RF:M_RX)
rf.mode @ %10000 <> if \ (%10000:RF:M_RX)
0 rf.rssi !
%10000 rf!mode \ (RF:M_RX)
%10000 rf!mode \ (%10000:RF:M_RX)
else rf-timeout rf-status then
$28 rf@ %10 and if \ (RF:IRQ2_CRCOK @ RF:IRQ2)
$00 rf@ 66 min \ fetch length and limit \ (RF:FIFO)
$28 rf@ %00000010 and if \ (%00000010:RF:IRQ2_CRCOK) \ ($28:RF:IRQ2)
$00 rf@ 66 min \ fetch length and limit \ ($00:RF:FIFO)
rf.buf over rf-n@spi
else 0 then ;
: rf-ack? ( ms -- b ) \ waits ms milliseconds for an ACK and returns #bytes recv'd
0 rf.rssi !
%10000 rf!mode \ (RF:M_RX)
%10000 rf!mode \ (%10000:RF:M_RX)
0 do
rf-status \ capture rssi, etc.
$28 rf@ %10 and if \ (RF:IRQ2_CRCOK @ RF:IRQ2)
$00 rf@ 66 min \ fetch length and limit \ (RF:FIFO)
$28 rf@ %00000010 and if \ (%00000010:RF:IRQ2_CRCOK) \ ($28:RF:IRQ2)
$00 rf@ 66 min \ fetch length and limit \ ($00:RF:FIFO)
rf.buf over rf-n@spi
unloop exit
then
1 ms
loop
%00100 rf!mode \ kill RX \ (RF:M_STDBY)
%00100 rf!mode \ kill RX \ (%00100:RF:M_STDBY)
0 ;

View file

@ -9,7 +9,7 @@ compiletoflash
include port-regs.fs
include timer-regs.fs
: init \ Launchpad hardware initialisations
: init
." <FR2433> free(flash/ram): " $D400 compiletoflash here compiletoram - . flashvar-here here - . cr
;

View file

@ -8,3 +8,5 @@ compiletoflash
%11 P1OUT cbic! \ LEDs off
%11 P1DIR cbis! \ LEDs are outputs
;
compiletoram

View file

@ -9,7 +9,7 @@ compiletoflash
include port-regs.fs
include timer-regs-min.fs
: init \ Launchpad hardware initialisations
: init
." <FR2476> free(flash/ram): " $D400 compiletoflash here compiletoram - . flashvar-here here - . cr
;

View file

@ -8,16 +8,8 @@ compiletoflash
does> begin dup $1FF and while 1+ repeat eraseflashfrom
;
: init \ Launchpad hardware initialisations
: init
." <G2553> free(flash/ram): " $D400 compiletoflash here compiletoram - . flashvar-here here - . cr
8 $21 cbis! \ High (P1OUT)
8 $27 cbis! \ Pullup for button (P1REN)
\ \ 1 64 or $21 cbic! \ LEDs off (P1OUT)
\ \ 1 64 or $22 cbis! \ LEDs are outputs (P1DIR)
\ 1 $21 cbic! \ red LED off (P1OUT)
\ 1 $22 cbis! \ red LED is output (P1DIR) \ P1.6 is MISO
\ \ 64 $22 cbis! \ P1.6 is output (if not, launchpad power consumption goes up by 20uA)
;
\ Measure Vcc/2 on analog channel 11 with 2.5V reference.

View file

@ -0,0 +1,11 @@
\ on_top_of basis-g2553.fs
<<basis>>
compiletoflash
include spi.fs
include ../drivers/rf69.fs
include i2c-bb-base.fs
include ../drivers/i2c-bb.fs
reset

View file

@ -2,6 +2,7 @@
\ There have to be 1..10 resistors on SDA and SCL to pull them up to idle state.
\ Master only. Supports clock stretching.
\ hardcoded SCL:P2.4 SDA:P2.5
\ from templates/i2c-bb-base.fs with g2553/i2c-bb-base.tmplfill
\ 270 bytes flash
: 0>scl ( -- ) \ drive SCL low

View file

@ -1,6 +1,6 @@
\ i2c-bb-base.tmplfill: bitbanged i2c driver template filler for MSP430G2553
\ compile into constant-free file with:
\ python templates/replace_constants.py templates/i2c-bb-base.fs g2553/i2c-bb-base.tmplfill g2553/pins.fs
\ python templates/replace_constants.py templates/i2c-bb-base.fs g2553/i2c-bb-base.tmplfill g2553/port-regs.fs
%00010000 constant SCLPIN
%00100000 constant SDAPIN

View file

@ -1,13 +1,14 @@
\ bit-banged i2c driver
\ from jcw's embello
\ adapted from http://excamera.com/sphinx/article-forth-i2c.html
\
\ This driver is master-only. It supports clock stretching.
\ There have to be 1..10 resistors on SDA and SCL to pull them up to idle state.
\ by flabbergast: hardcoded SCL:P2.4 SDA:P2.5, modified for MSP430
\ it's more complicated than it really needs to be for basic i2c stuff
\ 898 bytes flash
\ but it is word compatible with jcw's i2c driver for stm32
\ = 898 bytes flash
0 variable i2c.adr
0 variable i2c.nak
@ -95,6 +96,20 @@
dup if i2c-stop 0 i2c.cnt ! then \ ignore reads if we had a nak
;
\ extra stuff
: i2c. ( -- ) \ scan and report all I2C devices on the bus
128 0 do
cr i h.2 ." :"
16 0 do space
i j +
dup $08 < over $77 > or if drop 2 spaces else
dup i2c-addr 0 i2c-xfer if drop ." --" else h.2 then
then
loop
16 +loop ;
\ eeprom example (24lc02b, so only one byte address)
\ i2c-init

13
msp430/g2553/launchpad.fs Normal file
View file

@ -0,0 +1,13 @@
\ MSP430G2553 Launchpad-specific words
compiletoflash
: init init
8 $21 cbis! \ High (P1OUT)
8 $27 cbis! \ Pullup for button (P1REN)
1 64 or $21 cbic! \ LEDs off (P1OUT)
1 64 or $22 cbis! \ LEDs are outputs (P1DIR)
\ P1.6 is output: if not, launchpad power consumption goes up by 20uA
;
compiletoram

View file

@ -1,27 +1,17 @@
\ MSP430(G2553) SPI driver
\ uses USCI_B0 (P1.5 SCK, P1.6 MISO, P1.7 MOSI) {because USCI_A0 is used for USART}
\ hardcoded P2.0 for CS
\ (c) 2019 flabbergast
\ from templates/g2553-spi.fs with g2553/spi.tmplfill
\ hardcoded P2.0 for CS
\ registers, ref man p444
\ UCB0CTL0 068h
\ UCB0CTL1 069h
\ UCB0BR0 06Ah
\ UCB0BR1 06Bh
\ UCB0STAT 06Dh
\ UCB0RXBUF 06Eh
\ UCB0TXBUF 06Fh
\ IE2 001h
\ IFG2 003h
: +spi ( -- ) 1 $29 cbic! ; \ select SPI (P2OUT)
: -spi ( -- ) 1 $29 cbis! ; \ deselect SPI (P2OUT)
: +spi ( -- ) 1 $29 cbic! ; \ select SPI \ (1:SPIPIN) \ (P2OUT:SPIOUT) \ ($29:P2OUT)
: -spi ( -- ) 1 $29 cbis! ; \ deselect SPI \ (1:SPIPIN) \ (P2OUT:SPIOUT) \ ($29:P2OUT)
: >spi> ( c -- c ) \ hardware SPI, 1 byte in, 1 byte out
begin %1000 $03 bit@ until \ TXbuf ready? (UCB0TXIFG @ IFG2)
begin %1000 $003 bit@ until \ TXbuf ready? \ (%1000:IFG2_UCB0TXIFG) \ ($003:IFG2)
$6F c! \ send byte (UCB0TXBUF)
begin %0100 $03 bit@ until \ RXbuf ready? (UCB0RXIFG @ IFG2)
$6E c@ \ read byte (UCB0RXBUF)
begin %0100 $003 bit@ until \ RXbuf ready? \ (%0100:IFG2_UCB0RXIFG) \ ($003:IFG2)
$06E c@ \ read byte \ ($06E:UCB0RXBUF)
;
\ single byte shortcuts
@ -29,12 +19,13 @@
: >spi ( c -- ) >spi> drop ; \ write byte to SPI
: spi-init ( -- ) \ set up hardware SPI
-spi 1 $2A cbis! \ setup CS pin (P2DIR)
%11100000 $26 cbis! %11100000 $41 cbis! \ setup SCK|MISO|MOSI pins (P1SEL, P1SEL2)
1 $69 c! \ put USCI in reset (UCSWRST @ UCB0CTL1)
%10101001 $68 c! \ 3-pin 8-bit SPI master (UCCKPH|UCMSB|UCMST|UCSYNC @ UCB0CTL0)
%11000000 $69 cbis! \ clock source SMCLK (UCSSELx @ UCB0CTL1)
2 $6A c! 0 $6B c! \ prescaler /8 (UCB0BR0, UCB0BR1)
1 $69 cbic! \ leave reset, initialize USCI state machine (UCSWRST @ UCB0CTL1)
-spi 1 $2A cbis! \ setup CS pin \ (1:SPIPIN) \ (P2DIR:SPIDIR) \ ($2A:P2DIR)
%11100000 $26 cbis! \ setup SCK|MISO|MOSI pins \ (%11100000:SPISELBITS) \ (P1SEL:SPISEL) \ ($26:P1SEL)
%11100000 $41 cbis! \ setup SCK|MISO|MOSI pins \ (%11100000:SPISELBITS) \ (P1SEL2:SPISEL2) \ ($41:P1SEL2)
1 $069 c! \ put USCI in reset (UCSWRST) \ ($069:UCB0CTL1)
%10101001 $068 c! \ 3-pin 8-bit SPI master (UCCKPH|UCMSB|UCMST|UCSYNC) \ ($068:UCB0CTL0)
%11000000 $069 cbis! \ clock source SMCLK (UCSSELx) \ ($069:UCB0CTL1)
2 $06A c! 0 $06B c! \ prescaler /8 \ ($06B:UCB0BR1) \ ($06A:UCB0BR0)
1 $069 cbic! \ init USCI (UCSWRST) \ ($069:UCB0CTL1)
;

11
msp430/g2553/spi.tmplfill Normal file
View file

@ -0,0 +1,11 @@
\ spi.tmplfill: template filler for spi driver
\ compile into constant-free file with
\ python templates/replace_constants.py templates/g2553-spi.fs g2553/spi.tmplfill g2553/usci-regs.fs g2553/port-regs.fs
1 constant SPIPIN
P2OUT constant SPIOUT
P2DIR constant SPIDIR
P2IN constant SPIIN
P1SEL constant SPISEL
P1SEL2 constant SPISEL2
%11100000 constant SPISELBITS

33
msp430/g2553/usci-regs.fs Normal file
View file

@ -0,0 +1,33 @@
\ USCI registers
\ G2553 datasheet, p19
$001 constant IE2
$003 constant IFG2
%0001 constant IFG2_UCA0RXIFG
%0010 constant IFG2_UCA0TXIFG
%0100 constant IFG2_UCB0RXIFG
%1000 constant IFG2_UCB0TXIFG
$05D constant UCA0ABCTL
$05E constant UCA0IRTCTL
$05F constant UCA0IRRCTL
$060 constant UCA0CTL0
$061 constant UCA0CTL1
$062 constant UCA0BR0
$063 constant UCA0BR1
$064 constant UCA0MCTL
$065 constant UCA0STAT
$066 constant UCA0RXBUF
$067 constant UCA0TXBUF
$068 constant UCB0CTL0
$069 constant UCB0CTL1
$06A constant UCB0BR0
$06B constant UCB0BR1
$06C constant UCB0CIE
$06D constant UCB0STAT
$06E constant UCB0RXBUF
$06F constant UCB0TXBUF
$118 constant UCB0OA
$11A constant USB0SA

View file

@ -0,0 +1,29 @@
\ MSP430(G2553) SPI driver
\ uses USCI_B0 (P1.5 SCK, P1.6 MISO, P1.7 MOSI) {because USCI_A0 is used for USART}
\ (c) 2019 flabbergast
: +spi ( -- ) SPIPIN SPIOUT cbic! ; \ select SPI
: -spi ( -- ) SPIPIN SPIOUT cbis! ; \ deselect SPI
: >spi> ( c -- c ) \ hardware SPI, 1 byte in, 1 byte out
begin IFG2_UCB0TXIFG IFG2 bit@ until \ TXbuf ready?
$6F c! \ send byte (UCB0TXBUF)
begin IFG2_UCB0RXIFG IFG2 bit@ until \ RXbuf ready?
UCB0RXBUF c@ \ read byte
;
\ single byte shortcuts
: spi> ( -- c ) 0 >spi> ; \ read byte from SPI
: >spi ( c -- ) >spi> drop ; \ write byte to SPI
: spi-init ( -- ) \ set up hardware SPI
-spi SPIPIN SPIDIR cbis! \ setup CS pin
SPISELBITS SPISEL cbis! \ setup SCK|MISO|MOSI pins
SPISELBITS SPISEL2 cbis! \ setup SCK|MISO|MOSI pins
1 UCB0CTL1 c! \ put USCI in reset (UCSWRST)
%10101001 UCB0CTL0 c! \ 3-pin 8-bit SPI master (UCCKPH|UCMSB|UCMST|UCSYNC)
%11000000 UCB0CTL1 cbis! \ clock source SMCLK (UCSSELx)
2 UCB0BR0 c! 0 UCB0BR1 c! \ prescaler /8
1 UCB0CTL1 cbic! \ init USCI (UCSWRST)
;

View file

@ -14,6 +14,7 @@ replace_dict = {}
replace_re = {}
comments = re.compile(r"(^[^\\]*)(\\.*|$)")
# build replacement dictionary along with regexps that check for occurences
for filename in args.definitions + [args.template]:
with open(filename) as f:
for line in f:
@ -25,7 +26,7 @@ for filename in args.definitions + [args.template]:
with open(args.template) as f:
for line in f:
s = list(comments.match(line.rstrip()).groups())
for nam in replace_re:
for nam in sorted(replace_re.keys(),reverse=True): # revsort to avoid sub-name clashes
upd, k = replace_re[nam].subn(replace_dict[nam], s[0])
if k > 0:
s[0] = upd

View file

@ -0,0 +1,40 @@
\ rf69 driver constants
$00 constant RF:FIFO
$01 constant RF:OP
$07 constant RF:FRF
$11 constant RF:PA
$18 constant RF:LNA
$1F constant RF:AFC
$24 constant RF:RSSI
$27 constant RF:IRQ1
$28 constant RF:IRQ2
$2F constant RF:SYN1
$31 constant RF:SYN3
$39 constant RF:ADDR
$3A constant RF:BCAST
$3C constant RF:THRESH
$3D constant RF:PCONF2
$3E constant RF:AES
%00000 constant RF:M_SLEEP
%00100 constant RF:M_STDBY
%01000 constant RF:M_FS
%01100 constant RF:M_TX
%10000 constant RF:M_RX
$C2 constant RF:START_TX
$42 constant RF:STOP_TX
$80 constant RF:RCCALSTART
%10000000 constant RF:IRQ1_MRDY
%01000000 constant RF:IRQ1_RXRDY
%00001000 constant RF:IRQ1_RSSI
%00000100 constant RF:IRQ1_TIMEOUT
%00000001 constant RF:IRQ1_SYNC
%01000000 constant RF:IRQ2_FIFO_NE
%00001000 constant RF:IRQ2_SENT
%00000100 constant RF:IRQ2_RECVD
%00000010 constant RF:IRQ2_CRCOK

162
msp430/templates/rf69.fs Normal file
View file

@ -0,0 +1,162 @@
\ rf69 driver; this file contains both tx and rx parts (splittable)
\ - slightly modified version of a pretty smart jcw's driver:
\ https://git.jeelabs.org/jcw/embello
\ - mainly some 32 vs 16 bit fixes
\ - frequency is hardcoded and non-correctable
\ - for the names of registers and bits see the fancy driver
\ needs spi
\ generate constant-free source with:
\ python replace-constants.py rf69.fs rf69-constants.fs
\ = 1762 bytes in flash
\ TX part
0 variable rf.mode \ last set chip mode
0 variable rf.last \ flag used to fetch RSSI only once per packet
0 variable rf.rssi \ RSSI signal strength of last reception
66 buffer: rf.buf \ buffer with last received packet data
42 variable rf.group \ network group (1..250)
61 variable rf.nodeid \ node ID of this node (1..63)
create rf:init \ initialise the radio, each 16-bit word is <reg#,val>
hex
0200 , \ packet mode, fsk
0302 , 048A , \ bit rate 49,261 hz
0505 , 06C3 , \ 90.3kHzFdev -> modulation index = 2
07D9 , 0813 , 0900 , \ 868.3MHz frequency ( freq[Hz] * 2^19 / 32*10^6, MSB )
0B20 , \ low M
1942 , 1A42 , \ RxBw 125khz, AFCBw 125khz
1E0C , \ AFC auto-clear, auto-on
2607 , \ disable clkout
29C4 , \ RSSI thres -98dB
2B40 , \ RSSI timeout after 128 bytes
2D05 , \ Preamble 5 bytes
2E90 , \ sync size 3 bytes
2FAA , \ sync1: 0xAA -- this is really the last preamble byte
302D , \ sync2: 0x2D -- actual sync byte
312A , \ sync3: network group
37D0 , \ drop pkt if CRC fails \ 37D8 h, \ deliver even if CRC fails
3842 , \ max 62 byte payload
3C8F , \ fifo thres
3D12 , \ PacketConfig2, interpkt = 1, autorxrestart on
6F20 , \ Test DAGC
7102 , \ RegTestAfc
0 , \ sentinel
decimal align
\ r/w access to the RF registers
: rf!@ ( b reg -- b ) +spi >spi >spi> -spi ;
: rf! ( b reg -- ) $80 or rf!@ drop ;
: rf@ ( reg -- b ) 0 swap rf!@ ;
: rf-h! ( h -- ) dup $FF and swap 8 rshift rf! ;
: rf!mode ( b -- ) \ set the radio mode, and store a copy in a variable
dup rf.mode !
RF:OP rf@ $E3 and or $01 rf!
begin RF:IRQ1 rf@ RF:IRQ1_MRDY and until ;
: rf-config! ( addr -- ) \ load many registers from <reg,value> array, zero-terminated
RF:M_STDBY rf!mode \ some regs don't program in sleep mode, go figure...
begin dup @ ?dup while rf-h! 2+ repeat drop
;
: rf-group ( u -- ) RF:SYN3 rf! ; \ set the net group (1..250)
: rf-check ( b -- ) \ check that the register can be accessed over SPI
begin dup RF:SYN1 rf! RF:SYN1 rf@ over = until
drop ;
: rf-ini ( group -- ) \ internal init of the RFM69 radio module
spi-init
$AA rf-check $55 rf-check \ will hang if there is no radio!
rf:init rf-config!
rf-group ;
: rf-parity ( -- u ) \ calculate group parity bits
RF:SYN3 rf@ dup 4 lshift xor dup 2 lshift xor $C0 and ;
: rf-n@spi ( addr len -- ) \ read N bytes from the FIFO
0 do RF:FIFO rf@ over c! 1+ loop drop ;
: rf-n!spi ( addr len -- ) \ write N bytes to the FIFO
0 do dup c@ RF:FIFO rf! 1+ loop drop ;
\ this is the intended public API for the RF69 driver
: rf-power ( n -- ) \ change TX power level (0..31)
RF:PA rf@ $E0 and or RF:PA rf! ;
: rf-sleep ( -- ) RF:M_SLEEP rf!mode ; \ put radio module to sleep
: rf-encrypt ( addr -- ) \ load 16 bytes as AES password, enable encryption
RF:AES 16 + RF:AES do \ loop by register addr
dup c@ dup i rf! \ write one, leave ( addr b )
if 1+ then \ if b <> 0, advance addr
loop drop
RF:PCONF2 rf@ 1 or RF:PCONF2 rf! ;
: rf-deencrypt ( -- ) \ clear encryption
RF:PCONF2 rf@ $FE and RF:PCONF2 rf! ;
: rf-send ( addr count hdr -- ) \ send out one packet
RF:M_STDBY rf!mode
over 2+ RF:FIFO rf!
dup rf-parity or RF:FIFO rf!
$C0 and rf.nodeid @ or RF:FIFO rf!
( addr count ) rf-n!spi
RF:M_TX rf!mode
begin RF:IRQ2 rf@ RF:IRQ2_SENT and until
RF:M_STDBY rf!mode ;
: rf-init ( -- ) \ init RFM69 with current rf.group
rf.group @ rf-ini ;
: rf-info ( -- ) \ display reception parameters as hex string
rf.group @ h.2 rf.rssi @ h.2 ;
\ RX part
\ rf-timeout checks whether there is an rssi timeout and restarts the receiver if so.
: rf-timeout ( -- )
RF:IRQ1 rf@ RF:IRQ1_TIMEOUT and if
RF:M_FS rf!mode
then ;
\ rf-status fetches the IRQ1 reg, checks whether rx_sync is set and was not set
\ in rf.last. If so, it saves rssi value; and then updates rf.last.
\ rf.last ensures that the info is grabbed only once per packet.
: rf-status ( -- ) \ update status values on sync match
RF:IRQ1 rf@ RF:IRQ1_SYNC and rf.last @ <> if
rf.last RF:IRQ1_SYNC over xor! @ if
RF:RSSI rf@ rf.rssi !
then
then ;
\ this is the intended public API for the RF69 driver
: rf-recv ( -- b ) \ check whether a packet has been received, return #bytes
rf.mode @ RF:M_RX <> if
0 rf.rssi !
RF:M_RX rf!mode
else rf-timeout rf-status then
RF:IRQ2 rf@ RF:IRQ2_CRCOK and if
RF:FIFO rf@ 66 min \ fetch length and limit
rf.buf over rf-n@spi
else 0 then ;
: rf-ack? ( ms -- b ) \ waits ms milliseconds for an ACK and returns #bytes recv'd
0 rf.rssi !
RF:M_RX rf!mode
0 do
rf-status \ capture rssi, etc.
RF:IRQ2 rf@ RF:IRQ2_CRCOK and if
RF:FIFO rf@ 66 min \ fetch length and limit
rf.buf over rf-n@spi
unloop exit
then
1 ms
loop
RF:M_STDBY rf!mode \ kill RX
0 ;

View file

@ -1,5 +1,5 @@
\ on_top_of always.fs
\ board definitions
\ needs always.fs
eraseflash
compiletoflash

35
scripts/resolve_includes.py Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env python3
import os
import argparse
def print_file(abspath,ontop=False,depth=0):
(dirname,filename) = os.path.split(abspath)
beginnote = ' '*4*depth + '\ {{{ ' + ('='*5*(depth+1)) + ' included %s%s =====\n'
endnote = ' '*4*depth + '\ }}} ' + ('='*5*(depth+1)) + ' end of included %s =====\n'
with open(abspath) as f:
for line in f:
if line.startswith('include '):
newfile = line.split()[1]
print(beginnote % ('', os.path.basename(newfile)))
print_file(os.path.abspath(os.path.join(dirname,newfile)),ontop=ontop,depth=depth+1)
print(endnote % os.path.basename(newfile))
elif ontop and line.startswith('\\ on_top_of'):
newfile = line.split()[2]
print(beginnote % ('(on_top_of) ', os.path.basename(newfile)))
print_file(os.path.abspath(os.path.join(dirname,newfile)),ontop=ontop,depth=depth+1)
print(endnote % os.path.basename(newfile))
else:
print(' '*4*depth + line.rstrip())
argparser = argparse.ArgumentParser(description='Recursively replace ''include'' in forth sources by contents. No files changed, output will be send to stdout.')
argparser.add_argument('file', help='File to start with')
argparser.add_argument('-o', '--on_top_of', action='store_true', help='Resolve also "\ on_top_of"')
#argparser.add_argument('definitions', nargs='*', help='Where to read the constants from')
args = argparser.parse_args()
print_file(args.file,ontop=args.on_top_of,depth=0)
print('\n\ vim: filetype=forth:foldmethod=marker:')