Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

I2C communication problem

Status
Not open for further replies.

zkt_PiratesDen

New Member
This has had me stumped for a couple of days. Hoping someone can point out he error in my ways. I have a pic 16f87 contriling a tda 7439ds sound prosessor chip with I2C protocol. Since the requirements are rather simple the pic I2C registers are not being used. Instead the scl line is controled by portb.7 and sda by porta.7 with 10k pullup resistors on the pins.The pic code is straight line (sequential) - just one thing after the other, except for loops to shift the data to the sda line. Easier to read and less to go wrong. Yet something has, or I`m misunderstanding the tda datasheet. The code should switch the tda`s input from input2 to input4. The scope indicates the chip is working properly but not switching. The pic sends a start condition, then three 8 bit words, then a stop condition on sda. The start condition is a high to low transition on sda while scl is high. The stop condition is a low to high transition on sda while scl is high. Data is allowed to change only when scl is low and must remain stable while scl is high. The tda sends an acknowledgement bit following each 8 bit word during the 9th clock pulse bit by pulling the sda line low.
Please refer to the dta datasheet, pages 8 & 9 for the timing diagrams and word format to verify this:
**broken link removed**
Scope shows that there is a clock signal on the scl line and data on the sda line but NO ack pulse.There should be an ack atfer each word the pic sends.(three words: addr- $88, sub_adr-$00, data-$00). I suspected that the tda chip might have been damages so connected a new one- same results. The test circuit is as shown in the dataasheet ancept the rc equalizer omponents are omitted. the output is taken from the muxout. Hopefully there is something simple and obvious that I have overlooked- I`m pretty good at that. thanks much for your time and trouble
The variables that arent used are part of another part of the program that isnt posted for simplicitys sake.Also - ignore the comments except the ones that describe the input pins
Code:
program rc5
' pic 16F87
dim dly_half_period,n,tmp,addr,data,which_byte,scl,sda as byte

const tda_adr=$88    'array for all
dim tda_sub_adr,tda_data as byte
                             ' portb.0-5: display
                             ' portb.6: input pin
                             ' portb.7 scl
                             ' porta.7 sda
main:
osccon=%01100010
while iofs=1
wend
cmcon=7
trisa=32
trisb=64
portb=%10111111
porta=128
't2con.t2ckps1=1
t2con.t2ckps1=0
dly_half_period=0
tmp=0
n=12
addr=0
data=0
which_byte=0


start:

tda_data=$00000000       'tda_data  10=in2      00=in4
tda_sub_adr=%00000000

scl=%10000000
portb = scl and 128       'scl=high
sda=%10000000
porta = sda and 128          'sda=high
sda=%00000000                '(scl=high)
porta=sda and 128            'sda=low
pir1.tmr2if=0
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend

adr:
for n=0 to 7
scl=%00000000
portb=scl and 128           'sda change
delay_ms(10)
if portb.7=0 then
   tmp=tda_adr
   tmp=tmp<<n
   porta=tmp and 128
   end if
pir1.tmr2if=0
t2con.tmr2on=1
'con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000               'scl=high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
next n

'skip_9th_scl_bit
trisa=(32+128)              'switch scl snd sda lines to inp
scl= %00000000              'scl=low
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000              'scl= high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
trisa=32

sub_adr:
for n=0 to 7
scl=%00000000
portb=scl and 128           'sda change
delay_ms(10)
if portb.7=0 then
   tmp=tda_sub_adr
   tmp=tmp<<n
   porta=tmp and 128
   end if
pir1.tmr2if=0
t2con.tmr2on=1
'con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000               'scl=high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
next n

'skip_9th_scl_bit
trisa=(32+128)              'switch scl snd sda lines to inp
scl= %00000000              'scl=low
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000              'scl= high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend

data:
for n=0 to 7
scl=%00000000
portb=scl and 128           'sda change
delay_ms(10)
if portb.7=0 then
   tmp=tda_data
   tmp=tmp<<n
   porta=tmp and 128
   end if
pir1.tmr2if=0
t2con.tmr2on=1
'con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000               'scl=high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
next n

'skip_9th_scl_bit
trisa=(32+128)              'switch scl snd sda lines to inp
scl= %00000000              'scl=low
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl=%10000000              'scl= high
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend

stop:
sda=%10000000
porta = sda and 128          'sda=high
sda=%00000000                '(scl=high)
porta=sda and 128            'sda=low
pir1.tmr2if=0
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend
scl= %00000000              'scl=low
portb=scl and 128
pir1.tmr2if=0
t2con.tmr2on=1
't2con.t2ckps1=1
t2con.t2ckps1=0
while pir1.tmr2if=0
wend

zkt
 
Last edited:
Surprising !! Wouild have thot that I2C experts would be crawling out of the woodwork here. JUst in case someone searches for I2C info sometime in the future, I`ll continue to post my findings here. Might save someone a bit of trouble. To continue; this is about the simplest I2c communication possible. The tda chip operates solely as a slave. Only thing it does to talk to the pic is send an ack pulse at the end of each 8 bit word. I`ve ran the code thru the debugger and the timing is right as far as allowed data changes and data stability are concerned. Now, all the timing diagrams I`v seen show the sda line changing in the middle of the low scl period. My code changes sda near the biginning and end of the scl low period. Got a nice clean scope display of scl and sda now so Ill change the code so that the transition is in the center of the scl low period.
 
