PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] I2C auf 16F876A klappt nicht



RoboHolIC
26.10.2015, 00:57
Hallo.

Ich habe jetzt etwa 10-15 Stunden intensiv Microchip-Doku gelesen, probiert, geändert, allgemein recherchiert ..., ohne dem PIC16F876A eine I2C-Kommunikation entlocken zu können. Der Austausch mit einem fabrikneuen Teil hat nichts geändert. Versorgungsspannungen sind Oszi-gesichtet und OK.
Kupferseitig ist alles getestet, PullUps vorhanden (wobei die 3,3kOhm-Widerstände in der Schaltung bei gestecktem Controller als 1,7kOhm gemessen werden ???).

Auf der Basis von PIC16F886 hatte ich schon verschiedene I2C-Bausteine zum Sprechen gebracht; einer davon war aktuell verwendet worden - ebenfalls bewährte Hardware. Der Code war eigenentwickelt, sodass schon etwas Erfahrung mit diesem Bus und dem Controller vorliegt.

Was nicht gelingen will, ist die Portierung auf den PIC16F876A, obwohl ich keinerlei Hinweise darauf habe, dass sich Register, Bits oder Logik des MSSP-Moduls zwischen den beiden Typen unterscheiden könnten.

Die SCL-Leitung ist trotz des PullUps sehr EMV-empfindlich; bereits die Berührung mit dem Oszi-Tastkopf bringt den Programmablauf in einen Blockierzustand (Warten auf Godot). Diesen führe ich darauf zurück, dass meine I2C-Codeabschnitte keine Timeout-Absicherungen haben - das war bisher nie nötig.

Hat jemand ähnliche Erfahrungen und das Problem gelöst?

Gruß
RoboHolIC

P.S.:
Dieses Problem ist "Hobby" innerhalb des Hobbies - im Grunde unwichtig, aber Aufgeben ist keine Art, mit solchen Problemen umzugehen, oder?

PICture
26.10.2015, 07:37
Hallo!


Was nicht gelingen will, ist die Portierung auf den PIC16F876A, obwohl ich keinerlei Hinweise darauf habe, dass sich Register, Bits oder Logik des MSSP-Moduls zwischen den beiden Typen unterscheiden könnten.

Man kann nicht alle mögliche vom Microchip eingeführte Änderungen nur anhand Datenblätter erkennen. Ich habe immer nach Programablaufdiagram nacheinander kleine Fragmente von gesamten auf anderen PIC geprüften Program zum Laufen gebracht. Es gibt nie zu viel Übung und Erfahrung. ;)

witkatz
26.10.2015, 11:27
10-15 Stunden intensiv Microchip-Doku gelesen
Hast du auch das MSSP Erratum (http://ww1.microchip.com/downloads/en/DeviceDoc/80131e.pdf) studiert?

Gruß
witkatz

RoboHolIC
27.10.2015, 00:02
Danke Euch für die Hilfestellung.

Die 10-15 Stunden beziehen sich auf alle Tests, Arbeiten am Source, Verschmelzungen etc. gesamt.
Dieses Erratum kenne ich schon seit meinen PIC-Anfängen, ich sehe aber keine Anknüpfungspunkte zu meinem Code; trotzdem Danke.

Zur Konkretisierung reiche ich die Symptome des Nicht-Funktionierens nach:
- SDA ist (fast) immer "H", die angeblich gelesenen Bytes sind alle 0xFF
- gestern habe ich allerdings einiges Gezappel auf SDA entdeckt; dessen Wiederholfrequenz dürfte der Durchlaufzeit der Hauptschleife entsprechen, die fast auschließlich von der Aktualiserung des LCD bestimmt wird
- SCL scheint EMV-sensibel zu sein oder mit seinem Echo zu sprechen - bereits das begrabschen des I2C-Flachbandkabels führt zur Störung / Blockierung; Oszi-Tastkopf dito (allerdings bilden die Schirmungen von Oszi und PICkit3 -- USB -- PC eine "Brummschleife") Es sind keine SCL-Pulse darstellbar, weil der Controller dann sofort im Programmdurchlauf hängen bleibt.
- der elektrische Test bestand darin, SCL und SDA als I/Os mit L-Pegel zu konfigurieren, die beiden TRISC-Bits zu toggeln und den Pegelwechsel zu oszilloskopieren. Am SCL war bei der fallenden Flanke ein hässlicher Unterschwinger bis etwa -2V zu sehen.

Jetzt würde ich gerne die OpenDrain-Konfiguration testen. Man kann ja I2C per Firmware emulieren. Läuft das mit TRISC-Registern oder steuert man dann die OpenDrains direkt an? Und falls ja, wie? Das Blockschaltbild für I2C-Master-Betrieb macht mir allerdings wenig Hoffnung auf einen direkten Zugriff auf die OpenDrains.

Siro
27.10.2015, 08:32
Hallo RoboHolIC

Das klingt irgenwie als wäre der SCL Pin hochohmig.

Für einen Fullspeed Modus sollten deine beiden Pullups bei 3,3 Volt Versorgung 1K haben.
Hier sehe ich aber eher nicht das Problem.

Könnte es sein, dass dein TRISC-Register von einer anderen Initialisierung überschrieben wird.
Durch ein sogenanntes Read Modify Write kann dies unvorhergesehene Ergebnisse haben und deine Initialiseiugn des Registers stimmt nicht mehr.

Schau mal im Datenblatt: 4.3 PORTC and the TRISC Register, ist bei mir die Seite 46

Aus dem Datenblatt:
When enabling peripheral functions, care should be
taken in defining TRIS bits for each PORTC pin. Some
peripherals override the TRIS bit to make a pin an
output, while other peripherals override the TRIS bit to
make a pin an input. Since the TRIS bit override is in
effect while the peripheral is enabled, read-modifywrite
instructions (BSF, BCF, XORWF) with TRISC as the
destination, should be avoided. The user should refer
to the corresponding peripheral section for the correct
TRIS bit settings.

Versuche mal deine Software so zu ändern, dass Du nur ein "einziges" Mal einen Zugriff auf das TRISC Register hast.
weis jetzt nicht ob Du in Assembler oder C programmierst. Also TRISC = xxxx;

oder
movlw xxx
movwf TRISC

Siro

PICture
27.10.2015, 08:58
Für erfolgreiches Portieren eines geprüftes Programms muss die Software lauffähig ganz unabhängig von der Hardware sein.

witkatz
27.10.2015, 14:39
@RoboHolIC: wenn das mein ASM Projekt wäre, würde ich prüfen, ob ich die Bank für TRISC richtig selektiert habe (und zu 50% läge ich damit richtig). Für einen alten Hasen wie dich ist dieser Tipp wohl überflüssig. Leider habe ich keinen PIC16F876A um das Problem nachzustellen. Ich kann dir nur anbieten, über dein Quellcode drüberzuschauen, wenn du es posten magst (sonst gerne per PN oder Mail).

PICture
27.10.2015, 18:29
Ich vermute bisher unbekannte fehlerhafte Zuweisung von Pins. Ich mag eben nur über etwas konkretes reden.

RoboHolIC
27.10.2015, 22:23
Danke euch für die vielen Beiträge.

@Siro: Ich programmiere in ASM. Die TRISx-Register setze ich gleich zu Anfang und immer byteweise. Dann wird per bcf TRISC,3 und bsf TRISC,3 die eventuelle I2C-Busbelegung weggetaktet; erst dann erfolgt die Aktivierung des MSSP-Moduls als I2C-Master.

@witkatz: Jo, kann alles passieren, passt aber.

@PICture: Grundsätzlich gebe ich dir recht: Konkreter Code ist besser als rätseln. Weil ich jetzt eine Reihe zugemüllter und verbastelter Source-Varianten habe, ist da erstmal eine Konsolidierung fällig.

Ich habe eine ande Minimal-Schaltung mit PIC16F886, einem LCD-Modul und I2C-Anschluss herumliegen. Die war für mein Altimeter bestimmt, passte aber wider Erwarten (ich Dussel !) in keines der beim großen C..... verfügbaren handlichen Gehäuse #-o. Für sie - und eben den 16F886 - existiert funktionierender I2C-Code. Den hatte ich dieser Tage schon auf Minimum reduziert und mit meiner bewährten Ausleseroutine für den Zielbaustein (den bisher noch unerwähnten BMA020 auf dem Breakout von ELV) angepasst, um rauszukriegen, ob
a) das Trägerplatinchen für den BMA020 noch funktioniert und
b) ich nicht zu doof zum ASM-Programmieren geworden bin.

