I2C via DMA på STM32F4xx, exempel? *blev lite annat*

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

I2C via DMA på STM32F4xx, exempel? *blev lite annat*

Inlägg av Korken »

Godagens!

Jag har kommit till den lite större delen av KFly projektet och det är att få igång I2C-busen.
Det är då så, utav vad jag kunna läsa mig till, att det är väldigt problematiskt att få den att fungera under vanliga förhållanden (samt ineffektivt) så det rekommenderas att man använder DMAn.

DMAn ska (som jag har förstått det) fixa så att alla start, stop osv olika "conditions" sätts korrekt automatiskt, så man måste inte hålla koll på detta själv.
Referensmanualen är väldigt ofullständig om detta så jag undrar om någon har erfarenhet av detta och om det stämmer?

Problemet är de att jag har aldrig i mitt liv lyckats få liv i en DMA överföring, så jag undrar om det är någon här som har använt DMAn på STM32F4xx för att styra I2Cn? Alternativt om hur man ska göra för att starta det korrekt.
Om det är någon som har gjort det så får denne extremt gärna dela med sig av lite exempelkod eller tips! För jag sitter helt fast och vet inte åt vilket håll jag ska gå för att komma någon vart.

Det står i referensmanualen vilka "steg" man ska ta för att det ska fungera, men den listan är tyvärr inkomplett.
Det står inte vilka bittar som man måste kolla/seta själv eller vad den tar hand om själv.
Exempel:
Reception using DMA

DMA mode can be enabled for reception by setting the DMAEN bit in the I2C_CR2 register. Data will be loaded from the I2C_DR register to a Memory area configured using the DMA peripheral (refer to the DMA specification) whenever a data byte is received. To map a DMA channel for I2C reception, perform the following sequence. Here x is the channel number.
  • 1. Set the I2C_DR register address in DMA_CPARx register. The data will be moved from this address to the memory after each RxNE event.
    2. Set the memory address in the DMA_CMARx register. The data will be loaded from the I2C_DR register to this memory area after each RxNE event.
    3. Configure the total number of bytes to be transferred in the DMA_CNDTRx register. After each RxNE event, this value will be decremented.
    4. Configure the channel priority using the PL[0:1] bits in the DMA_CCRx register 5. Reset the DIR bit and configure interrupts in the DMA_CCRx register after half transfer or full transfer depending on application requirements.
    6. Activate the channel by setting the EN bit in the DMA_CCRx register. When the number of data transfers which has been programmed in the DMA Controller registers is reached, the DMA controller sends an End of Transfer EOT/ EOT_1 signal to the I2C interface and DMA generates an interrupt, if enabled, on the DMA channel interrupt vector.
Annars, om någon har fått detta att fungera på tex en STM32F1xx så berätta gärna hur ni gjorde då!
Det borde vara ganska lika och om inte annat kanske man får lite idéer om hur man ska göra det.

Lite info om det jag använder:
- STM32F405
- CodeSourcery (arm-none-eabi) under Ubuntu med OpenOCD för programmering och GDB för debugg

Hoppas det är någon här som kan dela med sig av sin kunskap! :pray:
Senast redigerad av Korken 19 juni 2012, 10:58:18, redigerad totalt 1 gång.
SvenW
Inlägg: 1156
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av SvenW »

Frågan är för svår för mig, men det finns kodexempel, dock utan DMA, här:

https://github.com/yigiter/Sample-STM32F4-codes

Vet inte om det ger något, eller ens om det fungerar?

Har en annan fråga som intresserar mig:
Är det samma programmeringsverktyg "CodeSourcery (arm-none-eabi)"
som gäller för STM32F405 som för STM32 Cortex-M3?
Eller är det olika variater?
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

Jag har äntligen lyckats hitta i referensmanualen vad DMAn tar hand om samt vad den inte gör.
Den tar inte automatiskt hand om conditions som måste settas, den skyfflar bara data mellan skicka/ta emot registren. :doubt:
De gör att jag bara tjänar på att använda DMAn om jag ska skicka stora mängder data.

Pga detta så kommer jag skriva en interruptdriven I2C rutin som läser/skriver istället. De har jag gjort en gång förut så jag satsar på det igen.
Om någon har information eller exempelkod som kan vara av nytta så tar jag gärna och kikar på den! :)

SvenW:
Jag har kollat mycket på den och han följer alla konstens regler enligt referensmanualen för pollade överföringar. Så den borde fungera. :)
Den är riktigt bra att ha som referens då han har tagit hänsyn till att STM32F4ans I2C läsning inte är symmetrisk (olika metoder ska följas om man ska läsa en byte, två bytes eller fler än två bytes).

