\ name : msp430FR5xxx_I2C_Master.asm
\
\ I2C MASTER Standard Mode software driver without interrupt
\ Target: MSP-EXP430FR5969 @ 8,16MHz
\ version 1.1 2016-03-18
\
\ ---------------------------------------------------------------------------------------------------------------------;
\ SCL clock generation, timing, and test of data(s) number are made by I2C_Master.
\ slave can strech SCL low after Start Condition and after any bit.
\
\ address Ack/Nack is generated by the slave on SDA line (released by the master)
\ Two groups of eight addresses (000$xxy and 1111xxxy) are not allowed (reserved)
\ after address or data is sent, the transmitter (Master or Slave) must release SDA line to allow (N)Ack by the receiver
\ data Ack/Nack are generated by the receiver (master or slave) on SDA line
\ a master receiver must signal the end of data to the slave transmitter by sending a Nack bit
\ Stop or restart conditions must be generated by master after a Nack bit.
\ after Ack bit is sent, Slave must release SDA line to allow master to do stop or restart conditions
\
\     __      _____ _____ _..._ _____ _____ _NACK _____ _____ _..._ _____ _____ _NACK     _
\ SDA   \____/_MSB_X_____X_..._X_LSB_X_R/W_x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X___/
\     _____     _     _           _     _     _     _     _           _     _     _     ___
\ SCL      \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/
\       ^   ^                             ^     ^                             ^     ^    ^
\       |   |Slave Stretch Low            |SSL  |SSL                          |SSL  |SSL |
\       |                                                                                |
\       |Start Condition                                                                 |stoP Condition
\
\     __      _____ _____ _..._ _____ _____ _NACK _____ _____ _..._ _____ _____ _NACK ___
\ SDA   \____/_MSB_X_____X_..._X_LSB_X_R/W_x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X   \____...
\     _____     _     _           _     _     _     _     _           _     _     _     ____
\ SCL      \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/    \_...
\       ^   ^                             ^     ^                             ^     ^    ^
\       |   |Slave Stretch Low            |SSL  |SSL                          |SSL  |SSL |
\       |                                                                                |
\       |Start Condition                                                                 |reStart Condition
\
\ tHIGH : SCL high time
\ tLOW : SCL low time
\ tBUF : SDA high time between Stop and Start conditions
\ tHD:STA : Start_Condition SCL high time after SDA is low
\ tSU:STO : Stop_Condition SCL high time before SDA rise
\ tSU:STA : Start_Condition SCL high time before SDA fall
\ tHD:DAT : SDA data change time after SCL is low
\ the SDA line must be strobe just after SCL is high
\ the SDA data must be change just after SCL is low
\ standard mode (up to 100 kHz) :   tHIGH   =   tHD:STA =   tSU:STO =   4µs
\                                   tLOW    =   tSU:STA =   tBUF    =   4,7µs
\                                   tHD:DAT <=  3,45 µs
\
\ fast mode     (up to 400 kHz) :   tHIGH   =   tHD:STA =   tSU:STO =   0,6µs
\                                   tLOW    =   tSU:STA =   tBUF    =   1,3µs
\                                   tHD:DAT <=  0,9 µs
\ -------------------------------------------------------------------------------------------------------------------;

\ =================================================================
\ =================================================================
                          
\ ###  #####   #####      #     #
\  #  #     # #     #     ##   ##   ##    ####  ##### ###### #####
\  #        # #           # # # #  #  #  #        #   #      #    #
\  #   #####  #           #  #  # #    #  ####    #   #####  #    #
\  #  #       #           #     # ######      #   #   #      #####
\  #  #       #     #     #     # #    # #    #   #   #      #   #
\ ### #######  #####      #     # #    #  ####    #   ###### #    #
                          
\ =================================================================
\ =================================================================

\ tested with P1.6 SDA, P1.7 SCL :
\ Start + Adr + Write 3 bytes + Stop + Start + adr + read 2 bytes + stop = 204us ==> 308 kHz  (STOP=10us)
\ See MSP430FR5xxx_I2C_Master.png



VARIABLE I2CS_ADR   \ low(I2CS_ADR) = slave I2C address with RW flag, high(I2CS_ADR) = RX buffer,data0
2 ALLOT             \ data1,data2
VARIABLE I2CM_BUF   \ low(I2CM_BUF) = RX or TX lentgh, high(I2CM_BUF) = TX buffer,data0
2 ALLOT             \ data1,data2
    \