Das Ergebnis lag binnen Stundenfrist vor und war in jeder Hinsicht erfreulich. Der (oben erwähnte) Portierungsversuch auf den anderen Controller und das andere "Biotop" (die eigentliche Ziel-Leiterplatte) war wie beschrieben erfolglos verlaufen.

Jetzt werde ich den Spieß umdrehen und versuchen, die funktionierende Kombination durch Chiptausch und Codeanpassung auf 16F876A umzustricken. Dieser Weg sollte ja der kürzere sein. Vielleicht entpuppt sich so die große handgestrickte und später umfangreich modifizierte Ziel-Leiterplatte als Fehlerquelle.

Ich bin gespannt und werde berichten.

Gruß
RoboHolIC

RoboHolIC
28.10.2015, 00:24
Hallo an alle, die bis hierher mitgedacht und sich bemüht haben.


Jetzt werde ich den Spieß umdrehen und versuchen, die funktionierende Kombination durch Chiptausch und Codeanpassung auf 16F876A umzustricken ...
... Ich bin gespannt und werde berichten.

Uuups - der Spieß hat sich gedreht, nur nicht so wie ich es mir vorgestellt hatte. Der angedeutete Testfall findet nicht statt, weil das für den 16F886 entwickelte Platinchen aus der Grabbelkiste dem 16F876A nicht den benötigten externen Taktgeber/Schwinger bieten kann. Da fällt mir doch glatt der technische Fortschritt auf die Füße ...

Natürlich könnte ich den Lötkolben anheizen, die Teilekiste auspacken und loslegen:

Dieses Problem ist "Hobby" innerhalb des Hobbies - im Grunde unwichtig, aber Aufgeben ist keine Art, mit solchen Problemen umzugehen, oder?
Aber angesichts eines Restbestands von zwei Stück 16F876A vielleicht wiederum doch ... :oops:

Gute Nacht wünscht
RoboHolIC

Andre_S
28.10.2015, 07:37
Hallo RoboHolIC!

Vor der Umstellung auf 18F422 und später dsPIC30F5013 haben wir den 16F876 in allen unseren Steuerungen eingesetzt.
Das es jetzt bezüglich der Version A Unterschiede im MSSP Modul oder dessen Initialisierung gibt hab ich erst mal nicht in Erinnerung, zumindest der 16F876 wurde von uns in sehr vielen Geräten verbaut. Dei A gabs wohl Unterschiede im flashen und der Geschwindigkeit.
Bei allen arbeitet der I²C-Bus zwecks Kommunikation mit Speicherbaustein, RTC und steckbarer Speicherkarte ohne Probleme. Der Bus ist somit auf die Größe des Platinenlayouts beschränkt und wird nicht extern weitergeführt.
Allerdings bediene ich diesen auf Grund der Umweltbedingungen (Freifeldeinsatz) und nicht benötigter höherer Geschwindigkeit, von vorn herein nicht mit maximaler Busgeschwindigkeit der Komponenten, sondern soweit ich mich erinnern kann mit ca. 125Khz. (MC mit 4Mhz)

Grundvoraussetzung um wirklich „effektiv“ helfen zu können, wäre sicherlich die Offenlegung der Quellcodes bezüglich Initialisierung und Kommunikation deinerseits, zumindest um so möglichen Fehlerquellen innerhalb der Programmierung ausschließen zu können.
Meine Quellen sind ebenfalls in Assembler, aber eventuell hast Du den Fehler inzwischen auch lokalisieren können…


Gruß André

Siro
28.10.2015, 09:02
Hallo RoboHolIC

Ich hab jetzt nicht ganz verstanden warum Du das RC3 Bit einmal auf Input und dann wieder auf Output setzt, das ist doch die Clockleitung
aber genau da könnte der kritische Punkt sein.

wenn Du mit dem BCF oder BSF Befehl arbeitest, liest der Controller den "gesamten" Port, also "ALLE" Bits vom C-Port (READ)
und speichert sie irgendwo zwischen. Dann wird das entsprechende "einzelne" Bit gesetzt oder gelöscht. (MODIFY)
Dann wird der zwischengespeicherte Wert zurück an den Port geschrieben. !! ALLE 8 Bits (WRITE)
Das Problem: Hat der Controller ein High auf einem anderen Bit gelesen, wird dieses Bit nun auch High gesetzt,
obwohl man dieses Bit garnicht anfassen wollte. Das bedeutet Du möchtest nur ein Bit verändern im TRIS Register aber
es ändern sich unter Umständen auch andere Bits, die man garnicht verändern wollte. Dann stimmt plötzlich zum Beispiel die Richtung IN/OUT nicht mehr

Ich habe mir um das zu vermeiden oft eine Variable angelegt und NUR Änderungen mittels BCF und BSF in dieser Variablen getätigt.
dann den gesamten Wert (8 Bit) auf den Port geschrieben.

Das ist natürlich nur eine Vermutung dass der Fehler daher rührt.
Bankselect ist ebenso "SEHR" wichtig, wie witkatz schon schrieb.
Das waren auch zu 99 Prozent meine Softwareprobleme.

Siro

witkatz
28.10.2015, 10:44
Dann wird per bcf TRISC,3 und bsf TRISC,3
das Gleiche passiert hoffentlich auch mit TRISC,4 (s. MSSP Erratum)


erst dann erfolgt die Aktivierung des MSSP-Moduls als I2C-Master.
Mit TRISC passiert nach der Aktivierung des MSSP-Moduls nichts mehr? V.a. kein bcf/bsf


Minimal-Schaltung mit PIC16F886, einem LCD-Modul und I2C-Anschluss
Falls du LCD auch mit PORTC ansteuerst, solltest du die TRISC Benutzung in den LCDRoutinen unter die Lupe nehmen. Ich hatte schon mal ein ähnliches Problem mit I2C allerdings mit einem anderen PIC Typ, als ich in der nachgeschalteten LCD Initialisierungsroutine TRISC auf 0 setzte (hat mich auch Zeit gekostet :rolleyes:)

Gruß
witkatz

RoboHolIC
28.10.2015, 23:45
Hallo an alle Mitdenker!


Grundvoraussetzung um wirklich „effektiv“ helfen zu können, wäre sicherlich die Offenlegung der Quellcodes ...
Hier kommt er - bewährt dysfunktional: (hoffentlich nicht sinnentstellend gekürzt)


; list p=16f876a ; list directive to define processor
#include P16F876A.INC ; processor specific variable definitions


__CONFIG _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

; STATUS bit definitions

#define _C STATUS,0 ; fuer die Kurzschreibung
#define _Z STATUS,2 ; von Bitpruefungen "BTFSS _Z"




; Hardware-Signale
; PORT A
; Pins RA0/1/3 analog beschaltet
; Pin RA2 Ausgang ENL fuer LCD
; Pin RA4 Eingang
; Pin RA5 Ausgang ENR fuer Register