När det gäller CodeSourcery (numera CodeBench) så fungerar den för Cortex-M3 för den på Cortex-M4, det är samma. Man måste bara ha med/byta flaggorna -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -fsingle-precision-constant
Det ända som man måste tänka på är vid programmering via GDB så måste man manuellt reseta MCUn, GDB gör inte detta för någon konstig anledning.
Så men får stänga av den och sedan starta den igen. Jag drar ur strömkabeln och stoppar i den igen.

När det gäller makefile så kan du kolla på min och ta lite insperation från samt att du behöver stm32f4xx.cfg till OpenOCD. :)
stm32f4xx.cfg:

Kod: Markera allt

# Modified stm32f2xxx script to work with the st32f4
# run something like this to flash your board
# flash write_image erase demo.hex 0 ihex

if { [info exists CHIPNAME] } {
   set  _CHIPNAME $CHIPNAME
} else {
   set  _CHIPNAME stm32f4xxx
}

if { [info exists ENDIAN] } {
   set  _ENDIAN $ENDIAN
} else {
   set  _ENDIAN little
}

# Work-area is a space in RAM used for flash programming
# By default use 64kB
if { [info exists WORKAREASIZE] } {
   set  _WORKAREASIZE $WORKAREASIZE
} else {
   set  _WORKAREASIZE 0x10000
}

# JTAG speed should be <= F_CPU/6. F_CPU after reset is 16MHz, so use F_JTAG = 2MHz
#
# Since we may be running of an RC oscilator, we crank down the speed a
# bit more to be on the safe side. Perhaps superstition, but if are
# running off a crystal, we can run closer to the limit. Note
# that there can be a pretty wide band where things are more or less stable.
jtag_khz 2000

jtag_nsrst_delay 100
jtag_ntrst_delay 100

#jtag scan chain
if { [info exists CPUTAPID ] } {
   set _CPUTAPID $CPUTAPID
} else {
  # See STM Document RM0033
  # Section 32.6.3 - corresponds to Cortex-M3 r2p0
   set _CPUTAPID 0x4ba00477
}
jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID

if { [info exists BSTAPID ] } {
   set _BSTAPID $BSTAPID
} else {
  # See STM Document RM0033
  # Section 32.6.2
  # 
  set _BSTAPID 0x06413041
}
jtag newtap $_CHIPNAME bs -irlen 5 -expected-id $_BSTAPID

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m3 -endian $_ENDIAN -chain-position $_TARGETNAME

$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f2x 0 0 0 0 $_TARGETNAME
Makefile:

Kod: Markera allt

#-------------------------------------------
# Makefile for STM32F4xx targets
# Author: (C) Emil Fresk
#-------------------------------------------

# External high speed crystal frequency
F_HSE = 12000000

# Use Standard Pheriphial Libraries (true = 1)
USE_STD_LIBS = 1

# Optimization
OPTIMIZATION = 2

# Build tools
GCC     = arm-none-eabi-gcc
SIZE    = arm-none-eabi-size
OBJDUMP = arm-none-eabi-objdump
OBJCOPY = arm-none-eabi-objcopy

# Flags
MCU     = -mcpu=cortex-m4 -mthumb -g -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -fsingle-precision-constant
CFLAGS  = $(COMMON) -std=gnu99 -O$(OPTIMIZATION) $(INCLUDE)
AFLAGS  = $(COMMON) $(INCLUDE)
LDFLAGS = $(COMMON) -Tstm32f4x_flash.ld -Wl,--build-id=none,-Map=main.map

# StdLibs to use if wanted
STDLIBDIR = Libraries/STM32F4xx_StdPeriph_Driver/src/
CSTD = $(STDLIBDIR)stm32f4xx_gpio.c
CSTD += $(STDLIBDIR)stm32f4xx_exti.c
CSTD += $(STDLIBDIR)stm32f4xx_rcc.c
CSTD += $(STDLIBDIR)stm32f4xx_syscfg.c
CSTD += $(STDLIBDIR)stm32f4xx_tim.c
CSTD += $(STDLIBDIR)misc.c

