Dear Jan,

thanks for your message.

see my answer below the text.


Jan Kromhout via Amforth-devel writes:

> Hello,
>
>
> After trying for a while, I failed to get these three words together.
> Do not master the bitmask. Despite the examples and the email. Can someone 
> please help me with this.
> This is my simple code. The source is from flashForth.
> The tree words are setBitmask,clearBitmask and testBitmask.
>
> Thanks for any help.
> With kindly regards,
>
> Jan
>
> $24 constant ddrb
> $25 constant portb
> $4c constant spcr
> $4d constant spsr
> $4e constant spdr
>
>
> \ bit masks
> %000100 constant mSS          ( PB2 - pin 10 )
> %001000 constant mMOSI        ( PB3 - pin 11 )
> %010000 constant mMISO        ( PB4 - pin 12 )
> %100000 constant mSCK         ( PB5 - pin 13 )
> $80     constant mSPIF
> $40     constant mWCOL
>
>
> : setBitmask ; ( bitmask port -- )
>
> : clrBitmask ; ( bitmask port -- )
>
> : testBitmask ; ( bitmask port -- flag )
>
> : spi.init ( -- )
>   mSCK ddrb setBitmask                \ SCK as output
>   mSCK portb clrBitmask               \ clock idles low
>   mMOSI ddrb setBitmask               \ MOSI as output
>   mMISO ddrb clrBitmask               \ MISO as input
>   mMISO portb setBitmask              \ activate pull -up on MISO
>   mSS ddrb setBitmask                         \ SS as output
>   mSS portb setBitmask                \ deselect
>   $51 spcr c!                                         \ enable as master with 
> cpolarity 0, cphase 0, fosc /16
>   $00 spsr c!                                         \ SPI2X =0 for fosc /16
>   spsr c@ drop spdr c@ drop           \ will clear SPIF
> ;
>
> : spi.wait ( -- )
>   begin mSPIF spsr testBitmask until 
> ;
>
>


your line
>  mSCK ddrb setBitmask   \ SCK as output

expands to
>  $20  $24  setBitmask

so your question is: what is "setBitmask" supposed to be? At first
sight you might want to set

> : setBitmask ( mask addr -- ) c! ;  \ no good!

But that would not work well. Setting exaktly 1 bit in 1 register
would work, but setting the second bit in the same register would
overwrite the first set bit. So what would help? Well we need to
1. read the actual content of the register
2. fiddle with the wanted bit
3. write the new content back to the register

> : setBitmask ( mask addr -- )
>                     \ mask addr
>   dup               \ mask addr addr
>   c@                \ mask addr content   1.
>   rot               \ addr content mask
>   or                \ addr new_content    2.
>   swap              \ new_content addr
>   c!                \                     3.
> ;

likewise for

> : clearBitmask ( mask addr -- )
>                     \ mask addr
>   dup               \ mask addr addr
>   c@                \ mask addr content   1.
>   rot               \ addr content mask
>   invert            \ addr content !mask
>   and               \ addr new_content    2.
>   swap              \ new_content addr
>   c!                \                     3.
> ;

UNTESTED CODE!

Now, the pattern "read modify write" and "content mask or" and
"content mask invert and" are needed very often. So this is, what
the bitnames library abstracts away for us.

If you look into "amforth/trunk/avr8/lib/bitnames.frt" you find

> \ Turn a port pin on, dont change the others.
> : high ( pinmask portadr -- )
>     dup  ( -- pinmask portadr portadr )
>     c@   ( -- pinmask portadr value )
>     rot  ( -- portadr value pinmask )
>     or   ( -- portadr new-value)
>     swap ( -- new-value portadr)
>     c!
> ;
> 
> \ Turn a port pin off, dont change the others.
> : low ( pinmask portadr -- )
>     dup  ( -- pinmask portadr portadr )
>     c@   ( -- pinmask portadr value )
>     rot  ( -- portadr value pinmask )
>     invert and ( -- portadr new-value)
>     swap ( -- new-value port)
>     c!
> ;

