2
\$\begingroup\$

I am working on my first PIC32 project on a custom board. I am utilizing a PIC32MZ2048EFH064 microcontroller which I am programming in MPLAB X IDE v5.40 (compiler: xc32 v2.41).

My goal is to receive data from an ADC (AD7768) via SPI (microcontroller as SPI slave). This worked so far.

But now I don't want to receive each sample separately in the SPI4 RX interrupt handler, but use the DMA controller (channel 0). I have configured the DMA controller so that a transfer is triggered when SPI4 has received data.

The SPI4 RX interrupt is still triggered periodically (LED2 blinking). But I never get an interrupt from the DMA controller (LED3 always off). Actually I want to get an interrupt when a block has been transferred (CHBCIE). But I have tried all other possible interrupts. None is triggered.

Does anyone have experience with PIC32 and DMA and has an idea what I am doing wrong? Thanks a lot!

main.c:

#include "main.h"

/*** DEVCFG0 ***/
#pragma config DEBUG =      ON
#pragma config JTAGEN =     OFF
#pragma config ICESEL =     ICS_PGx2
#pragma config TRCEN =      OFF
#pragma config BOOTISA =    MIPS32
#pragma config FECCCON =    OFF_UNLOCKED
#pragma config FSLEEP =     OFF
#pragma config DBGPER =     PG_ALL
#pragma config SMCLR =      MCLR_NORM
#pragma config SOSCGAIN =   GAIN_LEVEL_3
#pragma config SOSCBOOST =  ON
#pragma config POSCGAIN =   GAIN_LEVEL_3
#pragma config POSCBOOST =  ON
#pragma config EJTAGBEN =   NORMAL
#pragma config CP =         OFF

/*** DEVCFG1 ***/
#pragma config FNOSC =      SPLL
#pragma config DMTINTV =    WIN_127_128
#pragma config FSOSCEN =    OFF
#pragma config IESO =       OFF
#pragma config POSCMOD =    EC
#pragma config OSCIOFNC =   OFF
#pragma config FCKSM =      CSECME
#pragma config WDTPS =      PS1048576
#pragma config WDTSPGM =    STOP
#pragma config FWDTEN =     OFF
#pragma config WINDIS =     NORMAL
#pragma config FWDTWINSZ =  WINSZ_25
#pragma config DMTCNT =     DMT31
#pragma config FDMTEN =     OFF

/*** DEVCFG2 ***/
#pragma config FPLLIDIV =   DIV_3
#pragma config FPLLRNG =    RANGE_5_10_MHZ
#pragma config FPLLICLK =   PLL_POSC
#pragma config FPLLMULT =   MUL_50
#pragma config FPLLODIV =   DIV_2
#pragma config UPLLFSEL =   FREQ_24MHZ

/*** DEVCFG3 ***/
#pragma config USERID =     0xffff
#pragma config FMIIEN =     ON
#pragma config FETHIO =     ON
#pragma config PGL1WAY =    ON
#pragma config PMDL1WAY =   ON
#pragma config IOL1WAY =    ON
#pragma config FUSBIDIO =   ON

/*** BF1SEQ0 ***/
#pragma config TSEQ =       0xffff
#pragma config CSEQ =       0x0

void __ISR(_SPI4_RX_VECTOR) _SPI4_RX_InterruptHandler(void)
{
    SPI4_RX_InterruptHandler();
}

void __ISR(_DMA0_VECTOR) _DMA_Channel_0_InterruptHandler(void)
{
    DMA_Channel_0_InterruptHandler();
}

int main(void)
{
    /* Disable global interrupts */
    __builtin_disable_interrupts();
    
    /* Initialize GPIO */
    GPIO_Init();
    
    /* Reset AD7768 */
    GPIO_nRESET_Clear();     
    GPIO_nRESET_Set();
    
    /* Initialize SPI interfaces */
    SPI4_Init();
    
    /* Initialize DMA controllers */
    DMA_Init_Channel_0();
    
    /* Configure interrupt priorities */
    INTCONSET = _INTCON_MVEC_MASK;    
    IPC41SET = 0x4 | 0x0;  /* SPI4_RX: Priority 1 / Subpriority 0 */
    IPC33SET = 0x4 | 0x0;  /* DMA0: Priority 1 / Subpriority 0 */
    
    /* Enable global interrupts */
    __builtin_enable_interrupts();
    
    /* Endless loop */    
    while(1)
    {
        GPIO_LED1_Toggle();
    }

    /* Execution should not come here during normal operation */
    return (EXIT_FAILURE);
}

main.h:

#ifndef _MAIN_H    /* Guard against multiple inclusion */
#define _MAIN_H

#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <xc.h>
#include <sys/attribs.h>
#include "gpio.h"
#include "spi4.h"
#include "dma.h"

/* 
 * Translates a kernel virtual address in KSEG0 or KSEG1 to a real
 * physical address and back.
 */
typedef unsigned long _paddr_t; /* a physical address */
typedef unsigned long _vaddr_t; /* a virtual address */
#define KVA_TO_PA(v)     ((_paddr_t)(v) & 0x1fffffff)
#define PA_TO_KVA0(pa)    ((void *) ((pa) | 0x80000000))
#define PA_TO_KVA1(pa)    ((void *) ((pa) | 0xa0000000))

int main(void);

#endif /* _MAIN_H */

spi4.c:

#include "main.h"

uint32_t __attribute__((coherent)) spi4RxBuffer[SPI4_RX_BUFFER_SIZE] = { 0 };

void SPI4_Init(void)
{
    /* Disable SPI4 interrupts */
    IEC5bits.SPI4EIE = 0;       // Fault interrupt
    IEC5bits.SPI4RXIE = 0;      // Rx interrupt
    IEC5bits.SPI4TXIE = 0;      // Tx interrupt

    /* Stop and reset SPI */
    SPI4CON = 0;

    /* Clear receive buffer */
    uint32_t rData = SPI4BUF;
    rData = rData;

    /* Clear interrupt flags */
    IFS5bits.SPI4EIF = 0;       // Fault interrupt
    IFS5bits.SPI4RXIF = 0;      // Rx interrupt
    IFS5bits.SPI4TXIF = 0;      // Tx interrupt

    /* Clear the overflow */
    SPI4STATbits.SPIROV = 0;

    /* Configure control register */
    SPI4CONbits.FRMEN = 1;      // Framed SPI support
    SPI4CONbits.FRMSYNC = 1;    // Frame sync pulse input (slave mode))
    SPI4CONbits.FRMPOL = 1;     // Frame pulse is active-high
    SPI4CONbits.DISSDO = 1;     // SDO4 pin is not used by the module
    SPI4CONbits.MODE32 = 1;     // 32-bit mode
    SPI4CONbits.SSEN = 1;       // SS4 pin used for slave mode
    SPI4CONbits.SRXISEL = 3;    // SPI4RXIF is set when the buffer is full
    
    /* Enable receive done interrupt */
    IEC5bits.SPI4RXIE = 1;

    /* Enable SPI4 */
    SPI4CONbits.ON = 1;
}

void SPI4_RX_InterruptHandler(void)
{
    GPIO_LED2_Set();

    /* Clear Rx interrupt flag */
    IFS5CLR = _IFS5_SPI4RXIF_MASK;
    
    GPIO_LED2_Clear();
}

spi4.h:

#ifndef _SPI4_H    /* Guard against multiple inclusion */
#define _SPI4_H

#include "main.h"

#define SPI4_RX_BUFFER_SIZE                     8
    
uint32_t __attribute__((coherent)) spi4RxBuffer[SPI4_RX_BUFFER_SIZE];

void SPI4_Init(void);
void SPI4_RX_InterruptHandler(void);

#endif /* _SPI4_H */

dma.c:

#include "main.h"

void DMA_Init_Channel_0(void)
{
    /* Disable DMA channel 0 interrupt */
    IEC4bits.DMA0IE = 0;

    /* Clear interrupt flag */
    IFS4bits.DMA0IF = 0;
    
    /* Enable DMA controller */
    DMACONbits.ON = 1;
    
    /* Configure DMA channel 0 source address */
    DCH0SSA = KVA_TO_PA(&SPI4BUF);
    
    /* Configure DMA channel 0 desination address */
    DCH0DSA = KVA_TO_PA(&spi4RxBuffer);
    
    /* Number of source bytes */
    DCH0SSIZ = 4;
    
    /* Number of destination bytes */
    DCH0DSIZ = SPI4_RX_BUFFER_SIZE*4;
    
    /* Number of bytes transferred per event */
    DCH0CSIZ = 4;
    
    /* Channel is continously enabled */
    DCH0CONbits.CHAEN = 1;
    
    /* Start channel cell transfer on SPI4 RX complete interrupt */
    DCH0ECONbits.CHSIRQ = _SPI4_RX_VECTOR;
    DCH0ECONbits.SIRQEN = 1;
    
    /* Enable channel block transfer complete interrupt */
    DCH0INTbits.CHBCIE = 1;
    
    /* Enable DMA channel 0 interrupt */
    IEC4bits.DMA0IE = 1;
    
    /* Enable DMA channel 0 */
    DCH0CONbits.CHEN = 1;    
}

void DMA_Channel_0_InterruptHandler(void)
{
    GPIO_LED3_Set();
    
    /* Clear interrupt flags */
    DCH0INTCLR = 0xFF;
    IFS4CLR = _IFS4_DMA0IF_MASK;
    
    GPIO_LED3_Clear();
}

dma.h:

#ifndef _DMA_H    /* Guard against multiple inclusion */
#define _DMA_H

#include "main.h"

void DMA_Init_Channel_0(void);
void DMA_Channel_0_InterruptHandler(void);

#endif /* _DMA_H */
\$\endgroup\$
1
  • \$\begingroup\$ I just recognized that I had to add __attribute__((coherent)) to the target buffer (spi4RxBuffer[SPI4_RX_BUFFER_SIZE]). The DMA is now writing to the buffer and the CHBCIF flag in the DCH0INT register is set. However, the ISR is still never entered. \$\endgroup\$
    – Charly
    Aug 25 '20 at 18:04
0
\$\begingroup\$

I found the solution. Simply adjust the priorities, e.g. the following is working:

void __ISR(_DMA0_VECTOR, IPL3SRS) _DMA_Channel_0_InterruptHandler(void)
{
    DMA_Channel_0_InterruptHandler();
}

...

IPC33bits.DMA0IP = 3; // set DMA channel 0 priority to 3
IPC33bits.DMA0IS = 1; // set DMA channel 0 sub-priority to 1
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.