# Sources
INCLUDE = -I./include -I./include/drivers -I./CMSIS -I./Libraries/STM32F4xx_StdPeriph_Driver/inc
CSRCS = $(wildcard CMSIS/*.c) $(wildcard source/*.c) $(wildcard source/drivers/*.c) 

# FreeRTOS includes and source files
RTOS_DIR = ./FreeRTOS/
RTOS_PORT = $(RTOS_DIR)portable/GCC/ARM_CM4F/
RTOS_MEM = $(RTOS_DIR)portable/MemMang/
INCLUDE += -I$(RTOS_PORT) -I$(RTOS_DIR)include/
CSRCS += $(RTOS_DIR)list.c $(RTOS_DIR)tasks.c $(RTOS_DIR)queue.c $(RTOS_PORT)port.c $(RTOS_MEM)heap_1.c


# USB Libraries
USBLIB = $(wildcard Libraries/STM32_USB_Device_Library/Class/cdc/src/*.c)
USBLIB += $(wildcard Libraries/STM32_USB_Device_Library/Core/src/*.c)
USBLIB += $(wildcard Libraries/STM32_USB_OTG_Driver/src/*.c)

INCLUDE += -I./Libraries/STM32_USB_Device_Library/Class/cdc/inc
INCLUDE += -I./Libraries/STM32_USB_Device_Library/Core/inc
INCLUDE += -I./Libraries/STM32_USB_OTG_Driver/inc
CSTD    += $(USBLIB)

# Include different source files depending on USE_STD_LIBS

ifeq ($(USE_STD_LIBS),1)
	CSRCS += $(CSTD)
	COMMON = $(MCU) -DHSE_VALUE=$(F_HSE) -DUSE_STDPERIPH_DRIVER -DUSE_USB_OTG_FS
else
	COMMON = $(MCU) -DHSE_VALUE=$(F_HSE)
endif


ASRCS   = $(wildcard CMSIS/*.s) $(wildcard source/*.s) $(wildcard source/drivers/*.s) 
OBJECTS = $(ASRCS:.s=.o) $(CSRCS:.c=.o)

test:
	@echo $(USBLIB)
	@echo $(CSRCS)

dump: main.elf
	@$(OBJDUMP) -D main.elf > main.dump
	@echo "main.dump created"
	
bin: main.elf
	@$(OBJCOPY) -O binary main.elf main.bin
	@echo "main.bin created"

all: main.elf
	@echo
	@echo "Size:"
	@$(SIZE) main.elf		

main.elf: $(OBJECTS)
	$(GCC) $(LDFLAGS) $(OBJECTS) -o main.elf
	
clean:
	rm -f $(OBJECTS) *.elf *.bin *.dump *.map *.*~
	@echo
	
.c.o:
	$(GCC) $(CFLAGS) -c $< -o $(<:.c=.o)
	@echo

.s.o:
	$(GCC) -c $(AFLAGS) -o $(<:.s=.o) $<
	@echo
SvenW
Inlägg: 1156
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av SvenW »

Tackar!
Skall förhoppningsvis snart prova STM32F4-discovery
så den informationen kommer väl till pass :-)

Har kört I2C med STM32-M3 med Atmega som slav,
och det var krångligare än man kunde tro!
SPI är något lättare och även snabbare, men många problem även där.
Förefaller som en enda praktiska vägen är att hitta kod på nätet,
eller 'firmware', om man inte har tid att bli expert.
Den erfarenheten är nog det enda jag kan bidra med, tyvärr.
Användarvisningsbild
Icecap
Inlägg: 26652
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Icecap »

Korken: DMA är i grunden bara användbart om man på förhand vet om vilken storlek på datablocket som ska komma/sändas. Har man inte denna information är det nog mer problematisk att använda den, vara sig i komplexitet och CPU-tid.

Såklart ska du skriva en interruptdriven styrning av detta, det är inget speciellt svårt med lite states inblandade. Se bara till att det finns en "om allt annat skiter sig"-state som kan återställa ifall det blir fuck-up - och gärna varsla med en flagga, det blir så oerhört mycket lättare att felsöka då.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

De va ungefär som jag trodde då. Lite synd att DMAn inte gör allt, men antagligen blir den alldeles för komplex då.

När det kommer till koden så jag jag lagt upp det såhär beroende på hur man vill konfigurera allt. En typdef används för att tala om vad för typ av överföring man vill göra, pollad eller interrupt. Samt en typdef för att hålla koll på allt som har med överföringen att göra, den har kolla på statusar, var man är i överföringen, callback funktioner och om det har blivit något fel.

Jag gillar den här uppställningen, så nu är det bara att skriva själva ISR-rutinen så allt fungerar som det ska också. :)

Kod: Markera allt

typedef struct
{
	uint32_t	Slave_Address_7bit;		/* Slave address in 7bit mode */
	uint8_t	*TX_Data;				/* Pointer to Transmit data - NULL if data transmit is not used */
	uint32_t	TX_Length;				/* Transmit data length - 0 if data transmit is not used*/
	uint32_t	TX_Count;				/* Current Transmit data counter */
	uint8_t	*RX_Data;				/* Pointer to Receive data - NULL if data receive is not used */
	uint32_t	RX_Length;				/* Receive data length - 0 if data receive is not used */
	uint32_t	RX_Count;				/* Current Receive data counter */
	uint32_t	Retransmissions_Max;	/* Max Re-Transmission value */
	uint32_t	Retransmissions_Count;	/* Current Re-Transmission counter */
	uint16_t	Status;					/* Current status of I2C activity */
	void 		(*Callback)(void);		/* Pointer to Call back function when transmission complete used in interrupt transfer mode */
} I2C_MASTER_SETUP_Type;

