CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PIC24 I2C routines in assembly

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
allenhuffman



Joined: 17 Jun 2019
Posts: 660
Location: Des Moines, Iowa, USA

View user's profile Send private message Visit poster's website

PIC24 I2C routines in assembly
PostPosted: Wed Mar 04, 2026 7:57 pm     Reply with quote

While digging through some files tonight, I ran across this one. I am unsure where this came from, though it might have been something I made when looking at the .lst file to understand how things like i2c_read() and i2c_write() worked in the CCS PCD compiler.

I am sharing this here to see if anyone recognizes it.

If I compiled it, I did not know then that the registers/bits can vary from PIC24 to PIC24, and with no notes on which PIC24 this was for, I could probably figure it out by looking at data sheets.

Code:
===============================================================================
CCS PIC assmebly code for the i2c functions (currently just for Slave).
===============================================================================

....................     #use i2c (SLAVE, I2C3, FORCE_HW, stream=SYSTEM_BUS)
*******************************************************************************
* i2c_init - i2c_init(SYSTEM_BUS, 1);
*******************************************************************************
00B90:  BCLR.B  2E4.7   // LATE LATE7 to 0 (RE7, SDA data)
00B92:  BSET.B  277.7   // I2CEN 1=Enable
00B94:  BSET.B  277.5   // I2CSIDL 1=Stop when in idle mode
00B96:  BSET.B  276.6   // STREN 1=Enable receive clock stretching
00B98:  BCLR.B  277.3   // IMPIEN 0=Disabled
00B9A:  BSET.B  276.7   // GECN 1=Enable interrupt on general call address
00B9C:  BCLR.B  277.0   // SMEN 0=Disable SMBUS
00B9E:  BSET.B  277.1   // DISSLW 1=Disable slew rate control (yes, 1=disable)
00BA0:  BCLR.B  277.2   // A10M 0=7-bit slave address
00BA2:  CLR     27A      // I2C3ADD - address to 0.


*******************************************************************************
* i2c_slaveaddr - i2c_slaveaddr (SYSTEM_BUS, 100);
*******************************************************************************
00BA4:  MOV     #32,W4   // ????
00BA6:  MOV     W4,27A   // I2C3ADD - address


*******************************************************************************
* i2c_isr_state
*
* 85C     - @I2C_STATE (global, contains a counter)
* 974-975 - state (user return value, as an int16 in this sample)
*******************************************************************************
00824:  BTSC.B  278.5   // D/A - skip next if 0 (address)
                  //     if 0, go to ADDRESS
                  //     if 1, go to WRITE
00826:  BRA     830      // go to WRITE

* ADDRESS:
00828:  CLR.B   85C      // clear @I2C_STATE
                  //     @I2C_STATE = 0;
0082A:  BTSS.B  278.2   // R/W - skip next if 1 (read)
                  //     if 0, go to WRITE
                  //     if 1, go to READ
0082C:  BRA     830      // go to WRITE

* READ:
0082E:  BSET.B  85C.7   // set high bit in @I2C_STATE
                  //       @I2C_STATE |= 0b1xxxxxxxx;

* WRITE:
00830:  MOV.B   85C,W0L   // load @I2C_STATE value into 16-bit register (?)
                  //      reg16 = [xx | @I2C_STATE];
00832:  CLR.B   1      // clear first byte of 16-bit register (?)
                  //      reg16 = [00 | @I2C_STATE]
00834:  INC.B   085C   // increment @I2C_STATE
                  //      @I2C_STATE++;
...return value (pre-increment) to user...
00836:  MOV.B   W0L,974   // store new incremented value back to state
00838:  CLR.B   975      // clear other half of state
...back to user's code...


*******************************************************************************
* i2c_poll - data = i2c_poll()
*
* 860 - data (return value)
*******************************************************************************
00BD0:  CLR.B   860      // data = 0;
00BD2:  BTSC.B  278.1   // RBF - skip next if 0
                  //     if 0, go to SKIP (don't increment)
                  //     if 1, continue (increment)
00BD4:  INC.B   0860   // data++;
* SKIP:
...return...


*******************************************************************************
* i2c_read - data = i2c_read (SYSTEM_BUS, 0);
*
* 860 - data (return value)
*******************************************************************************
....................     data = i2c_read(SYSTEM_BUS, 0);
00BBC:  CLR.B   W1      // W1 is second parameter?
00BBE:  CALL    6C4      // read routine
00BC2:  MOV.B   W0L,860 // store byte in 'data'