\ \ ----------------------------------\
\ ASM I2C_M                           \
\ \ ----------------------------------\
\ \                                   \ in    I2CS_ADR/I2CM_BUF as RX/TX buffer requested by I2CS_ADR(0(0))
\ \                                   \       I2CS_ADR(0) = I2C_Slave_addr&R/w
\ \                                   \       I2CM_BUF(0) = TX/RX count of datas
\ \                                   \       I2CM_BUF(0) = 0 ==> send only I2C address
\ \                                   \ used  S = BUF ptr
\ \                                   \       T
\ \                                   \ out   I2CSLA_ADR & (R/W) unCHNGd
\ \                                   \       S = BUF PTR pointing on first data not exCHNGd
\ \                                   \       T = count of TX/RX datas exCHNGd
\ \                                   \       T = -1 ==> NACK on address
\ \                                   \       I2CS_ADR(0) = unCHNGd
\ \                                   \       I2CM_BUF(0) = unCHNGd
\ BIS #1,&UCB0CTLW0                   \ SWRST 
\ MOV #$0FD3,&UCB0CTLW0               \ master mode + UCTR + START + SWRST, IFG=IE=0 
\ MOV #$00C8,&UCB0CTLW1               \ set automatic stop (count byte reached)
\ MOV #$14,&UCB0BRW                   \ baudrate = SMCLK/20 = 400 kHz @8MHz ; 340 kHz measured
\ MOV #I2CM_BUF,S                     \ count & TX buf
\ MOV.B @S+,&UCB0TBCNT                \
\ CMP.B #0,&UCB0TBCNT                 \
\ 0= IF                               \ count = 0
\     BIS #4,&UCB0CTLW0               \ add Stop cmd to Start cmd ==> Master send only I2C address
\ THEN    
\ MOV #I2CS_ADR,T                     \ I2Cadr & RX buf
\ MOV.B @T+,&UCB0I2CSA                \ UCB0I2CSA = slave_address & R/w bit
\ RRA &UCB0I2CSA                      \ UCB0I2CSA = slave_address, C flag = R/w flag
\ U>= IF                              \ C flag = 1
\     MOV T,S                         \ Master read  : S = RX buffer
\     BIC #$10,&UCB0CTLW0             \                UCB0CTLW0 <-- UCTR=0
\ THEN                                \
\ \ ------------------------------    \
\ \ Start                             \
\ \ ------------------------------    \
\ MOV.B #-1,T                         \ T=-1
\ BIC #1,&UCB0CTLW0                   \ UCB0CTLW0 : clear SWRST, start I2C MASTER
\ BIT.B #1,&I2CS_ADR                  \ R/W test
\ 0= IF                               \
\ \   ----------------------------    \
\ \   MASTER TX                       \
\ \   ----------------------------    \
\     BEGIN                           \
\         MOV.B &UCBCNT0,T            \ store count of byte
\         BIT #8,&UCB0IFG             \ test UCSTPIFG
\         0<> IF                      \
\             MOV @RSP+,PC            \ end of I2C_M TX driver
\         THEN                        \
\         BIT #$20,&UCB0IFG           \ test UCNACKIFG
\         0<> IF                      \
\             BIS #4,&UCB0CTLW0       \ generate stop bit
\             MOV @RSP+,PC            \ end of I2C_M TX driver
\         THEN                        \
\         BIT #2,&UCB0IFG             \ test UCTXIFG0
\         0<> IF                      \
\             MOV.B @S+,&UCB0TXBUF    \ load data into UCB0TXBUF
\         THEN                        \
\     AGAIN                           \
\ THEN                                \
\ \ ------------------------------    \
\ \ MASTER RX                         \
\ \ ------------------------------    \
\ BEGIN                               \ of Master RX
\     MOV.B &UCBCNT0,T                \ store count of byte
\     BIT #8,&UCB0IFG                 \ test UCSTPIFG
\     0<> IF                          \
\         MOV @RSP+,PC                \ end of I2C_M RX driver
\     THEN                            \
\     BIT #1,&UCB0IFG                 \ test UCRXIFG0
\     0<> IF                          \
\         MOV.B &UCB0RXBUF,0(S)       \ load data from UCB0RXBUF
\         ADD   #1,S                  \
\     THEN                            \
\ AGAIN                               \
\ ENDASM                              \ 62 words + 9 init words
\     \

\ ------------------------------\
ASM I2C_M                       \
\ ------------------------------\
\                               \ in    I2CS_ADR(S) = -4(S) = I2C address
\                               \       I2CS_CNT(S) = -2(S) = count of data to be exchanged
\                               \       0(S) = data1
\                               \       1(S) = data2...
\                               \ used  S = BUF ptr, W
\                               \ out   I2CS_EXG(S) = -1(S) = count of bytes exchanged
\
BIS #1,&UCB0CTLW0               \ SWRST 
MOV #$0FD3,&UCB0CTLW0           \ master mode + UCTR + START + SWRST, IFG=IE=0 
MOV #$00C8,&UCB0CTLW1           \ set automatic stop (count byte reached)
FREQ_KHZ @ 16000 =              \ 16 MHz or 8 MHz ?
[IF]                            \ if 16 MHz
    MOV #$40,&UCB0BRW           \ baudrate = SMCLK/40 = 400 kHz @16MHz ; 340 kHz measured