typedef enum
{
	I2C_TRANSFER_POLLING = 0,	/* Transfer in polling mode */
	I2C_TRANSFER_INTERRUPT		/* Transfer in interrupt mode */
} I2C_TRANSFER_OPTION_Type;
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

Nu när jag ändå håller på med detta så kan jag ta och ställa en fråga. Jag har kollat på lite olika koder och en sak återkommer som jag inte riktigt förstår vad den gör.
Det är:

Kod: Markera allt

(void) I2Cx->SR2;
Det ska tydligen läsa värdet ur I2C-enhetens SR2 register (man måste göra det för att cleara flaggor), men hur fungerar detta?
Jag "castar" en variabel till en void? Hur gör detta att den läser variabeln men inte använder den sedan?
remne
Inlägg: 241
Blev medlem: 11 februari 2007, 14:11:21
Ort: Linköping

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av remne »

Du behöver inte casta den, men jag vill minnas att LINT tex kan klaga om man inte gör det.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

Hmm, okej.
Hur kommer det sig att kompilatorn inte optimerar bort detta? Då det är en operation som inte gör något vettigt så att säga.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av bearing »

Kompilatorn kommer att optimera bort den raden ifall pekaren inte är volatile (eller pekar på volatile).
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

Aha, så det är så det fungerar.
Nu vet jag det, tackar! :)
SvenW
Inlägg: 1156
Blev medlem: 24 april 2007, 16:23:10
Ort: Göteborg

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av SvenW »

Just det. Om man tittar i stm32f10x_i2c.h ser man att

I2Cx har typen 'I2C_TypeDef' där medlemmarna har typen '__IO uint16_t'
där det gäller:
#define __IO volatile

De som skriver biblioteken kan sånt där. Därför är det bra att använda
bibliotek av det slaget, även om man många gånger önskar att
sakerna vore bättre kommenterade och förklarade.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av bearing »

Jag hade fel angående att det räcker med att pekaren är volatile. Koden kommer endast generera läsning av registret ifall medlemmen i structen är definierad volatile (som i det aktuella fallet). Åtminstone i kompilatorn jag använder just nu.

4 fall:

Kod: Markera allt

typedef struct
{
  volatile uint8_t a;
  volatile uint8_t b;
  volatile uint8_t c;
} abc_t;

abc_t* abc;

void main(void)
{
  (void) abc->b; //Will load address of abc, and read from address + 1 (address of b)
}

Kod: Markera allt

typedef struct
{
  uint8_t a;
  uint8_t b;
  uint8_t c;
} abc_t;

abc_t* abc;

void main(void)
{
  (void) abc->b; //Will not do anything
}

Kod: Markera allt

typedef struct
{
  uint8_t a;
  uint8_t b;
  uint8_t c;
} abc_t;

volatile abc_t* abc;

void main(void)
{

  (void) abc->b; //Will not do anything
}

Kod: Markera allt

typedef struct
{
  uint8_t a;
  uint8_t b;
  uint8_t c;
} abc_t;

abc_t* volatile abc;

void main(void)
{
  (void) abc->b; //Will load address of abc, but not read from address + 1
}
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: I2C via DMA på STM32F4xx, exempel?

Inlägg av Korken »

Tack för att du testade det! :)
Jag tar och gör lika dant imorgon så ser jag att det fungerar som det ska för mig också.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: I2C via DMA på STM32F4xx, exempel? *blev lite annat*

Inlägg av bearing »

Testade i avr-gcc, och där gav fall 3 (volatile pekare) samma resultat som fall 1. Övriga fall gav samma resultat. Så enligt avr-gcc hade jag alltså inte fel. Första testet gjorde jag med Freescale Codewarrior för HCS08.
Skriv svar