PDA

View Full Version : Hardware I2C



Toley00
- 20th January 2007, 01:47
Is it possible to use the Hardware I2C in PicBasic Pro?

I need to read faster than I2CREAD PicBasic Pro command. Even with a 20MHz crystal I'm unable to obtain full speed I2C.

Surely someone have ever work on that issue. Maybe an assembly include?

Thank you

BobK
- 20th January 2007, 03:28
Hi Toley00,

It may be worth your time to download the I2C Bus Specification manual from Phillips. There are 3 data modes for I2C, Standard Mode = upto 100kbits/sec, Fast Mode = upto 400kbits/sec, and High Speed Mode = 3.4Mbits/sec. I've only used I2C for RTC's using both software I2C and Hardware I2C. I didn't have to set anything up special on my PICs for speed. But you do have to put a 10ms pause after any write command to give it time to catch up or settle down. I have seen some posts on this forum where I2C worked better when it was slowed down.

Don't know if this helps, but I think maybe their manual might give you a better insight into the whole process.

BobK

Toley00
- 20th January 2007, 04:13
Thank you BobK for your reply.
I already know about I2C timing protocol, and I use it in many project already. I just need it to read an EEPROM faster than I2CREAD can do.
Thanks for your help but a 10ms pause will not give me speed...

SteveB
- 20th January 2007, 05:38
I don't know what PIC you are using, so I can't promise anything, but I've attached a file containing some portions of code I have used with the hardware I2C on a PIC 18F4620.

The file is a compilation of cut and past from a few include files, and has only been minimally "cleaned up" for public viewing. It has 4 sections:
1) Basic PIC and HSEROUT setup (mostly to reinforce what pic and how it's configured)
2) The meat of the I2C commands
3) Samples of Large Block Reading/Writing to an EEPROM
4) Samples of Reading/Writing to an RTC

Hopefully this will get you started.
Best of luck,
Steve B

Disclaimer: Looking a little closer I recall this being one of my first PBP-ASM items I tackled. It worked, and with other things pressing, I haven't gone back and cleaned things up, like unused variables (DelayCtr1 and DelayCtr2) and rements of HSEROUT commands used to debug.

Toley00
- 20th January 2007, 14:57
Thank you SteveB, looks like what I need. I will take a closer look and adapt it to my needs. I'm using a PIC16F886, but I think all MSSP modules work the same way, and I only need to read from the EEPROM.

J. Mark Wolf
- 20th January 2007, 15:18
Also consider the RAMTRON FM24C256 32K X 8 eeprom.

It reads and writes at FULL I2C buss speed. No artificial delays required.

It is also pin-for-pin compatible with many common serial eeproms, and might just plug directly into your eeprom socket.

SteveB
- 20th January 2007, 15:52
Thank you SteveB, looks like what I need. I will take a closer look and adapt it to my needs. I'm using a PIC16F886, but I think all MSSP modules work the same way, and I only need to read from the EEPROM.

A couple of things right off the top of my head. The buffer size will need to be reduced for a 16F. Also, the way I allocated variables to "BANKA" or "BANKX" in the declaration statements will also need changing. If I get a chance later I'll try to give it a look-over for anything else. As I recall, I think I got the basics of the ASM routines from a Microchip document/source, and adapted them for the 18F. So that might also be an avenue to persue.

Steve B

sougata
- 20th January 2007, 15:55
Hi,

SteveB your routines are neat (at least than mine) and I learnt from it as well. Thanks to you and Toley for raising the topic.

SteveB
- 20th January 2007, 22:35
I made a quick run through the code (while my kids took a nap). Changed all the ASM to PBP. Also modified the buffer size, and some SFR lables to correspond the 16F. It compiles OK, but no 16F's to test it (nor anything to setup the circut without canibalizing something else). Keep in mind, on many of the 18Fs, you've got RAM to burn, so the buffers reflect that. On the 16Fs, you don't have that luxary.

Hopefully, if you are working it on your end, you can compare/use what I've done to move further down the road.