; PORT B
; LCD-Modul Und Latch: nur Ausgaenge
; Datenpuffer: cio_lcd, dat_reg

; Flag-Bits
#define LCD_TXF flaxa,0 ; =1: Sendeauftrag fuer ISR ans LCD
#define LCD_RS flaxa,1 ; =1: Datenregister, =0: Befehlsregister
#define LZBLANK flaxa,2 ; =1: unterdruecke fuehrende Nullen
#define REG_TXF flaxa,3 ; =1: Sendeauftrag fuer Registerwerte
#define SCRFLAG flaxa,6 ; =1: temporaeres Flag
; #define NEWDATA flaxa,7 ; =1:



; gültig für f_OSZ = 12MHz (HS_OSC)
T1PREH EQU 0xF4 ; preload = 2^16 - d'3000' = 0xF448 für 1000,0 Hz @ 12MHz /4, 1:1
T1PREL EQU 0x48 ; d.h. 3000x 1µs bis zum Overflow



;***** KONSTANTEN *****
ADR_BMA020 EQU 0x70 ; I2C-Basisadresse des g-Sensors


; Pin-Nummern fuer LCD und Out-Register
BIT_ENL EQU d'2' ; Port A: LCD-Enable-Signal
BIT_ENR EQU d'5' ; Port A: Out-Register-EN an
BIT_RW EQU d'2' ; Port B: Read- /Write-Signal LCD
BIT_RS EQU d'1' ; Port B: RegisterSelect-Signal, Funktion: Data bzw. /Instruction


;***** BANK(0)-EXKLUSIVE REGISTER 0x20..0x6F
CBLOCK 0x20
cio_lcd ; Datenpuffer LCD
dat_reg ; Datenpuffer OpenColl, Register
cio_dac ; Datenpuffer Digital-Analog-Wandler (D7..D0 --> D0..7, seitenverkehrt)
mscr ; Scratch Byte fuer das Hauptprogramm (alles ausser ISR)
iscr ; Scratch Byte fuer die Interrruptausfuehrung (ISR)
buf_ra ; EN-Ausgaenge LCD und Register
sta_ra ; dig. Eingang
buf_rc ; falls mal Ausgaenge sein werden
sta_rc ; Tasteneingaenge
flaxa ; div. Flags
dat_lcd ; Netto-/Nutz-Daten zum LCD; NICHT die Bitleiste inkl. Steuersignale am Bus: das ist cio_lcd
cur_zsp ; Text-Position Zeile,Spalte
cur_cmd ; DisplayDataRAM-Adresse fuer LCD-Modul
txt_fst ; "first char"-Index des gewaehlten Fixtexts
txt_idx ; Laufvariable
i2c_adr ; Device-Adresse
i2c_reg ; Registeradresse im Device
i2c_dat ; Dateninhalt des Registers
loopcount ; frei verfuegbarer Durchlaufzaehler
gxraw ; Rohwert-MSB g-Sensor x-Achse
gyraw ; Rohwert-MSB g-Sensor y-Achse
gzraw ; Rohwert-MSB g-Sensor z-Achse
ENDC ;


;***** GLOBALE VARIABLEN 0x70..0x7F
dlycnt EQU 0x7A ; allg. Delay-Restzyklen zu je 1ms
ticker EQU 0x7B ; allg. Laufvariable u.a. fuer das Lebenszeichen
tickerh EQU 0x7C ; Folgestufe zur Zaehlung der ticker-Ueberlaeufe
temp_w EQU 0x7D ; variable used for context saving
temp_status EQU 0x7E ; variable used for context saving
temp_pclath EQU 0x7F ; variable used for context saving






;************************************************* *********************
; ; Begin of 2k-Code Page #0 (1/4)
ORG 0x0000 ; processor reset vector
;************************************************* *********************

nop ; nop required for icd
goto MAIN ; go to beginning of program
nop
nop



;************************************************* *********************
ORG 0x004 ; interrupt vector location
;************************************************* *********************


;********** Kontext sichern **********
movwf temp_w ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf temp_status ; save off contents of STATUS register
movf PCLATH,w ; move pclath register into w register
movwf temp_pclath ; save off contents of PCLATH register

; Lead-In
bcf STATUS,RP0 ;
bcf STATUS,RP1 ; Bank 0 als default
bcf PCLATH,4 ;
bcf PCLATH,3 ; Code Page #0




;;************** ANFANG DER TMR1-ISR *******************
ISR_TMR1
bcf PIR1,TMR1IF ; auslösendes Flag löschen
movlw T1PREH ; Preload-Werte laden
movwf TMR1H ;
movlw T1PREL ;
movwf TMR1L ;


ISR_DIGIN
; Momentaufnahmen der dig. Eingaenge
movf PORTA,w ;
movwf sta_ra ;
movf PORTC,w ;
movwf sta_rc ;
ISR_DIGIN_E


ISR_LCD
; LCD bedienen
btfss LCD_TXF ; ToDo-Flag da? Sendeauftrag?
goto ISR_LCD_E ; =0: nix zu tun
bcf LCD_TXF ; Arbeit da; Flag loeschen
movf cio_lcd,w ;
movwf PORTB ;
bsf buf_ra,BIT_ENL ; das Bit fuer EN am LCD
movf buf_ra,w ;
movwf PORTA ; LCD enable aktiv
nop ; Wartezeit, ...
nop ;
nop ;
nop ;
nop ;
nop ; 6* 0,33us = 2us
bcf buf_ra,BIT_ENL ;
movf buf_ra,w ;
movwf PORTA ; LCD enable wieder passiv
nop ; Wartezeit, ...
nop ;
nop ;
nop ;
nop ;
nop ; 6* 0,33us = 2us
ISR_LCD_E


ISR_REG
; Register bedienen
btfss REG_TXF ; neue Daten ?
goto ISR_REG_E ; DAC ohne Auftrag nicht stoeren (DAC haengt DIREKT am PORTB)
bcf REG_TXF ; Arbeit da; Flag loeschen
movf dat_reg,w ;
movwf PORTB ;
bsf buf_ra,BIT_ENR ; das Bit fuer EN am Register
movf buf_ra,w ;
movwf PORTA ; Register-enable aktiv
nop ; Wartezeit, ...
nop ;
nop ;
nop ;
nop ;
nop ; 6* 0,33us = 2us
bcf buf_ra,BIT_ENR ;
movf buf_ra,w ;
movwf PORTA ; Register-enable wieder passiv
nop ; Wartezeit, ...
nop ;
nop ;
nop ;
nop ;
nop ; 6* 0,33us = 2us
ISR_REG_E



; Millisekunden-Eieruhr
movf dlycnt,f ; Z-Flag generieren
btfss STATUS,Z ;
decf dlycnt,f ; dekr., wenn nicht null (Z=0)


; Hier laeuft die 'Unruh' des Controllers bzw. seiner Firmware
incf ticker,f ; universell und immer in Bewegung
movf ticker,w ;
btfsc _Z ;
incf tickerh,f ;




; abschliessende Arbeiten vor Ende der ISR:
ISR_TAIL
; ... derzeit nichts



;************** ENDE DER TMR1-ISR *******************

ISR_RESTORE
; Kontext wiederherstellen
movf temp_pclath,w ; retrieve copy of PCLATH register
movwf PCLATH ; restore pre-isr PCLATH register contents
movf temp_status,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf temp_w,f ; Kniff: W laden, ohne den Status zu veraendern !
swapf temp_w,w ; restore pre-isr W register contents

retfie ; return from interrupt (bewirkt auch ein "bsf INTCON,GIE")


