cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F105 SPI3 over DMA causes HardFault

stst9184
Associate II
Posted on March 12, 2012 at 15:21

I have a following code to initial SPI3 communication. The code below causes a HardFault and I am still not able to find the reason:(


void spi_init(void)

{

DMA_InitTypeDef DMA_InitStructure;

SPI_InitTypeDef SPI_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;


while(gFlag != 1);

gFlag = 0;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);


NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel2_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


NVIC_Init(&NVIC_InitStructure);


GPIO_PinRemapConfig(GPIO_Remap_SPI3, ENABLE); 


vGPIO_Set2(&gSpiCfg.OutPin);

vGPIO_Set2(&gSpiCfg.InpPin);

vGPIO_Set2(&gSpiCfg.ClkPin);

vGPIO_Set2(&gSpiCfg.ChsPin);


RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);


/* SPI3 configuration */

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 7;


SPI_Init(SPI3, &SPI_InitStructure); 


DMA_DeInit(DMA2_Channel2);


DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI3->DR);

DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dma;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_BufferSize = SPI_DEV1_DATA_SIZE;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 

DMA_Init(DMA2_Channel2, &DMA_InitStructure);

DMA_ITConfig(DMA2_Channel2, DMA_IT_TC | DMA_IT_TE, ENABLE);

DMA_Cmd(DMA2_Channel2, ENABLE);


SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

SPI_Cmd(SPI3, ENABLE); 


/* while (DMA_GetFlagStatus(DMA2_FLAG_TC2) == RESET) { ; } */

}


void main(void)

{

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 

APPLICATION_SP_ADDRESS-FLASH_START_ADDRESS);


/* Configure one bit for preemption priority */

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


/* Initialize microcontroller clocks and busses */

SystemInit();


while (1) 

{

spi_init();

}

}


/* in stm3210x_it.c */

...

void DMA2_Channel2_IRQHandler(void)

{ 

DMA_ClearFlag(DMA2_FLAG_TC2);

//assert_param(mSPIDevice[0].Cfg->fncSoftChipDeselect);

//mSPIDevice[0].Cfg->fncSoftChipDeselect();

gFlag = 1; 

}

...

Can somebody tell me what can be a reason. I've tried to disable DMA interrupt and check it with a while loop (line 63) it works fine. So I assume it is something with interrupt part, but I can't find wthat is it. Thank you for any hint #stm32-spi-dma
10 REPLIES 10
Posted on March 12, 2012 at 15:41

Well while(1); loops in Hard Fault handlers are useless at pinpointing the faulting instructions. Look at the example handler Joseph Yiu put together.

Absent that, you'll need to look at the registers in your debugger, and dig back in the stack, and look at the core's fault registers.

If the SPI is in 16-bit mode, wouldn't you want DMA to send 16-bit words, not 8-bit bytes?

Make sure the buffer is suitably aligned, and large enough. Buffers on the stack can be problematic, and might run off the end of memory.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
stst9184
Associate II
Posted on March 12, 2012 at 16:58

I've modifed a DMA to move also 16bit value and added a hadnler,. The iutput is here:

[Hard fault handler - all numbers in hex] R0 = 20000490 R1 = 0 R2 = 2 R3 = 0 R12 = 1 LR [R14] = fffffff9 subroutine call return address PC [R15] = 0 program counter PSR = 80000049 BFAR = e000ed38 CFSR = 20000 HFSR = 40000000 DFSR = 0 AFSR = 0 SCB_SHCSR = 0 If I change size of DMA transfer to 1, I am getting a following output:

DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI3->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dma;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

