(* Display driver for the noForth OLED badge-1

   It has 6 leds arranged each on its own output bit
   One LDR as light sensor using ADC, LiPo battery condition
   measurement using ADC. RTC using 32kHz clock crystal and
   two switches to control the programs.

   Uses for the 7-pin extension connector:
   - Flashing the MSP430 using a launchpad board
   - Serial communication with noForth using
     an USB to RS232 cable or a Bluetooth 4.0 dongle.
   - Loading the LiPo battery from 4.4 Volt to 5 Volt.

   021 = P1OUT
   029 = P2OUT

   Two switches:    S1=P1.4 & S2=P1.5
   Six leds are:    P2.0 to P2.5
   RS232:           RX=P1.1 & TX=P1.2
   I2C:             SCL=P1.6 & SDA=P1.7
   32kHz xt:        P2.6 & P2.7
   LiPo voltage:    P1.0 (ADC0)
   LDR:             P1.3 (ADC3)

   USCI I2C on MSP430G2553-IPW20 using pull-ups.
   The bitrate values are for an 8 MHz DCO

   To add:  USCII EEPROM I2C routines.
            EEPROM movie compiler.
            Different ASCII character sets
*)

hex
code INT-OFF  C232 ,  4F00 ,  end-code

: SETUP-I2C ( -- )
    int-off
    C0 026 *bis     \ P1SEL     I2C to pins
    C0 041 *bis     \ P1SEL2
    01 069 *bis     \ UCB0CTL1  reset USCI
    0F 068 c!       \ UCB0CTL0  I2C master
    81 069 c!       \ UCB0CTL1  Use SMclk
    dm 20 06A c!    \ UCB0BR0   Bitrate 400 KHz with 8 MHz DCO
\   dm 80 06A c!    \ UCB0BR0   Bitrate 100 KHz with 8 MHz DCO
\   dm 160 06A c!   \ UCB0BR0   Bitrate 100 KHz with 16 MHz DCO
    00 06B c!       \ UCB0BR1
    01 069 *bic     \ UCB0CTL1  Resume USCI
    08 003 *bis ;   \ IFG2      Start with TX buffer empty

: I2C   ( fl -- )   ?abort ;        \ I2C error message

code I2READY    ( bit -- )
    4378 ,  B7C2 ,  3 ,  2802 ,  4437 ,  4F00 ,
    8318 ,  23F9 ,  ( chere >r )  1205 ,
    4035 ,  ' i2c >body ,  4F00 ,
end-code

code I2DONE     ( bit -- )
    4378 ,  B7C2 ,  69 ,  2C02 ,  4437 ,
    4F00 ,  8318 ,  23F9 ,  3FEE ,  ( r> jmp )
end-code

: I2OUT1    ( b -- )    06F c!  02 i2done  noop ; \ UCB0TXBUF Send first data to TX
: I2OUT     ( b -- )    06F c!  08 i2ready  noop ; \ UCB0TXBUF TX to shiftreg.
: I2IN      ( -- b )    04 i2ready  06E c@ ;  \ UCB0RXBUF Read databyte
: I2IN}     ( -- b )    04 069 *bis  i2in  04 i2done ; \ UCB0CTL1 Read last I2C databyte
: I2STOP}   ( -- )      04 069 *bis  04 i2done ; \ UCB0CTL1 Stop condition & check
: >DEV      ( a -- )    2/ 11A c! ;             \ UCB0I2CSA Set I2C device address

: {I2WRITE) ( -- )              \ Send I2C device address for writing
    12 069 *bis  08 i2ready  noop ;   \ UCB0CTL1  Send start condition

: {I2READ)  ( -- )              \ Send I2C device address for reading
    10 069 *bic  02 069 *bis    \ UCB0CTL1  Setup read & start
    08 i2ready  02 i2done ;     \ Wait for start condition & ack