;***
MAIN ; Das Hauptprogramm



; RAM (partiell) initialisieren
movlw 0x20 ; erste Speicherzelle
movwf FSR ; Zeiger setzen
RAMCLR
clrf INDF ; Zugriff !
incf FSR,f ; naechste Zelle
btfss FSR,7 ; fertig ? Zeiger >= 0x80 ?
goto RAMCLR ; NEIN
RAMCLR_E


; Portregister fuer digitale Ausgaenge initialisieren

movlw b'00010000' ; 5: EN des Registers, 4: OC, 2: EN des LCD
movwf buf_ra ;
movwf PORTA ; (Umbau-bedingte Abweichung von meinem Anschlussprinzip)
movlw b'00000000' ;
movwf cio_lcd ;
movwf PORTB ;
movlw b'00000000' ; PORTC[4;3] unbedingt auf "L" setzen,
movwf buf_rc ;
movwf PORTC ; damit der I2C-Bus-Reset durchfuehrbar ist


; I/O-Leitungen konfigurieren
banksel TRISA

movlw b'11001011' ; 5: EN Register, 4: OC, 3, 1, 0: ANx, 2: EN LCD
movwf TRISA ;
movlw b'00000000' ; alles Ausgaenge fuer LCD-Modul, Register, DAC
movwf TRISB ;
movlw b'11111000' ; 7..5: Tasten; 4,3: I2C; 2,1: PWM, 0: I/O
movwf TRISC ;

banksel PORTA ; Bank 0



; ADC konfigurieren
bsf STATUS,RP0 ; Bank 1
movlw b'01000100' ; Fosc/64 left just.
movwf ADCON1 ;
bcf STATUS,RP0 ; Bank 0

movlw b'10011000' ; Start mit Poti-Kanal
movwf ADCON0 ;




; Timer1 Prescaler konfig. & Preload
; gültig für f_OSZ = 4MHz (HFINTOSC-default prescaler 2:1)
movlw b'00000000' ;
movwf T1CON ;
movlw T1PREH ; preload = 2^16 - d'3000' für 1000,0 Hz @ 12MHz /4, 1:1
movwf TMR1H ; d.h. 3000x 0,333µs bis zum Overflow
movlw T1PREL ;
movwf TMR1L ;


; Interrupts enablen, Timer in Gang setzen
bsf INTCON,PEIE ; Gruppe der Peripherals
bsf INTCON,GIE ; globales Enable
bsf STATUS,RP0 ; Bank 1
bsf PIE1,TMR1IE ; Timer 1: 1= IRQ enable
bcf STATUS,RP0 ; Bank 0
bsf T1CON,TMR1ON ; Timer 1: 1= starten


; I2C Bus Recovery, falls erforderlich
I2CCLR_A
btfss PORTC,3 ; SCL-Signal
goto I2CCLR ;
btfss PORTC,4 ; SDA-Signal
goto I2CCLR ;
goto I2CCLR_E ; keines von beiden ist low, Bus ist frei
I2CCLR
bsf STATUS,RP0 ; Bank 1
; bcf TRISC,3 ; SCL aktiv = low, fallende Taktflanke
movf TRISC,w
andlw b'11110111'
movwf TRISC
bcf STATUS,RP0 ; Bank 0
movlw d'5' ;
call P0_IDLE ; ... und warten
;
bsf STATUS,RP0 ; Bank 1
; bsf TRISC,3 ; SCL passiv = high, steigende Taktflanke
movf TRISC,w
iorlw b'00001000'
movwf TRISC
bcf STATUS,RP0 ; Bank 0
movlw d'5' ;
call P0_IDLE ; ... und warten

goto I2CCLR_A ; Bus frei? Testen!

I2CCLR_E
bsf buf_rc,3 ;
bsf buf_rc,4 ; eventuell loest dies hier das Nichtfunktionieren der I2C-Verbindung
movf buf_rc,w ;
movwf PORTC ;




; LCD-Modul initialisieren
pagesel LCD_INIT ;
call LCD_INIT ; Basisinitialisierung
; pagesel LCD_CGRAM ;
; call LCD_CGRAM ; laden der frei definierbaren Zeichen
pagesel MAIN ;


; I2C initialisieren
pagesel I2C_INIT100 ;
call I2C_INIT100 ; I2C mit 100kHz starten
pagesel MAIN ;


movlw d'5'
call P0_IDLE



;************************************************* *********************
; Anfang der Hauptschleife
;************************************************* *********************


SCHLEIFE

; incf loopcount,f

pagesel ACC_GETGXYZ
call ACC_GETGXYZ
pagesel SCHLEIFE

; Unterfunktion zum Test des g-Sensormoduls BMA020

; X-Komponente anzeigen
movlw 0x11 ; Cursor positionieren
movwf cur_zsp ;
pagesel LCD_SETCURSOR ;
call LCD_SETCURSOR ;

; gekürzt

; Y-Komponente anzeigen
movlw 0x21 ; Cursor positionieren
movwf cur_zsp ;
pagesel LCD_SETCURSOR ;
call LCD_SETCURSOR ;

; gekürzt


; Z-Komponente anzeigen
movlw 0x28 ; Cursor positionieren
movwf cur_zsp ;
pagesel LCD_SETCURSOR ;
call LCD_SETCURSOR ;

; gekürzt

; Kontrolle fuer zyklischen Schleifendurchlauf
movlw 0x18 ; Cursor positionieren
movwf cur_zsp ;
pagesel LCD_SETCURSOR ;
call LCD_SETCURSOR ;

; gekürzt



pagesel SCHLEIFE
goto SCHLEIFE
;************************************************* *********************
; Ende der Hauptschleife
;************************************************* *********************




P0_IDLE
; Prinzip: "Jeder Code Page ihre eigene Bummelfunktion"
; wartet stur, bis die ISR den Zaehler dlycnt bis Null heruntergezaehlt hat
; benutzt dabei den selben Zaehler wie LCD_IDLE
movwf dlycnt ; Delayzeit wird im W-Register geliefert
P0_IDLE_1
movf dlycnt,f ; zero flag generieren
btfss STATUS,Z ; bei Z=1 ausbrechen
goto P0_IDLE_1 ;

return ;






;************************************************* *********************
;************************************************* *********************
ORG 0x0800 ; Begin of 2k-Code Page #1 (2/4)
;************************************************* *********************
;************************************************* *********************





;;************************************************ **********************
;; Initialisierung des I2C-Moduls
;I2C_INIT100
; movlw b'00001000' ; I2C Master Mode mit SSPADD
; movwf SSPCON ;
; bsf SSPCON,SSPEN ; und aktivieren
; ;
; bsf STATUS, RP0 ; Bank 1
; movlw 0x27 ; Achtung: Tabelle im Datenblatt des PIC16F87xA ist falsch; siehe MSSP-Errata
;; movlw 0xFF ; Test, minimale Frequenz
; movwf SSPADD ; Reload-Wert fuer Clock-Takt
; bcf STATUS, RP0 ; Bank 0
;
; banksel SSPSTAT
; bsf SSPSTAT,SMP ; =1: _disable_ slew rate control
; bcf SSPSTAT,CKE ; disable SMP-specific inputs
; banksel PORTA
; ;
; return ;

;************************************************* *********************
; Initialisierung des I2C-Moduls
I2C_INIT100
bsf STATUS, RP0 ; Bank 1
movlw 0x27 ; etwa 100kHz bei 12MHz-Oszillator
movwf SSPADD ; Reload-Wert für Clock-Takt
bcf STATUS, RP0 ; Bank 0
;
movlw b'00001000' ; I2C Master Mode mit SSPADD
movwf SSPCON ;
bsf SSPCON,SSPEN ; und aktivieren
;
return ;






