Why is the LED blinking without any physical interrupt? - embedded

The following is the interrupt handler for LED0 to toggle when an interrupt occurs on the UART0 due to LIN transmission from master to slave, my device is the slave. But without connecting my device(slave) to the master that means without any LIN transmission over UART, the LED0 is toggling. I cannot understand, how can this happen automatically? How can an interrupt be generated and Toggle of LED0 can happen?
void FTM0_IRQHandler()
{
if (1==((FTM0_C0SC & FTM_CnSC_CHF_MASK)>>FTM_CnSC_CHF_SHIFT) ) /* If the CHF of the channel is equal to 0 */
{
(void)FTM0_C0SC; /* Read to clear flag */
FTM0_C0SC ^= FTM_CnSC_CHF_MASK; /* Clear flag */
FTM0_C0V = FTM0_C0V + 391 ; /* Refresh interrupt period */
if (LED_counter>=50){
/* Toggle LED for LIN transmission */
/* Reset counter */
LED0_TOGGLE;
LED_counter = 0;
}
LED_counter++;
}
}
In my main funtion I have called my timer initialization as follows:
lin_application_timer_FTM0();
And the above function is defined as follows:
void lin_application_timer_FTM0()
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK; /* Enable Clock for FTM0 */
FTM0_SC |= FTM_SC_PS(7); /* Select Preescaler in this case 128. 20 Mhz /128 =156.25 Khz. */
/* Counter increase by one every 6.4 us */
/* Enable Channle 0*/
FTM0_C0SC |= FTM_CnSC_CHIE_MASK; /* Enable channel 0 interrupt */
FTM0_C0SC |= FTM_CnSC_MSA_MASK; /* Channel as Output compare mode */
/*Select interrupt frequency*/
FTM0_C0V = FTM_CnV_VAL(391) ; /* Interrupt every 2.5ms */
FTM0_SC |= FTM_SC_CLKS(1); /*FTM0 use system clock*/
/* Set the ICPR and ISER registers accordingly */
NVIC_ICPR |= 1 << ((INT_FTM0-16)%32);
NVIC_ISER |= 1 << ((INT_FTM0-16)%32);
}
Can anyone explain why is the LED0 toggling without any interrupt?

It’s possible that the interrupt is actually being triggered. Does your interrupt pin have a pull up or pull-down resistor attached? If it’s disconnected and just “floating”, electrical noise in the environment can cause spurious interrupts.

Related

PLL Init function in STM32F429 to configure max frequency (180Mhz)

