allenhuffman
Joined: 17 Jun 2019 Posts: 660 Location: Des Moines, Iowa, USA
|
| PIC24 I2C routines in assembly |
Posted: Wed Mar 04, 2026 7:57 pm |
|
|
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. |
|