*******************************************************************************
* i2c_read - data = i2c_read (SYSTEM_BUS, 1);
*
* 860 - data (return value)
*******************************************************************************
....................     data = i2c_read(SYSTEM_BUS, 1);
00BC4:  MOV.B   #1,W1L   // W1 is second parameter?
00BC6:  CALL    6C4      // read routine
00BCA:  MOV.B   W0L,860   // store byte in 'data'


**** 6C4: Read subroutine?
....................     #use i2c (SLAVE, I2C3, FORCE_HW, stream=SYSTEM_BUS)
*
006C4:  MOV     #FFFF,W0

* START:
006C6:  BTSS.B  278.3   // S - skip next if 0 (no start bit)
                  //     if 0, go to START.
                  //     if 1, go to GCSTAT.
006C8:  BRA     6C6      // go to START

* GCSTAT:
006CA:  BTSC.B  279.1   // GCSTAT - skip next if 1 (broadcast)
                  //     if 0, go to READ.
                  //     if 1, go to CHECK10BIT.
006CC:  BRA     6D6      // go to READ

* CHECK10BIT:
006CE:  BTSS.B  277.2   // A10M - skip next if 1 (10-bit address)
                  //     if 0, go to READ.
                  //     if 1, go to CHECK10MATCH.
006D0:  BRA     6D6      // go to READ

* CHECK10MATCH:
006D2:  BTSS.B  279.0   // ADD10 - skip next if 1 (10-bit matched)
                  //     if 0, go to START.
                  //     if 1, go to READ.
006D4:  BRA     6C6      // go to START:

* READ:
006D6:  BTSS.B  278.1   // RBF - skip next if 1 (receive buffer full)
                  //     if 0, go to READ.
                  //     if 1, go to READBYTE.
006D8:  BRA     6D6      // go to READ

* READBYTE:
006DA:  MOV     270,W0   // I2C3RCV - read byte

006DC:  BTSC.B  278.6   // I2COV - skip next if 0 (no overflow)
                  //     if 0, go to CLOCKRELEASE.
                  //     if 1, go to CLEAROV.
* CLEAROV:                  
006DE:  BCLR.B  278.6   // I2COV - clear overflow bit

* CLOCKRELEASE:
006E0:  BSET.B  277.4   // SCLREL - set to 1 (release SCLx clock)
006E2:  RETURN 


*******************************************************************************
* i2c_read - data = i2c_read (SYSTEM_BUS, 2);
*
* 860 - data (return value)
*******************************************************************************
....................     data = i2c_read(SYSTEM_BUS, 2);
00BCC:  MOV.B   270,W0L   // I2C3RCV - read byte (address)
00BCE:  MOV.B   W0L,860   // store byte in 'data'


*******************************************************************************
* i2c_read - data = i2c_read (SYSTEM_BUS);
*******************************************************************************
007A2:  MOV     #FFFF,W0

* START
007A4:  BTSS.B  278.3   // S - skip next if 0 (no start bit)
                  //     if 0, go to START.
                  //     if 1, go to GCSTAT.
007A6:  BRA     7A4      // go to START

* GCSTAT:
007A8:  BTSC.B  279.1   // GCSTAT - skip next if 1 (broadcast)
                  //     if 0, go to READ.
                  //     if 1, go to CHECK10BIT.
007AA:  BRA     7B4      // go to READ

* CHECK10BIT:
007AC:  BTSS.B  277.2   // A10M - skip next if 1 (10-bit address)
                  //     if 0, go to READ.
                  //     if 1, go to CHECK10MATCH.
007AE:  BRA     7B4      // go to READ

* CHECK10MATCH:
007B0:  BTSS.B  279.0   // ADD10 - skip next if 1 (10-bit matched)
                  //     if 0, go to START.
                  //     if 1, go to READ.
007B2:  BRA     7A4      // go to START

* READ:
007B4:  BTSS.B  278.1    // RBF - skip next if 1 (receive buffer full)
                  //     if 0, go to READBYTE.
                  //     if 1, go to READ.
007B6:  BRA     7B4      // go to READ.

* READBYTE:
007B8:  MOV     270,W0   // I2C3RCV - read byte

007BA:  BTSC.B  278.6   // I2COV - skip next if 0 (no overflow)
                  //     if 0, go to CLOCKRELEASE.
                  //     if 1, go to CLEAROV.
                  
* CLEAROV:                  
007BC:  BCLR.B  278.6   // I2COV - receive overflow flag bit

* CLOCKRELEASE:
007BE:  BSET.B  277.4   // SCLREL - set to 1 (release SCLx clock)
007C0:  RETURN 