[ELSE]                          \ if 8 MHz
    MOV #$20,&UCB0BRW           \ baudrate = SMCLK/20 = 400 kHz @8MHz ; 340 kHz measured
[THEN]                          \
MOV.B I2C_CNT(S),W              \ W = count of data
CMP.B #0,W                      \
0= IF                           \ if count = 0
    BIS #4,&UCB0CTLW0           \ add Stop cmd ==> Master send only I2C address then STOP (doesn't work with MASTER RX!)
THEN                            \
MOV.B W,&UCB0TBCNT              \ set threshold byte count to generate automatic STOP
MOV.B I2C_ADR(S),W              \ 3 W = slave_address & R/w bit
RRA.B W                         \ 1 C flag =  R/w bit
U>= IF                          \ if read
    BIC #$10,&UCB0CTLW0         \ UCB0CTLW0 <-- UCTR=0
THEN                            \
MOV W,&UCB0I2CSA                \ 3 UCB0I2CSA = slave_address
MOV.B #0,W                      \ clear buffer pointer
\ ------------------------------\
BIC #1,&UCB0CTLW0               \ UCB0CTLW0 : clear SWRST ==> start I2C MASTER
\ ------------------------------\
BW2                             \ 
BIT #$20,&UCB0IFG               \ test Nack flag UCNACKIFG (on address | on TX data) from slave
0<> IF                          \
    BIS #4,&UCB0CTLW0           \ generate STOP
    CMP.B #0,W                  \ Nack on TX data ?
    0<> IF                      \ yes 
        SUB.B #1,W              \ remove this TX data from count
    THEN                        \
    GOTO BW2                    \
THEN                            \
BIT #8,&UCB0IFG                 \ test stop flag UCSTPIFG
0<> IF                          \
    MOV.B W,I2C_EXG(S)          \ store count of bytes
    BIS #1,&UCB0CTLW0           \ SWRST
    RET                         \ ===================> end of I2C_M driver
THEN                            \
BIT #2,&UCB0IFG                 \ test TX flag UCTXIFG0
0<> IF                          \
\   ----------------------------\
\   MASTER TX                   \
\   ----------------------------\
    MOV.B I2C_BUF(W),&UCB0TXBUF \ load byte into UCB0TXBUF
    ADD.B #1,W                  \
    GOTO BW2                    \
THEN                            \
BIT #1,&UCB0IFG                 \ test RX flag UCRXIFG0
0<> IF                          \
\   ----------------------------\
\   MASTER RX                   \
\   ----------------------------\
    MOV.B &UCB0RXBUF,I2C_BUF(W) \ load byte from UCB0RXBUF
    ADD.B #1,W                  \
THEN
GOTO BW2                        \
ENDASM                          \ 64 words

\ ------------------------------\
CODE START                      \ init
\ ------------------------------\
\ init I2C_Master               \
\ ------------------------------\
\       %0000 1111 1101 0011     $640 = $0FD3
\           -                     UCMM = 1  : multi master mode
\              -                  UCMST = 1 : I2C_Master
\               --                UCMODE = %11 = I2C
\                 _               USYNC=1 (always 1)
\                   --            UCSSEL=SMCLK=8MHz
\                     -           UCTXACK=0 not auto ACK slave address
\                      -          UCTR=1/0 : TX/RX modes
\                         -       UCTXSTP
\                          -      UCTXSTT send start
\                           -     UCSWRST=1
\ ------------------------------\
\       %0000 0000 1100 1000     $642 = $00C8
\                 -               UCETXINT=0 : UCTXIFG0 set address match UCxI2COAx and TX mode
\                   --            UCCLTO=%11 : SCL low time out = 34 ms
\                      -          UCSWACK=1 : UCTXACK must be written to continue
\                        --       UCASTP0=%10 : automatic Stop when UCBxTBCNT is reached
\ ------------------------------\
\ PORTX (PORTx:y) default values\ DIR0,REN1,OUT1 (input with pullup resistors)
\ ------------------------------\
\ notice : UCB0 I2C driver seems to control only DIR register !!!
BIC.B #M_BUS,&I2CM_REN          \ REN0 : no_resistor
BIC.B #M_BUS,&I2CM_OUT          \ OUT0 : preset output low
BIS.B #M_BUS,&I2CM_SEL1         \ SEL11 : enable I2C I/O
COLON
." type stop to stop :-)"
    LIT RECURSE IS WARM         \ insert this starting routine between COLD and WARM...
    ['] WARM >BODY EXECUTE      \ ...and continue with WARM (very, very usefull after COLD or RESET !:-)
 ;

: STOP                          \ stops multitasking, must to be used before downloading app
    ['] WARM >BODY  IS WARM     \ remove START app from FORTH init process
    ECHO COLD                   \ reset CPU, interrupt vectors, and start FORTH
;