: {I2WRITE  ( a -- )            \ Send I2C device address for writing
    >dev  {i2write) ;           \ Set slave address

: {I2READ   ( a -- )            \ Set and send I2C device address for reading
    >dev   {i2read) ;           \ Set slave address

: {I2ACK?}  ( -- fl )           \ Flag 'fl' is true when in ACK is received
    {i2write)  i2stop}  08 06D bit* 0= ;

: {POLL}    ( -- )      begin  {i2ack?} until ; \ Wait until an ACK is received


\ Prints -1 if device with address 'a' is present on I2C-bus otherwise 0.
\ : I2C?      ( a -- )            \ Result is true when device 'a' is on I2C bus
\     >dev setup-i2c {i2ack?} . ; \ Address device, present?

setup-i2c   shield HWI2C\   freeze


(* I2C EEPROM

  This code will work on a 64 kByte EEPROM, 128 kByte EEPROM to 512 kByte EEPROM.
  May be used with a 24C64, 24C65, 24C128, 24C256 to a 24C512(B) EEPROM.

*)

hex
\ Address EE-device and sent 16-bit EE-address 'ea'
: {EEADDR   ( ea -- )       A0 {i2write  dup >< i2out1  i2out ;
\ Read next byte from 24C512 EEPROM like COUNT but without address
: NEC@      ( -- b )        {i2read) i2in} ;
\ Read data 'x' from EEPROM address 'ea'.
: EC@       ( ea -- b )     {eeaddr nec@ ;
\ Write 'x' to EEPROM address 'ea'
: EC!       ( b ea -- )     {eeaddr  i2out  i2stop}  {poll} ;
: EC@+      ( ea -- ea+ x ) dup 1+  swap ec@ ;

\ Cell wide read and store operators for 24C512 EEPROM
: E@            ( ea -- x )         ec@  nec@  >< or ;
: E@+           ( ea1 -- ea2 x )    dup 2 +  swap e@ ;
: E!            ( x ea -- )         >r  dup r@ ec!  >< r> 1+ ec! ;
: E+!           ( n ea -- )         >r  r@ e@ +  r> e! ;

\ First cell in EEPROM is used as EHERE, this way it is always up to date
\ We have to take care manually of the forget action on this address pointer
\ Note that EHERE is initialised at address 2 right behind itself!!
\ The out of EE-memory error message, shows an error in EALLOT !!
0 constant EDP   2 edp e!   \ Define and initialise EHERE
: EHERE         ( -- ea )   edp e@ ;
: EFREE         ( -- u )    0000 ehere - ;
: EALLOT        ( +n -- )   efree  over u< ?abort  edp e+! ;
: EC,           ( b -- )    ehere  1 eallot  ec! ;
: E,            ( x -- )    ehere  2 eallot  e! ;
: ECREATE       ( -- ea )   ehere  constant ;

shield EE\  freeze


: {ol       ( b -- )        78 {i2write  i2out1 ;   \ Start an oled command: b=00 or old data: b=40
                                                    \ Single byte command: b=80, single byte data: b=C0
: ol}       ( b -- )        i2out  i2stop} ;        \ End an oled stream
: CMD       ( b -- )        80 {ol  noop  ol} ;     \ Single byte oled command
: 2CMD      ( b1 b0 -- )    00 {ol  i2out  ol} ;    \ Dual byte oled command
: CONTRAST  ( b -- )        81 2cmd ;               \ b = 0 to 255 (max. contrast)
: ON/OFF    ( flag -- )     1 and  AE or cmd ;      \ Display on/off
: INVERSE   ( flag -- )     1 and  A6 or cmd ;      \ Display black or white

value X  value Y
: >XY       ( x y -- )          \ Set OLED column and row
    00 {ol                      \ Command stream
    dup to y  7 and B0 or i2out \ Set page
    dup to x  dup 0F and i2out  \ Set column
    F0 and 4 rshift 10 or ol} ;

: DISPLAY-SETUP ( -- )
    setup-i2c               \ Init. 400kHz USCI I2C
    false on/off            \ Display off
    00 {ol                  \ Start oled-command stream
    0A8 i2out  03F i2out    \ Set multiplexer ratio
    0D3 i2out  000 i2out    \ Display offset = 0
    040 i2out               \ Display starts at line 0
    0A1 i2out               \ Mirror X-axis
    0C8 i2out               \ Mirror Y-axis
    0DA i2out  012 i2out    \ Alternate Com pin map
    0A4 i2out               \ Enable rendering from GDRAM
    0D5 i2out  080 i2out    \ Set oscillator clock
    08D i2out  014 i2out    \ Charge pump on
    0D9 i2out  022 i2out    \ Set precharge cycles to high cap.
    0DB i2out  030 i2out    \ VCOMH voltage to max.
    020 i2out  000 ol}      \ Horizontal display mode, end stream
    C0 contrast             \ Set contrast to 75%
    false inverse           \ Oled in normal mode
    true on/off ;           \ Display on

: &FILL         ( +n b -- ) \ Pattern 'b' to +n columns
    40 {ol   swap           \ Start oled-data stream
    begin                   \ Whole screen buffer
        over i2out          \ Output pattern
    1- ?dup 0= until  i2stop}  drop ;   \ End stream

: &ERASE        ( -- )          400 0 &fill ;       \ Erase screen
: &HOME         ( -- )          0 0 >xy ;           \ To upper left corner
: &PAGE         ( -- )          &erase  &home ;

shield SSD1306\  freeze


\ Display tools

: >PICTURE      ( addr -- )     \ Copy picture from mem. address to display
    dm 10 0 do
        i + c@ 80 or  picture i + c! \ Bit-7 always high
    loop
    drop ;

value DELAY                     \ Frame hold time
: FRAME         ( -- )      delay ms ;

\ Picture and movies from EEPROM

: EE>PICTURE    ( eeaddr -- )   \ Copy picture from EE address to display
    dm 10 0 do
        i + ec@ 80 or  picture i + c! \ Bit-7 always high
    loop
    drop ;

\ Note that the first cell of a movie must contain the
\ full length of the stored picture frames in bytes!!
: EEMOVIE       ( eeaddr -- )   \ Play movie from EEPROM
    @+ bounds ?do               \ Get length of movie
        i ee>picture  frame     \ Play one frame at a time
    dm 10 +loop ;               \ To next picture


shield DISPLAY\  freeze

\ End

