cancel
Showing results for 
Search instead for 
Did you mean: 

SPI Transmit Interrupt Enable causes STM8 device reset

DOman.1
Associate II

Hi,

I am using STM8 device as a SPI master and SMP580 is a slave. When I enable SPI Transmit Interrupt (SPI_ICR-> TXIE = 1) MCU is resetting and when not enable MCU reset not occurring. I am enabling these interrupt during MCU Init. MCU not reset when Rx interrupt is enabled.

My Observations are:

  1. Break point in ISR is hitting once debug mode is running.
  2. Break point in main() is hitting always. Which indicate MCU reset.
  3. When Tx interrupt not enable MCU reset NOT occurred
  4. When Rx interrupt is enabled MCU reset NOT occurred.
  5. Tried to read and write to SPI->DR in ISR but issue is same.

Please give your suggestions for how to use SPI transmit interrupt. Below is code for your reference.

#pragma vector = 0x0C // ToDo: To be checked
__interrupt void SPI_IRQHandler(void)
{	
	disableInterrupts();	
	if(SPIT_GetFlagStatus(SPI_SR_RXNE))
	{
		/* Receiving SPI Data */
		temp = (uint8_t)(SPI->DR);
		
		/* Clearing Receive FIFO drain request enable flag */
		SPI_ClearFlag(SPI_SR_RXNE);		
	}
	
	if(SPIT_GetFlagStatus(SPI_SR_TXE))
	{ 		
		/* Transmit complete */
		/* Clearing Receive FIFO drain request enable flag */
		SPI_ClearFlag(SPI_SR_TXE);
		
	}
	
	enableInterrupts();	
 
	return;
}
 
 
void SPIT_ITConfig(uint8_t SPIT_IT, uint8_t NewState)
{
  VAR(uint8_t, SPIT_VAR) itpos;  
  
  /* Get the SPI IT index */
  itpos = (uint8_t)((uint8_t)1 << (uint8_t)((uint8_t)SPIT_IT & (uint8_t)0x0F));
  
  if (NewState != SPIT_DISABLE)
  {
    SPI->ICR |= itpos; /* Enable interrupt*/
  }
  else
  {
    SPI->ICR &= (uint8_t)(~itpos); /* Disable interrupt*/
  }
}
 
 
void SPIT_DeInit(void) 
{
  SPI->CR1    = ((uint8_t)0x00);
  SPI->CR2    = ((uint8_t)0x00);
  SPI->ICR    = ((uint8_t)0x00);
  SPI->SR     = ((uint8_t)0x02);
  SPI->CRCPR  = ((uint8_t)0x07);
  SPI_ClearFlag(SPI_SR_RXNE);
  
}
 
void SPIT_Init(void)
{
    VAR(uint8_t, SPIT_VAR) SPIT_Loop = SPIT_ZERO; 
 
    SPIT_DeInit();
  
      CLK->ICKR = 0;                       //  Reset the Internal Clock Register.
      CLK->ICKR |= CLK_ICKR_HSIEN;        //  Enable the HSI.
      CLK->ECKR = 0;                       //  Disable the external clock.
      while (CLK_ICKR_HSIRDY == 0);       //  Wait for the HSI to be ready for use.
      CLK->CKDIVR = 0;                     //  Ensure the clocks are running at full speed.
      CLK->PCKENR1 |= (uint8_t)((uint8_t)1 << ((uint8_t)SPIT_CLK_PERIPHERAL_SPI & (uint8_t)0x0F)); //  Enable all peripheral clocks.
//    CLK->CCOR = 1;                       //  Turn on CCO.
      CLK->HSITRIMR = 0;                   //  Turn off any HSIU trimming.
      CLK->SWIMCCR = 0;                    //  Set SWIM to run at clock / 2.
      CLK->SWR = 0xe1;                     //  Use HSI as the clock source.
      CLK->SWCR = 0;                       //  Reset the clock switch control register.
      CLK->SWCR |= CLK_SWCR_SWEN;                 //  Enable switching.
      while (CLK->SWCR & CLK_SWCR_SWBSY != 0);        //  Pause while the clock switch is busy.
    
        
	/* Reset corresponding bit to GPIO_Pin in CR2 register */
	  GPIOC->CR2 &= (uint8_t)(~(SPIT_CFG_GPIOC_PIN));
		GPIOE->CR2 &= (uint8_t)(~(SPIT_CFG_GPIOE_PIN));
		
	/*-----------------------------*/
	/* Input/Output mode selection */
	/*-----------------------------*/	  
	  GPIOC->ODR |= (uint8_t)SPIT_CFG_GPIOC_PIN;
	  GPIOE->ODR |= (uint8_t)SPIT_CFG_GPIOE_PIN;
		
	/* Set Output mode */
	  GPIOC->DDR |= (uint8_t)SPIT_CFG_GPIOC_PIN;
	  GPIOE->DDR |= (uint8_t)SPIT_CFG_GPIOE_PIN;
		GPIOC->DDR &= (uint8_t)(~(SPIT_GPIOC_PIN_7_MISO));// Setting MISO Pin as Input
 
	/*------------------------------------------------------------------------*/
	/* Pull-Up/Float (Input) or Push-Pull/Open-Drain (Output) modes selection */
	/*------------------------------------------------------------------------*/	  
	  GPIOC->CR1 |= (uint8_t)SPIT_CFG_GPIOC_PIN;
	  GPIOE->CR1 |= (uint8_t)SPIT_CFG_GPIOE_PIN;
 
	/*-----------------------------------------------------*/
	/* Interrupt (Input) or Slope (Output) modes selection */
	/*-----------------------------------------------------*/
	  
	  //GPIOC->CR2 |= (uint8_t)SPIT_CFG_GPIOC_PIN; 	
	  //GPIOC->CR2 |= (uint8_t)(SPIT_GPIOC_PIN_7_MISO);
	  GPIOE->CR2 |= (uint8_t)SPIT_CFG_GPIOE_PIN;
		
	  /* Frame Format, BaudRate, Clock Polarity and Phase configuration */
	  SPI->CR1 = (uint8_t)((uint8_t)((uint8_t)(uint8_t)0x00 | (uint8_t)0x18) |
                       (uint8_t)((uint8_t)(uint8_t)0x00 | (uint8_t)0x01));
	
	
	  /* Data direction configuration: BDM, BDOE and RXONLY bits */
	  SPI->CR2 = (uint8_t)((uint8_t)((uint8_t)0x00) | (uint8_t)((uint8_t)0x02));
	
	  SPI->CR2 |= (uint8_t)((uint8_t)0x01);
	  
	  /* Master/Slave mode configuration */
	  SPI->CR1 |= (uint8_t)(0x04);
	  
	  /* CRC configuration */
	  SPI->CRCPR = (uint8_t)0x00;
 
	
	SPIT_ITConfig(((uint8_t)0x06),ENABLE);// Rx Interrupt Enable
	SPIT_ITConfig(((uint8_t)0x07),ENABLE);// Tx Interrupt Enable
	
	SPI->CR1 &= (uint8_t)(~((uint8_t)0x40));	 
 
	/* Enable the SPI peripheral*/	
	SPI->CR1 |= ((uint8_t)0x40); 
	
 
}

