(* Display driver for the noForth OLED badge-1 EK-version

   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

   File volgorde:
        noForth badge-1.f  ( deze file )
        ssd1306-fat-chars.f
        ssd1306-thin-chars.f
        ssd1306-graphic.f
        movies-01a.f
        ssd1306-hedgehog.f
        finish-1b.f
*)

hex
(*
\ Get u1 from R-stack, decr by 1. When u2 is not zero
\ leave false and u2 on the stack, otherwise only true!
code DONE?      ( r:+u1 -- +u2 false | true )
8324 ,  4784 ,  0 ,  4137 ,
8317 ,  2405 ,  8324 ,  4784 ,
0 ,  4337 ,  4F00 ,  4307 ,
4F00 ,  end-code
*)

\ Bit pattern and PWM value for all six LEDS
value LEDS
value POWER

\ Period length is 100 clock cycles here
\ Set pulselength in steps from 0 to dm 100
: >POWER    ( u -- )        dm 99 umin  to power ;

routine PWM
    120A ,  421A , adr power ,
    9A0B ,  2003 ,  C0F2 , 3F ,  29 ,
    903B ,  64 ,  2006 ,  930A ,  2403 ,
    D2D2 ,  adr leds ,  29 ,  433B ,  531B ,
    413A ,  1300 ,
end-code

code INT-ON    430B ,  D232 ,  4F00 ,  end-code
code INT-OFF   C232 ,  4F00 ,  end-code

\ PWM at Px,y etc.
: PWM-OFF   ( -- )
    0000 160 !              \ TA0CTL   Stop timer-A0
    0010 162 **bic ;        \ TA0CCTL0 Disable interrupt

: PWM-ON    ( -- )
    pwm-off                 \ Stop timer
    0200  172 !             \ TA0CCR0  Interrupt frequency
    0214  160 !             \ TA0CTL   Start timer
    0010  162 **bis         \ TA0CCTL0 Enable compare 0 interrupt
    0 >power                \ Leds off at startup
    int-on ;                \ Activate

pwm     FFF2 vec!           \ Install Timer-A0 vector


: SETUP-ADC ( -- )
    39 22 *bic              \ P1.0, P1.3,4,5 are inputs
    30 27 *bis  30 21 *bis  \ P1.4,5 are inputs with pullup
\ P2.0 to P2.5 are positive on led outputs
    12 1B0 **bic            \ ADC10CTL0  Deactivate ADC & clear ENC
    10 1B0 **bis            \ ADC10CTL0  Activate ADC
    09 04A c!               \ ADC10AE0   P1.0 & P1.3 are ADC in
\   0010 1B0 ! ;            \ ADC10CTL0  T-sample 4 ADC10 clocks,
    0810 1B0 ! ;            \ ADC10CTL0  T-sample 8 ADC10 clocks,
                            \            ADC on & Vref = VCC & VSS
                            \            Ref. buf on during sample & hold

: ADC       ( +n -- u )     \ Use ADC10OSC as clock
    F000 and                \ Mask ADC input channel selector
    02 1B0 **bic            \ ADC10CTL0  Clear ENC
    60 or 1B2 !             \ ADC10CTL1  Select input, ADC10CLK/4
    03 1B0 **bis            \ ADC10CTL0  Set ENC & ADC10SC
    begin 4 1B0 bit** until \ ADC10CTL0  ADC10 ready?
    1B4 @ ;                 \ ADC10MEM   Read result

: LIPO          0000 adc ;  \ Read level at P1.0
: LDR           3000 adc ;  \ Read level at P1.3


\ LEDS & switches
: S1?           10 20 bit* 0= ; \ True if S1 is pressed
\ : S2?           20 20 bit* 0= ; \ True if S2 is pressed
: SW?           s1? s? or ;     \ True if S1 or S2 is pressed


