For a taste of FlashForth 5,
below are the words that I developed to define: standard GPIO, SPI, and stepper functions.
[FORTH]$ more atmega328_gpio-base.ff5
-gpio-base
marker -gpio-base
\ gpio ports
#37 constant PORTB \ Port B Data Register
#36 constant DDRB \ Port B Data Direction Register
#35 constant PINB \ Port B Input Pins
#40 constant PORTC \ Port C Data Register
#39 constant DDRC \ Port C Data Direction Register
#38 constant PINC \ Port C Input Pins
#43 constant PORTD \ Port D Data Register
#42 constant DDRD \ Port D Data Direction Register
#41 constant PIND \ Port D Input Pins
[FORTH]$ more atmega328_spi-base.ff5
-spi-base
marker -spi-base
\ spi ports
$4c constant SPCR \ SPI Control Register
$4d constant SPSR \ SPI Status Register
$4e constant SPDR \ SPI Data Register
\ spi bit masks
%000100 constant mSS1 ( PB2 )
%001000 constant mMOSI ( PB3 )
%010000 constant mMISO ( PB4 )
%100000 constant mSCK ( PB5 )
\ spi flags
$80 constant mSPIF \ SPI Interrupt Flag
$40 constant mWCOL \ Write Collision Flag
\
: slave.select ( mSSx -- ) \ select slave x in mSSx constant
dup DDRB mset \ SS as output
PORTB mclr \ deselect
;
: slave.deselect ( mSSx -- ) \ unselect slave
dup DDRB mset \ SS as output
PORTB mset \ deselect
;
%01000000 constant SPCR_SPE \ SPI Enable
: spi.enable ( ---)
SPCR_SPE SPCR mset
;
: spi.disable ( ---)
SPCR_SPE SPCR mclr
;
%00100000 constant SPCR_DORD \ Data Order
: LSB.first ( --- ) \ select LOW Signifiant Bit first
SPCR_DORD SPCR mset
;
: MSB.first ( --- ) \ select MOST Signifiant Bit first
SPCR_DORD SPCR mclr
;
%00010000 constant SPCR_MSTR \ Master/Slave Select
: Master.mode
SPCR_MSTR SPCR mset
;
: Slave.mode
SPCR_MSTR SPCR mclr
;
\ SPI mode
%00000100 constant SPCR_CPHA \ Clock Phase
%00001000 constant SPCR_CPOL \ Clock Polarity
: Mode0
SPCR_CPHA SPCR mclr \ Idle CLK = 0
SPCR_CPOL SPCR mclr \ Sample on leading edge
;
: Mode1
SPCR_CPHA SPCR mclr \ Idle CLK = 0
SPCR_CPOL SPCR mset \ Sample on trailing edge
;
: Mode2
SPCR_CPHA SPCR mset \ Idle CLK = 1
SPCR_CPOL SPCR mclr \ Sample on trailing edge
;
: Mode3
SPCR_CPHA SPCR mset \ Idle CLK = 1
SPCR_CPOL SPCR mset \ Sample on leading edge
;
\ SPI clock speed
%00000010 constant SPCR_SPR1 \ SPI Clock Rate Selects
%00000001 constant SPCR_SPR0 \ SPI Clock Rate Selects
%00000001 constant SPSR_SPI2x \ Double SPI Speed
: spi2X.off ( --- )
SPSR_SPI2x SPSR mclr
;
: spi2X.on ( --- )
SPSR_SPI2x SPSR mset
;
: fosc/4
SPCR_SPR1 SPCR mclr
SPCR_SPR0 SPCR mclr
spi2X.off
;
: fosc/16
SPCR_SPR1 SPCR mclr
SPCR_SPR0 SPCR mset
spi2X.off
;
: fosc/64
SPCR_SPR1 SPCR mset
SPCR_SPR0 SPCR mclr
spi2X.off
;
: fosc/128
SPCR_SPR1 SPCR mset
SPCR_SPR0 SPCR mset
spi2X.off
;
: spi.init ( -- )
mSCK DDRB mset \ SCK as output
mSCK PORTB mclr \ clock idles low
mMOSI DDRB mset \ MOSI as output
mMISO DDRB mclr \ MISO as input
mMISO PORTB mset \ activate pull-up on MISO
mSS1 DDRB mset \ SS as output
mSS1 PORTB mset \ deselect
spi.enable
Master.mode
Mode0
fosc/64
SPSR c@ drop SPDR c@ drop \ will clear SPIF
;
: spi.close ( -- )
$00 SPCR c!
;
: spi.wait ( -- )
begin
mSPIF SPSR mtst
until
;
: spi.cexch ( c1 -- c2 )
SPDR c! spi.wait SPDR c@
;
: spi.csend ( c1 -- )
spi.cexch drop
;
[FORTH]$ more atmega328_step-base.ff5
-step-base
marker -step-base
: step.init ( -- ) \ 4 wire, bipolar 293d on PORTC pins 0..3, phases are 0+1, 2+3
$0f DDRC mset ;
: step.off ( -- )
$0 PORTC c! ;
: step.fwd.ms ( ms -- )
$9 PORTC c! dup ms $5 PORTC c! dup ms $6 PORTC c! dup ms $a PORTC c! ms ;
: step.rev.ms ( ms -- )
$a PORTC c! dup ms $6 PORTC c! dup ms $5 PORTC c! dup ms $9 PORTC c! ms ;
: step.fwd.to ( nsteps speed steps -- ) \ steps/rev rev/s qty
rot rot / swap for dup step.fwd.ms next drop ;
: step.rev.to ( nsteps speed steps -- ) \ steps/rev rev/s qty
rot rot / swap for dup step.rev.ms next drop ;
Assuming a common anode LED display+74HC595 attached to the standard SCK,MISO,MOSI,SS pins on a ATmega328, then issuing the following will light the display accordingly.
spi.init mSS1 slave.deselect $00 spi.csend mSS1 slave.select \ all on
\fed.gcba is segment order
%11111111 spi.csend mSS1 slave.deselect mSS1 slave.select \ all off
%11111000 spi.csend mSS1 slave.deselect mSS1 slave.select \ "7"
%11111001 spi.csend mSS1 slave.deselect mSS1 slave.select \ "1"
%11010000 spi.csend mSS1 slave.deselect mSS1 slave.select \ "3"
%01110001 spi.csend mSS1 slave.deselect mSS1 slave.select \ "4"
Or,
bipolar stepper+L293D driver attached to PORTC pins PC0..3, then issuing the following will cause it to turn 1000 steps at a particular speed.
step.init
200 50 1000 step.fwd.to step.off
I haven't revisited this in 4 years. I just remember that it irked me that every defined word would update the flash. I would have preferred it to write to RAM and only when you're done debugging commit it to flash. Oh well.