3 REPLIES 3
Cristian Gyorgy
Senior III

Enabling TXIE interrupt means that the interrupt will ​fire up as long as the tx buffer is empty. In the interrupt routine you need to actually write some data into the tx buffer or disable the interrupt. You cannot simply reset the TXE bit in SPI_SR, it is read only, and it tells you that the tx buffer is empty.

Your MCU resets because it executes continuously the SPI interrupt and you probably have a watchdog enabled.​

DOman.1
Associate II

In my current implementation at the time of SPI INIT I have made correction as follows,

  1. I have enabled RXNE interrupt only (RXIE = 1) and Disabled TXIE interrupt. So MCU reset not occurring.
  2. But why TXE flag is getting set (TXE = 1) always. I can see the code inside ISR is executing when TXE = 1.
  3. To clear TXE flag i am writing SR->DR = 0x00 but TXE flag is not clearing. Then How to clear TXE flag.
  4. My another question is,

When RXIE = 1 then I am writingdata to SR->DR = 0x00. After this line immediately again interrupt is called. Should i write the data to DR in ISR ?

if(SPIT_GetFlagStatus(SPI_SR_TXE))
	{	
		SPIT_ITConfig(SPIT_ICR_TXEI,DISABLE);
		//SPI->DR = 0x00;
	}
		
if(SPIT_GetFlagStatus(SPI_SR_RXNE))	
	{
		
		/* Receiving SPI Data */
		Data_Read = (uint8_t)(SPI->DR);
		Counter++;
		if(Counter >= 0x2u)
		{
			/* Reset the Count and Length */
			Counter = SPIT_ZERO;			
			status = (uint8_t)RX_COMPLETE;
			// SPIT_ITConfig(SPIT_ICR_RXEI,DISABLE); /* Check it is required or not */
		}
		else
		{
			status = (uint8_t)SPIT_TX_INPROGRESS;
			/* Write Next byte to transmit */
			SPI->DR = 0x00;
		}              		
	}

Please let me know your suggestions .

Cristian Gyorgy
Senior III
  1. Good.
  2. Like I said, the TXE flag tells you the SPI_DR is ready to be written with new data, it's empty. The TXE will only be reset when you have data shifting out on the MOSI pin and also a byte already written into SPI_DR, waiting to be transferred into the shift register.
  3. Write one byte in SPI_DR. Wait until TXE is set again (a few cycles only) and the write another byte into SPI_DR. Then for as long as the first byte shifts out the TXE will be reset.
  4. If your interrupt is on receive buffer not empty only, if you don't read the data from SPI_DR, the interrupt will be called again.

It's quite clear you don't really understand how SPI bus works. It's better to take the time to understand the SPI interface and than write your code, instead of just trying to put together some C functions in the hope that something will work.

The manuals provide very good explanations, I suggest you read them thoroughly.

Good luck!