1 2 list p=16f88 3 include <p16f88.inc> 4 include <coff.inc> 5 6 __CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _INTRC_IO & _PWRTE_ON & _LVP_OFF & _BODEN_OFF & _MCLR_OFF 7 __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF 8 9 ;; The purpose of this program is to test gpsim's ability to simulate a pic 16F88. 10 ;; Specifically, I2C 11 12 errorlevel -302 13 14; Printf Command 15.command macro x 16 .direct "C", x 17 endm 18 19_ClkIn equ 8000000 ; Input Clock Frequency 20 21_ClkOut equ (_ClkIn >> 2) 22 23; 24; Compute the delay constants for setup & hold times 25; 26_40uS_Delay set (_ClkOut/250000) 27_47uS_Delay set (_ClkOut/212766) 28_50uS_Delay set (_ClkOut/200000) 29 30 31TRUE equ 1 32FALSE equ 0 33 34LSB equ 0 35MSB equ 7 36 37#define SCL_PIN 2 38#define SDA_PIN 3 39#define I2CPORT PORTB 40#define _SCL_D I2CPORT,SCL_PIN 41#define _SCL TRISB,SCL_PIN 42#define _SDA TRISB,SDA_PIN 43 44#define T0IE TMR0IE 45#define T0IF TMR0IF 46 47#define _ENABLE_BUS_FREE_TIME TRUE 48#define _CLOCK_STRETCH_CHECK TRUE 49#define _OPTION_INIT (0xC0 | 0x02) ; Prescaler to TMR0 for Appox 1 mSec timeout 50 51;***************************************************************************** 52; I2C Bus Status Reg Bit Definitions 53;***************************************************************************** 54 55#define _Bus_Busy Bus_Status,0 56#define _Abort Bus_Status,1 57#define _Txmt_Progress Bus_Status,2 58#define _Rcv_Progress Bus_Status,3 59 60#define _Txmt_Success Bus_Status,4 61#define _Rcv_Success Bus_Status,5 62#define _Fatal_Error Bus_Status,6 63#define _ACK_Error Bus_Status,7 64 65;***************************************************************************** 66; I2C Bus Contro Register 67;***************************************************************************** 68#define _10BitAddr Bus_Control,0 69#define _Slave_RW Bus_Control,1 70#define _Last_Byte_Rcv Bus_Control,2 71 72#define _SlaveActive Bus_Control,6 73#define _TIME_OUT_ Bus_Control,7 74 75 76 77RELEASE_BUS MACRO 78 bsf STATUS,RP0 ; select page 1 79 bsf _SDA ; tristate SDA 80 bsf _SCL ; tristate SCL 81; bcf _Bus_Busy ; Bus Not Busy, TEMP ????, set/clear on Start & Stop 82 ENDM 83 84 85;**************************************************************************** 86; A MACRO To Load 8 OR 10 Bit Address To The Address Registers 87; 88; SLAVE_ADDRESS is a constant and is loaded into the SlaveAddress Register(s) 89; depending on 8 or 10 bit addressing modes 90;**************************************************************************** 91 92LOAD_ADDR_10 MACRO SLAVE_ADDRESS 93 94 bsf _10BitAddr ; Slave has 10 bit address 95 movlw (SLAVE_ADDRESS & 0xff) 96 movwf SlaveAddr ; load low byte of address 97 movlw (((SLAVE_ADDRESS >> 7) & 0x06) | 0xF0) ; 10 bit addr 11110AA0 98 movwf SlaveAddr+1 ; hi order address 99 100 ENDM 101 102LOAD_ADDR_8 MACRO SLAVE_ADDRESS 103 104 bcf _10BitAddr ; Set for 8 Bit Address Mode 105 movlw (SLAVE_ADDRESS & 0xff) 106 movwf SlaveAddr 107 108 ENDM 109 110;**************************************************************************** 111; I2C_WRITE_SUB 112; 113; Writes a message just like I2C_WRITE, except that the data is preceeded 114; by a sub-address to a slave device. 115; Eg. : A serial EEPROM would need an address of memory location for 116; Random Writes 117; 118; Parameters : 119; _BYTES_ #of bytes starting from RAM pointer _SourcePointer_ (constant) 120; _SourcePointer_ Data Start Buffer pointer in RAM (file Registers) 121; _Sub_Address_ Sub-address of Slave (constant) 122; 123; Sequence : 124; S-SlvAW-A-SubA-A-D[0]-A.....A-D[N-1]-A-P 125; 126; If an error occurs then the routine simply returns and user should check for 127; flags in Bus_Status Reg (for eg. _Txmt_Success flag 128; 129; Returns : WREG = 1 on success, else WREG = 0 130; 131; NOTE : The address of the slave must be loaded into SlaveAddress Registers, 132; and 10 or 8 bit mode addressing must be set 133; 134; COMMENTS : 135; I2C_WR may prove to be more efficient than this macro in most situations 136; Advantages will be found for Random Address Block Writes for Slaves with 137; Auto Increment Sub-Addresses (like Microchip's 24CXX series Serial 138; EEPROMS) 139; 140;**************************************************************************** 141 142I2C_WR_SUB MACRO _BYTES_, _SourcePointer_, _Sub_Address_ 143 144 movlw (_BYTES_ + 1) 145 movwf tempCount 146 147 movlw (_SourcePointer_ - 1) 148 movwf FSR 149 150 movf INDF,W 151 movwf StoreTemp_1 ; temporarily store contents of (_SourcePointer_ -1) 152 movlw _Sub_Address_ 153 movwf INDF ; store temporarily the sub-address at (_SourcePointer_ -1) 154 155 call _i2c_block_write ; write _BYTES_+1 block of data 156 157 movf StoreTemp_1,W 158 movwf (_SourcePointer_ - 1) ; restore contents of (_SourcePointer_ - 1) 159 160; call TxmtStopBit ; Issue a stop bit to end transmission 161 162 ENDM 163I2C_WR_SUB2 MACRO _BYTES_, _SourcePointer_, _Sub_Address_, _Sub_Address2_ 164 165 movlw (_BYTES_ + 2) 166 movwf tempCount 167 168 movlw (_SourcePointer_ - 2) 169 movwf FSR 170 171 movf INDF,W 172 movwf StoreTemp_1 ; temporarily store contents of (_SourcePointer_ -2) 173 movlw _Sub_Address_ 174 movwf INDF ; store temporarily the sub-address at (_SourcePointer_ -2) 175 movlw (_SourcePointer_ - 1) 176 movwf FSR 177 movlw _Sub_Address2_ 178 movwf INDF 179 movlw (_SourcePointer_ - 2) 180 movwf FSR 181 182 call _i2c_block_write ; write _BYTES_+1 block of data 183 184 movf StoreTemp_1,W 185; movwf (_SourcePointer_ - 2) ; restore contents of (_SourcePointer_ - 1) 186 187; call TxmtStopBit ; Issue a stop bit to end transmission 188 189 ENDM 190 191 192;**************************************************************************** 193; I2C_WRITE 194; 195; A basic macro for writing a block of data to a slave 196; 197; Parameters : 198; _BYTES_ #of bytes starting from RAM pointer _SourcePointer_ 199; _SourcePointer_ Data Start Buffer pointer in RAM (file Registers) 200; 201; Sequence : 202; S-SlvAW-A-D[0]-A.....A-D[N-1]-A-P 203; 204; If an error occurs then the routine simply returns and user should check for 205; flags in Bus_Status Reg (for eg. _Txmt_Success flag) 206; 207; NOTE : The address of the slave must be loaded into SlaveAddress Registers, 208; and 10 or 8 bit mode addressing must be set 209;**************************************************************************** 210 211 212I2C_WR MACRO _BYTES_, _SourcePointer_ 213 214 movlw _BYTES_ 215 movwf tempCount 216 movlw _SourcePointer_ 217 movwf FSR 218 219 call _i2c_block_write 220 call TxmtStopBit ; Issue a stop bit for slave to end transmission 221 222 ENDM 223 224;***************************************************************************** 225; 226; I2C_READ 227; 228; The basic MACRO/procedure to read a block message from a slave device 229; 230; Parameters : 231; _BYTES_ : constant : #of bytes to receive 232; _DestPointer_ : destination pointer of RAM (File Registers) 233; 234; Sequence : 235; S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P 236; 237; If last byte, then Master will NOT Acknowledge (send NACK) 238; 239; NOTE : The address of the slave must be loaded into SlaveAddress Registers, 240; and 10 or 8 bit mode addressing must be set 241; 242;***************************************************************************** 243 244I2C_READ MACRO _BYTES_, _DestPointer_ 245 246 247 movlw (_BYTES_ -1) 248 movwf tempCount ; -1 because, the last byte is used out of loop 249 movlw _DestPointer_ 250 movwf FSR ; FIFO destination address pointer 251 252 call _i2c_block_read 253 254 ENDM 255 256;*************************************************************************** 257; 258; I2C_READ_SUB 259; This MACRO/Subroutine reads a message from a slave device preceeded by 260; a write of the sub-address. 261; Between the sub-addrers write & the following reads, a STOP condition 262; is not issued and a "REPEATED START" condition is used so that an other 263; master will not take over the bus, and also that no other master will 264; overwrite the sub-address of the same salve. 265; 266; This function is very commonly used in accessing Random/Sequential reads 267; from a memory device (e.g : 24Cxx serial of Serial EEPROMs from Microchip). 268; 269; Parameters : 270; _BYTES_ # of bytes to read 271; _DestPointer_ The destination pointer of data to be received. 272; _BubAddress_ The sub-address of the slave 273; 274; Sequence : 275; S-SlvAW-A-SubAddr-A-S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P 276; 277; 278;*************************************************************************** 279 280I2C_READ_SUB MACRO _BYTES_, _DestPointer_, _SubAddress_ 281 282 bcf _Slave_RW ; set for write operation 283 call TxmtStartBit ; send START bit 284 call Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set 285 286 287 movlw _SubAddress_ 288 movwf DataByte ; START address of EEPROM(slave 1) 289 call SendData ; write sub address 290; 291; do not send STOP after this, use REPEATED START condition 292; 293 294 I2C_READ _BYTES_, _DestPointer_ 295 296 ENDM 297I2C_READ_SUB2 MACRO _BYTES_, _DestPointer_, _SubAddress_, _SubAddress2_ 298 299 bcf _Slave_RW ; set for write operation 300 call TxmtStartBit ; send START bit 301 call Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set 302 303 304 movlw _SubAddress_ 305 movwf DataByte ; START address of EEPROM(slave 1) 306 call SendData ; write sub address 307 movlw _SubAddress2_ 308 movwf DataByte ; START address of EEPROM(slave 1) 309 call SendData ; write sub address 310; 311; do not send STOP after this, use REPEATED START condition 312; 313 314 I2C_READ _BYTES_, _DestPointer_ 315 316 ENDM 317;---------------------------------------------------------------------- 318;---------------------------------------------------------------------- 319INT_VAR UDATA 320 321IO_buf RES 10 322IN_buf RES 10 323 324GPR_DATA UDATA_SHR 325 326w_temp RES 1 327status_temp RES 1 328SlaveAddr RES 1 ; Slave Addr must be loader into this reg 329SlaveAddrHi RES 1 ; for 10 bit addressing mode 330DataByte RES 1 ; load this reg with the data to be transmitted 331BitCount RES 1 ; The bit number (0:7) transmitted or received 332Bus_Status RES 1 ; Status Reg of I2C Bus for both TXMT & RCVE 333Bus_Control RES 1 ; control Register of I2C Bus 334DelayCount RES 1 335DataByteCopy RES 1 ; copy of DataByte for Left Shifts (destructive) 336 337SubAddr RES 1 ; sub-address of slave (used in I2C_HIGH.ASM) 338SrcPtr RES 1 ; source pointer for data to be transmitted 339 340tempCount RES 1 ; a temp variable for scratch RAM 341StoreTemp_1 RES 1 ; a temp variable for scratch RAM, do not disturb contents 342 343_End_I2C_Ram RES 1 ; unused, only for ref of end of RAM allocation 344 345 346 347;---------------------------------------------------------------------- 348; ********************* RESET VECTOR LOCATION ******************** 349;---------------------------------------------------------------------- 350RESET_VECTOR CODE 0x000 ; processor reset vector 351 movlw high start ; load upper byte of 'start' label 352 movwf PCLATH ; initialize PCLATH 353 goto start ; go to beginning of program 354 355 ;; 356 ;; Interrupt 357 ;; 358INT_VECTOR CODE 0x004 ; interrupt vector location 359 movwf w_temp 360 swapf STATUS,W 361 movwf status_temp 362 bcf STATUS,RP0 ;adcon0 is in bank 0 363 364 if _CLOCK_STRETCH_CHECK ; TMR0 Interrupts enabled only if Clock Stretching is Used 365 btfss INTCON,T0IF 366 goto check ; other Interrupts 367 bsf _TIME_OUT_ ; MUST set this Flag 368 bcf INTCON,T0IF 369 goto int_ret 370 endif 371 372 373check: 374 btfss PIR1,SSPIF 375 goto int_ret 376 bcf PIR1,SSPIF 377 call sspint 378int_ret: 379 swapf status_temp,w 380 movwf STATUS 381 swapf w_temp,F 382 swapf w_temp,W 383 retfie 384 385 386 387;---------------------------------------------------------------------- 388; ******************* MAIN CODE START LOCATION ****************** 389;---------------------------------------------------------------------- 390MAIN CODE 391start: 392 393 .sim "module lib libgpsim_modules" 394 .sim "p16f88.xpos = 96" 395 .sim "p16f88.ypos = 144" 396 397 .sim "module load pu pu1" 398 .sim "pu1.xpos = 276" 399 .sim "pu1.ypos = 72" 400 401 .sim "module load pu pu2" 402 .sim "pu2.xpos = 96" 403 .sim "pu2.ypos = 48" 404 405 .sim "node n1" 406 .sim "attach n1 portb2 pu1.pin portb4" ; ee.SCL" 407 .sim "node n2" 408 .sim "attach n2 portb3 pu2.pin portb1" ; ee.SDA" 409 .sim "node n3" 410 ;.sim "attach n3 porta0 ee.WP" 411 .sim "node n4" 412 ;.sim "attach n4 porta1 ee.A0" 413 .sim "node n5" 414 ;.sim "attach n5 porta2 ee.A1" 415 .sim "node n6" 416 ;.sim "attach n6 porta3 ee.A2" 417 .sim "scope.ch0 = 'portb2'" 418 .sim "scope.ch1 = 'portb3'" 419 420 421 bsf STATUS,RP0 ; bank 1 422 movlw 0xf6 ; set internal RC to 8 Mhz 423 movwf OSCCON 424 clrf TRISA 425 bsf PIE1,SSPIE ; allow SSP interrupts 426 bsf INTCON,GIE ; allow interrupts 427 bsf INTCON,PEIE ; allow interrupts 428 bcf STATUS,RP0 ; bank 0 429; bsf PORTA,0 ; Write protect 430 bsf PORTA,1 ; A0 431 movlw 0x10 432 movwf tempCount 433 movlw IO_buf 434 movwf FSR 435 436Fill_loop: 437 movf FSR,W 438 movwf INDF 439 incf FSR,F 440 decfsz tempCount,F 441 goto Fill_loop 442 443 movlw 0x36 444 movwf SSPCON 445 bsf STATUS,RP0 ; bank 1 446 movlw 0xa2 447 movwf SSPADD 448 bcf STATUS,RP0 ; bank 0 449 450 call InitI2CBus_Master 451 452 LOAD_ADDR_8 0xa2 453 call IsSlaveActive 454 btfss _SlaveActive 455 .assert "'Slave not active'" 456 nop 457 LOAD_ADDR_8 0xa2 458; I2C_WR_SUB 8, IO_buf, 0x0c 459 I2C_WR_SUB2 8, IO_buf, 0x0c, 0x0c 460 call TxmtStopBit ; Issue a stop bit to end transmission 461 movf Bus_Status,W 462 .assert "W == 0x10, '*** FAILED I2C write status'" 463 nop 464 465poll_ready: 466 LOAD_ADDR_8 0xa2 467 call IsSlaveActive 468 btfss _SlaveActive 469 goto poll_ready ; slave not active yet 470 nop 471 472; 473; write 0xa2 0x0c 0x0c to set an address 474; write RSTART 0xa3 to initiate read 475; read 8 bytes of data into ram starting at IN_buf 476; 477 LOAD_ADDR_8 0xa2 478 I2C_READ_SUB2 8, IN_buf, 0x0c, 0x0c 479 nop 480 bcf STATUS,RP0 ; bank 0 481 movf IN_buf,W 482 .assert "W == 0xf5, '*** FAILED read data'" 483 nop 484 movf Bus_Status,W 485 .assert "W == 0x30, '*** FAILED 8 bit read status'" 486 nop 487 488; 489; write 8 bytes of data in slave 10bit mode 490; 491 bcf STATUS,RP0 ; bank 0 492 movlw 0x37 493 movwf SSPCON 494 bsf STATUS,RP0 ; bank 1 495 movlw 0xf0 496 movwf SSPADD 497 bcf STATUS,RP0 ; bank 0 498 LOAD_ADDR_10 0x0c 499 I2C_WR 8, IO_buf 500 501 .assert "'*** PASSED p16f88 I2C test'" 502 nop 503 504 goto $ 505 506IsSlaveActive 507 bcf _Slave_RW ; set for write operation 508 call TxmtStartBit ; send START bit 509 call Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set 510; 511 bcf _SlaveActive 512 btfss _ACK_Error ; skip if NACK, device is not present or not responding 513 bsf _SlaveActive ; ACK received, device present & listening 514 call TxmtStopBit 515 return 516 517include "i2c_low.inc" 518 519 520_i2c_block_write: 521 call TxmtStartBit ; send START bit 522 bcf _Slave_RW ; set for write operation 523 call Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set 524; 525_block_wr1_loop: 526 btfss _Txmt_Success 527 return 528 movf INDF,W 529 movwf DataByte ; start from the first byte starting at _DataPointer_ 530 incf FSR, F 531 call SendData ; send next byte, bus is our's ! 532 decfsz tempCount, F 533 goto _block_wr1_loop ; loop until desired bytes of data 534 ; transmitted to slave 535 return 536; 537;**************************************************************************** 538 539_i2c_block_read: 540 call TxmtStartBit ; send START bit 541 bsf _Slave_RW ; set for read operation 542 bcf _Last_Byte_Rcv ; not a last byte to rcv 543 call Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set 544 btfsc _Txmt_Success 545 goto _block_rd1_loop ; end 546 call TxmtStopBit ; Issue a stop bit for slave to end transmission 547 retlw FALSE ; Error : may be device not responding 548; 549_block_rd1_loop: 550 call GetData 551 movf DataByte,W 552 movwf INDF ;start receiving data, starting at Destination Pointer 553 incf FSR, F 554 decfsz tempCount, F 555 goto _block_rd1_loop ; loop until desired bytes of data transmitted to slave 556 bsf _Last_Byte_Rcv ; last byte to rcv, so send NACK 557 call GetData 558 movf DataByte,W 559 movwf INDF 560 call TxmtStopBit ; Issue a stop bit for slave to end transmission 561 retlw TRUE 562 563sspint: 564 bsf STATUS,RP0 ; bank 1 565 btfsc SSPSTAT,R_W ; write test ? 566 goto sspint_wr 567 btfsc SSPSTAT,UA ; UA bit set 568 goto sspint_ua 569 570 bcf STATUS,RP0 ; bank 0 571 movf SSPBUF,W 572 return 573 574sspint_wr: 575 bcf STATUS,RP0 ; bank 0 576 movlw 0xf5 ; byte to send 577 movwf SSPBUF 578 bsf SSPCON,CKP ; turn off clock stretch 579 return 580 581sspint_ua: 582 movlw 0x0c ; second byte address 583 movwf SSPADD 584 bcf STATUS,RP0 ; bank 0 585 movf SSPBUF,W 586 return 587 588 end 589