I did notice a number of areas where things could be improved or I would do thing differently. The error handling is especially weak. However, I never got any errors with the code and my setup, so never needed it to be very robust. So, I will put this on my *LONG* list of things to do when I get around to it.

SteveB

Toley00
- 21st January 2007, 21:00
Thank you SteveB, with your help (and a lot of time) I finally make it work. I've changed some things in your Routines. First I changed the Labels for shorter names. I've also changed the lines refer to PIR1.3 for SSPSTAT.2 to indicate when a transmit is over. And I fixed a little error in your code RCEN is SSPCON2.3 instead of SSPCON2.4.

In Fact all that is need is to correctly set the MSSP module :

SSPSTAT.7 = 0 'High Speed Filter
SSPADD = $0C '400 kHz @ 20 MHz
SSPCON = %00101000 'I2C Master Mode Enable

And in your main program use the 6 next sub routines as needed :

;-----------------------------------------------------------
; Process Routines
;-----------------------------------------------------------
I2CSTART:
SSPCON2.0 = 1 ; SEN - Start Condition Enable Bit
WHILE SSPCON2.0 = 1 : WEND ; Wait for Start to complete
RETURN
;-----------------------------------------------------------
I2CRD:
SSPCON2.3 = 1 ; RCEN - Enable receive mode
WHILE SSPCON2.3 = 1 : WEND ; Wait for Read to complete
I2CDATA = SSPBUF
Return
;-----------------------------------------------------------
I2CWR:
SSPBUF = I2CDATA ; Move data to SSPBUF
WHILE SSPSTAT.2 = 1 : WEND ; SSPSTAT = 1 Transmit in progress
While SSPCON2.6 = 1 : WEND ; Wait for Acknowledge from slave 1=NotAck
RETURN ; Acknowledge recieved
Return
;-----------------------------------------------------------
I2CSTOP:
SSPCON2.2 = 1 ; PEN - send stop bit
While SSPCON2.2 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------
I2CNOACK:
SSPCON2.5 = 1 ; ACKDT - Set Ack bit to NotAck
SSPCON2.4 = 1 ; ACKEN - send ACKDT bit
While SSPCON2.4 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------
I2CACK:
SSPCON2.5 = 0 ; ACKDT - Set Ack bit to Ack
SSPCON2.4 = 1 ; ACKEN - send ACKDT bit
While SSPCON2.4 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------

Now By using thoses sub routines, I'm able to access an EEPROM fast enough to read a 8 sec. wav files at 8 kHz. Everything is written in PicBasic Pro (no assembly) and is at least 3 times faster than standard I2CREAD statement.

Ok I know it's not really "User friendly" but my programming skills are limited and it work fine for me like this. Maybe someone can write something easier to use, it could be very handy.

SteveB
- 22nd January 2007, 01:37
It's great to here when someone has success. Especially when they have put in the work (like getting into the datasheet) that you have.

SteveB

CuriousOne
- 3rd July 2022, 10:53
I tried to use above code with 24C32 and PIC16F887 but it does not works. It stuck on this loop: while SSPCON2.0 = 1 : wend

Here's complete code:



'************************************************* ***************
'* Name : I2C EEPROM *
'* Author : Daniel T. Barber *
'* Notice : Copyright (c) 2018 *
'************************************************* ***************

@ ERRORLEVEL -306 ' turn off crossing page boundary message

'************ Config Settings for 16F877A *************
#CONFIG
cfg1 = _INTRC_OSC_NOCLKOUT ; INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN
cfg1&= _WDT_ON ; WDT enabled
cfg1&= _PWRTE_OFF ; PWRT disabled
cfg1&= _MCLRE_OFF ; RE3/MCLR pin function is digital input, MCLR internally tied to VDD
cfg1&= _CP_OFF ; Program memory code protection is disabled
cfg1&= _CPD_OFF ; Data memory code protection is disabled
cfg1&= _BOR_OFF ; BOR disabled
cfg1&= _IESO_ON ; Internal/External Switchover mode is enabled
cfg1&= _FCMEN_ON ; Fail-Safe Clock Monitor is enabled
cfg1&= _LVP_OFF ; RB3 pin has digital I/O, HV on MCLR must be used for programming
cfg1&= _DEBUG_OFF ; In-Circuit Debugger disabled, RB6/ICSPCLK and RB7/ICSPDAT are general purpose I/O pins
__CONFIG _CONFIG1, cfg1

