Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
LiFePO4 Speicher Test

Im Zusammenhang für das Projekt "RN-Netzwerk", wo es ja darum geht, PC und Avr zu verbinden, ist ja natürlich auch eine Integration des I2C-Bus unbedingt erforderlich. Da ich mit einer RNBFRA 1.2 Karte gesegnet bin, sollte natürlich gerade der AT90S2313 (der Co-Prozessor) auch zum Mitspieler werden. Das bisherige "durchrouten" über die RS232 ist ja doch nicht sehr befriedigend.

Über die Therorie und Praxis des I2C bzw. TWI -Bus gibt es hervorragende Artikel in RN-Wissen. Bitte dort nachzulesen, wenn irgendetwas unklar ist.

Und nochwas: Ich betrachte das als "Open-Source" Projekt. Wenn also jemand Hand anlegen möchte oder sonstwelche Ideen dazu hat, bitte um Rückmeldungen. Aber immer auch so, dass alle was davon haben. Also eventuell mit "Change-History" und Kommentaren.

Bascom Software I2C Routinen

Das Ziel ist:

  • Kommunikation auf der PC-Plattform via IP (Stream-Sockets),
  • vom PC zum Atmega32 über COMx und AVR-UART,
  • und auf der Mikrocontroller-Ebene dann über I2C, (später ev. auch andere Busse)

I2C auf AT90S2313

In diesem Prozessor (und auch auf dem Tiny2313) gibt's keine Hardwarunterstützung. Und in einer Multimaster-Umgebung war auch mit der I2C.LIB von Bascom nichts zu machen. Denn gerade bei I2CSTART kümmert sich Bascom in keiner Weise darum, ob auf dem Bus gerade irgendwas abläuft. Und dann war ja immer noch das Problem, daß es keine SLAVE-Funktionen gibt (Nur um Geld).

Step 1: Multimasterfähige I2c-Funktionen

Es genügte fürs Erste,

  • die Start-Routine durch eine multimasterfähige Funktion zu ersetzen,
$external I2cwaitbus                              'wait for free bus & reserve it
  • und das Adressen- oder Bytesenden durch eine Funktion, die auch die Arbitrierung kontrolliert.
$external I2cwmaster                              'send byte & watch arbitration
  • I2crbyte u. I2cstop kann man eigentlich im Moment lassen, da sie nichts böses tun.

Step 2: I2c-Slave-Funktionen

Da gibt es eigentlich nur drei Funktionen:

  • warten auf eine Startbedingung und lesen der I2C-Adresse
$external I2c_get_addr                            'wait for start-cond & read in I2C address

Und, je nachdem, ob Read oder Write

  • Bytes vom Master empfangen
$external I2c_slave_rx_data                       'ACK & receive master data
  • Bytes zum Master senden
$external I2c_slave_tx_byte                       'ACK & send data to  master


I2c_get_addr

Ich habe aus mehreren Gründen darauf verzichtet, die Adress-Prüfung zu automatisieren, wie das ja sonst üblich ist:

  • Ich möchte in weiterer Folge die Funktionen auch als eine Art I2C-Monitor/Logger verwenden
  • Ich seh' nicht ein, warum ein Slave immer sowohl Read als auch Write zulassen soll
  • Und ich wollt mir auch die Parametriesierung ersparen, ob auch General-Calls oder nicht akzeptiert werden sollen. Irgendeine Art von "IF" muss der User ja sowieso machen, also soll er auch gleich selbst entscheiden, was er "acked" oder nicht.

d.h. also, wenn die Funktion zurückkommt, ist ein I2C-Start erfolgt und eine Adresse ist gesendet worden. Die steht dann vollständig in Feld "I2C_ADDR"

  • NULL ist eine General Call Adresse
  • SLA+W ist eine "Slave-Write" Adresse
  • SLA+R ist eine "Slave-Read" Adresse

Welche SLA-Adresse seine eigene ist, kann der User nun selbst entscheiden.

I2c_slave_rx_data