which look suspiciously similar to what I wrote above. The difference
is that this code is tested!

So

> : setBitmask ( mask addr -- ) high ;
> : clearBitmask ( mask addr -- ) low ;

should work in my opinion.


Now, there is one more thing that "bitnames.frt" can do for you.
Instead of writing

> $24 constant ddrb
> %100000 constant mSCK         ( PB5 - pin 13 )
> ...
> mSCK ddrb setBitmask          \ SCK as output

you could write instead

> #include bitnames.frt
> $25 constant portb
>
> portb 5 portpin: mSCK
> ...
> mSCK high

Now what happens is this:

This line
> portb 5 portpin: mSCK

defines a new word "mSCK". When you call this word it will
place two items on the stack:
1. the bitmask corresponding to 5, namely %0010_0000
2. the address of the portb register, namely $24   (top of stack)

This is exactly what the functions "high" or "low" expect. They will
do the "read modify write" thing for you and all there is left to
write is this:

> mSCK high

isn't this wonderfully short and easy to read? Well, maybe not
at first sight.

So looking at my own spi start code from way back:


> \ 2010-05-24  EW   ewlib/spi.fs
> \ spi, using hw interface
> \ needs in dict_appl.inc: .include "words/spirw.asm"
> 
> \ words:
> \     +spi   ( -- )
> \     -spi   ( -- )
> \     ><spi  ( x -- x' )   transfer 2 bytes
> 
> \ Needs at least these definitions
> \ SPI
> \ $2D constant SPCR        \ SPI Control Register
> \ $2F constant SPDR        \ SPI Data Register
> \ $2E constant SPSR        \ SPI Status Register
> 
> \ needs lib/bitnames.frt   \ port_pin: high low pin_{out,in}put
> \ ----------------------------------------------------------
> 
> \ SPCR (control register)
> \ . 7 SPIE   spi interrupt enable
> \ . 6 SPE    spi enable
> \ . 5 DORD   data order, 0 msb first
> \ . 4 MSTR   master/slave mode, 1 master
> \ . 3 CPOL   clock polarity, 0 clock low on idle
> \ . 2 CPHA   clock phase,    0 sample on leading edge
> \ . 01 SPIR  data rate, 00 f/4, 01 f/16, 10 f/64, 11 f/128
> \ SPE | MSTR | SPIR0  ==> $51
> 
> \ needs these defined before loading:
> PORTB 4 portpin: /ss
> \ PORTB 5 portpin: _mosi
> \ PORTB 6 portpin: _miso
> \ PORTB 7 portpin: _clk
> 
> : +spi ( -- )
>   /ss high \ activate pullup!
>   _mosi high _mosi pin_output
>   _clk  low  _clk  pin_output
> \  _miso pin_pullup_on  \ not needed, see datasheet
>   $53 SPCR c! \ enable, master mode, f/128 data rate
> ;
> : -spi  0 SPCR c! ;
> 
> \ transfer 1 byte:  spirw (  c -- c' )
> \ transfer 1 cell: 2spirw ( n1 -- n2 )

Please note, the bit definitions are for a atmega644 and need to
be adjusted accordingly.


There we go: "high" and "low" do their bit as explained above.
Additionally "pin_output" and "pin_input" will deduce
the address of the DataDirectionRegister from the Port address.
No need to pay any attention.


There are two more noteworthy items in function "+spi"

1. /ss high \ activate pullup

*even if nothing is connected to that pin!*
If you do not activate this, your spi unit remains in slave mode iirc.

2. \ _miso pin_pullup_on  \ not needed, see datasheet

if I remember correctly, calling this command breaks spi. But I might
be wrong on this.

If you reread the spi section in the datasheet this information
should now stare right at you. It was there on first read, but
you (and I as well :-) did not understand its implications.
Keep going!


So, hopefully this will get you out of this somewhat dark area.

Cheers,
Erich

<snip>

-- 
May the Forth be with you ...


_______________________________________________
Amforth-devel mailing list for http://amforth.sf.net/
Amforth-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/amforth-devel

Reply via email to