\ name : msp430FR5xxx_I2C_Soft_Master.asm WIPE \ version 2.0 2015-07-30 \ ---------------------------------------------------------------------------------------------------------------------\ \ 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 (000xxxy 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. \ \ \ first byte = address + R/W flag | byte data (one, for example) \ __ _____ _____ _..._ _____ __R__ _NAK_ _____ _____ _..._ _____ _____ _NAK_ _ \ SDA \____/_MSB_X_____X_..._X_LSB_X__W__x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X___/ \ _____ _ _ _ _ _ _ _ _ _ _ ___ \ SCL \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/ \ ^ ^ ^ ^ ^ ^ ^ \ | |SSL |SSL |SSL |SSL |SSL | \ | | \ |Start Condition |stoP Condition \ \ first byte = address + R/W flag | byte data (one, for example) \ __ _____ _____ _..._ _____ __R__ _NAK_ _____ _____ _..._ _____ _____ _NAK_ ___ \ SDA \____/_MSB_X_____X_..._X_LSB_X__W__x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X \____... \ _____ _ _ _ _ _ _ _ _ _ _ ____ \ SCL \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/ \_... \ ^ ^ ^ ^ ^ ^ ^ \ | |SSL |SSL |SSL |SSL |SSL | \ | | \ |Start Condition |reStart Condition \ \ SSL : Slave can strech SCL low \ 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 \ -------------------------------------------------------------------------------------------------------------------\ \ \ \ -------------------------------------------------------------------------------------------------------------------\ \ I2C Soft MASTER, Standard MODE, 8,16,24MHz \ -------------------------------------------------------------------------------------------------------------------\ \ =============================================================================================== \ =============================================================================================== \ ### ##### ##### ##### # # \ # # # # # # # #### ###### ##### ## ## ## #### ##### ###### ##### \ # # # # # # # # # # # # # # # # # # # \ # ##### # ##### # # ##### # # # # # # #### # ##### # # \ # # # # # # # # # # ###### # # # ##### \ # # # # # # # # # # # # # # # # # # # # \ ### ####### ##### ##### #### # # # # # # #### # ###### # # \ =============================================================================================== \ =============================================================================================== \ use Px.0 to Px.3 pins as SCL and SDA pins to use immediate instruction in one byte (#1,#2,#4,#8) \ tested with P1.6 SDA, P1.7 SCL @8 MHZ : \ Start + Adr + Write 3 bytes + Stop + Start + adr + read 2 bytes + stop = 600us ==> 105 kHz \ See MSP430FR5xxx_I2C_Soft_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 T_I2C \ 4 init first once !!! \ ------------------------------\ BEGIN \ 3~ loop SUB #1,Y \ 1 0= UNTIL \ 2 MOV #1,Y \ 2 set I2C tHIGH time @ 8MHz \ MOV #9,Y \ 2 set I2C tHIGH time @ 16MHz \ MOV #20,Y \ 2 set I2C tHIGH time @ 24MHz MOV @RSP+,PC \ 4 ret ENDASM \ \ \ ------------------------------\ _ ASM I2C_PLS \ SCL _| |_ pulse \ ------------------------------\ CALL #T_I2C \ _ wait tLOW BIC.B #SMSCL,&I2CSM_DIR \ _^ release SCL (high) BEGIN BIT.B #SMSCL,&I2CSM_IN \ test if SCL is released 0<> UNTIL CALL #T_I2C \ wait tHIGH BIT.B #SMSDA,&I2CSM_IN \ _ get SDA BIS.B #SMSCL,&I2CSM_DIR \ v_ force SCL low MOV @RSP+,PC \ ret ENDASM \ \ \ ------------------------------\ ASM I2C_MTX \ MASTER TX \ shared code for address and TX data \ ------------------------------\ BEGIN \ ADD.B X,X \ 1 l shift one left U>= IF \ 2 l carry set ? BIC.B #SMSDA,&I2CSM_DIR \ 4 l yes : SDA as input ==> SDA high because pull up resistor ELSE \ 2 l BIS.B #SMSDA,&I2CSM_DIR \ 4 l no : SDA as output ==> SDA low THEN \ l _ CALL I2C_PLS \ _| |_ SCL SUB.B #1,W \ l count of bits 0= UNTIL \ l BIC.B #SMSDA,&I2CSM_DIR \ 5 l _ SDA as input : release SDA high to prepare read Ack/Nack MOV @RSP+,PC \ ret ENDASM \ \ \ ==================================\ 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 datas countdown \ \ W bits countdown \ \ X dataI2CM_ \ \ out I2CSLA_ADR & (R/W) unCHNGd \ \ S = BUF PTR pointing on first data not exCHNGd \ \ T = count+1 of TX/RX datas exCHNGd \ \ I2CS_ADR(0) = unCHNGd \ \ I2CM_BUF(0) = count of data not exCHNGd (normally = 0) \ \ I2CM_BUF(0) = -1 <==> Nack on address \ ----------------------------------\ \ I2C_MR_DC_ListenBeforeStart: \ test if SCL & SDA lines are idle (high) \ ----------------------------------\ BIC.B #SM_BUS,&I2CSM_DIR \ SDA & SCL pins as input BIC.B #SM_BUS,&I2CSM_OUT \ preset output LOW for SDA & SCL pins MOV #2,T \ I2C_MR_DC_Wait_Start_Loop = 8 µs @ 8 MHz \ MOV #4,T \ I2C_MR_DC_Wait_Start_Loop = 8 µs @ 16 MHz \ MOV #6,T \ I2C_MR_DC_Wait_Start_Loop = 8 µs @ 24 MHz BEGIN \ BEGIN \ BEGIN \ BIT.B #SMSCL,&I2CSM_IN \ 4 P1DIR.3 SCL high ? 0<> UNTIL \ 2 BIT.B #SMSDA,&I2CSM_IN \ 4 P1IN.2 SDA high ? 0<> UNTIL \ 2 SUB #1,T \ 1 0= UNTIL \ 2 here the I2C bus is idle \ ------------------------------\ \ I2C_Master_Start_Cond: \ here, SDA and SCL are in idle state \ ------------------------------\ BIS.B #SMSDA,&I2CSM_DIR \ 4- P1DIR.2 force SDA output (low) MOV #5,Y \ 2 tHD\STA time 8 MHz \ MOV #15,Y \ 2 tHD\STA time 16MHz \ MOV #25,Y \ 2 tHD\STA time 24MHz CALL #T_I2C \ wait tHD\STA BIS.B #SMSCL,&I2CSM_DIR \ 4- P1DIR.3 force SCL output (low) \ ------------------------------\ \ I2C_Master_Start_EndOf: \ \ ------------------------------\ MOV #I2CS_ADR,S \ 2 l MOV.B @S+,X \ 3 l X = slave address, S = RX buffer MOV #I2CM_BUF,W \ 2 l MOV.B @W+,T \ 2 l T = count of datas, W = TX buffer BIT.B #1,X \ 1 l test I2C R/w flag 0= IF \ 2 l write flag MOV W,S \ 3 l TX buffer THEN \ \ ------------------------------\ \ I2C_Master_Send_address \ SCL is held low by slave \ ------------------------------\ ADD #1,T \ to add address in count MOV #8,W \ 1 l prepare 8 bit Master writing MOV #1,Y \ 2 tHD\STA time 8 MHz value \ MOV #5,Y \ 2 tHD\STA time 16MHz value \ MOV #15,Y \ 2 tHD\STA time 24MHz value CALL #I2C_MTX \ 4 to send address \ ------------------------------\ \ I2C_Master_Loop_Data \ \ ------------------------------\ BEGIN \ \ ----------------------------\ \ Master TX/RX ACK/NACK \ \ ----------------------------\ MOV #2,Y \ 2 tLOW time complement @ 8MHz \ MOV #15,Y \ 2 tLOW time complement @ 16MHz \ MOV #20,Y \ 2 tLOW time complement @ 24MHz CALL #I2C_PLS \ _| |_ SCL with BIT SDA, then ret 0<> IF BIS #2,SR \ l if Nack (TX), force Z=1 ==> StopCond ELSE SUB.B #1,T \ else dec count THEN \ l \ ----------------------------\ \ I2C_Master_CheckCountDown \ count=0 or Nack received \ ----------------------------\ 0= IF \ count reached or Nack \ ----------------------------\ \ I2C_Master_StopCond \ \ ----------------------------\ before releasing SCL BIS.B #SMSDA,&I2CSM_DIR \ l P1DIR.2 as output ==> SDA low CALL #T_I2C \ l _ wait 4 us BIC.B #SMSCL,&I2CSM_DIR \ _| P1DIR.2 release SCL (high) MOV #5,Y \ 2 tSU:STO time 8 MHz value \ MOV #15,Y \ 2 tSU:STO time 16MHz value \ MOV #25,Y \ 2 tSU:STO time 24MHz value CALL #T_I2C \ _ wait tSU:STO BIC.B #SMSDA,&I2CSM_DIR \ _| P1DIR.2 as input ==> SDA high with pull up resistor SUB.B T,&I2CM_BUF \ 4 l refresh buffer length and reach tSU:STO MOV @RSP+,PC \ ====> \ ----------------------------\ THEN \ \ ----------------------------\ MOV.B #8,W \ 1 l prepare 8 bits transaction BIT.B #1,&I2CS_ADR \ 3 l I2C_Master Read/write bit test 0= IF \ 2 l write flag test \ ------------------------\ \ I2C write \ \ ------------------------\ MOV.B @S+,X \ 2 l next byte to transmit CALL #I2C_MTX \ 4 to send data + test ack ELSE \ l \ ------------------------\ \ I2C read \ \ ========================\ \ I2C_Master_RX: \ here, SDA is indetermined, SCL is strech low by master \ ========================\ BEGIN \ BIC.B #SMSDA,&I2CSM_DIR \ 4 l _ P1DIR.2 as input ==> release SDA high because pull up resistor MOV #3,Y \ 2 tLOW time complement @ 8 MHz \ MOV #15,Y \ 2 tLOW time complement @ 16MHz \ MOV #24,Y \ 2 tLOW time complement @ 24MHz CALL #I2C_PLS \ _| |_ SCL + BIT SDA input (SDA-->carry) ADDC.B X,X \ 1 l C <-- X <--- C SUB #1,W \ 1 l count of bits 0= UNTIL \ 2 l MOV.B X, 0(S) \ 3 l store byte in buffer ADD #1,S \ 1 l \ ------------------------\ \ Compute Ack Or Nack \ here, SDA is released by slave, SCL is strech low by master \ ------------------------\ CMP #1,T \ 1 l here, SDA is released by slave = Nack 0<> IF \ 2 BIS.B #SMSDA,&I2CSM_DIR \ 4 l send Ack if byte count <> 1 THEN \ l THEN \ AGAIN \ l ENDASM \ \ \ ------------------------------\ CODE START \ \ ------------------------------\ \ init PORTA (P2:P1) (complement) when reset occurs all I/O are set in input with resistors pullup BIC.B #SM_BUS,&I2CSM_OUT \ P1OUT.32 preset SDA + SCL output low BIC.B #SM_BUS,&I2CSM_REN \ P1REN.32 SDA + SCL pullup/down disable \ ------------------------------\ LO2HI ." \ 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 ;