-St [Hard fault handler - all numbers in hex] R0 = 40003c00 R1 = b47 R2 = 2 R3 = 0 R12 = 1 LR [R14] = fffffff9 subroutine call return address PC [R15] = 0 program counter PSR = 20000049 BFAR = e000ed38 CFSR = 20000 HFSR = 40000000 DFSR = 0 AFSR = 0 SCB_SHCSR = 0 Buffer is defined as a globbal variable: volatile uint16_t dma[SPI_DEV1_DATA_SIZE] __attribute__ ((aligned)) ={1,2}; I know already iti a Usage fault, but there is is my endpoint:( What now?
Posted on March 12, 2012 at 17:30

I don't have your context. I'm not sitting in front of the debugger, or have complete code.

The value for LR is the magic number to allow the Hard Fault to be returned from, it's not a return address. The PC also appears to be bogus. You'll need to fish on the stack for the LR/PC value of the FAULT.

Once you pin point where, roughly, the code is faulting, and the registers at the time, you can look at a disassembly of your code and conclude if it is trying to touch memory outside RAM/FLASH/PERIPHERAL, or executing at an EVEN address (ie call or branch to 32-bit code).

There is no valid code at zero, it's supposed to be the vector table, make sure your code targets the correct STM32F1xx processor, and the vector table is sufficiently large to contain the DMA2 interrupt vector, see startup_stm32f1xx.s or whatever . You also enable two interrupts conditions, you need code to service and clear both.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on March 12, 2012 at 17:39

Check the vector memory at 0x08000124, if you have 0 or a bogus address there the processor will crash when the interrupt fires.

203 00000114 00000000 DCD UART5_IRQHandler
204 00000118 00000000 DCD TIM6_IRQHandler
205 0000011C 00000000 DCD TIM7_IRQHandler
206 00000120 00000000 DCD DMA2_Channel1_IRQHandler
207 00000124 00000000 DCD DMA2_Channel2_IRQHandler
208 00000128 00000000 DCD DMA2_Channel3_IRQHandler
209 0000012C 00000000 DCD DMA2_Channel4_5_IRQHandler
210 00000130

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
stst9184
Associate II
Posted on March 12, 2012 at 18:10

Thanks a lot for hint. I am now one step forward, it does not depend on the SPI.

I've taken a demo example for DMA1_Channel6, which works fine. After that I've modified a channel to DMA2_Channel2 and the bug is shown. See below a code. By the way I have an offset for the application, I will try to check this.

/**
******************************************************************************
* @file DMA/main.c 
* @author MCD Application Team
* @version V1.0.0
* @date 09/13/2010
* @brief Main program body
******************************************************************************
* @copy
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <
h2
><
center
>© COPYRIGHT 2010 STMicroelectronics</
center
></
h2
>
*/ 
/* Includes ------------------------------------------------------------------*/
#include ''ECU.h''
#include ''rgm_ecu.h''
#include ''FOC_ADC_PWM.h''
#include ''stm32f10x_it.h''
#include ''spi_dev.h''
#include ''spi_cfg.h''
#include ''rgm_nviccfg.h''
#include ''stm32f10x_dma.h''
#include ''stm32f10x_spi.h''
#include ''stm32f10x_rcc.h''
#include ''stm32f10x.h''
/** @addtogroup Examples
* @{
*/
/* Private typedef -----------------------------------------------------------*/
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
/* Private define ------------------------------------------------------------*/
#define BufferSize 32
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
DMA_InitTypeDef DMA_InitStructure;
__IO uint16_t CurrDataCounterBegin = 0, CurrDataCounterEnd = 10;
__IO TestStatus TransferStatus = FAILED;
const uint32_t SRC_Const_Buffer[BufferSize]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
uint32_t DST_Buffer[BufferSize];
extern int32_t gFlag = 1;
/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void NVIC_Configuration(void);
TestStatus Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength);
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
/*!< At this stage the microcontroller clock setting is already configured, 
this is done through SystemInit() function which is called from startup
file (startup_stm32f10x_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f10x.c file
*/ 
/* System Clocks Configuration */
RCC_Configuration();
/* NVIC configuration */
NVIC_Configuration();
#if 0
/* DMA1 channel6 configuration */
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
/* Enable DMA1 Channel6 Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
/* Get Current Data Counter value before transfer begins */
CurrDataCounterBegin = DMA_GetCurrDataCounter(DMA1_Channel6);
/* Enable DMA1 Channel6 transfer */
DMA_Cmd(DMA1_Channel6, ENABLE);
#else
/* DMA1 channel6 configuration */
DMA_DeInit(DMA2_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Const_Buffer;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(DMA2_Channel2, &DMA_InitStructure);
/* Enable DMA1 Channel6 Transfer Complete interrupt */
DMA_ITConfig(DMA2_Channel2, DMA_IT_TC, ENABLE);
/* Get Current Data Counter value before transfer begins */
CurrDataCounterBegin = DMA_GetCurrDataCounter(DMA2_Channel2);
/* Enable DMA1 Channel6 transfer */
DMA_Cmd(DMA2_Channel2, ENABLE);
#endif
/* Wait the end of transmission */
while (CurrDataCounterEnd != 0)
{
}
/* Check if the transmitted and received data are equal */
TransferStatus = Buffercmp(SRC_Const_Buffer, DST_Buffer, BufferSize);
/* TransferStatus = PASSED, if the transmitted and received data 
are the same */
/* TransferStatus = FAILED, if the transmitted and received data 
are different */
while (1)
{
}
}
void RCC_Configuration(void)
{
/* Enable peripheral clocks ------------------------------------------------*/
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 | RCC_AHBPeriph_DMA2, ENABLE);
}
/**
* @brief Configure the nested vectored interrupt controller.
* @param None
* @retval None
*/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 
APPLICATION_SP_ADDRESS-FLASH_START_ADDRESS);
/* Configure one bit for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable DMA1 channel6 IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief Compares two buffers.
* @param pBuffer, pBuffer1: buffers to be compared.
* @param BufferLength: buffer's length
* @retval PASSED: pBuffer identical to pBuffer1
* FAILED: pBuffer differs from pBuffer1
*/
TestStatus Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength)
{
while(BufferLength--)
{
if(*pBuffer != *pBuffer1)
{
return FAILED;
}
pBuffer++;
pBuffer1++;
}
return PASSED; 
}