I want to configure PLL in STM32F429 to its max frequency (180Mhz) without using STMCube-generated configurations. I am using my own register definitions like this
#define RCC_CFGR (*((volatile uint32 *)0x40023808))
and my own SET_BIT()/CLEAR_BIT() macros
My questions are:
1- Is this procedure correct?
2- How can I check if it is working?
3- May the MCU can not handle this speed (reading/writing in registers)?
#define PLL_M 16
#define PLL_N 360
#define PLL_P 2
#define PLL_Q 7
void PLL_init(void)
{
/* System Init */
/* HSI ON */
SET_BIT(RCC_CR, HSION);
/* Reset CFGR register */
RCC_CFGR = 0x00000000 ;
/* Reset PLLCFGR register */
RCC_PLLCFGR = 0x24003010;
/* Reset HSEON, CSSON and PLLON bits */
RCC_CR &= (uint32_t)0xFEF6FFFF;
/************* SetSysClock ************/
RCC_PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16)| (PLL_Q << 24);
/* PLL clock Source HSI or HSE */
CLEAR_BIT(RCC_PLLCFGR, PLLSRC);
/* APB1 PWR Enable*/
SET_BIT(RCC_APB1ENR, PWREN);
/* Select regulator voltage output Scale 1 mode, System frequency up to 180 MHz */
SET_BIT(PWR_CR, VOS0);
SET_BIT(PWR_CR, VOS1);
/* AHB div 1 */
CLEAR_BIT(RCC_CFGR,HPRE3);
CLEAR_BIT(RCC_CFGR,HPRE0);
/* APB2 Div = 2*/
CLEAR_BIT(RCC_CFGR,PPRE20);
CLEAR_BIT(RCC_CFGR,PPRE21);
SET_BIT(RCC_CFGR, PPRE23);
/* APB 1 Div = 8 */
CLEAR_BIT(RCC_CFGR, PPRE10);
SET_BIT(RCC_CFGR, PPRE11);
SET_BIT(RCC_CFGR, PPRE12);
/* SET PLL ON */
SET_BIT(RCC_CR, PLLON);
/* Check PLL is ready */
while(BIT_IS_CLEAR(RCC_CR,PLLRDY));
/* Enable the Over-drive to extend the clock frequency to 180 Mhz */
SET_BIT(PWR_CR, ODEN);
while(BIT_IS_CLEAR(PWR_CSR,ODRDY));
SET_BIT(PWR_CR, ODSWEN);
while(BIT_IS_CLEAR(PWR_CSR,ODSWRDY));
SET_BIT(FLASH_ACR, PRFTEN);
SET_BIT(FLASH_ACR, ICEN);
SET_BIT(FLASH_ACR, DCEN);
FLASH_ACR |= FLASH_ACR_LATENCY_5WS;
/* Select the main PLL as system clock source */
CLEAR_BIT(RCC_CFGR, SW0);
SET_BIT(RCC_CFGR, SW1);
/* Wait till the main PLL is used as system clock source */
while(!BIT_IS_CLEAR(RCC_CFGR,SWS0) && !BIT_IS_SET(RCC_CFGR,SWS1) ); /* Loop till is Set */
}
I did exactly this thing on my STM32F746 - set 216MHz with registers. It looks very similar to mine. Not observing any obvious things. You wait for all ready flags and all. Overdrive and overdrive switching are there, flash wait states are there.
How to check if it's working - timer is an easy idea. Timer that is clocked by a system clock with some prescaler and that outputs PWM to some pin. Given the configuration of the timer is set by you, you should know what output frequency to expect. You can set timer parameters in such a way that you expect timer-interrupt-toggle an LED every 1s.
The MCU should handle it OK if all prescalers for AHB/APB are within limits, as well as Flash latency is set correctly. Which seems to be the case.
If you want to compare the logic with what I do with STM32F746 (which looks almost identical in terms of how you do it), you can check it in this rcc.c file of mine. It definitely works, I tested it.

STM32F3 Dual ADC with interleaved mode