wird das aufgerufen, wird ein ACK ausgesendet und es werden Daten vom Master empfangen und in den Buffer geschrieben, der im Feld "I2c_write" angegeben ist. Wieviel das sind, entscheidet der Master durch "STOP" oder "Repeated Start". Einen Begrenzung seitens der Funktion ist nicht vorgesehen, da sich ja die Applikationen im Master und im Slave sowieso irgendwie vereinbart haben müssen, wieviele Bytes wann zu senden sind.

Ist die Routine fertig, steht die Anzahl der empfangenen Bytes im Feld "I2c_cntr"


I2c_slave_tx_byte

wird das aufgerufen, wird ein ACK ausgesendet und es werden die Daten zum Master gesendet, die im Feld "I2c_read" angegeben sind. Beendet wird das vom Master duch ein "NACK". Einen Begrenzung der Anzahl durch die Funktion ist auch hier nicht vorgesehen.

Ist die Routine fertig, steht die Anzahl der gesendeten Bytes im Feld "I2c_cntr", falls das den User interessieren sollte.


DEMO MYI2C.ZIP

Das Demo-Set Bascom_Soft-I2c_Library#Web_Links kann heruntergeladen werden, besteht aus drei Teilen

  • 2313.bas Demo Programm
  • myi2c.bas Include File
  • myi2c.lib eine Bascom library, die gehört zu den anderen Bascom Libraries, irgendwo im Installations-directory.

Beispiel 2313.bas

Das Demo-Programm macht eigentlich nix ausser Bytes zu empfangen oder zu senden. Wurde was empfangen und es ist auch gerade eine Sekunde 'rum, sendet er selbst an das RNBFRA Out-Port (PCF) das erste Byte, das im Empfangsbuffer steht. Damit sind die I2C Routinen prinzipiell gut zu testen.

Das vollständige Programm ist in der Zip-File. Das Wesentliche ist folgendes:

$crystal = 4000000                                ' Quarzfrequenz
$hwstack = 48

   I2cinit                                        ' normale Bascom Funktion

   I2c_write = Varptr(m32_byte(1));               ' Data receive-Buffer
   I2c_read = Varptr(m32_byte(1));                ' Data send-Buffer

   Do
         Loadadr I2c_flag , Z
         Gosub I2c_get_addr                       ' I2C Address read

         Select Case I2c_addr

         Case Co1_adr:
            Loadadr I2c_flag , Z
            Gosub I2c_slave_rx_data               ' daten empfangen bis Stop/Rep
            Sent_flag = 1

            '------------------------------------
            ' WORKOUT Message
            '------------------------------------

         Case Co1_adrr:
            Loadadr I2c_flag , Z
            Gosub I2c_slave_tx_byte               ' daten senden bis NAK

         Case Else

            I2cinit                               'Bus freigeben

         End Select

         If Sent_flag = 1  And Timeout = 1 Then
            Sent_flag = 0
            Timeout = 0
            Gosub Out_transmit                    'senden an PCF
         End If
   Loop

End
'-----------------------------------------------------
'
'-----------------------------------------------------
Out_transmit:
   Do
      Gosub I2cwaitbus                            ' wait for Bus

      $asm
      ldi   r17, out_adr
      !Call I2cwmaster                            ' send SLA+W  & arbitr.Check
      $end Asm

      If Err = 0 Then
            Loadadr M32_byte(1) , X
            $asm
            ld    r17, x+
            !Call I2cwmaster                      ' send data  & arbitr.Check
            $end Asm
      End If
      If Err = 0 Then
         I2cstop
      End If
   Loop Until Err = 0
   I2cinit
   Return

Anmerkung: Das Senden muß eventuell mehrmals wiederholt werden. Auf einem Multimaster-Bus ist es ja nicht gesagt, daß nicht schon mal der Bus verloren gehen kann, weil ein anderer dazwischenfunkt.

Include-File MyI2c.bas

'----------------------------------------------
' MyI2C.BAS
'    I2c Soft-functions
'----------------------------------------------
'   PicNick was here  www.roboternetz.de
'----------------------------------------------

