Sida 1 av 2

I2C problem. (pic + I2C eeprom)

Postat: 21 september 2008, 11:20:45
av Rocky_AL
Har under gårdagen kodat en assemblerkod till en pic16f628a som har till uppgift att skicka och ta emot data från ett externt eeprom (M24C64-WBN6 8Kx8).
Problemet är att det inte verkar fungera alls, får bara tillbaka 0xFF. Antingen är det fel på skrivrutinen eller läsrutinen. Lägger upp hela koden. Jag har provat att lägga till lite kod till funktionen "ACK" som efter klockpulsen (SCL = från 1 till 0) kollar om SDA är låg eller inte och den var inte låg en enda gång under hela skriv eller läsningen (obs denna kodbit är borta i denna koden). Så det verkar vara något knas med skrivningen, om jag nu inte har missförstått hur man kollar om man fått ack eller inte.

Många kakor till den som klarar ut felet.

Kod: Markera allt

	list      p=16F628A           ; list directive to define processor
	#include <p16F628A.inc>       ; processor specific variable definitions

	errorlevel  -302              ; suppress message 302 from list file


;Config
	__CONFIG   _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_ON & _WDT_OFF & _PWRTE_ON & _HS_OSC

;Interupt
INT   CODE 0x0004
	goto Intr

;Reset
RST   CODE     0x0000
	goto     Init

;Variablar
	UDATA


;Delade variablar
	UDATA_SHR
	COUNT1				RES 1
	COUNT2				RES 1
	COUNT3				RES 1
	TEMP				RES 1
	EEPROM_ADR_H		RES 1
	EEPROM_ADR_L		RES 1
	EEPROM_DEV			RES 1
	EEPROM_DATA			RES 1


	Intr CODE
Intr
	retfie

	CODE
Init
;Define
	#Define		PORT_SDA		PORTA,0
	#Define		PORT_SCL		PORTA,1
	#Define		TRIS_SDA		TRISA,0
	#Define		TRIS_SCL		TRISA,1

;Stänger av analoga funktioner
	BANKSEL PORTA
	clrf PORTA
	BANKSEL CMCON
	movlw 0x07
	movwf CMCON

;Sätter variablar
	clrf COUNT1
	clrf COUNT2
	clrf COUNT3
	clrf TEMP
	clrf EEPROM_ADR_H
	clrf EEPROM_ADR_L
	clrf EEPROM_DEV
	clrf EEPROM_DATA

;Sätter portar
	BANKSEL TRISA
	clrf TRISA
	clrf TRISB
	BANKSEL PORTA
	clrf PORTA
	clrf PORTB

;Testloop, skickar ett värde till eepromet och läser sedan av samma värde från eepromet och lägger på PORTB
Start
	movlw 0x45
	movwf EEPROM_ADR_H
	movlw 0x3A
	movwf EEPROM_ADR_L
	movlw .0
	movwf EEPROM_DEV
	movlw b'10010110'
	call Rand_write

	movlw 0x45
	movwf EEPROM_ADR_H
	movlw 0x3A
	movwf EEPROM_ADR_L
	movlw .0
	movwf EEPROM_DEV
	movlw b'10010110'
	call Rand_read

	BANKSEL PORTB
	movf TEMP,W
	movwf PORTB
hej
	goto hej

Rand_read ;Läser innehållet på adressen EEPROM_ADR_H/EEPROM_ADR_L från minne nr. EEPROM_DEV och sparar i W-reg
	call EEPROM_Start ;Utför start condition
	bcf STATUS,C ;skapa första byten (1010ABC0) A,B,C är EEPROM_DEV, sista nollan är för write
	rlf EEPROM_DEV,F
	movlw b'10100000'
	addwf EEPROM_DEV,W
	call Send_byte ;skickar första byten
	call ACK ;acknowledge
	movf EEPROM_ADR_H,W ;skickar adressbitar 15-8
	call Send_byte
	call ACK
	movf EEPROM_ADR_L,W ;skickar adressbitar 7-0
	call Send_byte
	call ACK
	call EEPROM_Start
	bsf STATUS,C ;skapa första byten (1010ABC1) A,B,C är EEPROM_DEV, sista ettan är för read
	rlf EEPROM_DEV,F
	movlw b'10100000'
	addwf EEPROM_DEV,W
	call Send_byte ;Skickar device select
	call ACK
	call Read_byte
	call NOACK
	call EEPROM_Stop
	return