;************************************************* *********************
; Beschleunigungsmesswerte aus dem BMA020 abholen: b.a.w. nur 3 MSBytes;
; Die hier implementierte I2C-Protokoll-Makrostruktur ist Chip-spezifisch.

ACC_GETGXYZ
; *********************** Ordnung und Ruhe am I2C-Bus ***************
; call I2C_SETTLE ; MSSP-Modul neu starten
; ******
; call I2C_W_STOP ; weitere Bus-Aktivitaeten unterbinden


; ************************************************** ***********
; *** Protokollzyklus: Accelerometer auslesen ***
; ************************************************** ***********

; *********************** Kommunikation starten ***************

call I2C_W_START ; (erstmalig) Start-Bedingung senden



; *********************** Adresspointer setzen ***************
movlw ADR_BMA020 ; Device schreibend (R/W = 0) adressieren
movwf SSPBUF ; ... und senden
call I2C_R_ACK ; /ACK vom Slave abwarten
; ******
movlw 0x02 ; Adresspointer
movwf SSPBUF ; ... senden
call I2C_R_ACK ; /ACK vom Slave abwarten


; *********************** Device auslesen ***************
call I2C_W_START ; (erneut) Start-Bedingung senden
; ******
movlw ADR_BMA020 ; Device lesend (R/W = 1) adressieren
iorlw 0x01 ; R/W = 1
movwf SSPBUF ; senden
call I2C_R_ACK ; /ACK vom Slave abwarten
; ******
call I2C_R_BYTE ; Byte anfordern (02h) und verwerfen
movf SSPBUF,w ; dummy read
call I2C_W_ACK ; /ACK senden
; ******
call I2C_R_BYTE ; Byte anfordern (03h)
movf SSPBUF,w ; Datum
movwf gxraw ; ... uebernehmen
call I2C_W_ACK ; /ACK senden
; ******
call I2C_R_BYTE ; Byte anfordern (04h) und verwerfen
movf SSPBUF,w ; dummy read
call I2C_W_ACK ; /ACK senden
; ******
call I2C_R_BYTE ; Byte anfordern (05h)
movf SSPBUF,w ; Datum
movwf gyraw ; ... uebernehmen
call I2C_W_ACK ; /ACK senden
; ******
call I2C_R_BYTE ; Byte anfordern (06h) und verwerfen
movf SSPBUF,w ; dummy read
call I2C_W_ACK ; /ACK senden
; ******
call I2C_R_BYTE ; LETZTES Byte (07h) anfordern
movf SSPBUF,w ; Datum
movwf gzraw ; ... uebernehmen
call I2C_W_NACK ; NACK senden

incf loopcount,f ; zu Testzwecken

; *********************** Kommunikation beenden ***************
call I2C_W_STOP ; Stop-Bedingung senden

; ************************************************** ***********
; *** Ende Protokollzyklus ***
; ************************************************** ***********

return ;






;************************************************* *********************
; nachfolgend die granularen I2C-Protokollelemente
;************************************************* *********************


;************************************************* *********************
; universeller Pseudo-Befehl "I2C-Bus zur Ruhe bringen" (Master)
I2C_SETTLE
; bcf I2C_TO ; Stoermeldung optimistisch loeschen
bcf PIR1,SSPIF ; I2C-Int.Request
bcf SSPCON,SSPEN ; MSSP deaktivieren
nop ;
nop ;
nop ;
bsf SSPCON,SSPEN ; und neu starten
nop ;
nop ;
nop ;

return ;
;************************************************* *********************
; universeller I2C-Befehl "Start senden" (Master)
; Blockierungspotential: Fehldiagnose "Bus-Kollision" durch EMI oder Kupferwurm
I2C_W_START
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bsf SSPCON2,SEN ; Start-Kondition einleiten
bcf STATUS, RP0 ; Bank 0
btfsc SSPCON,WCOL
goto I2C_W_START
I2CWSTART
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

btfss PIR1,SSPIF ; /ACK schon empfangen ?
goto I2CWSTART ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Repeated Start senden" (Master)
; Blockierungspotential: Fehldiagnose "Bus-Kollision" durch EMI oder Kupferwurm
I2C_W_RSTART
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bsf SSPCON2,RSEN ; Repeated-Start-Kondition einleiten
bcf STATUS, RP0 ; Bank 0
; btfsc SSPCON,WCOL
; goto I2C_W_RSTART
I2CWRSTART
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

btfss PIR1,SSPIF ; /ACK schon empfangen ?
goto I2CWRSTART ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Acknowledge /ACK vom Slave abwarten" (Master)
; Blockierungspotential: /ACK nicht detektiert
I2C_R_ACK
I2CRACK
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

btfss PIR1,SSPIF ; /ACK schon empfangen ?
goto I2CRACK ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Byte vom Slave holen" (Master)
; Blockierungspotential:
I2C_R_BYTE
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bsf SSPCON2,RCEN ; Empfangsbereitschaft herstellen
bcf STATUS, RP0 ; Bank 0
I2CRBYTE
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

; Dateneingang abwarten
btfss PIR1,SSPIF ; Empfangsdaten im Buffer ?
goto I2CRBYTE ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Acknowledge /ACK senden" (Master)
; (das ist ein Acknowledge mit aufforderndem Beigeschmack)
I2C_W_ACK
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bcf SSPCON2,ACKDT ; Ackn.-Pegel =0 setzen: --> /ACK (=0)
bsf SSPCON2,ACKEN ; /ACK-Sequenz starten
bcf STATUS, RP0 ; Bank 0
I2CWACK
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

; warten auf Ende der Acknowledge-Sequenz
btfss PIR1,SSPIF ; Sequenz schon beendet ?
goto I2CWACK ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Not-Acknowledge NACK senden" (Master)
; (das ist ein Acknowledge mit abweisendem Beigeschmack)
I2C_W_NACK
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bsf SSPCON2,ACKDT ; Ackn.-Pegel =1 setzen --> NACK : "OK, aber keine weiteren Daten erwuenscht"
bsf SSPCON2,ACKEN ; NACK-Sequenz starten
bcf STATUS, RP0 ; Bank 0
I2CWNACK
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

btfss PIR1,SSPIF ; Sequenz schon beendet ?
goto I2CWNACK ;
bcf PIR1,SSPIF ; JA, fertig !

return ;
;************************************************* *********************
; universeller I2C-Befehl "Stop senden" (Master)
I2C_W_STOP
bcf PIR1,SSPIF ; I2C-Int.Request
bsf STATUS, RP0 ; Bank 1
bsf SSPCON2,PEN ; Stop-Sequenz starten
bcf STATUS, RP0 ; Bank 0
I2CWSTOP
; btfsc I2C_TO ; wenn I2C-Stoerung,
; return ; dann nicht warten !

btfss PIR1,SSPIF ; Sequenz schon beendet ?
goto I2CWSTOP ;
bcf PIR1,SSPIF ; JA, fertig !

return ;

;************************************************* *********************
;************************************************* *********************
ORG 0x1000 ; Begin of 2k-Code Page #2 (3/4)
;************************************************* *********************
;************************************************* *********************








;************************************************* *********************
;************************************************* *********************
ORG 0x1800 ; Begin of 2k-Code Page #3 (4/4)
;************************************************* *********************
;************************************************* *********************







;************************************************* *********************
LCD_IDLE
; wartet stur, bis die ISR den Zaehler dlycnt bis Null heruntergezaehlt hat
movwf dlycnt ; Delayzeit wird im W-Register geliefert
LCD_IDLE_1
movf dlycnt,f ; zero flag generieren
btfss STATUS,Z ; bei Z=1 ausbrechen
goto LCD_IDLE_1 ;