'---------------------------  Library und definitionen ------------------
$lib "MyI2c.LIB"


'--------------------------- Slave receive & Send ---------------------
Const I2c_m_start = 1

$external I2c_get_addr                            'wait for start-cond & read in I2C address
Declare Sub I2c_get_addr

$external I2c_slave_rx_data                       'ACK & receive master data
Declare Sub I2c_slave_rx_data

$external I2c_slave_tx_byte                       'ACK & send data to  master
Declare Sub I2c_slave_tx_byte

'--------------------------- Multimaster sending ---------------------
$external I2cwaitbus                              'wait for free bus & reserve it
Declare Sub I2cwaitbus

$external I2cwmaster                              'send byte & watch arbitration
Declare Sub I2cwmaster

'-------------------------------------
'  I2C-STRUCTURE
'-------------------------------------
Dim I2c_flag As Byte                              '0   Kontroll-flags
Dim I2c_addr As Byte                              '1   slave adresse
Dim I2c_cntr As Byte                              '2   bei empfang anzahl bytes
Dim I2c_write As Word                             '3/4 adresse empfangsbuffer
Dim I2c_read As Word                              '5/6 adresse sendebuffer

'---------------------------------------------
'
'---------------------------------------------
   I2cinit


Bascom Library Myi2c.lib

copyright 	= R.Toegel
www       	= http://www.oldformation.at
email     	= roberttoegel@aon.at
comment   	= BASCOM-AVR I2C Slave / Multimaster library add to I2c.LIB
libversion   	= 1.0.0.1
date         	= 26 Jun 2006
statement    	= Based on ATMEL application note
history      	= This lib is an addon to i2c.lib

;-----------------------------------------------------
;	I2c_slave_tx_ack	Send ACK
;	I2c_slave_tx_nak	Send NACK
;	I2c_release		Release Lines
;	I2c_wait_start		Wait for Start Condition
;	I2c_get_addr		Get Message Address
;	I2c_slave_tx_byte	Slave Transmitter
;	I2c_slave_rx_data	Slave Receiver
;	I2c_read_byte		Internal
;-----------------------------------------------------
;	I2cwaitbus	
;	I2cwmaster
;-----------------------------------------------------
[I2c_slave_tx_ack]
I2c_slave_tx_ack:
* 	sbi 	_sdaDDR,_sda    ;Hold SDA LO (ACK)
Wt_ack_scl:
*	in	r1, SREG	; save SREG
	cli			; disable interrupts
* 	cbi 	_sclDDR,_scl	;SCL Release
Wt_scl_hi2:
* 	sbis 	_sclPIN,_scl    ;SCL
  	rjmp    wt_scl_hi2     	;wait SCL hi
Wt_scl_lo3:
* 	sbic 	_sclPIN,_scl    ;SCL
   	rjmp     wt_scl_lo3     ;wait SCL lo
* 	sbi 	_sclDDR,_scl	;SCL Stretch
* 	cbi 	_sdaDDR,_sda    ;release SDA
*	out	SREG, R1
   	ret
[end]
;-----------------------------------------------------
[I2c_slave_tx_nak]
I2c_slave_tx_nak:
$external I2c_slave_tx_ack
* 	cbi 	_sdaDDR,_sda    ;Release SDA  
	rjmp 	Wt_ack_scl
[end]
;-----------------------------------------------------
[I2c_release]
I2c_release:
* 	cbi 	_sdaDDR,_sda    ;SDA Release
* 	cbi 	_sclDDR,_scl	;SCL Release
   	clr      r22
   	std      Z + 0, r22     ;clear flag
	ret
[end]
;-----------------------------------------------------
[I2c_wait_start]
I2c_wait_start:
	ldi	r22, 0
   	std  	Z + 0, r22     	; clear I2c_flag
Wt_hi_hi:
* 	sbis 	_sclPIN,_scl    ; SCL ?
   	rjmp     wt_hi_hi       ; wait SCL hi