cfg2 = _BOR40V ; Brown-out Reset set to 4.0V
cfg2&= _WRT_OFF ; Write protection off
__CONFIG _CONFIG2, cfg2

#ENDCONFIG

TRISA=%00000000 'SET A TO OUTPUT 1=input
TRISC=%00011000 'set C 3 4 input
TRISB=%00000000 'set PortB to output
ANSELH=%00000000 ' ADC OFF B
ANSEL=%00000000 'configure PortA as digital except first 2
ADCON1=%10000000 'adc justify
OSCCON=%01110101 'SET FREQUENCY TO 8MHZ
WPUB=%00000000 'turn off Pullups
CM1CON0=0 'DISABLE COMPARATORS
CM2CON0=0 'SAME HERE

Define OSC 8 'Adjust consistent with oscillator speed being used

ADCON1 = 7 ' Set PORTA and PORTE to digital

DEFINE LCD_DREG PORTB
DEFINE LCD_DBIT 4
DEFINE LCD_RSREG PORTB
DEFINE LCD_RSBIT 2
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 3
DEFINE LCD_BITS 4
DEFINE LCD_LINES 2
DEFINE LCD_COMMANDUS 1500
DEFINE LCD_DATAUS 44
'clear


I2CBufferSize con 64

Reg_Pointer Var Byte ; Address register
I2CDATA Var Byte ; data to read/write
I2CAddr Var Byte ; Device address

I2C_Wait var byte
countI2C var byte
I2CWRBuff var byte (I2CBufferSize)
I2CWRsize Var Byte
I2CRDBuff var byte (I2CBufferSize)
I2CRDsize Var Byte

I2CErrCode Var Byte
I2CErrCode = 0

x var byte 'temp var