return ;
;************************************************* *********************
LCD_PUSH ; nur jeweils EIN 4-bit-Uebertragungsvorgang, weil in der fruehen Initialisierungsphase
; variable Wartezeiten, daher kein delay eingebaut
movf dat_lcd,w ; Nettodaten holen
andlw 0xF0 ; D7..D4 isolieren; RS (D/I) und RW sind damit passend "L" eingestellt
movwf cio_lcd ; Uebergabewert speichern
bsf LCD_TXF ; Uebergabe-Flag fuer ISR setzen
LCD_PUSH_1
btfsc LCD_TXF ; Flag=0 --> ISR hat uebernommen! ?
goto LCD_PUSH_1 ;

return ;

;************************************************* *********************
LCD_INIT
pagesel LCD_IDLE

movlw d'30' ;
call LCD_IDLE ; >= 15ms nach PowerOn warten

movlw 0x30 ; 0x30: Reset
movwf dat_lcd ;
call LCD_PUSH ;
movlw d'10' ;
call LCD_IDLE ; >= 4.1ms warten

movlw 0x30 ; 0x30: Reset
movwf dat_lcd ;
call LCD_PUSH ;
movlw d'3' ;
call LCD_IDLE ; >= 100us warten; 1ms kann auch 0us sein!

movlw 0x30 ; 0x30: Reset
movwf dat_lcd ;
call LCD_PUSH ;
movlw d'3' ;
call LCD_IDLE ; Verarbeitungszeit weiterhin jeweils 1ms + X ?????

movlw 0x20 ; 0x20: 4-bit-Datenbus
movwf dat_lcd ;
call LCD_PUSH ;
movlw d'3' ;
call LCD_IDLE ; >= 1ms interne Verarbeitungszeit ?????

; ab hier wird explizit 4-bit-weise auf D[7:4] kommuniziert,
; zuerst oberes Nibble, dann unteres Nibble

movlw 0x28 ; 0x28: System Set: 4-Bit / 2-zeilig
movwf dat_lcd ;
call LCD_CMD ;
movlw d'3' ;
call LCD_IDLE ;

movlw 0x06 ; 0x06: Entry Mode: v.l.n.r. ohne shift
movwf dat_lcd ;
call LCD_CMD ;
movlw d'3' ;
call LCD_IDLE ;

movlw 0x01 ; 0x01: Clear Display
movwf dat_lcd ;
call LCD_CMD ;
movlw d'10' ;
call LCD_IDLE ;

movlw 0x0C ; 0x0C: Display On, kein Cursor
movwf dat_lcd ;
call LCD_CMD ;
movlw d'10' ;
call LCD_IDLE ;

return ;

END ; directive 'end of program'



Ich hab jetzt nicht ganz verstanden warum Du das RC3 Bit einmal auf Input und dann wieder auf Output setzt, das ist doch die Clockleitung

das Gleiche passiert hoffentlich auch mit TRISC,4 (s. MSSP Erratum)
Nein, warum auch?? Wir sprechen hier über einen I2C-Master. Dieses Vorgehen emuliert den OpenDrain der SCL-Leitung des Masters. Das funktioniert bei einem anderen Projekt makellos und ich habe dieses Vorgehen auch bei meiner Recherche bestätigt gefunden. Es werden so lange SCL-Takte erzeugt, bis die SDA-Leitung frei (=1) ist und der Master den I2C-Startbefehl auf dem Bus durchsetzen kann.


Mit TRISC passiert nach der Aktivierung des MSSP-Moduls nichts mehr? V.a. kein bcf/bsf
Definitiv nein, soweit es um explizite Zugriffe auf TRISC geht. Zwischenzeitlich habe ich die bcf/bsf-Befehle für den Busreset auch noch durch lesen, AND/OR und schreiben ersetzt, alles ohne Erfolg.


Falls du LCD auch mit PORTC ansteuerst, ...
Ist nicht der Fall.


Liebe Grüße
RoboHolIC

witkatz
29.10.2015, 08:12
Nein, warum auch??
Ich meinte damit, dass beide TRIS bits für OD auf 1 gesetzt sein sollten:

Selection of any I2C mode, with the SSPEN bit set, forces the SCL and SDA pins to be open-drain, provided these pins are programmed to inputs by setting the appropriate TRISC bits.

Gruß
witkatz

- - - Aktualisiert - - -

Auch in der AN735 ist zu lesen:

before selecting any I2C mode, the SCL and SDA pins must be configured to inputs by setting the appropriate TRIS bits

- - - Aktualisiert - - -

Das Quellcode ist so noch nicht kompilierbar, LCD_SETCURSOR und LCD_CMD sind "not previously defined", aber egal. Um über die I2C Routinen drüberzuschauen reicht das auch so.

RoboHolIC
29.10.2015, 08:39
Nur falls SDA ebenfalls OD sein soll:

SDA braucht während des Busresets ja nicht OpenDrain zu sein. Es genügt nach meiner Erfahrung TriState mit Pullup, ruhend.

Gruß
Christian

witkatz
29.10.2015, 09:25
In welchem Teil hängt sich das Programm auf? Kann es sein dass es gar nicht bis zu I2C_INIT100 kommt, sondern an I2CCLR_A hängen bleibt?

Andre_S
29.10.2015, 09:40
Hallo Christian,

komme leider, da noch bis nächste Woche im Urlaub, nicht dazu es genauer zu prüfen, da hier am Ort einfach alles fehlt.
Init habe ich mal geschaut, denke bist trotz errata deutlich unter 100 khz, da fehlen mir aber jetzt auch die Zugriffe auf die PDF's.
Eventuell kannst Du ja etwas ausschließen, deshalb mal als Vergleich eine Auszug aus dem Quellcode der 16F876 Steuerung 4 Mhz (XT)

- Init
- 1 Byte schreiben
- 10 Byte einzelnd lesen
- I²C Unterprogramme

Gruß André


;************************************************* ************************************************
;Clock 4 Mhz 16F876 (XT)

#define Bank_0 bcf STATUS,RP0
#define Bank_1 bsf STATUS,RP0

;************************************************* ************************************************
INIT
;PORT C initialisieren (RS232 TX als Ausgang)
bsf STATUS,RP0 ; Bank 1
movlw B'10111110' ; Pin als Eingänge/Ausgänge
movwf TRISC
bcf STATUS,RP0 ; Bank 0
clrf PORTC ; Pin loeschen
;******* I²C Takt einstellen
Bank_1
movlw d'4' ; clock=4000/4*(7+1) ohne Errata
movwf SSPADD ; für I²C
Bank_0
;******* I²C Bus aktivieren und einschalten
movlw B'00001000' ; master Mode, clock=Fosc/(4*(SSPADD+1))
movwf SSPCON
bsf SSPCON,SSPEN ; einschalten
;------------------------------------------------------------------------------------------------

;************************************************* ************************
MAIN

;********* 1 Byte Info auf I²C Speicher-Karte schreiben
Info clrwdt
call i2c_on ; I²C Bus übernehmen
movlw b'10100000' ; adressieren für Karte
call i2c_tx ; zum schreiben
movlw d'0'
call i2c_tx ; high Adresse Speicherplatz
movlw d'9'
call i2c_tx ; low Adresse Speicherplatz
movlw h'35' ; Merker für Karte voll
call i2c_tx ; low Adresse Speicherplatz
call i2c_off ; BUS freigeben
call pause_02 ; fertig schreiben lassen (Pause 10ms - Karte)
return