As I said before, I2C is tricky to get working, and the problem is most probably in your code. You might try using a different I2C chip?, memory EEPROM are commonplace, and a good example to start with.

Feel free to check my I2C tutorial, but it's in assembler!.
 
I2C is a lot easier if you use a built-in I2C controller versus bit-banging it.

Thing is that the bus is open-collector - you *MUST* use pullup resistors, and you are *never* allowed to drive a '1' on either the SDA or SCL lines. You can only tristate the pin, and wait for it to rise. If you are driving the pin directly by driving the '1' values the TDA chip won't work.

OTOH, I've never used that language - basic? so I could be misreading things horribly...
 
Ok, I AM using pullup resistors. And I should be using portb instead of porta for the sda signal. But what do you mean by "You can only tristate the pin, and wait for it to rise.". Whats going to cause it to rise when the pic sends a high over the sda line if a one isnt written to the port latch? And what can a built in I2C routine do that the asm instructions cant do ? I looked over the I2C routine that Nigel has written.and believe he was driving the pins directly I`ll look at it agiain in more detail. And what about the weak pullups on portb ? on or off ? Might as well read the i2c spec again while i`m at it.
Hjames, could you explain what you mean by 'You can only tristate the pin, and wait for it to rise"
also, I
 
Ok, I AM using pullup resistors. And I should be using portb instead of porta for the sda signal. But what do you mean by "You can only tristate the pin, and wait for it to rise.". Whats going to cause it to rise when the pic sends a high over the sda line if a one isnt written to the port latch? And what can a built in I2C routine do that the asm instructions cant do ? I looked over the I2C routine that Nigel has written.and believe he was driving the pins directly I`ll look at it agiain in more detail. And what about the weak pullups on portb ? on or off ? Might as well read the i2c spec again while i`m at it.
Hjames, could you explain what you mean by 'You can only tristate the pin, and wait for it to rise"
also, I`m ignoring the ack pulse fron the tda
 
When the PIC wants to send a '1', it tristates the pin, and the pullup resistor does the work. Driving the SDA line high is "okay", but not exactly to the I2C spec. Driving the SCL line high is really not recommended since part of the spec allows chips other than the sender to hold the clock line low.

The way it works is that the sender puts the data onto SDA, then pulls the SCK line low for some minimum time. Then the sender tristate's it's SCK line - howver the receiver chips can then *hold* SCK low in order to stall the bus. The sender needs to wait for all the receiver chips to release the SCK line, at which point the pullup resistor will drag SCK back to high.

The pullup resistors should supply ~1mA or so of current - much more than the weak-pullup, so that setting doesn't matter too much.
 
Yes, in my I2C code I don't use the Port registers, they are simply set LOW during initialisation and never changed again - all the work is done using the TRIS register, setting it as INPUT for HIGH, and output for LOW. This effectively simulates the required open-collector I/O.

Depending what you're doing this may not be required for the clock line?, which is generally only driven by the master - but the I2C spec does allow other devices to control the clock line so you may as well implement it as not.
 
LOL that just about the scheme that I was about type up and post. Yes might as well switch the clovk that way too. One more point if ypu please, Did you wait till midmidway thru the scl low period to transition sda or do it just right after the scl transition.? Similiarly for the ending transition ?
Thanks very much gentlemen. I blew right past that open collector part of the spec.
as they say-haste preceeds the devil :)
 
You don't transistion SDA at all, you set SDA how you want it (HIGH or LOW), then send a clock pulse - check my first I2C tutorial below, it's assembler but I purposely wrote it using small subroutines as much as possible (probably too many really), but it makes it easier to understand.

Code:
;Tutorial 6_1
;Read EEPROM with LCD display
;Nigel Goodwin 2002

	LIST	p=16F628		;tell assembler what chip we are using
	include "P16F628.inc"		;include the defaults for the chip
	ERRORLEVEL	0,	-302	;suppress bank selection messages
	__config 0x3D18			;sets the configuration settings (oscillator type etc.)




		cblock	0x20			;start of general purpose registers
			count			;used in looping routines
			count1			;used in delay routine
			counta			;used in delay routine
			countb			;used in delay routine
			LoX
			Bit_Cntr
			Cmd_Byte
			Dev_Byte
			Timer_H
			Flags
			Flags2
			tmp1			;temporary storage
			tmp2
			tmp3

			templcd			;temp store for 4 bit mode
			templcd2
			lcdtmp
	
			Adr_Lo			; EEPROM memory address to be accessed
			Adr_Hi
			DAT_VAL
			_N
			InputByte		; byte read from EEPROM is stored in this register
			Data_Page		; EEPROM page, 0-7
			OutputByte		; used for holding byte to be output to EEPROM
			I2Cflags		; flag bit register
		endc

LCD_PORT	Equ	PORTA
LCD_TRIS	Equ	TRISA
LCD_RS		Equ	0x04			;LCD handshake lines
LCD_RW		Equ	0x06
LCD_E		Equ	0x07

I2C_PORT	Equ	PORTB
I2C_TRIS	Equ	TRISB

ErrFlag		Equ	0x00
StartFlag	Equ	0x01			;flags used for received bit
One		Equ	0x02
Zero		Equ	0x03


#define		SDA 	7
#define		SCL	6

Chip_Read	Equ	0xA1			;EEPROM address values
Chip_Write	Equ	0xA0


		org	0x0000
		goto	Start

            	org     0x0004
            	retfie

;TABLES - moved to start of page to avoid paging problems,
;a table must not cross a 256 byte boundary.
HEX_Table  	addwf   PCL       , f
            	retlw   0x30
            	retlw   0x31
            	retlw   0x32
            	retlw   0x33
            	retlw   0x34
            	retlw   0x35
            	retlw   0x36
            	retlw   0x37
            	retlw   0x38
            	retlw   0x39
            	retlw   0x41
            	retlw   0x42
            	retlw   0x43
            	retlw   0x44
            	retlw   0x45
            	retlw   0x46

Table1  	addwf   PCL       , f
            	retlw   'W'
            	retlw   'r'
            	retlw   'i'
            	retlw   't'
            	retlw   'i'
            	retlw   'n'
            	retlw   'g'
            	retlw   '.'
            	retlw   '.'
            	retlw   0x00

Table2  	addwf   PCL       , f
            	retlw   'R'
            	retlw   'e'
            	retlw   'a'
            	retlw   'd'
            	retlw   'i'
            	retlw   'n'
            	retlw   'g'
            	retlw   '.'
            	retlw   '.'
            	retlw   0x00

Table3  	addwf   PCL       , f
            	retlw   'F'
            	retlw   'i'
            	retlw   'n'
            	retlw   'i'
            	retlw   's'
            	retlw   'h'
            	retlw   'e'
            	retlw   'd'
            	retlw   '.'
            	retlw   0x00

;end of tables

Start		movlw	0x07
		movwf	CMCON			;turn comparators off (make it like a 16F84)

Initialise	clrf	count
		clrf	PORTA
		clrf	PORTB	



SetPorts	bsf 	STATUS,		RP0	;select bank 1
		movlw	0x00			;make all LCD pins outputs
		movwf	LCD_TRIS
		movlw	b'11111111'		;make all I2C port pins inputs
		movwf	I2C_TRIS
		bcf 	STATUS,		RP0	;select bank 0

		call	LCD_Init		;setup LCD module

Main

; Display 'Writing..' message

		clrf	count			;set counter register to zero
Message		movf	count, w		;put counter value in W
		call	Table1			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	$+4
		call	LCD_Char
		incf	count, f
		goto	Message
	

; write sequential numbers to EEPROM

		movlw	0x01
		movwf	Data_Page		;set EEPROM page
		clrf	tmp1
Next_Tmp	movf	tmp1,	w
		movwf	Adr_Lo
		call	Write_EEPROM
		incfsz	tmp1, f
		goto	Next_Tmp

; Display 'Reading..' message

		call	LCD_Line1
		clrf	count			;set counter register to zero
Message2	movf	count, w		;put counter value in W
		call	Table2			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	$+4
		call	LCD_Char
		incf	count, f
		goto	Message2
		
; and read them back from EEPROM
; displaying 4 at a time on line 2 of LCD
; with 0.5 second delay between lines		
		
		movlw	0x01
		movwf	Data_Page		;set EEPROM page
		clrf	tmp1
		clrf	tmp2
Next_Tmp3	call	LCD_Line2		;set to second line
		clrf	tmp3		
Next_Tmp2	movf	tmp1,	w
		movwf	Adr_Lo
		call	Read_EEPROM
		call	LCD_HEX
		movlw	' '
		call	LCD_Char
		incf	tmp1,	f
		incf	tmp3,	f
		movf	tmp3,	w
		xorlw	0x04			;is it a 4
		btfss	STATUS, Z
		goto	Next_Tmp2
		call	Delay255
		call	Delay255
		incf	tmp2,	f
		movf	tmp2,	w
		xorlw	d'64'			;is it a 64
		btfss	STATUS, Z
		goto	Next_Tmp3

; Display 'Finished.' message

		call	LCD_Line1
		clrf	count			;set counter register to zero
Message3	movf	count, w		;put counter value in W
		call	Table3			;get a character from the text table
		xorlw	0x00			;is it a zero?
		btfsc	STATUS, Z
		goto	$+4
		call	LCD_Char
		incf	count, f
		goto	Message3

Done		goto	Done			;loop for ever

;Start of I2C routines

Write_EEPROM					; write W register to address Adr_Lo
		movwf	DAT_VAL			; save W
		call	I2C_Start
		call	I2C_Set_Write

; If ACK error display 'e'

		btfsc	I2Cflags, 0
		call	Error_Routine

;        	call	I2C_Hi_Adr		; used for extendd addressing (24L64/5)

		call	I2C_Lo_Adr

; If ACK error display 'f'

		btfsc	I2Cflags, 0
		call	Error_Routine1

        	movf	DAT_VAL, 	W	; send the actual data
        	movwf	OutputByte
        	call	I2C_Out
        	call	I2C_NAK

; If ACK error display 'g'

		btfsc	I2Cflags, 0
		call	Error_Routine2

        	call	I2C_Stop 
		call	WaitForWrite
;		call	Delay10			; 10ms delay max. required for EEPROM write cycle
        	return

Read_EEPROM					; reads data at location specified in Adr_Lo (& Adr_Hi for 24LC64)
						; and page specified in Data_Page
						; returns result in W
		call 	I2C_Start
		call	I2C_Set_Write

; If ACK error display 'e'

		btfsc	I2Cflags, 0
		call	Error_Routine

;		call	I2C_Hi_Adr		; used for extended addressing (24LC64/5)
		call	I2C_Lo_Adr

; If ACK error display 'f'

		btfsc	I2Cflags, 0
		call	Error_Routine1

        	call 	I2C_Start		; note there is no STOP
		call	I2C_Set_Read

; If ACK error display 'g'

		btfsc	I2Cflags, 0
		call	Error_Routine2

        	call 	I2C_Read		; fetch the byte
        	call 	I2C_Send_NAK		; send no acknowledgement

        	call 	I2C_Stop
		movf 	InputByte, 	W	; return the byte in W
        	return


; The following routines are low level I2C routines applicable to most
; interfaces with I2C devices.

I2C_Read					; read byte on i2c bus
		clrf 	InputByte
		movlw 	0x08
		movwf 	_N			; set index to 8	
		call	HIGH_SDA		; be sure SDA is configured as input
In_Bit
		call 	HIGH_SCL		; clock high
		btfss 	I2C_PORT,	SDA	; test SDA bit
		goto	In_Zero
		goto	In_One

In_Zero
		bcf 	STATUS, 	C	; clear carry
		rlf 	InputByte, 	f	; i_byte = i_byte << 1 | 0
		goto 	Cont_In

In_One
		bsf	STATUS, 	C	; set carry
		rlf 	InputByte, 	f

Cont_In
		call	LOW_SCL			; bring clock low
		decfsz 	_N, 	F		; decrement index
		goto 	In_Bit
		return

I2C_Out:					; send w register on I2C bus
        	movwf 	OutputByte
		movlw 	0x08
		movwf 	_N
Out_Bit:
		bcf 	STATUS,		C	; clear carry
		rlf 	OutputByte, 	f	; left shift, most sig bit is now in carry
		btfss 	STATUS, 	C	; if one, send a one
		goto 	Out_Zero
		goto 	Out_One

Out_Zero:
		call 	LOW_SDA			; SDA at zero
		call 	Clock_Pulse	
		call 	HIGH_SDA
		goto 	Out_Cont

Out_One:
		call 	HIGH_SDA		; SDA at logic one
		call 	Clock_Pulse
Out_Cont:
		decfsz 	_N,		F	; decrement index
		goto 	Out_Bit
		return	

;;;;;;
		
I2C_NAK:					; bring SDA high and clock
		call 	HIGH_SDA
		call 	HIGH_SCL

		clrf	count			; wait for ACK
WaitForACK	incf	count, f		; increase timeout counter each time ACK is not received
		btfsc	STATUS, Z
		goto	No_ACK_Rec
		btfsc	I2C_PORT,	SDA	; test pin. If clear, EEPROM is pulling SDA low for ACK
		goto	WaitForACK		; ...otherwise, continue to wait
		bcf	I2Cflags, 0		; clear flag bit (ACK received)
		call 	LOW_SCL
		return

I2C_Send_NAK:					; bring SDA high and clock
		call 	HIGH_SDA
		call 	Clock_Pulse
		return

WaitForWrite					; poll ACK for write timing
		call 	I2C_Start
		call	I2C_Set_Write
		btfsc	I2Cflags, 0
		goto	WaitForWrite
		return


;------ No ACK received from slave (must use "return" from here)
;; Typically, set a flag bit to indicate failed write and check for it upon return.
No_ACK_Rec
		bsf	I2Cflags, 0		; set flag bit
		return	

;------ No ACK received from slave.  This is the error handler.
Error_Routine
; Output error message, etc. here
		movlw	'e'
		call	LCD_Char
		return				; returns to INITIAL calling routine

Error_Routine1
; Output error message, etc. here
		movlw	'f'
		call	LCD_Char
		return

Error_Routine2
; Output error message, etc. here
		movlw	'g'
		call	LCD_Char
		return

I2C_ACK:
		call 	LOW_SDA
		call 	Clock_Pulse
		return

I2C_Start:				
		call 	LOW_SCL
		call 	HIGH_SDA
		call 	HIGH_SCL
		call 	LOW_SDA			; bring SDA low while SCL is high
		call 	LOW_SCL
		return

I2C_Stop:
		call 	LOW_SCL
		call 	LOW_SDA
		call 	HIGH_SCL
		call 	HIGH_SDA		; bring SDA high while SCL is high
		call 	LOW_SCL
		return

I2C_Set_Write	rlf 	Data_Page, 	W	; shift device page
		andlw	b'00001110'		; AND to make sure in range
        	iorlw 	Chip_Write
        	call 	I2C_Out
        	call 	I2C_NAK
		return

I2C_Set_Read	rlf 	Data_Page, 	W	; shift device page
		andlw	b'00001110'		; AND to make sure in range
        	iorlw 	Chip_Read
        	call 	I2C_Out
        	call 	I2C_NAK
		return

I2C_Lo_Adr      movf 	Adr_Lo, 	W	; send low byte of address
        	call 	I2C_Out
        	call 	I2C_NAK
		return

I2C_Hi_Adr      movf 	Adr_Hi, 	W	; send low byte of address
        	call 	I2C_Out
        	call 	I2C_NAK
		return

Clock_Pulse:					; SCL momentarily to logic one
		call 	HIGH_SCL
		call 	LOW_SCL
		return		

HIGH_SDA:					; high impedance by making SDA an input
		bsf 	STATUS, 	RP0	; bank 1
		bsf 	I2C_TRIS, 	SDA	; make SDA pin an input
		bcf 	STATUS, 	RP0	; back to bank 0
		return

LOW_SDA:
		bcf 	I2C_PORT, 	SDA	
		bsf 	STATUS, 	RP0	; bank 1
		bcf 	I2C_TRIS, 	SDA	; make SDA pin an output
		bcf 	STATUS, 	RP0	; back to bank 0
		return

HIGH_SCL:
		bsf 	STATUS, 	RP0	; bank 1
		bsf 	I2C_TRIS, 	SCL	; make SCL pin an input
		bcf 	STATUS, 	RP0	; back to bank 0
		return

LOW_SCL:
		bcf	I2C_PORT, 	SCL
		bsf 	STATUS, 	RP0	; bank 1
		bcf 	I2C_TRIS, 	SCL	; make SCL pin an output
		bcf 	STATUS, 	RP0	; back to bank 0
		return

; End of I2C routines

;LCD routines

;Initialise LCD
LCD_Init	call 	LCD_Busy		;wait for LCD to settle

		movlw	0x20			;Set 4 bit mode
		call	LCD_Cmd

		movlw	0x28			;Set display shift
		call	LCD_Cmd

		movlw	0x06			;Set display character mode
		call	LCD_Cmd

		movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd			;Set cursor off

		call	LCD_Clr			;clear display

		retlw	0x00

; command set routine
LCD_Cmd		movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bcf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high
		call 	LCD_Busy
		retlw	0x00

LCD_CharD	addlw	0x30			;add 0x30 to convert to ASCII
LCD_Char	movwf	templcd
		swapf	templcd,	w	;send upper nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high

		movf	templcd,	w	;send lower nibble
		andlw	0x0f			;clear upper 4 bits of W
		movwf	LCD_PORT
		bsf	LCD_PORT, LCD_RS	;RS line to 1
		call	Pulse_e			;Pulse the E line high
		call 	LCD_Busy
		retlw	0x00

LCD_Line1	movlw	0x80			;move to 1st row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line2	movlw	0xc0			;move to 2nd row, first column
		call	LCD_Cmd
		retlw	0x00

LCD_Line1W	addlw	0x80			;move to 1st row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_Line2W	addlw	0xc0			;move to 2nd row, column W
		call	LCD_Cmd
		retlw	0x00

LCD_CurOn	movlw	0x0d			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_CurOff	movlw	0x0c			;Set display on/off and cursor command
		call	LCD_Cmd
		retlw	0x00

LCD_Clr		movlw	0x01			;Clear display
		call	LCD_Cmd
		retlw	0x00

LCD_HEX		movwf	lcdtmp
		swapf	lcdtmp,	w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		movf	lcdtmp, w
		andlw	0x0f
		call	HEX_Table
		call	LCD_Char
		retlw	0x00

Pulse_e		bsf	LCD_PORT, LCD_E
		nop
		bcf	LCD_PORT, LCD_E
		retlw	0x00

LCD_Busy
		bsf	STATUS,	RP0		;set bank 1
		movlw	0x0f			;set Port for input
		movwf	LCD_TRIS
		bcf	STATUS,	RP0		;set bank 0
		bcf	LCD_PORT, LCD_RS	;set LCD for command mode
		bsf	LCD_PORT, LCD_RW	;setup to read busy flag
		bsf	LCD_PORT, LCD_E
		swapf	LCD_PORT, w		;read upper nibble (busy flag)
		bcf	LCD_PORT, LCD_E		
		movwf	templcd2 
		bsf	LCD_PORT, LCD_E		;dummy read of lower nibble
		bcf	LCD_PORT, LCD_E
		btfsc	templcd2, 7		;check busy flag, high = busy
		goto	LCD_Busy		;if busy check again
		bcf	LCD_PORT, LCD_RW
		bsf	STATUS,	RP0		;set bank 1
		movlw	0x00			;set Port for output
		movwf	LCD_TRIS
		bcf	STATUS,	RP0		;set bank 0
		return

;end of LCD routines


;Delay routines

Delay255	movlw	0xff		;delay 255 mS
		goto	d0
Delay100	movlw	d'100'		;delay 100mS
		goto	d0
Delay50		movlw	d'50'		;delay 50mS
		goto	d0
Delay20		movlw	d'20'		;delay 20mS
		goto	d0
Delay10		movlw	d'10'		;delay 10mS
		goto	d0
Delay1		movlw	d'1'		;delay 1mS
		goto	d0
Delay5		movlw	0x05		;delay 5.000 ms (4 MHz clock)
d0		movwf	count1
d1		movlw	0xC7
		movwf	counta
		movlw	0x01
		movwf	countb
Delay_0		decfsz	counta, f
		goto	$+2
		decfsz	countb, f
		goto	Delay_0

		decfsz	count1	,f
		goto	d1
		retlw	0x00

;end of Delay routines

		end
 
yep, thats the most subroutines I`ve ever seen in one place ! But, you`re right. It does make it real readable. Hate to blatently steal your work, but if I thinned it out a bit and changed the port names it would do the job nicely. I was thinking that since my requirements are so simple to set up tmr0 for the scl, clock off 8 bits with the sda value starting at tmr0=64 and ending at the same time in the next cycle That way sda would be stable while scl is high and would change when scl is low. And check for the ack at the 9th pulse. It would be short and sweet if it works. What do you think ? worth a try ?
 
zkt_PiratesDen said:
yep, thats the most subroutines I`ve ever seen in one place ! But, you`re right. It does make it real readable. Hate to blatently steal your work, but if I thinned it out a bit and changed the port names it would do the job nicely.

Feel free to steal it as you wish!.

I was thinking that since my requirements are so simple to set up tmr0 for the scl, clock off 8 bits with the sda value starting at tmr0=64 and ending at the same time in the next cycle That way sda would be stable while scl is high and would change when scl is low. And check for the ack at the 9th pulse. It would be short and sweet if it works. What do you think ? worth a try ?

Sorry?, I don't see the slightest reason in using a timer for I2C?.
 
Guess I hadnt mentioned that this poor old scope has a little trouble holding it`s sync. It`s a little more more readable a lower freqs or with delays between the transitions, especially if your trying to see when pulse transitions occur. Looking at it right now-scl on one trace and sda inside it and still not sure if the code is working. The code follows yours closely. Exactly so as far as the start contition goes, then trises porta (sda) as an input or output based apon the current tda_address bit. Its easier to read the code than describe it. Porta`s (sda) last condition, during the 9th. pulse, is as an input and the scope shows sda as low. So I think it works, if I`m not losing track of which bit I`m looking at. I`m repeatedly sending a start, condtition, an 8 bit address and setting sda as as input during the 9th pulse.I think the dta chip is sending an ack each time.heres the code. Your comments are as always appreciated (as are everyone elses)
Code:
cmcon=7
portb.7=0                   ' portb.7 scl
porta.7=0                   ' porta.7 sda
tda_adr=$88
tda_sub_adr=%00000000        'tda sub_adr #00000000=in select
tda_data=$00000000           'tda_data  #00000010=in2      #00000000=in4

I2C_Start:
trisb=0       'scl_low
trisa=128     'sda_high
delay_us(100) ' 100usec delay makes for better scope display
trisb=128     'scl_high
delay_us(100)
trisa=0       'sda_low
delay_us(100)
trisb=0       'scl_low

adr:
for n=0 to 7
trisb=0       'scl_low
   tmp=tda_adr
   tmp=tmp<<n    ' shift current tda_data bit to MSB of tmp
   if tmp and 128=128 then    'set tda high if current tda_data bit is high
   trisa=128
   else trisa=0               ' else set tda low
   end if
trisb=0
delay_us(100)
trisb=128     'scl_high
delay_us(100)
next n

ack_adr:
trisb=0        'scl_low
delay_us(100)
trisb=128      'scl_high
delay_us(300)

goto i2c_start
 
My only comment is trying to use a scope to see what's happening, despite my great fondness for analogue scopes, this is a completely useless application for one!. You need a digital storage scope, and that might not even tell you what you want!.
 
You need a digital storage scope,
You`re right on the money there.
Would you be willing to talk to my wife after I tell her ? Cause I`m pretty sure she won`t be talking to me for awhile. :D
But belvieve it or not, the analog does a good enough job with digital signals. The trick is to put the PIC in a loop and the creative use of delays within the code. Right now I`m looking at the scl and sda pulse trains (all 34 bits of it) and theyre clear and stable, slowly scrolling across the scope display. If I expand the scale 5 or 10 time- well I`m sure you get he idea.
Actually that first bit of code I posted was the basis of a digital stoprage scope. Either as an add-on th your analog scope or witha lot more work. a stand alone. It simply measured the pulse periods of the input pulse train and then outputted the same continously on a gpio pin. If youre not working on anything at the moment it might be worth considering. With the proper chip and a nice display you would have a marketeble product there.
 
Well, worst comes to worst, you can probably use the parallel port on your computer - at least to verify that the I2C is actually working. If you google around, you can probably find a couple projects.

... And the utterly extreme version would be to tap the I2C bus in your computer itself. There's actually an I2C bus running to all the DIMM sockets and temperature sensors in your computer, and a slightly hacked up variant running in the VGA cable between the computer and monitor...
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top