I'm trying to achieve 10MSPS as documented in STM32F30x ADC modes and application under the section Dual interleaved mode.
Firstly, i tried to use single DMA. I configured the DMA1 Channel1 to read from ADC1&2 Common data register. It worked but i could only achieve a sample rate of 8.47MSPS. Beyond that limit, ADC1 starts to overrun.
(Register ADC1_2->CCR: MULT=0x07, MDMA=0x02, DELAY=0x04) Considering the DMA reading the common data register after the slave adc ends its conversion, the problem seems reasonable at high sample rates.
So i decided to use 2 DMAs. One for each ADC:
DMA1 Channel1 copies from ADC1->DR to SRAM
DMA2 Channel1 copies from ADC2->DR to SRAM
(Register ADC1_2->CCR: MULT=0x07, MDMA=0x00, DELAY=0x04)
This configuration also worked but again up to 8MSPS. Above that rate, ADC2 starts to overrun. I cannot understand why ADC2 overruns. I expected that this setup would work.
When i run ADC1 & ADC2 in independent mode with DMA configuration above, everything seems to work fine. No overruns, both ADC samples at 5.1MSPS but independently.
One question: What happens when both ADCs run in independent mode and triggered from the same source (e.g. TIM2) but ADC1 is triggered at the rising edge and ADC2 is triggered at the falling edge of the clock ? Would it work? This is the next thing i will try.
The MCU i work with is STM32F303CB.
ADC sampling times were 1.5 Cycles.
Any advice will be appreciated.
Edit: I have provided a minimal sample code that runs on STM32F3 Discovery with an 8 MHz Crystal. Program directly jumps to main()
// main.c
#include "stm32f30x.h"
#define DUALDMA
void sysinit();
void clockconfig();
void delay(int d);
void timerinit();
void adcinit();
void dmainit();
void dualdmainit();
int main(){
sysinit();
clockconfig();
timerinit();
#ifdef DUALDMA
dualdmainit();
#else
dmainit();
#endif
adcinit();
RCC->AHBENR |= RCC_AHBENR_GPIOEEN; // GPIOE enable
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // GPIOA enable
GPIOE->MODER = 0x55555555; // GPIOE -> output
GPIOA->MODER |= 0x0000FFFF;// GPIOA -> analog
// Reset SRAM memory area
for(int i = 0;i<1024*4;i+=4){
*((uint32_t*)(0x20000800+i)) = 0;
}
// Blink LEDs
while(1){
GPIOE->ODR = 0xFFFF;
delay(1000);
GPIOE->ODR = 0x00FF;
delay(1000);
}
}
void delay(int d){
// Dummy delay
int l = d*1000;
for(int i = 0;i<l;i++);
}
void sysinit(){
//STM32F303 reset state
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= 0x00000001U;
/* Reset CFGR register */
RCC->CFGR &= 0xF87FC00CU;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= 0xFEF6FFFFU;
/* Reset HSEBYP bit */
RCC->CR &= 0xFFFBFFFFU;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE bits */
RCC->CFGR &= 0xFF80FFFFU;
/* Reset PREDIV1[3:0] bits */
RCC->CFGR2 &= 0xFFFFFFF0U;
/* Reset USARTSW[1:0], I2CSW and TIMs bits */
RCC->CFGR3 &= 0xFF00FCCCU;
/* Disable all interrupts */
RCC->CIR = 0x00000000U;
SCB->VTOR = 0x08000000; /* Vector Table Relocation in Internal FLASH */
}
void adcinit(){
RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable ADC clock
RCC->CFGR2 |= RCC_CFGR2_ADCPRE12_4;// ADC clock prescaler = 1
ADC1->CFGR |= ADC_CFGR_EXTEN_0; // Trigger on rising edge
ADC1->CFGR |= ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1; // TIM1 TRGO2
ADC1->SQR1 |= ADC_SQR1_SQ1_0 ; // ch 1
ADC1->CFGR |= ADC_CFGR_OVRMOD; // Stop on overrun
ADC1->CFGR |= ADC_CFGR_DMAEN; // DMA enable
ADC1->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0); // Enable VREG
ADC1->CR |= ADC_CR_ADVREGEN_0;
ADC1->CR |= ADC_CR_ADEN;
while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );
ADC2->SQR1 |= ADC_SQR1_SQ1_0 ; // ch 1
ADC2->CFGR |= ADC_CFGR_DMAEN;
ADC2->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0);
ADC2->CR |= ADC_CR_ADVREGEN_0;
ADC2->CR |= ADC_CR_ADEN;
while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );
ADC1_2->CCR |= ADC12_CCR_DELAY_2 ; // Delay = 4, 5 Cycles
#ifndef DUALDMA
ADC1_2->CCR |= ADC12_CCR_MDMA_1; // If single DMA is selected, configure MDMA bits for 12 bits
#endif
ADC1_2->CCR |= ADC12_CCR_MULTI_2 | ADC12_CCR_MULTI_1 | ADC12_CCR_MULTI_0; // Interleaved mode
}
void dmainit(){
// DMA config for Single DMA, 32 bits
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel1->CPAR = (uint32_t)&ADC1_2->CDR;
DMA1_Channel1->CMAR = 0x20000800;
DMA1_Channel1->CNDTR = 1024;
DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_1 | DMA_CCR_PSIZE_1;
//DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC ;
}
void dualdmainit(){
// DMA config for DUAL DMA, 16bits
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // DMA1 Enable
RCC->AHBENR |= RCC_AHBENR_DMA2EN; // DMA2 Enable
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
DMA1_Channel1->CMAR = 0x20000800;
DMA1_Channel1->CNDTR = 1024;
DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;
DMA2_Channel1->CPAR = (uint32_t)&ADC2->DR;
DMA2_Channel1->CMAR = 0x20000800+1024*2;
DMA2_Channel1->CNDTR = 1024;
DMA2_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;
}
void timerinit(){
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Enable TIM1
TIM1->CR2 |= TIM_CR2_MMS2_1; // Update event selected as TRGO2
TIM1->PSC = 0;
TIM1->ARR = 0x0d; // 5 MHz (72 MHz / 14 )
TIM1->CR1 |= TIM_CR1_CEN;
}
void clockconfig(){
// External oscillator (HSE): 8MHz
RCC->CR |= RCC_CR_HSEON; // Enable HSE
while( (RCC->CR & RCC_CR_HSERDY) == 0 );
RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL MUL = x9
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 Prescaler = 2
RCC->CFGR |= RCC_CFGR_PLLSRC; // PLL source = HSE
FLASH->ACR |= FLASH_ACR_LATENCY_1; // Two wait states
RCC->CR |= RCC_CR_PLLON; // Enable and wait PLL
while( (RCC->CR & RCC_CR_PLLRDY) == 0 );
RCC->CFGR |= RCC_CFGR_SW_PLL; // Select PLL as system clock
}
Scatter file:
LR_IROM1 0x08000000 0x00020000 { ; load region size_region
ER_IROM1 0x08000000 0x00020000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM2 0x10000000 0x00000200 { ; RW data
.ANY (+RW +ZI)
}
}
You cant do it this way. You need to use only one DMA channel and both samples are transmitted in one 32 bit DMA transaction.
In 6 bits mode I have archived more than 18MSPS
I do not know how to program it using HAL as I personally do only the bare register approach
There is a hardware problem as well (read the errata) and sometimes in >8bit modes the transfer does not work properly.
For dual DMA you need to:
Prevent any core accesses to the SRAM memory by placing the stack and the variables (except the ADC buffers) in the CCM RAM or suspending any core activity by entering the sleep mode.