*******************************************************************************
* i2c_write - i2c_write(SYSTEM_BUS, x);
*******************************************************************************
007C2:  MOV     #FFFF,W0

* START:
007C4:  BTSS.B  278.3   // S - start bit (skip next instruction if set)
                  //     if 0, go to RETURN.
                  //     if 1, go to GCSTAT.
007C6:  BRA     7D8      // go to RETURN

* GCSTAT:
007C8:  BTSS.B  278.2   // RW (1=slave write, 0=slave read)
                  //     if 0, go to RETURN.
                  //     if 1, go to CHECK10BIT.
007CA:  BRA     7D8      // go to RETURN

* CHECK10BIT:
007CC:  BTSS.B  277.2   // A10M - (1=10-bit addr, 0=7-bit address)
                  //     if 0, go to SEND.
                  //     if 1, go to CHECK10MATCH.
007CE:  BRA     7D4      // go to START

* CHECK10MATCH:
007D0:  BTSS.B  279.0   // ADD10 - 10-bit slave addressing bit (1=matched, 0=not)
                  //     if 0, go to RETURN.
                  //     if 1, go to SEND.
007D2:  BRA     7D8      // go to RETURN

* SEND:
007D4:  MOV     W1,272   // I2C3TRN - send byte
007D6:  BSET.B  277.4   // SCLREL - set to 1 (release SCLx clock)

* RETURN:
007D8:  RETURN 

08E     IFS5
08E.4   I2C_SYSTEM_BUS_IRQ_PENDING_BIT
278.0   TBF
278     I2C3STAT
278.0   TBF
278.1   RBF
278.2   RW
278.3   S
278.4   P
278.5   DA


I do recall that, some years ago, we were having many I2C issues. Ultimately, a rework of our I2C interrupt resolved them and allowed us to send 14+ million messages between a PC and PIC24 with zero errors. (Our protocol is a 5-byte header + data bytes + 2-byte checksum, getting a response that is at least a 7-byte header+checksum back.)

I also found some work-in-progress C routines that attempted to recreate the CCS functions:

Code:
unsigned int i2cSlaveRead (unsigned int ack)
{
    uint16_t data;

    data = 0;
    while (1)
    {
        // Wait for start.
        while (S == 0);

        // GCSTAT:
        // Check for broadcast.
        if (GCSTAT == 1)
        {
            // CHECK10BIT:
            // Check for 10-bit addressing.
            if (A10M == 1)
            {
                // CHECK10MATCH:
                // Check for 10-bit address match.
                if (ADD10 == 0)
                {
                    // 10-bit address was not ours. Go back to start.
                    continue;
                }
            }
        }

        // If here, it was either a broadcast, a matching 10-bit address,
        // or our 7-bit address.
        break;
    }

    // READ:
    // Wait for receive buffer full.
    while (RBF == 0);

    // READBYTE:
    // Read byte.
    data = I2C3RCV;

    // Check receive overflow flag.
    if (I2COV == 1)
    {
        // CLEAROV:
        // Clear receive overflow flag.
        I2COV = 0;
    }

    // CLOCKRELEASE:
    // Release SCLx clock.
    SCLREL = 1;

    return data;
}


 void i2cSlaveWrite (unsigned int data)
 {
    // START:
    // If no start bit, return.
    if (S == 0) return;

    // GCSTAT:
    // Check for broadcast.
    if (GCSTAT == 0) return;

    // CHECK10BIT:
    // Check for 10-bit addressing.
    if (A10M == 1)
    {
        // CHECK10MATCH:
        // Check for 10-bit address match.
        if (ADD10 == 0) return;
    }

    // If here, it was either a broadcast, a matching 10-bit address,
    // or our 7-bit address.

    // SEND:
    // Send byte.
    I2C3TRN = data;

    // CLOCKRELEASE:
    // Release SCLx clock.
    SCLREL = 1;
 }


I do not know if any of this works.

Having just recently had an A.I. help me convert our legal I2C ISR into something that used the registers (via #word and #bit from the CCS compiler), and I plan to go through it and see what may have been missed. (Though the new code also appears to work perfectly, with new checks to recover from things like a missing stop bit or other glitches in the line.)

Sharing this in case it might be of interest...
_________________
Allen C. Huffman, Sub-Etha Software (est. 1990) http://www.subethasoftware.com
Embedded C, Arduino, MSP430, ESP8266/32, and PIC24 programmer.
http://www.whywouldyouwanttodothat.com ?

Using: 24FJ256GA106, 24EP256GP202, 24FJ64GA002 and 24FJ1024GA606.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group