Read_byte ;Läser en byte och sparar i W-reg
	movlw .8
	movwf COUNT3 ;loopvariabel
Read_byte_0
	call SDA_H ;Gör SDA till en input
	call SCL_H
	BANKSEL PORTA
	btfss PORT_SDA
	goto Read_0
	goto Read_1
Read_0
	bcf STATUS,C
	rlf TEMP,F
	goto Read_count
Read_1
	bsf STATUS,C
	rlf TEMP,F
	goto Read_count
Read_count
                call SCL_L
	decfsz COUNT3,F ;kör loopen 8 gånger för att läsa en byte
	goto Read_byte_0
	movf TEMP,W ;loopen är klar, sparar svaret i W-reg
	return

Rand_write ;Skriver innehållet i W-reg på adress EEPROM_ADR_H/EEPROM_ADR_L till minne nr. EEPROM_DEV
	movwf EEPROM_DATA ;sparar W-reg i en temporär variabel.
	call EEPROM_Start ;Utför start condition
	bcf STATUS,C ;skapa första byten (1010ABC0) A,B,C är EEPROM_DEV, sista nollan är för write
	rlf EEPROM_DEV,F
	movlw b'10100000'
	addwf EEPROM_DEV,W
	call Send_byte ;skickar första byten
	call ACK ;acknowledge
	movf EEPROM_ADR_H,W ;skickar adressbitar 15-8
	call Send_byte
	call ACK
	movf EEPROM_ADR_L,W ;skickar adressbitar 7-0
	call Send_byte
	call ACK
	movf EEPROM_DATA,W ;skickar databyten
	call Send_byte
	call ACK
	call Delay_long ;väntar 10ms för intern writecykel
	return

Send_byte ;Skickar innehållet i W-reg till det externa eepromet
	movwf TEMP
	movlw .8
	movwf COUNT3 ;loopvariabel
Send_byte_0
	rlf TEMP,F ;flyttar ut högsta byten och kollar om den är 1 eller 0
	btfss STATUS,C
	goto Send_0
	goto Send_1
Send_0 ;klocka ut en nolla till eepromet
	call SDA_L
	call SCL_H
	call SCL_L
	goto Send_count
Send_1 ;klocka ut en nolla till eepromet
	call SDA_H
	call SCL_H
	call SCL_L
	goto Send_count
Send_count
	decfsz COUNT3,F ;kör loopen 8 gånger för att skicka en byte
	goto Send_byte_0
	return

EEPROM_Start ;SDA 1-0 medan SCL = 1
	call SCL_L ;Säkerhetsåtgärd ifall SCL råkar vara 1
	call SDA_H
	call SCL_H
	call SDA_L
	call SCL_L
	return

EEPROM_Stop ;SDA 0-1 medan SCL = 1
	call SDA_L
	call SCL_H
	call SDA_H
	call SCL_L
	return

SDA_H ;SDA = input
	BANKSEL TRISA
	bsf TRIS_SDA
	call Delay_short
	return

SDA_L ;SDA = output (0)
	BANKSEL PORTA
	bcf PORT_SDA
	BANKSEL TRISA
	bcf TRIS_SDA
	call Delay_short
	return

SCL_H ;SCL = input
	BANKSEL TRISA
	bsf TRIS_SCL
	call Delay_short
	return

SCL_L ;SDA = output (0)
	BANKSEL PORTA
	bcf PORT_SCL
	BANKSEL TRISA
	bcf TRIS_SCL
	call Delay_short
	return

ACK ;klockar ut en acknowledge
	call SDA_H
	call SCL_H
	call SCL_L
	return


NOACK ;klockar ut en acknowledge
	call SDA_L
	call SCL_H
	call SCL_L
	return



Delay_short
			;46 cycles
	movlw	0x2F
	movwf	COUNT1
Delay_short_0
	decfsz	COUNT1, f
	goto	Delay_short_0

			;4 cycles (including call)
	return


Delay_long
			;249993 cycles
	movlw	0x4E
	movwf	COUNT1
	movlw	0xC4
	movwf	COUNT2
Delay_long_0
	decfsz	COUNT1, f
	goto	$+2
	decfsz	COUNT2, f
	goto	Delay_long_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

	end

Postat: 21 september 2008, 11:25:28
av sodjan
Det löser kanske inte problemet med din 628A, men har du
funderat på att använda än modell med hårdvarustöd för I2C ?
T.ex F88 (somma pinout som 628A).

