; -*-asm-*- ; ; Definitions and macros for the bit-banging I2C drivers. ; ; ; Copyright 2014 Greg Stein ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; ------------------------------------------------ ; ; Define macros for the SCL and SDA pins ; ; Define the pins. These MUST be set via compilation flags. ; ; Applications do not (always) require values for these macros, since ; the values are only used behind the I2C call interface. ; ; NOTE: use of PortC is not allowed if interrupts will be used (but totally ; fine for BUSY_WAIT or master operation). ; NOTE: PortA3 *may* be used as SCL (input-only), but *never* as SDA. ; If RA3 is used, clock extension will not be possible since ; it cannot be configured as an output to hold SCL LOW. ; ; ### RC5/RX/DT *may* be used as SDA because the EUSART WUE flag may ; ### be used to detect a falling SDA edge (the START condition) ; constant UNDEFINED_PORT = 0xFF ifndef SCL_PORT ifdef PORTS_REQUIRED error "SCL_PORT must be defined in the Makefile." endif constant SCL_PORT = UNDEFINED_PORT endif ifndef SDA_PORT ifdef PORTS_REQUIRED error "SDA_PORT must be defined in the Makefile." endif constant SDA_PORT = UNDEFINED_PORT endif ifndef SCL ifdef PORTS_REQUIRED error "SCL must be defined in the Makefile." endif constant SCL = UNDEFINED_PORT endif ifndef SDA ifdef PORTS_REQUIRED error "SDA must be defined in the Makefile." endif constant SDA = UNDEFINED_PORT endif ; Construct some derived values from the SCL/SDA pins if SDA_PORT == PORTA && SDA == 3 error "RA3 cannot be used for SDA, since it cannot output a N/ACK." endif if SDA_PORT == PORTC && SDA != 5 ; ### maybe relax this condition since we busy-wait? error "most PORTC pins cannot be used for SDA, since they cannot interrupt on START" endif if SCL_PORT == PORTA constant SCL_TRISTATE = TRISA else constant SCL_TRISTATE = TRISC endif if SDA_PORT == PORTA constant SDA_TRISTATE = TRISA else constant SDA_TRISTATE = TRISC endif ; ------------------------------------------------ ; Define some well-known I2C addresses ; ; NOTE: these are specific to gstein's physical design constant I2C_MIDDLEMAN = 8 constant I2C_WATCHDOG = 14 constant I2C_LOCAL0 = 70 constant I2C_LOCAL1 = 71 constant I2C_LOCAL2 = 72 constant I2C_LOCAL3 = 73 constant I2C_MPR0 = 0x5A ; ADDR to VSS/GND constant I2C_MPR1 = 0x5B ; ADDR to VDD/VCC constant I2C_MPR2 = 0x5C ; ADDR to SDA constant I2C_MPR3 = 0x5D ; ADDR to SCL #define I2C_READ_FROM(addr) (((addr) << 1) | 0x01) #define I2C_WRITE_TO(addr) ((addr) << 1) ; ------------------------------------------------ ifdef USING_I2C_SLAVE ; Entrypoints from the I2C slave module. extern I2C_S_listen extern I2C_S_detect_START extern I2C_S_saw_START extern I2C_S_read extern I2C_S_read_stretch extern I2C_S_end_stretch extern I2C_S_write endif ifdef USING_I2C_MASTER ; Entrypoints from the I2C master module. extern I2C_M_start extern I2C_M_send_one extern I2C_M_read_one extern I2C_M_read_last extern I2C_M_restart extern I2C_M_stop endif ; Link in handy/i2c.o ifndef DEFINING_I2C_FUNCTIONS extern I2C_skip extern I2C_skip_unknown endif ; ------------------------------------------------ ; Set SCL as an INPUT/HIGH-Z and let it float HIGH. ; NOTE: this macro generally shouldn't be used. see release_wait_SCL_HIGH ; Note: (1) instruction cycle executes before SCL is released ; (1) instruction cycle executes after SCL is released release_SCL: macro bank1s bsf SCL_TRISTATE, SCL bank0s endm ; Latch SCL LOW, then OUTPUT that onto the I2C bus. ; Note: (2) instruction cycle executes before SCL is pulled LOW ; (1) instruction cycle executes after SCL is pulled LOW assert_SCL_LOW: macro bcf SCL_PORT, SCL ; clear SCL bank1s bcf SCL_TRISTATE, SCL ; Set the tristate to OUTPUT bank0s endm ; Set SDA as an INPUT/HIGH-Z and let it float HIGH. ; Note: W is unchanged by this macro. ; Note: (1) instruction cycle executes before SDA is released ; (1) instruction cycle executes after SDA is released release_SDA: macro bank1s bsf SDA_TRISTATE, SDA bank0s endm ; Assuming that SCL is LOW, then we can latch SDA LOW, then OUTPUT that onto ; the I2C bus. ; Note: (2) instruction cycle executes before SCL is pulled LOW ; (1) instruction cycle executes after SCL is pulled LOW assert_SDA_LOW: macro bcf SDA_PORT, SDA ; clear SDA bank1s bcf SDA_TRISTATE, SDA ; Set the tristate to OUTPUT bank0s endm ; Wait until SCL goes LOW ; NOTE: W and STATUS are unchanged by this macro. ; Note: (1) instruction cycle executes AFTER we see SCL as LOW wait_SCL_LOW: macro btfsc SCL_PORT, SCL goto $ - 1 endm ; Wait until SCL goes HIGH ; NOTE: W and STATUS are unchanged by this macro. ; Note: (1) instruction cycle executes AFTER we see SCL as HIGH wait_SCL_HIGH: macro btfss SCL_PORT, SCL goto $ - 1 endm ; Release SCL, then wait for it to actually float HIGH. At any point where ; we might release SCL, is an opportunity for clock-stretching. Whether ; per-bit or per-byte ... busy-loop until SCL *reaches* the HIGH state. ; NOTE: (1) instruction cycle executes before SCL is released ; (1) instruction cycle executes AFTER we see SCL as HIGH release_wait_SCL_HIGH: macro release_SCL wait_SCL_HIGH endm ; Possibly set a bit in W, based on SDA. ; NOTE: always 2 instruction cycles. setbit_SDA: macro MASK btfsc SDA_PORT, SDA iorlw MASK endm ; Deliver an ACK back to the Master after the successful delivery of a byte ; ; ACK is performed by pulling SDA low. We need to switch SDA to a digital ; output, then pull it low. ; ; NOTE: W and STATUS are unchanged by this macro. ; NOTE: SCL is LOW on exit (which allowed us to release SDA) ; NOTE: this is a macro to avoid call overheads during a timing-critical ; part of the protocol ; ; EXPECT: Bank 0 ; EXIT: Bank 0 ; send_ACK: macro ; Note: table 10 of I2C spec states we have 3.45 µs to set the ; SDA value, after SCL goes LOW (t/VD;ACK). That is ; approximately instructions when operating at ; 8 MHz (0.5 µs instruction period). ; Wait for SCL to hit the LOW state. Note that reading SCL may ; also read SDA into the latch, so we must wait before attempting ; to latch an output value for SDA. wait_SCL_LOW ; BEGIN: time-sensitive. about instruction cycles used within ; wait_SCL_LOW. left. ; SCL is LOW, so we are now allowed to change SDA. Latch a LOW ; value for the ACK, then switch SDA to an output. assert_SDA_LOW ; SDA became an output (and pulled LOW) instruction cycles ; into the above macro. Thus, it was pulled low at 3.0 µs, which ; is within the specification. ; END: time-sensitive. ; Wait for the Master to cycle SCL. It will read our SDA value ; during SCL HIGH, and then return SCL to LOW, where we are ; allowed to modify the SDA value. wait_SCL_HIGH wait_SCL_LOW ; Return to digital input. This releases the bus, letting it ; float high. release_SDA endm ; Wait for the next bit, and save it into W. ; ; Note: six instructions per bit. TBD cycles per bit (ref: branch taken). read_bit: macro MASK wait_SCL_LOW wait_SCL_HIGH setbit_SDA MASK endm ; Read a full byte off the I2C bus. read_byte: macro clrw read_bit 0x80 read_bit 0x40 read_bit 0x20 read_bit 0x10 read_bit 0x08 read_bit 0x04 read_bit 0x02 read_bit 0x01 endm