1;**************************************************************************** 2; 3; Low Level I2C Routines 4; 5; Single Master Transmitter & Single Master Receiver Routines 6; These routines can very easily be converted to Multi-Master System 7; when PIC16C6X with on chip I2C Slave Hardware, Start & Stop Bit 8; detection is available. 9; 10; The generic high level routines are given in I2C_HIGH.ASM 11; 12; 13; Program: I2C_LOW.ASM 14; Revision Date: 15; 1-16-97 Compatibility with MPASMWIN 1.40 16; 17;*************************************************************************** 18 19 20 21 22 23;************************************************************************** 24; I2C Bus Initialization 25; 26;************************************************************************** 27InitI2CBus_Master: 28 29 bcf STATUS,RP0 30 movlw ~(1<<SCL_PIN | 1<<SDA_PIN) 31 andwf PORTB,F ; must not use BSF, BCF on portb 32 ; set SDA & SCL to zero. From Now on, simply play with tris 33 RELEASE_BUS 34 clrf Bus_Status ; reset status reg 35 clrf Bus_Control ; clear the Bus_Control Reg, reset to 8 bit addressing 36 return 37; 38;************************************************************************** 39; Send Start Bit 40; 41;************************************************************************** 42 43TxmtStartBit: 44 bsf STATUS,RP0 ; select page 1 45 bsf _SDA ; set SDA high 46 bsf _SCL ; clock to high 47; 48; Setup time for a REPEATED START condition (4.7 uS) 49; 50 call Delay40uSec ; only necesry for setup time 51; 52 bcf _SDA ; give a falling edge on SDA while clock is high 53; 54 call Delay47uSec ; only necessary for START HOLD time 55; 56 bsf _Bus_Busy ; on a start condition bus is busy 57; 58 return 59 60 61;********************************************************************************************************* 62; Send Stop Bit 63; 64;********************************************************************************************************* 65 66TxmtStopBit: 67 bsf STATUS,RP0 ; select page 1 68 bcf _SCL 69 bcf _SDA ; set SDA low 70 bsf _SCL ; Clock is pulled up 71 call Delay40uSec ; Setup Time For STOP Condition 72 bsf _SDA ; give a rising edge on SDA while CLOCK is high 73; 74 if _ENABLE_BUS_FREE_TIME 75; delay to make sure a START bit is not sent immediately after a STOP, ensure BUS Free Time tBUF 76; 77 call Delay47uSec 78 endif 79; 80 bcf _Bus_Busy ; on a stop condition bus is considered Free 81; 82 return 83 84 85;********************************************************************************************************* 86; Abort Transmission 87; 88; Send STOP Bit & set Abort Flag 89;********************************************************************************************************* 90 91AbortTransmission: 92 93 call TxmtStopBit 94 bsf _Abort 95 return 96 97;********************************************************************************************************* 98; Transmit Address (1st Byte)& Put in Read/Write Operation 99; 100; Transmits Slave Addr On the 1st byte and set LSB to R/W operation 101; Slave Address must be loaded into SlaveAddr reg 102; The R/W operation must be set in Bus_Status Reg (bit _SLAVE_RW): 0 for Write & 1 for Read 103; 104; On Success, return TRUE in WREG, else FALSE in WREG 105; 106; If desired, the failure may tested by the bits in Bus Status Reg 107; 108;********************************************************************************************************* 109 110Txmt_Slave_Addr: 111 bcf _ACK_Error ; reset Acknowledge error bit 112 btfss _10BitAddr 113 goto SevenBitAddr 114; 115 btfss _Slave_RW 116 goto TenBitAddrWR ; For 10 Bit WR simply send 10 bit addr 117; 118; Required to READ a 10 bit slave, so first send 10 Bit for WR & Then Repeated Start 119; and then Hi Byte Only for read opreation 120; 121TenBitAddrRd: 122 123 bcf _Slave_RW ; temporarily set for WR operation 124 call TenBitAddrWR 125 btfss _Txmt_Success ; skip if successful 126 retlw FALSE 127 128 call TxmtStartBit ; send A REPEATED START condition 129 bsf _Slave_RW ; For 10 bit slave Read 130 131 movf SlaveAddr+1,W 132 movwf DataByte 133 bsf DataByte,LSB ; Read Operation 134 call SendData ; send ONLY high byte of 10 bit addr slave 135 goto _AddrSendTest ; 10 Bit Addr Send For Slave Read Over 136; 137; if successfully transmitted, expect an ACK bit 138; 139 btfss _Txmt_Success ; if not successful, generate STOP & abort transfer 140 goto _AddrSendFail 141; 142 143TenBitAddrWR: 144 145 movf SlaveAddr+1,W 146 movwf DataByte 147 bcf DataByte,LSB ; WR Operation 148; 149; Ready to transmit data : If Interrupt Driven (i.e if Clock Stretched LOW Enabled) 150; then save RETURN Address Pointer 151; 152 call SendData ; send high byte of 10 bit addr slave 153; 154; if successfully transmitted, expect an ACK bit 155; 156 btfss _Txmt_Success ; if not successful, generate STOP & abort transfer 157 goto _AddrSendFail 158; 159 movf SlaveAddr,W 160 movwf DataByte ; load addr to DatByte for transmission 161 goto EndTxmtAddr 162 163SevenBitAddr: 164 movf SlaveAddr,W 165 movwf DataByte ; load addr to DatByte for transmission 166 bcf DataByte,LSB 167 btfsc _Slave_RW ; if skip then write operation 168 bsf DataByte,LSB ; Read Operation 169 170EndTxmtAddr: 171 call SendData ; send 8 bits of address, bus is our's 172; 173; if successfully transmitted, expect an ACK bit 174; 175_AddrSendTest: 176 btfss _Txmt_Success ; skip if successful 177 goto _AddrSendFail 178 clrwdt 179 retlw TRUE 180; 181_AddrSendFail: 182 clrwdt 183 btfss _ACK_Error 184 retlw FALSE ; Addr Txmt Unsuccessful, so return 0 185; 186; Address Not Acknowledged, so send STOP bit 187; 188 call TxmtStopBit 189 retlw FALSE ; Addr Txmt Unsuccessful, so return 0 190; 191;********************************************************************************************************* 192; Transmit A Byte Of Data 193; 194; The data to be transmitted must be loaded into DataByte Reg 195; Clock stretching is allowed by slave. If the slave pulls the clock low, then, the stretch is detected 196; and INT Interrupt on Rising edge is enabled and also TMR0 timeout interrupt is enabled 197; The clock stretching slows down the transmit rate because all checking is done in 198; software. However, if the system has fast slaves and needs no clock stretching, then 199; this feature can be disabled during Assembly time by setting 200; _CLOCK_STRETCH_ENABLED must be set to FALSE. 201; 202;********************************************************************************************************* 203SendData: 204 205; 206; TXmtByte & Send Data are same, Can check errors here before calling TxmtByte 207; For future compatibility, the user MUST call SendData & NOT TxmtByte 208; 209 goto TxmtByte 210 211; 212TxmtByte: 213 movf DataByte,W 214 movwf DataByteCopy ; make copy of DataByte 215 bsf _Txmt_Progress ; set Bus status for txmt progress 216 bcf _Txmt_Success ; reset status bit 217 movlw 0x08 218 movwf BitCount 219 bsf STATUS,RP0 220 if _CLOCK_STRETCH_CHECK 221; set TMR0 to INT CLK timeout for 1 mSec 222; do not disturb user's selection of RPUB in OPTION Register 223; 224 movf OPTION_REG,W 225 andlw _OPTION_INIT ; defined in I2C.H header file 226 movwf OPTION_REG 227 endif 228 229TxmtNextBit: 230 clrwdt ; clear WDT, set for 18 mSec 231 bcf _SCL 232 rlf DataByteCopy, F ; MSB first, Note DataByte Is Lost 233 bcf _SDA 234 btfsc STATUS,C 235 bsf _SDA 236 call Delay47uSec ; guareentee min LOW TIME tLOW & Setup time 237 bsf _SCL ; set clock high , check if clock is high, else clock being stretched 238 call Delay40uSec ; guareentee min HIGH TIME tHIGH 239 if _CLOCK_STRETCH_CHECK 240 bcf STATUS,RP0 241 clrf TMR0 ; clear TMR0 242 bcf INTCON,T0IF ; clear any pending flags 243 bsf INTCON,T0IE ; elable TMR0 Interrupt 244 bcf _TIME_OUT_ ; reset timeout error flag 245Check_SCL_1: 246 btfsc _TIME_OUT_ ; if TMR0 timeout or Error then Abort & return 247 goto Bus_Fatal_Error ; Possible FATAL Error on Bus 248 bcf STATUS,RP0 249 btfss _SCL_D ; if clock not being stretched, it must be high 250 goto Check_SCL_1 ; loop until SCL high or TMR0 timeout interrupt 251 bcf INTCON,T0IE ; Clock good, diable TMR0 interrupts 252 bsf STATUS,RP0 253 endif 254 decfsz BitCount, F 255 goto TxmtNextBit 256; 257; Check For Acknowledge 258; 259 bcf _SCL ; reset clock 260 bsf _SDA ; Release SDA line for Slave to pull down 261 call Delay47uSec ; guareentee min LOW TIME tLOW & Setup time 262 bsf _SCL ; clock for slave to ACK 263 call Delay40uSec ; guareentee min HIGH TIME tHIGH 264 bcf STATUS,RP0 ; select PAGE 0 to test PortB pin SDA 265 btfsc _SDA ; SDA should be pulled low by slave if OK 266 goto _TxmtErrorAck 267; 268 bsf STATUS,RP0 269 bcf _SCL ; reset clock 270 271 bcf _Txmt_Progress ; reset TXMT bit in Bus Status 272 bsf _Txmt_Success ; transmission successful 273 bcf _ACK_Error ; ACK OK 274 return 275_TxmtErrorAck: 276 RELEASE_BUS 277 bcf _Txmt_Progress ; reset TXMT bit in Bus Status 278 bcf _Txmt_Success ; transmission NOT successful 279 bsf _ACK_Error ; No ACK From Slave 280 return 281; 282;********************************************************************************************************* 283; 284; Receive A Byte Of Data From Slave 285; 286; assume address is already sent 287; if last byte to be received, do not acknowledge slave (last byte is testted from 288; _Last_Byte_Rcv bit of control reg) 289; Data Received on successful reception is in DataReg register 290; 291; 292;********************************************************************************************************* 293; 294 295GetData: 296 goto RcvByte 297; 298RcvByte: 299 300 bsf _Rcv_Progress ; set Bus status for txmt progress 301 bcf _Rcv_Success ; reset status bit 302 303 movlw 0x08 304 movwf BitCount 305 if _CLOCK_STRETCH_CHECK 306 bsf STATUS,RP0 307; set TMR0 to INT CLK timeout for 1 mSec 308; do not disturb user's selection of RPUB in OPTION Register 309; 310 movf OPTION_REG,W 311 andlw _OPTION_INIT ; defined in I2C.H header file 312 movwf OPTION_REG 313 endif 314 315RcvNextBit: 316 clrwdt ; clear WDT, set for 18 mSec 317 bsf STATUS,RP0 ; page 1 for TRIS manipulation 318 bcf _SCL 319 bsf _SDA ; can be removed from loop 320 call Delay47uSec ; guareentee min LOW TIME tLOW & Setup time 321 bsf _SCL ; clock high, data sent by slave 322 call Delay40uSec ; guareentee min HIGH TIME tHIGH 323 if _CLOCK_STRETCH_CHECK 324 bcf STATUS,RP0 325 clrf TMR0 ; clear TMR0 326 bcf INTCON,T0IF ; clear any pending flags 327 bsf INTCON,T0IE ; elable TMR0 Interrupt 328 bcf _TIME_OUT_ ; reset timeout error flag 329Check_SCL_2: 330 btfsc _TIME_OUT_ ; if TMR0 timeout or Error then Abort & return 331 goto Bus_Fatal_Error ; Possible FATAL Error on Bus 332 bcf STATUS,RP0 333 btfss _SCL_D ; if clock not being stretched, it must be high 334 goto Check_SCL_2 ; loop until SCL high or TMR0 timeout interrupt 335 bcf INTCON,T0IE ; Clock good, diable TMR0 interrupts 336 bsf STATUS,RP0 337 endif 338 bcf STATUS,RP0 ; select page 0 to read Ports 339 bcf STATUS,C 340 btfsc _SDA 341 bsf STATUS,C 342; ; TEMP ???? DO 2 out of 3 Majority detect 343 rlf DataByte, F ; left shift data ( MSB first) 344 decfsz BitCount, F 345 goto RcvNextBit 346; 347; Generate ACK bit if not last byte to be read, 348; if last byte Gennerate NACK ; do not send ACK on last byte, main routine will send a STOP bit 349; 350 bsf STATUS,RP0 351 bcf _SCL 352 bcf _SDA ; ACK by pulling SDA low 353 btfsc _Last_Byte_Rcv 354 bsf _SDA ; if last byte, send NACK by setting SDA high 355 call Delay47uSec ; guareentee min LOW TIME tLOW & Setup time 356 bsf _SCL 357 call Delay40uSec ; guareentee min HIGH TIME tHIGH 358RcvEnd: 359 bcf _SCL ; reset clock 360 361 bcf _Rcv_Progress ; reset TXMT bit in Bus Status 362 bsf _Rcv_Success ; transmission successful 363 bcf _ACK_Error ; ACK OK 364 365 return 366 367 if _CLOCK_STRETCH_CHECK 368;********************************************************************************************************* 369; Fatal Error On I2C Bus 370; 371; Slave pulling clock for too long or if SCL Line is stuck low. 372; This occurs if during Transmission, SCL is stuck low for period longer than appox 1mS 373; and TMR0 times out ( appox 4096 cycles : 256 * 16 -- prescaler of 16). 374; 375;********************************************************************************************************* 376 377Bus_Fatal_Error: 378 379; diable TMR0 Interrupt 380; 381 bcf INTCON,T0IE ; disable TMR0 interrupts, until next TXMT try 382 383 RELEASE_BUS 384; 385; Set the Bus_Status Bits appropriately 386; 387 .assert "'FAILED IC2 Clock stretch timeout'" 388 nop 389 bsf _Abort ; transmission was aborted 390 bsf _Fatal_Error ; FATAL Error occured 391 bcf _Txmt_Progress ; Transmission Is Not in Progress 392 bcf _Txmt_Success ; Transmission Unsuccesful 393; 394 call TxmtStopBit ; Try sending a STOP bit, may be not successful 395; 396 return 397; 398;********************************************************************************************************* 399 endif 400 401 402;********************************************************************************************************* 403; General Purpose Delay Routines 404; 405; Delay4uS is wait loop for 4.0 uSec 406; Delay47uS is wait loop for 4.7 uSec 407; Delay50uS is wait loop for 5.0 uSec 408; 409;********************************************************************************************************* 410; 411 412Delay50uSec: 413 movlw ((_50uS_Delay-5)/3 + 1) 414DlyK 415 movwf DelayCount 416 decfsz DelayCount, F 417 goto $-1 418 return 419; 420Delay47uSec: 421 movlw ((_47uS_Delay-8)/3 + 1) 422 goto DlyK 423; 424Delay40uSec: 425 movlw ((_40uS_Delay-8)/3 + 1) 426 goto DlyK 427; 428;********************************************************************************************************* 429