* 	sbis 	_sdaPIN,_sda    ; SDA ?
   	rjmp     wt_hi_hi       ; wait SDA hi
Wt_sda_lo0:
* 	sbic 	_sdaPIN,_sda    ; SDA
	rjmp     wt_sda_lo0     ; wait SDA lo
* 	sbis 	_sclPIN,_scl    ; SCL start condition ?
   	rjmp     wt_hi_hi       ; no, repeat the whole sequence
Wt_scl_lo0:
* 	sbic 	_sclPIN,_scl    ; SCL
   	rjmp     wt_scl_lo0     ; wait SCL lo	
* 	sbi 	_sclDDR,_scl	; SCL Stretch
   	ret
[end]
;-----------------------------------------------------
[I2c_get_addr]
I2c_get_addr:
$external I2c_wait_start
   	ldd	r22, Z + 0 	; i2c_flag
*	cpi	r22, I2c_m_Start
        breq    rept_s		; start-cond. already set
	rcall	I2c_wait_start	; wait for Bus-start
rept_s:
*	clr 	r22
   	std  	Z + 0, r22     	;clear I2c_flag
   	ldi	r24 ,1         	;set 'end'-Bit
*	in	r1, SREG	; save SREG
	cli			; disable interrupts
   	clc			;clear carry
Wta_scl_hi:
* 	cbi 	_sclDDR,_scl	;SCL Release
* 	sbis 	_sclPIN,_scl    ;SCL
   	rjmp     wta_scl_hi     ;wait SCL hi
* 	sbic 	_sdaPIN,_sda    ;SDA = 0 ?
   	sec                     ;SDA = 1 -> set carry
Wta_scl_lo:
* 	sbic 	_sclPIN,_scl    ;SCL
   	rjmp     wta_scl_lo     ;wait SCL lo
* 	sbi 	_sclDDR,_scl	;SCL Stretch
   	rol      r24            ;roll carry in/out
   	brcc     Wta_scl_hi     ;Bit loop 
   	std      Z + 1, r24     ;Store i2c address
*	out	SREG, r1
   	ret
[end]
;-----------------------------------------------------
[I2c_slave_rx_data]
I2c_slave_rx_data:
   	ldd      xl, Z + 3           	; Load Buffer Address
   	ldd      xh, Z + 4           	;
   	clr      r23
   	std      Z + 2, r23          	; clear counter
I2c_read_loop:
$external I2c_slave_tx_ack
   	rcall	I2c_slave_tx_ack	; Send ACK 

*	in	r1, SREG	; save SREG
	cli			; disable interrupts
   	clt                          	; clear T-Bit
   	rcall   I2c_read_byte		; read 8 Bit
   	brtc    Rd_sto_ack		; T-Bit clear ? o.k.--> 
*	out	SREG, r1	
   	std     Z + 2, r23    		; store counter
   	std     z + 0, r22     		; set start/stop/none flag
   	ret
Rd_sto_ack:
*	out	SREG, r1	
   	st      x+, r24			; store character
	inc	r23			; data counter++
   	rjmp   I2c_read_loop		; no, cont'd next byte

'-------------------------------------------
I2c_read_byte:
   	ldi      r24 ,1                 ;set end-bit
   	clc                             ;clear carry
Rd_loop:
* 	cbi 	_sclDDR,_scl		;SCL Release
Wt_scl_hi:
* 	sbis 	_sclPIN,_scl    	; SCL
   	rjmp     wt_scl_hi              ; wait SCL hi
* 	sbis 	_sdaPIN,_sda    	; SDA ?
   	rjmp     Wt_chk_0		; SDA Low-Check
   	sec              		; SDA Hi-Check
Wt_chk_1:
* 	sbis 	_sclPIN,_scl    	; SCL
   	rjmp     wt_ok_x                ; scl low---> ok, next bit
* 	sbic 	_sdaPIN,_sda    	; SCL Hi & SDA lo --> repeated Start
   	rjmp     wt_chk_1               ; SCL & SDA Hi --> cont'd wait