SSPSTAT.7 = 0 ; SMP<7> - Slew rate control Enabled (400kHz)
SSPCON = %00101000
; | ''''-- SSPM<3:0> - I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
; '-------- SSPEN<5> - Synchronous Serial Port Enable bit

SSPADD = $0E ; set I2C clock rate to 400kHz
'SSPADD = $59 ; set I2C clock rate to 100kHz



for x=0 to 255
lcdout $fe, $2, "wait..."
I2CAddr=x
gosub read1byte
lcdout $fe, $2, "data=", dec i2cdata
pause 1000
next
stop





;-----------------------------------------------------------
; Main Routines
;-----------------------------------------------------------
Write1Byte:
I2CErrCode.1 = 0
I2CAddr.0 = 0 ;Set Write Bit
Gosub StartI2C ;Send Start, Addr, Wait for Ack
if I2CErrCode.1 = 1 then RETURN
I2CDATA = I2CWRBuff(0)
gosub I2CWRByte ;Send I2CDATA Byte
Gosub SendStopBit ;Send Stop Bit
Return
;-----------------------------------------------------------
WriteXBytes:
I2CErrCode.1 = 0
I2CAddr.0 = 0 ;Set Write Bit
Gosub StartI2C ;Send Start, Addr, Wait for Ack
if I2CErrCode.1 = 1 then RETURN
For countI2C = 0 to I2CWRsize-1
I2CDATA = I2CWRBuff(countI2C)
; hserout [dec countI2C,"-",ihex2 I2CDATA," ",13,10]
gosub I2CWRByte ;Send I2CDATA Byte
next countI2C
Gosub SendStopBit ;Send Stop Bit
Return
;-----------------------------------------------------------
Read1Byte:
I2CErrCode.1 = 0
I2CAddr.0 = 1 ;Set Read Bit
Gosub StartI2C ;Send Start, Addr, Wait for Ack
if I2CErrCode.1 = 1 then RETURN
gosub I2CRDByte
I2CRDBuff(0) = I2CDATA
gosub Send_NO_Ack
Gosub SendStopBit
Return
;-----------------------------------------------------------
ReadXBytes:
I2CErrCode.1 = 0
I2CAddr.0 = 1 ;Set Read Bit
Gosub StartI2C ;Send Start, Addr, Wait for Ack
if I2CErrCode.1 = 1 then RETURN
for countI2C = 0 to I2CRDsize-2
gosub I2CRDByte
gosub Send_Ack
I2CRDBuff(countI2C) = I2CDATA
next countI2C
countI2C = I2CRDsize - 1
gosub I2CRDByte
gosub Send_NO_ACK
Gosub SendStopBit
I2CRDBuff(countI2C) = I2CDATA
Return
;-----------------------------------------------------------
; Process Routines
;-----------------------------------------------------------
StartI2C:

;Send Start Bit
SSPCON2.0 = 1 ; SEN<0> - Send start bit
while SSPCON2.0 = 1 : wend ; Wait for completion
;Send Slave Address
PIR1.3 = 0 ; SSPIF - Clear interrupt Flag
SSPBUF = I2CAddr ; move slave address to SSPBUF
While PIR1.3 = 0 : Wend ; SSPIF - Wait for SSP to complete sending slave address
;Wait for Acknowledge or Restart
i2c_wait = 0 ; Reset wait counter
While SSPCON2.6 = 1 ; Wait for Acknowledge from slave 1=NotAck
I2C_Wait = I2C_Wait + 1 ; Increment wait counter
if I2C_Wait = 0 then ; Wait for 256 counts
goto I2CResError ; Acknowledge "Time Out" - Report error and RETURN
else
; Send Repeated Start
SSPCON2.1 = 1 ; RSEN - Send Repeated Start
while SSPCON2.1 = 1 : wend ; Wait for completion
; Resend the Slave Address
PIR1.3 = 0 ; SSPIF - Clear interrupt Flag
SSPBUF = I2CAddr ; move slave address to SSPBUF
While PIR1.3 = 0 : Wend ; SSPIF - Wait to complete sending Slave Address
endif
wend
RETURN ; Acknowledge recieved
Return
;-----------------------------------------------------------
I2CWRByte:
PIR1.3 = 0 ; SSPIF - Clear interrupt Flag
SSPBUF = I2CDATA ; Move data to SSPBUF
While PIR1.3 = 0 : Wend ; SSPIF - Wait for SSP to complete sending

;Wait for Acknowledge or Abort
i2c_wait = 0 ; Reset wait counter
While SSPCON2.6 = 1 ; Wait for Acknowledge from slave 1=NotAck
I2C_Wait = I2C_Wait + 1 ; Increment wait counter
if I2C_Wait = 0 then ; Wait for 256 counts
goto I2CResError ; Acknowledge "Time Out" - Report error and RETURN
endif
wend
RETURN ; Acknowledge recieved
Return
;-----------------------------------------------------------
I2CRDByte:
PIR1.3 = 0 ; SSPIF - Clear interrupt Flag
SSPCON2.3 = 1 ; RCEN - Enable receive mode

While PIR1.3 = 0 : Wend ; SSPIF - Wait for SSP to complete receiving

I2CDATA = SSPBUF

Return
;-----------------------------------------------------------
SendStopBit
SSPCON2.2 = 1 ; PEN - send stop bit
While SSPCON2.2 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------
Send_NO_Ack:
SSPCON2.5 = 1 ; ACKDT - Set Ack bit to NotAck
SSPCON2.4 = 1 ; ACKEN - send ACKDT bit
While SSPCON2.4 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------
Send_Ack:
SSPCON2.5 = 0 ; ACKDT - Set Ack bit to Ack
SSPCON2.4 = 1 ; ACKEN - send ACKDT bit
While SSPCON2.4 = 1 : Wend ; Wait for SSP to complete
Return
;-----------------------------------------------------------
I2CResError:
I2CErrCode.1 = 1
lcdout $fe, $2, "I2C Error!!!"
Return
;-----------------------------------------------------------