Input Capture mapping using PPS

I am trying to configure input capture module (IC1) on dsPIC33EP32MC204 to use it for duty cycle measurement. As input I would like to use RP35 pin. To test the correctness of the configuration I set up an experiment: I connected a pulse generator with 1Hz PWM pulses and set up the input capture to capture every rising edge. I reckoned that these rising edges will trigger the input capture interrupt and each call of the interrupt will cause the LED to blink. Unfortunatelly it is not working. I would be grateful if someone could tell me where is the problem. The code:
void Init_InputCapture(void)
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 1; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
The interrupt:
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
LATBbits.LATB9 = ~LATBbits.LATB9;
IFS0bits.IC1IF = 0;
}
And the related code from the main():
__builtin_write_OSCCONL(OSCCON & ~(1<<6));
RPINR7 = 0x23; ; // IC1 mapped to RP35
__builtin_write_OSCCONL(OSCCON | (1<<6));
During the setup I followed the instuctions from the family reference manual.
The code you have posted could be more complete. For dsPIC controllers it is essential to show exactly how all of the configuration bits are set and how the system clock is initialized.
This is your code corrected with the minimum required setup code:
/*
* file: main.c
* target: dsPIC33EP32MC204
* IDE: MPLABX v4.05
* Compiler: XC16 v1.35
*
* Description:
* Use Input Capture 1 module to catch a 1Hz pulse on GPIO RP35 and toggle LED on RB9.
*
*/
#pragma config ICS = PGD2 // ICD Communication Channel Select bits (Communicate on PGEC2 and PGED2)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = OFF // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = OFF // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
#pragma config FNOSC = FRC // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config PWMLOCK = ON // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = OFF // Two-speed Oscillator Start-up Enable bit (Start up with user-selected oscillator source)
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
#include <xc.h>
/* Setup the clock to run at about 60 MIPS */
#define FOSC (7372800L) /* nominal fast RC frequency */
#define PLL_N1 (2L) /* PLLPRE CLKDIV<4:0> range 2 to 33 */
#define PLL_M (65L) /* PLLDIV PLLFBD<8:0> range 2 to 513 */
#define PLL_N2 (2L) /* PLLPOST CLKDIV<7:6> range 2, 4 or 8 */
#define FSYS (FOSC*PLL_M/(PLL_N1*PLL_N2))
#define FCYC (FSYS/2L)
/*
* Global constant data
*/
const unsigned long gInstructionCyclesPerSecond = FCYC;
/*
* Initialize this PIC
*/
void PIC_Init( void )
{
unsigned int ClockSwitchTimeout;
/*
** Disable all interrupt sources
*/
__builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
IEC0 = 0;
IEC1 = 0;
IEC2 = 0;
IEC3 = 0;
IEC4 = 0;
IEC5 = 0;
IEC6 = 0;
IEC8 = 0;
IEC9 = 0;
__builtin_disi(0x0000); /* enable interrupts */
CLKDIV = 0; /* Disable DOZE mode */
if(!OSCCONbits.CLKLOCK) /* if primary oscillator switching is unlocked */
{
/* Select primary oscillator as FRC */
__builtin_write_OSCCONH(0b000);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* Configure PLL prescaler, PLL postscaler, PLL divisor */
PLLFBD=PLL_M-2; /* M=65 */
#if PLL_N2==2
CLKDIVbits.PLLPOST=0; /* N2=2 */
#elif PLL_N2==4
CLKDIVbits.PLLPOST=1; /* N2=4 */
#elif PLL_N2==8
CLKDIVbits.PLLPOST=3; /* N2=8 */
#else
#error invalid PLL_N2 paramenter
#endif
CLKDIVbits.PLLPRE=PLL_N1-2; /* N1=2 */
/* Select primary oscillator as FRCPLL */
__builtin_write_OSCCONH(0b001);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* wait, with timeout, for the PLL to lock */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && !OSCCONbits.LOCK;);
/* at this point the system oscillator should be 119.808MHz */
}
/* make all inputs digital I/O */
ANSELA = 0x00;
ANSELB = 0x00;
ANSELC = 0x00;
/* Unlock PPS Registers */
__builtin_write_OSCCONL(OSCCON & ~(_OSCCON_IOLOCK_MASK));
/* map all PPS pins */
RPINR7bits.IC1R = 35; // Select RP35 (RB3/PGED1) as input for IC1
/* Lock PPS Registers */
__builtin_write_OSCCONL(OSCCON | (_OSCCON_IOLOCK_MASK));
}
/*
* Initialize Input Capture 1
*/
void Init_InputCapture( void )
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 4; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
/*
* Interrupt Service Routine for IC1
*/
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
IFS0bits.IC1IF = 0;
LATBbits.LATB9 ^= 1; // toggle LED
}
/*
* Main process loop
*/
int main( void )
{
PIC_Init();
TRISBbits.TRISB9 = 0; // make RB9 an output
LATBbits.LATB9 = 0; // turn off LED
Init_InputCapture();
for(;;)
{
/* process loop */
}
return 0;
}