Rep_start_cond:
* 	sbic 	_sclPIN,_scl    	; SCL			
   	rjmp     Rep_start_cond		; wait SCL lo
* 	sbi 	_sclDDR,_scl		; SCL STRETCH
   	SET				; set t-Bit
*	ldi 	r22, I2c_m_Start	; signal REP-Start
   	ret                                     
Wt_chk_0:				; SDA is Low
* 	sbis 	_sclPIN,_scl    	; SCL ?
   	rjmp     wt_ok_x             	; scl low---> ok, next bit
* 	sbis 	_sdaPIN,_sda    	; SDA ?
   	rjmp     wt_chk_0		; SCL Hi & SDA lo --> cont'd wait
Stop_cond:				: stop-condition
   	SET				; set t-bit
   	ret				; xit
Wt_ok_x:
* 	sbi 	_sclDDR,_scl		; STRETCH
   	rol      r24                    ; roll carry in/out
   	brcc     rd_loop		; next bit
   	ret				; byte done
[end]

;-----------------------------------------------------
[I2c_slave_tx_byte]
I2c_slave_tx_byte:
$external I2c_slave_tx_ack
   	rcall    I2c_slave_tx_ack	; Send ACK
   	ldd      xl, Z + 5 		;Load Buffer Address
   	ldd      xh, Z + 6      	;
*	in	r1, SREG	; save SREG
	cli			; disable interrupts
	rcall	Tx_loop
*	out	SREG, r1
	ret
;--------------------------------------------------
Tx_loop:
   	ld       r24, x+		; next character
   	ldi      r25, 8			; 8 Bit
Tx_c_loop:
   	Sbrc     R24 , 7        	;Data Bit ?
   	rjmp     Tx_Bit_1		;set SDA High
;--------------------------------------------------
;---------------- send NULL------------------------
* 	sbi 	_sdaDDR,_sda    	;set SDA Low
* 	cbi 	_sclDDR,_scl		;SCL Release--------------------
Tx0_scl_hi_0:
* 	sbis 	_sclPIN,_scl    	;SCL
   	rjmp     Tx0_scl_hi_0		;wait scl hi
	nop
	nop
Tx0_scl_lo_0:
* 	sbic 	_sclPIN,_scl    	;SCL
   	rjmp     Tx0_scl_lo_0		;wait scl lo

* 	sbi 	_sclDDR,_scl		;SCL Stretch
	rjmp	Tx_step
;--------------------------------------------------
;---------------- send HIGH------------------------
Tx_bit_1:
* 	cbi 	_sdaDDR,_sda    	;SDA Release High
* 	cbi 	_sclDDR,_scl		;SCL Release
Tx1_scl_hi_0:
* 	sbis 	_sclPIN,_scl    	;SCL
   	rjmp     Tx1_scl_hi_0		;wait scl hi
* 	sbis 	_sdaPIN,_sda    	;SDA HI  ?
	ret				;Bus Lost , anyhow
Tx1_scl_lo_0:
* 	sbic 	_sclPIN,_scl    	;SCL
   	rjmp     Tx1_scl_lo_0		;wait scl lo
* 	sbi 	_sclDDR,_scl		;SCL Stretch
Tx_step:
   	rol      r24            	;roll carry in/out
   	dec      r25			;count bits
   	brne     Tx_c_loop		;next bit
;--------------------------------------------------
;---------------- get ACK/NAK ---------------------
* 	cbi 	_sdaDDR,_sda    	;SDA Release
* 	cbi 	_sclDDR,_scl		;SCL Release
Tx_scl_hi_1:				;get ack bit
* 	sbis 	_sclPIN,_scl    	;SCL
   	rjmp     Tx_scl_hi_1		;wait scl hi
* 	sbic 	_sdaPIN,_sda    	;SDA (ACK) ?
   	ret             		;no-ack --> return
Tx_scl_lo_1:
* 	sbic 	_sclPIN,_scl    	;SCL
   	rjmp     Tx_scl_lo_1		;wait scl lo