Posted on March 12, 2012 at 18:26

The vector table needs to fall at 512 byte boundary. I think that is the minimum for this part.

                EXPORT  __Vectors

__Vectors       DCD     __initial_sp              ; Top of Stack

                DCD     Reset_Handler             ; Reset Handler

                DCD     NMIException              ; NMI Handler

...

extern void * __Vectors;

...

NVIC_SetVectorTable((u32)(&__Vectors), 0x0); // Smart Base Location

The bigger problem however, is that a lot of the startup code only has a subset of vectors from the low end STM32F1xx parts, without DMA2, etc. Look at a listing, map, or disassembly file to be sure.
Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
stst9184
Associate II
Posted on March 12, 2012 at 19:13

The Vector offset is OK, if not it can't jump to the Handler of DMA1_Channel6. I am sending

part of the map file, where are interrupt handlers.And the startup file, I don't see any problem

with them.

On the address : SCB->VTOR+292 = 0x08005224 => 09 57 00 08

which corresponds with a map file:

...

DMA2_Channel2_IRQHandler 0x08005709 Thumb Code 36 stm32f10x_it.o(i.DMA2_Channel2_IRQHandler)

...

________________

Attachments :

dump.txt : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HtTj&d=%2Fa%2F0X0000000aQX%2Fl7CeD2Vo1VRzRVV5kAzqQPwUMyx.rwxejd7SdJ5fBUE&asPdf=false

startup_stm32f10x_cl.s : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HtGv&d=%2Fa%2F0X0000000aKx%2F2BzWa1xYcC7Ai54eh6XeiInErlFWTPQGKIijbGCTt0s&asPdf=false
Posted on March 12, 2012 at 20:14

adtAppHeader 0x08005000 Data 32 rgm_apphdrvar.o(.constdata) __Vectors 0x08005100 Data 4 startup_stm32f10x.o(RESET) __Vectors_End 0x08005250 Data 0 startup_stm32f10x.o(RESET) __main 0x08005251 Thumb Code 0 entry.o(.ARM.Collect$$$$00000000)

You have the vectors on a 256 byte boundary, they need to be on at least a 512 byte boundary. Personally, I'd opt for a flash page boundary, so the boot loader and app can be erased/replaced independently of each other.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
stst9184
Associate II
Posted on March 13, 2012 at 11:11

Yes, that was it. I've moved a vector table to the address 0x08005200. The variable

__Vectors_Size in the map file has now size 150 (before 130).

Thank you a lot for your help

joee