STM32L4 SPI Transfer complete interrupt using DMA fires only once

I'm trying to send an array of 10 bytes between 2 nucleo boards (NUCLEO-L432KCU) using SPI and DMA. My goal is to develop the code for the slave board using the Low Level APIs. The master board is used simply for testing and, when everything will work, it will be replaced with the real system.
Before continuing, here are some more details about the system: The sender is configured as master. The code for the master is developed using the HAL API. The Chip Select on the master board is implemented using a GPIO.
The receiver is configured as slave with the option Receive only slave enabled and Hardware NSS input. The initialization code is generated automaGically using the CubeMX tool.
With my current implementation I'm able to receive data on the slave board but only once: in practice is seems that the interrupt fires only once and I'm having hard time to figure out what I am missing!
I believe the error has something to do with clearing some interrupt flags. I went through the reference manual but I cannot see what I'm doing wrong.
Following is my code for both sender and receiver.
Code for the sender
Note: Concerning the sender I report only the main function since all the other code is auto-generated. Furthermore, I have checked with a logic analyzer that the code works. Please let me know if you need more details.
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
uint8_t test[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);
HAL_SPI_Transmit(&hspi1,test,sizeof(test),1000);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Code for the receiver
Note: The configuration of the DMA and the SPI is mostly done automatically by the CubeMX tool. The other initializations for my project are provided into the main function.
uint8_t aRxBuffer[10];
uint8_t received_buffer[100];
uint16_t cnt = 0;
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_SPI3_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
// Custom configuration of DMA (after calling function MX_SPI3_INIT()
// Configure address of the buffer for receiving data
LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, LL_SPI_DMA_GetRegAddr(SPI3), (uint32_t)aRxBuffer,LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1));
// Configure data length
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
// Enable DMA Transfer complete interrupt
LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
Following is the IRQ handler (the commented code represents the various attempts to make it working!):
void DMA2_Channel1_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel1_IRQn 0 */
// Transfer-complete interrupt management
if(LL_DMA_IsActiveFlag_TC1(DMA2))
{
//LL_DMA_ClearFlag_TC1(DMA2);
LL_DMA_ClearFlag_GI1(DMA2);
/* Call function Transmission complete Callback */
DMA1_TransmitComplete_Callback();
}
else if(LL_DMA_IsActiveFlag_TE1(DMA2))
{
/* Call Error function */
int _error = 0;
}
// Enable SPI_3
//LL_SPI_Disable(SPI3);
// Enable DMA_2,CHANNEL_1
//LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);
//LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
// We Want the SPI3 to receive 8-bit data
// Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
// See pag. 1221 of the TRM
//LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
//LL_SPI_EnableDMAReq_RX(SPI3);
// Enable SPI_3
//LL_SPI_Enable(SPI3);
// Enable DMA_2,CHANNEL_1
LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);
// LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);
/* USER CODE END DMA2_Channel1_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel1_IRQn 1 */
/* USER CODE END DMA2_Channel1_IRQn 1 */
}
Following is the initialization for the SPI and the DMA (auto-generated):
/* SPI1 init function */
void MX_SPI1_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
/**SPI1 GPIO Configuration
PA1 ------> SPI1_SCK
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = SCLK1_to_SpW_Pin|MOSI1_to_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_EnableNSSPulseMgt(SPI1);
}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);
/**SPI3 GPIO Configuration
PA4 ------> SPI3_NSS
PB3 (JTDO-TRACESWO) ------> SPI3_SCK
PB5 ------> SPI3_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SCLK_from_SpW_Pin|MOSI_from_SpW_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI3 DMA Init */
/* SPI3_RX Init */
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_3);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);
LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* SPI3 interrupt Init */
NVIC_SetPriority(SPI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(SPI3_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_SIMPLEX_RX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI3, &SPI_InitStruct);
LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_DisableNSSPulseMgt(SPI3);
}
Thank you.
I recently implemented a similar system, and I hope I can help. I have a few questions, comments, which can possibly solve your problem, but it is hard to do so without being there.
Do you know if it is the SPI or DMA that is fauly? Does an SPI interrupt occur on the slave? This would mean the DMA is faulty, and not the SPI. It is important to know exactly where the system fails.
LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER); is necessary, but should be done during the init
The TCIF flag should be cleared (as you did) during the IRQ.
You should set the SPI to trigger the DMA (I don't see it in your code) using the SPI_CR2_RXDMAEN register. This you should also do during the init if you do not know when you will receive data.
For the same reason I think you should enable the DMA channel during the init, and keep it enabled.
I hope one of these comments help. If not, we will try again.
Edit: Good work. I am glad you got it running by solving most of the issues. With the information you provided I figured out what was the main problem with the buffer.
You set the DMA to receive 10 bytes with:
LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
This sets the DMA internal counter to 10. For every byte that it receives the counter decreases by one, until it reaches zero. This is what enables it to count 10 bytes. In normal mode, if you want to receive another 10 bytes, then you need to send that command again. In circular mode this value will reset automatically to 10, which means that it can receive another 10 bytes.
Therefore, if you are expecting to always receive 10 bytes then the cicular mode should work just fine for you. If not, then you will have to use normal mode, and specify to the MCU how many bytes you expect (a little more complicated).
From the code
stm32l4xx_hal_spi.c: 55
Master Receive mode restriction:
(#) In Master unidirectional receive-only mode (MSTR =1, BIDIMODE=0, RXONLY=1)
or bidirectional receive mode (MSTR=1, BIDIMODE=1, BIDIOE=0), to ensure
that the SPI does not initiate a new transfer the following procedure has
to be respected:
(##) HAL_SPI_DeInit()
(##) HAL_SPI_Init()
So before you call HAL_SPI_Receive_DMA()
call HAL_SPI_DeInit and HAL_SPI_Init and it should work.
I found that if you just call
HAL_DMA_DeInit(HSPI_Handle->hdmatx) ;
HAL_DMA_Init(HSPI_Handle->hdmtx);
Also works and is only 70us vs 106us.

No interrupts being triggered in UART Receive on PIC18F2680

I have been working with this code for days and cannot figure out why my interrupts are not being triggered. I know data is coming through successfully because I used a probe on a logic analyzer, also my baud rate is correct as I can transmit with UART successfully.
At this point I'm lost, I've read the datasheet over and over and can't figure out my problem. I will try to include only the relative code but enough that you can see how things work in my project.
Please let me know if you see issues with this code.
Thank you!
Code snippets from main.c:
// USART RX interrupt priority
IPR1bits.RCIP = 0;
IPR1bits.TXIP = 0;
// configure the hardware USART device
OpenUSART(USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT &
USART_CONT_RX & USART_BRGH_LOW, 14);
Code snippets from interrupts.c
//----------------------------------------------------------------------------
// Low priority interrupt routine
// this parcels out interrupts to individual handlers
#pragma code
#pragma interruptlow InterruptHandlerLow
// This works the same way as the "High" interrupt handler
void InterruptHandlerLow() {
// check to see if we have an interrupt on USART RX
if (PIR1bits.RCIF) {
PIR1bits.RCIF = 0; //clear interrupt flag
uart_recv_int_handler();
}
// check to see if we have an interrupt on USART TX
if (PIR1bits.TXIF && PIE1bits.TXIE) {
// cannot clear TXIF, this is unique to USART TX
// so just call the handler
uart_tx_int_handler();
}
}
UART RX Interrupt Handler snippet:
void uart_recv_int_handler() {
int msgLen;
//if (DataRdyUSART()) {
uc_ptr->buffer[uc_ptr->buflen] = RCREG;
//uc_ptr->buffer[uc_ptr->buflen] = ReadUSART();
uc_ptr->buflen++;
}
}
Did you
- Set trisC6/7 correctly?
- if you have a part with analog inputs multiplexed on those pins, did you disable them?
- Is your BRG value validated for this part and these oscillator settings?
See also
http://www.piclist.com/techref/microchip/rs232.htm
I migrated to dspic, but I used to do the serial receive under interrupt. This I had in the interrupt (serialin1 is a power of two circular buffer, lastserialin1 the pointer into it, and ser1bufinmask is size of buffer-1)
if (PIR1bits.RCIF == 1) /* check if RC interrupt (receive USART) must be serviced
{
while (PIR1bits.RCIF == 1) /* flag becomes zero if buffer/fifo is empty */
{
lastserialin1=(lastserialin1+1)&ser1bufinmask;
serialin1[lastserialin1]=RCREG;
}
}
To initialize the uart I had:
// Configure USART
TXSTA = 0x20; // transmit enable
RCSTA = 0x90; // spen en cren
RCONbits.IPEN = 1; /* Interrupt Priority Enable Bit. Enable priority levels on interrupts */
INTCONbits.GIE = 1; /* Set GIE. Enables all high priority unmasked interrupts */
INTCONbits.GIEL = 1; /* Set GIEL. Enables all low priority unmasked interrupts */
TRISCbits.TRISC6 = 0; // page 237
TRISCbits.TRISC7 = 1; // page 237
Open1USART (
USART_TX_INT_OFF
&
USART_RX_INT_ON &
USART_ASYNCH_MODE &
USART_EIGHT_BIT & // 8-bit transmit/receive
USART_CONT_RX & // Continuous reception
// USART_BRGH_HIGH, 155); // High baud rate, 155 eq 19k2
USART_BRGH_HIGH, brgval); // High baud rate, 25 eq 115k2
IPR1bits.RCIP = 0;
PIR1bits.RCIF = 0;
with brgval calculated using
#define GetInstructionClock() (GetSystemClock()/4)
#define GetPeripheralClock() GetInstructionClock()
// See if we can use the high baud rate setting
#if ((GetPeripheralClock()+2*BAUD_RATE)/BAUD_RATE/4 - 1) <= 255
#define BRGVAL ((GetPeripheralClock()+2*BAUD_RATE)/BAUD_RATE/4 - 1)
#define BRGHVAL (1)
#else // Use the low baud rate setting
#define BRGVAL ((GetPeripheralClock()+8*BAUD_RATE)/BAUD_RATE/16 - 1)
#define BRGHVAL (0)
#endif