;******** 10 Byte (einzelnd) von I²C Speicher-Karte lesen
Info_holen movlw d'0' ; Byte 1 beginnen
movwf z1
movlw d'10' ; 10 Byte lesen
movwf z
movlw 0x20 ; Adressierung wieder auf 20
movwf FSR ; Index
weiter_lesen clrwdt ; Watch-Dog löschen
call i2c_on ; I²C Bus übernehmen
movlw B'10100000' ; zum schreiben adressieren
call i2c_tx
movlw d'0' ; high Teil Adresse
call i2c_tx ; senden
movfw z1 ; low Teil Adresse
call i2c_tx ; senden
call i2c_off ; BUS freigeben
call i2c_on ; Bus wieder übernehmen
movlw B'10100001' ; zum lesen adressieren
call i2c_tx
call i2c_rx ; Byte lesen
bcf STATUS,IRP ; bit für Bank 1 und 0 bei FSR
movwf INDF ; sichern in adresse
call i2c_off ; BUS freigeben
decfsz z,1 ; schon 10 byte gelesen
goto ww_1
goto absprung ; -> ja weiter
ww_1 incf FSR,1 ; erhöhe Indexzeiger
incf z1,1 ; erhöhe low Adresse von Karte
goto weiter_lesen

absprung movfw ...

;----------------------------------------------------------------------------------------------


;************************************************* ************************************
;******** I²C-Bus übernehmen
i2c_on bcf PIR1,SSPIF ; SSPIF BIT löschen
bsf STATUS,RP0
bsf SSPCON2,SEN ; Bus übernehmen anweisen
bcf STATUS,RP0
ok_1 btfss PIR1,SSPIF ; Testen ob BUS schon übernommen
goto ok_1
bcf PIR1,SSPIF ; ja der Bus ist übernommen
return

;******** I²C-BUS freigeben
i2c_off bsf STATUS,RP0
bsf SSPCON2,PEN ; Busfreigabe anweisen
bcf STATUS,RP0
ok_2 btfss PIR1,SSPIF ; Bus schon frei
goto ok_2 ; nochmal
bcf PIR1,SSPIF ; ja alles OK
return ; Rücksprung ohne Fehler

;******** I²C-Bus senden
i2c_tx movwf SSPBUF ; -> zum Slave übertragen
ok_3 btfss PIR1,SSPIF ; ACK schon empfangen
goto ok_3 ; nein noch nicht
bcf PIR1,SSPIF ; ja, nun rücksetzen
return

;******** I²C-Bus empfangen
i2c_rx bsf STATUS,RP0
bsf SSPCON2,RCEN ; Datenempfang einschalten
bcf STATUS,RP0
ok_4 btfss PIR1,SSPIF ; Datenempfang fertig?
goto ok_4 ; nein noch nicht
bcf PIR1,SSPIF ; ja, noch zurücksetzen
movfw SSPBUF ; Empfangsregister lesen
return

RoboHolIC
29.10.2015, 15:58
@witkatz
Das Programm durchläuft - den LCD-Anzeigen nach zu urteilen - den ganzen Vorspann, landet in der Hauptschleife und zeigt auch den pro Schleifendurchlauf sich erhöhenden Zähler 'loopcount' an. Lediglich die scheinbar von I2C gelesenen Zeichen sind hartnäckig 0xFF.
Im Augenblick - mit der geposteten Initalisierung - hängt es sich gar nicht auf. In manchen Situationen (fragt mich nicht, wovon das abhängt: experimentelle Codevariationen, Luftdruck ...) läuft das Programm bisweilen nur 3x, 7x, 84x ... durch die Hauptschleife, bevor es steckenbleibt.

Programmtechnisch kommen in der Hauptschleife als Bremskeile eigentlich nur in Frage:
- die Abfragen auf die Vollendung von Busaktivitäten des Masters (ziemlich unplausibel, da nur vom Baud Rate Generator abhängig)
- die ausbleibenden Reaktionen des Slave
Maßgeblich für das Eintreten dieser Ereignisse ist das Interruptbit SSPIF.

@Andre_S
Danke für deinen Codeextrakt. Ich habe ihn mit meinen Werken verglichen, soweit das wegen der unterschiedlichen Strukturierung überschaubar war. In den wesentlichen Passagen könnte ich glatt von Dir abgeschrieben haben. Aus meiner Sicht also kein Fortschritt - allerdings wird man mit der Zeit ja auch betriebsblind für die selbst programmierten Fehler...

An dieser Stelle wäre es bestimmt aufschlussreich, mit einem DSO den gesamten Verlauf auf SLC und SDA anzusehen; auf dem analogen Oszi sehe ich vermutlich nur den Havaristen, nicht aber das Riff.

Peter(TOO)
29.10.2015, 18:27
Hallo,


Das Programm durchläuft - den LCD-Anzeigen nach zu urteilen - den ganzen Vorspann, landet in der Hauptschleife und zeigt auch den pro Schleifendurchlauf sich erhöhenden Zähler 'loopcount' an. Lediglich die scheinbar von I2C gelesenen Zeichen sind hartnäckig 0xFF.
Im Augenblick - mit der geposteten Initalisierung - hängt es sich gar nicht auf. In manchen Situationen (fragt mich nicht, wovon das abhängt: experimentelle Codevariationen, Luftdruck ...) läuft das Programm bisweilen nur 3x, 7x, 84x ... durch die Hauptschleife, bevor es steckenbleibt.

Das sieht nach Störungen aus. Liegt dann am konkreten Aufbau.

MfG Peter(TOO)

RoboHolIC
29.10.2015, 21:46
Das sieht nach Störungen aus. Liegt dann am konkreten Aufbau.

Das ist allmählich auch mein Verdacht. Die Software wurde hier ja von vielen Seiten erfolglos beleuchtet und mit funktionierendem Code verglichen.
Was wie ein Hardwaredefekt oder Programmierfehler aussieht, ist vielleicht doch "nur" ein blockierter Bus bzw. wartender Master aufgrund einer einzigen elektrischen Störung, die nicht so leicht dingfest zu machen ist.

Wie bereits angedeutet hängt vom Ausgang dieses Threads nicht Erfolg oder Scheitern eines Projekts ab. Daher würde ich ihn jetzt gerne beenden.
Vielen Dank nochmals Euch allen, die Ihr Euch intensiv um eine Lösung bemüht habt!

Christian.

Peter(TOO)
30.10.2015, 01:54
Hallo Christian,

Wie bereits angedeutet hängt vom Ausgang dieses Threads nicht Erfolg oder Scheitern eines Projekts ab. Daher würde ich ihn jetzt gerne beenden.

Allerdings würde eine Lösung des Problems dazu führen, dass du den selben Fehler in Zukunft nicht mehr machst!

Genau dies ist ein Teil des Profi-Wissens, wenn man weiss wie es nicht geht und, vor allem, warum es so nicht geht.

Du solltest vor allem die Führung der Masseleitungen unter die Lupe nehmen und auch nicht zu wenige Abblockkondensatoren verwenden.

Vor rund 40 Jahren habe ich Logikschaltungen für 60MHz entwickelt.
Heute gewinnt man damit keinen Blumentopf mehr, aber damals ging dies nur mit der 74Sxx TTL-Familie oder ECL. Damalige µP liefen noch typischerweise mit rund 1MHz und schnelle ROMs und SRAMs hatten Zugriffszeiten von 450ns (Die langsamen lagen knapp unter 1µs).
Die damals gemachten Erfahrungen, und damit erlerntes Wissen, bringen mir aber auch heute noch oft Vorteile wenn es Probleme gibt.


MfG Peter(TOO)

PICture
30.10.2015, 07:07
Hallo!

@ RoboHolIC