Postat: 21 september 2008, 11:44:32
av Swech
Kollat som hastigast...
du verkar trixa med bara TRIS för klockpulsen
Om du inte har extern pull up så måste du se till att din clock blir
"1".
Är dålig på PIC och intern pull up.. men har du kollat att det
verkligen blir någon klockpuls

Swech

Postat: 21 september 2008, 11:50:56
av Rocky_AL
Glömde säga att jag har pull-up motstånd på både SDA och SCL (10kΩ), så när jag sätter portarna till input så blir det "1". Har kollat med oscilloskop men det är analogt så man hinner knapt se något, men man hinner helt klart se lite fyrkantsvågor. Skulle vara jättelätt att se vad felet var om man hade ett digitalt oscilloskop eftersom man kan studera vågorna i efterhand, synd bara att de ska kosta så mycket.

Och jag vill kunna använda dessa funktioner på picar utan inbyggt I2C. Dessutom kan man alltid välja portar fritt om man gör egna funktioner.

edit: Om någon har färdig assemblerkod för detta så skulle även det lösa problemet. picen körs i 20MHz.

Postat: 21 september 2008, 14:22:55
av Swech
kör inte så fort då :D
Det går att köra riktigt långsamt med i2c också....

Postat: 21 september 2008, 15:00:07
av Rocky_AL
När är det man ska kolla om man får ett ack egentligen? Jag vet att det är klockpuls nr. 9 man ska få ett ack, men under vilken del?

1. Innan klockpuls nr. 9 går från Låg till hög
2. När klockpuls nr. 9 är på stadig hög nivå.
3. Efter klockpuls nr. 9 har gått från hög till låg nivå.

Jag antar att picen (master) ska sätta SDA som input och sedan läsa av värdet på SDA. Om den är noll så har man fått ett ack, om den är ett så har man inte fått ett ack (förutsatt att man har pull-up). Rätta mig om jag har fel.

Postat: 21 september 2008, 15:07:46
av Icecap
Jag har testat att göra en IIC i mjukvara, det var egentligen inte så speciellt besvärligt men jag ogillar den buss generellt.

ACK får man vid att slaven HÅLLER SCL låg till den är klar, mastern ger alltså "som vanligt" en SCL och släpper den och kollar när den blir hög.

Ibland går det fort och då hinner slaven släppa innan/samtidig som mastern.

Postat: 21 september 2008, 15:22:17
av Rocky_AL
Vänta nu, ska inte slaven hålla SDA låg och inte SCL. SCL är ju klockpulsen.

Postat: 21 september 2008, 15:27:25
av Icecap
Oj... just det. Ett tag sedan jag pillade med IIC...

Postat: 21 september 2008, 17:09:46
av Rocky_AL
Blir bara mer och mer förvirrad med det här, jag hhittar verkligen inget fel i koden, men det vägrar fungera.

Postat: 21 september 2008, 22:37:56
av Marta
För att testa med ett vanligt scope så gör Du ett speciellt testprogram som skickar den sekvens som skall testas om oc om igen med så hög hastighet som det är möjligt så Du får en stabil bild. Om det behövs så pulsa en extra utgång varje gång sekvensen startar så scopet kan triggas stabilt. Sedan ka Du bläddra igenom förloppet med fördröjd tidbas. Har Du ingen DTB så flytta triggpulsgenereringen. Denna metod fungerar oftast mycket bra.

Postat: 22 september 2008, 06:46:01
av Swech
kan detta vara något?

To read starting at a particular address in the EEPROM, a combined message is used. After a START, the master first writes that chip's bus address with the direction bit clear ("write") and then the two bytes of EEPROM data address. It then sends a (repeated) START and the EEPROM's bus address with the direction bit set ("read"). The EEPROM will then respond with the data bytes beginning at the specified EEPROM data address -- a combined message, first a write then a read.

Postat: 22 september 2008, 17:44:33
av Rocky_AL
Precis så min rand_read fungerar. Det jobbigaste med allt detta är att jag inte kan veta om det är läs eller skrivfunktionen som är fel.
Ingen som har färdig kod eller kan prova att först använda min skrivfunktion och sedan använda en redan fungerande läsfunktion för att se om något har hänt?

Postat: 22 september 2008, 18:26:33
av Stranne

Postat: 22 september 2008, 18:36:00
av Swech
Har du valt rätt ID på EE minnet? Den har väl 3 pinnar för detta...