* 	sbi 	_sclDDR,_scl		;SCL Stretch
   	rjmp    tx_loop			; next

[end]

;---------------------------------------------------------
;	MASTER Functions
;---------------------------------------------------------
;	Wait for free Bus
;---------------------------------------------------------
[I2cwaitbus]
I2cwaitbus:
$EXTERNAL _I2C
*	in	r1, SREG	; save SREG
	cli			; disable interrupts
Ck_1_x:
      	LDI   	r23,0x0f  	; 15 times
Ck_1_y:
* 	sbis	_sclPIN,_scl    ; SCL 
	RJMP	Ck_1_x          ; wait SCL Hi
* 	sbis 	_sdaPIN,_sda    ; SDA HI  ?
      	RJMP  	Ck_1_x          ; wait SDA Hi
      	DEC   	r23
      	BRNE  	Ck_1_y          ; (8) hang on
* 	sbi 	_sdaDDR,_sda    ; SDA down for Start
*	out	SREG, r1
	Rjmp 	_i2c_hp_delay   ; half period delay
[end]
;---------------------------------------------------------
;	Send one Byte R17 & check Arbitration
;---------------------------------------------------------
[I2cwmaster]
I2cwmaster:
$EXTERNAL _I2C
*	in	r1, SREG	; save SREG
	cli			; disable interrupts
      	Sec                                     ; set carry flag
      	Rol   	r17                             ; shift in carry and out bit one
	rcall	I2cwmaster_loop
*	out	SREG, r1
	ret
I2cwmaster_loop:
* 	sbi 	_sclDDR,_scl	                ;SCL DOWN
      	BRCC  	I2cwmaster_low                  ;carry clear --> send Low
; send High ------------------------------------------
      	NOP                                     ; filler
* 	cbi 	_sdaDDR,_sda      		; release SDA
      	RCALL 	_i2c_hp_delay                   ; wait
* 	cbi 	_sclDDR,_scl			; SCL Release
Tx_1_ck:
* 	sbis	_sclPIN,_scl    		; SCL 
      	RJMP  	Tx_1_ck                         ; wait SCL Hi
* 	sbis 	_sdaPIN,_sda    		; SDA still HI  ?
      	RJMP  	Tx_lost                         ; No, ---> arbitration lost
Tx_1_ok:
      	RCALL 	_i2c_hp_delay      		;wait
      	LSL   	r17                             ;2^^7 -->Cy
      	brne  	I2cwmaster_loop			;more bits ? 
;---------------------------------------------------
I2cwmaster_loop_x:                           	; Get ACK / NACK
* 	sbi 	_sclDDR,_scl	                ; SCL DOWN
* 	cbi 	_sdaDDR,_sda      		; release SDA
      	RCALL 	_i2c_hp_delay                   ; wait
* 	cbi 	_sclDDR,_scl			; SCL Release
Tx_x_ck:
* 	sbis	_sclPIN,_scl    		; SCL 
      	RJMP  	Tx_x_ck                         ; wait Hi
      	CLT                                     ; clear t-bit
* 	sbic 	_sdaPIN,_sda    		; SDA = ACK/NAK ?
Tx_lost:
      	SET                                     ; set t-bit
      	BLD 	r6,2              	    	; t-Bit -> to Bascom "ERR"
      	RJMP 	_i2c_hp_delay                   ; wait & return
' send Low ------------------------------------------
I2cwmaster_low:
* 	sbi 	_sdaDDR,_sda      		;down SDA
      	RCALL 	_i2c_hp_delay       		;wait
* 	cbi 	_sclDDR,_scl			;SCL Release
Tx_0_ck:
* 	sbis	_sclPIN,_scl    		;SCL 
      	RJMP  	Tx_0_ck                         ;wait SCL Hi
      	RJMP  	Tx_1_ok				;continue
[end]









Autor

Benutzer:PicNick

Web Links

http://www.oldformation.at/electronic/download/down.htm

Siehe auch


LiFePO4 Speicher Test