\ Set DCO to 8 MHz, current use of CPU without leds = 2,56mA
: 8MHZ      ( -- )  \ Baudrate 38K4
    int-off
    10FD c@ 0057 c! \ BCSCTL1   Set dco = 8 mhz
    10FC c@ 0056 c! \ DCOCTL
    0000 0058 c!    \ UCA0CTL2  DCO on
    0081 0061 *bis  \ UCA0CTL1  Uart use SMCLK
    00D0 0062 c!    \ UCA0BR0   8 mhz, baudrate 38400
    0000 0063 c!    \ UCA0BR1   idem
    0006 0064 c!    \ UCAMCTL   modulation ucbrsx = 3
    0001 0061 *bic  \ UCA0CTL   enable USCI
    A550 012A !     \ FCTL2     mclk/17=470KHz flash timing
    ;


: SETUP-I2C ( -- )
    8mhz            \ Lower DCO frequency
    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
    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
    2/ 11A c!  {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


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}  begin  {i2ack?} until ;
: 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 ;
: E$!           ( a u ea )  2dup ec! 1+ swap bounds ?do count i ec! loop drop ;

: EDMP          ( ea -- )
    hex  begin
        cr  dup 4 u.r ." : "
        10 0 do  dup i + ec@ 2 .r space  loop  ch | emit \ Show hex
        10 0 do  dup i + ec@ pchar emit  loop  10 +      \ Show Ascii
    key bl <> until  drop ;

shield EE\  freeze


: {ol       ( b -- )        78 {i2write  i2out1 ;   \ Start oled command: b=00,
                                                    \ or oled 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
: ROW       ( b -- )        C0 {ol  noop  ol} ;     \ Single pixel row
: 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
    dup to y  over to x     \ Remember Y and X
    >r  7F min  0 max  r>   \ Keep x valid for OLED screen
    00 {ol                  \ Command stream
    7 and B0 or i2out       \ Set page
    dup 0F and i2out        \ Set column
    F0 and 4 rshift 10 or ol} ;

: SETUP-DISPLAY ( -- )
    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

: .BITROW   ( a +n -- )     \ Output half of (+n)x14 (big) character
    0 row
    0 ?do
        ec@+ row  1+        \ Output every second bit row
    loop  drop
    0 row ;

value O-EMIT                \ OLED emit vector
: &ERASE        ( -- )          400 0 &fill ;       \ Erase screen
: &HOME         ( -- )          0 0 xy ;            \ To upper left corner
: &PAGE         ( -- )          &erase  &home ;
: &CR           ( -- )          0 y 2 + xy ;
: &EMIT         ( c -- )        o-emit execute ;
: &SPACE        ( -- )          bl &emit ;
: &SPACES       ( u -- )        0 ?do  &space  loop ;
: &TYPE         ( a u -- )      bounds ?do  i c@ &emit  loop ;
: &U.           ( n -- )        0 d.str &type &space ;
: E&TYPE        ( a u -- )      bounds ?do  i ec@ &emit  loop ;
: E"(           ( -- )          inl# ec@+ e&type ;

: &"            ( -- )          \ ." voor OLED
    flyer  postpone E"(  ehere ,
    ch " parse  ehere e$! ;  immediate

: XY"       ( x y ccc -- )      \ XY" voor OLED
    flyer  postpone xy  postpone &" ; immediate

code OFFSCR?    ( -- flag )     \ Flag is true when bitrow is off screen
8324 ,  4784 ,  0 ,
90B2 ,  80 ,  adr X ,
2C06 ,  90B2 ,  8 ,  adr Y ,
2C02 ,  4307 ,  4F00 ,  4337 ,
4F00 ,  end-code

code C>N        ( c -- +n )     \ Convert char to bitmap index number
9037 ,  80 ,  2802 ,
4037 ,  80 ,  8037 ,  20 ,
4F00 ,  end-code


\ value O-CR
\ : &CR       o-cr execute ;

shield SSD1306\  freeze

\ End