Ändern von verwendeten µC habe ich immer in funktionierender Hardware (HW) mit einstecken des per Adapter umsockelnden µC angefangen. Ich kenne deshalb keine Probleme mit Wechseln vom µC für vorher richtig funktionierende HW. :confused:

Andre_S
30.10.2015, 08:09
Hallo Christian,

sorry,… es sei mir gegönnt, auch wenn Du das Thema abschließen möchtest, noch einen letzten Hinweis zu geben!
Du hast recht, in weiten Teilen sind die Sequenzen ähnlich. Ich kenne auch Deinen zu lesenden Sensor nicht, scheinbar hat dieser keine low/high Adressierung wie mein Beispiel, aber das ist nicht das Problem.
Was mir aber aufgefallen ist und ich zumindest bei meinen I²C Baugruppen so nicht kenne, ist der Ablauf des Auslesens. Da fehlt mir die Stopp Bedingung, also die erneute Freigabe des Busses beim Lesen von Informationen.
Ich habe aber nur mal eine Ausleseroutine von Deinem Beispiel (Protokollzyklus: Accelerometer auslesen) „versucht“ durchzugehen und ich hoffe ich habe nichts übersehen oder es ist bei Deinem Sensor eventuell nicht notwendig,… dann Sorry für die Verwirrung.



Bus übernehmen
zum schreiben Adressieren
zu lesende Adresse(n) senden
Bus wieder freigeben
Bus erneut übernehmen
Zum Lesen adressieren
Byte lesen



Gruß André

oberallgeier
30.10.2015, 08:28
.. Genau dies ist ein Teil des Profi-Wissens, wenn man weiss wie es nicht geht und, vor allem, warum es so nicht geht ..OT:

Ein guter Bäcker ist aber wohl nicht der, der gute Brötchen bäckt. Ein guter Bäcker ist einer, der weiß wie man schlechte Brötchen bäckt und doch gute macht . . .


- - -

...

zu lesende Adresse(n) senden
Bus wieder freigeben
Bus erneut übernehmen
Zum Lesen adressieren
Byte lesen


Darüber bin ich viele Tage lang gestolpert :-/

RoboHolIC
30.10.2015, 22:49
Hallo Ihr lieben Helfer.

Zuerst das Wichtigste: Mein Code funktioniert jetzt !
Der Lorbeerkranz dafür gebührt Andre_S mit seinem zutreffenden Hinweis auf den möglicherweise fehlenden STOP-Befehl.
Mein anfänglicher Code für den BMA020 passte tatsächlich nicht zur Spezifikation des Bausteins.

Aber warum ist das nicht schon früher aufgefallen??
Es klingt absurd, aber ich habe es soeben verifiziert: Auf zwei Eigenbauten mit PIC16F886-Controllern funktioniert der I2C-Code tatsächlich auch ohne das zwischengeschaltete STOP-Signal.
Auf dem hier behandelten PIC16F876A-System hingegen klappt es nur mit diesem STOP.

Zur Bestätigung habe ich den existierenden Code für einen anderen Chip (eine RTC: PCF8583) in die PIC16F876A-Software eingebaut - auch hier die bekannte Blockierung.
Gezielte Suche - es fehlte das STOP an der entsprechenden Stelle. STOP eingefügt - und schon lief auch dieser Baustein. Vermutlich hatte ich den zunächst folgenlos bleibenden Fehler bereits früher vom PCF8583-Code in den BMA020-Code übernommen.

Nochmals herzlichen Dank an alle!
Christian.

P.S.: Nein, ich denke nicht im Traum daran, zu erforschen, warum der '886 ohne dieses STOP-Signal auskommt ... ;)

Peter(TOO)
31.10.2015, 00:08
Hallo,

P.S.: Nein, ich denke nicht im Traum daran, zu erforschen, warum der '886 ohne dieses STOP-Signal auskommt ... ;)
Aber in Zukunft wirst du den STOP immer drin haben :-)

Verstehe mich richtig, ich will jetzt keinen hier runter machen:

Das ist der Unterschied zwischen basteln und professionellem Entwickeln.
Du hast geübt, bis es auf deinem '886 funktioniert hat, hast aber nie überprüft ob du die Spezifikationen auch wirklich erfüllst.
Dann wären noch die Worst Case Berechnungen, möglicherweise funktioniert es auch nicht bei jedem '886 ....

MfG Peter(TOO)

Andre_S
31.10.2015, 09:28
Nochmals herzlichen Dank an alle!
Christian.


Hallo Christian,

super das es nun klappt und wir Dir doch noch helfen konnten!


Gruß Andre

RoboHolIC
01.11.2015, 01:48
Aber in Zukunft wirst du den STOP immer drin haben :-)
Aber klar doch - sofern er ins Protokoll gehört, denn ich kenne einen I2C-Kandidaten, wo im selben Zusammenhang statt STOP - START ein RepeatedSTART hingehört ...


möglicherweise funktioniert es auch nicht bei jedem '886 ...
Darauf würde ich es auch gar nicht nicht ankommen lassen. Wissentlich gegen die Spezifikation zu verstoßen "weil es aber doch funktioniert" wäre unclever.

Gruß
Christian.

Klebwax
01.11.2015, 08:10
Auf dem I2C-Bus beginnt jede Transaktion mit einem START und endet mit einem STOP. Die I2C Spec sagt dazu:

Data transfers follow the format shown in Fig.10. After the START condition (S), a slave address is sent. This address is 7 bits long followed by an eighth bit which is a data direction bit (R/W) - a ‘zero’ indicates a transmission (WRITE), a ‘one’ indicates a request for data (READ). A data transfer is always terminated by a STOP condition (P) generated by the master.
und:

START and STOP conditions are always generated by the master. The bus is considered to be busy after the START condition. The bus is considered to be free again a certain time after the STOP condition.
Solage also am Ende einer Transaktion kein STOP kommt, gilt der Bus also als belegt.

Eine Transaktion hat immer nur eine Richtung, in der Daten übertragen werden. Die Richtung macht sich am R/W Bit im Adressbyte fest. Wenn man also sowohl schreiben, z.B. eine Registeradresse, als auch lesen will, z.B. den Registerinhalt, braucht man zwei Transaktionen. Zu jeder gehören ein START am Anfang und ein STOP am Ende.


Aber klar doch - sofern er ins Protokoll gehört, denn ich kenne einen I2C-Kandidaten, wo im selben Zusammenhang statt STOP - START ein RepeatedSTART hingehört ...
STOP-START und repeated START sind equivalent. Die Spec sagt dazu:

The bus stays busy if a repeated START (Sr) is generated instead of a STOP condition. In this respect, the START (S) and repeated START (Sr) conditions are functionally identical (see Fig. 10). For the remainder of this document, therefore, the S symbol will be used as a generic term to represent both the START and repeated START conditions, unless Sr is particularly relevant.
Der einzige Unterschied ist, daß der Bus zwischen zwei Transaktionen nicht frei wird. Das spielt eine Rolle beim Multimasterbetrieb. Da könnte sonst ein anderer Master Kontrolle über den Bus und den gerade adressierten Slave bekommen und zum Beispiel die gerade gesetzte Registeradresse umprogrammieren.

Ein I2C Slave, der der I2C Spec entspricht, muß also auf ein repeated START genauso reagieren wie auf STOP-START und natürlich umgekehrt. Ein I2C Master muß aber jede Transaktion mit einem STOP oder einem repeated START abschließen.

Nun ist es aber gut möglich, daß die HW eines I2C Masters START und repeated START auf die gleiche Weise erzeugt. So kann also ein Programm funktionieren, ohne daß explizit ein STOP erzeugt wird, da statt START ein repeated START entsteht. Sind die Umstände aber anders (Timing ?, andere HW ?) versagt es.

MfG Klebwax