Hi ![]()
I'm working on some kind of library for the Arduino Zero, and I need global variables. When I put all my class code in the header file, the code compile fine, but when I want to move the definition in a cpp file, I have lot's of error of multiple definition of global variables. I'm very lost with this issue and any help is really welcome.
My code is on GitHub here.
For those who prefer to read the code here :
Arduino "sketch" :
#include <SPI.h>
#include "project_DMA_SPI.h"
DMA_SPI DMASPI(SERCOM2, 0, 1, SERCOM2_DMAC_ID_TX, SERCOM2_DMAC_ID_RX);
uint8_t buffer[256] = {0};
void setup()
{
 while (!SerialUSB);
 SPI.begin();
 SPI.beginTransaction(SPISettings(12000000, MSBFIRST, SPI_MODE0));
 //DMA setup for SPI transaction, arduino SPI class init must have been called already
 DMASPI.init();
 DMASPI.registerTXCallbacks(callback);
 for (int i = 0; i < 128; i++)
 {
  for (int i = 0; i < 256; i++)
   buffer[i] = random(0xFF);
  while (!DMASPI.transferDone());
  DMASPI.write(buffer, 256);
 }
 //Allow standard SPI use again
 DMASPI.disable();
}
void loop()
{
}
static bool led_state = true;
void callback()
{
 //blink led during transfer to prove callbacks are working
 digitalWrite(LED_BUILTIN, led_state);
 led_state = !led_state;
 delayMicroseconds(50000);
}
DMA Helper global stuff :
#ifndef _DMA_HELPER_
#define _DMA_HELPER_
#include "Arduino.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
 uint16_t btctrl;  //Block Transfer Control
 uint16_t btcnt;  //Block Transfer Count
 uint32_t srcaddr; //Source Address
 uint32_t dstaddr; //Destination Address
 uint32_t descaddr; //Next Descriptor Address
} dmacdescriptor;
//Write Back Descriptor (x12)
volatile dmacdescriptor descriptor_wrb[12] __attribute__((aligned(16)));
//DMA Channels Descriptor (x12)
dmacdescriptor descriptor_section[12] __attribute__((aligned(16)));
//DMA Channel Interrupt Result (x12)
volatile uint32_t dmaChannelDone[12];
//DMA Channel Callback (x12)
void(*dma_callback[12])(void) = { NULL };
//DMAC already initialized
uint32_t dmac_initialized = 0;
//DMAC_Handler : Interrupt Service Routine :
//override DMAC_Handler (attribute is weak in Reset_Handler.c)
void DMAC_Handler() {
 uint8_t active_channel;
 //Disable global interrupts
 __disable_irq();
 //Get the triggering channel number
 active_channel = DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk;
 DMAC->CHID.reg = DMAC_CHID_ID(active_channel);
 //Get Channel Result Flags
 //DMAC_CHINTENCLR_TERR = 0x01  =>Transfer Error
 //DMAC_CHINTENCLR_TCMPL = 0x02  =>Transfer Complete
 //DMAC_CHINTENCLR_SUSP = 0x04  =>Transfer Suspended
 dmaChannelDone[active_channel] = DMAC->CHINTFLAG.reg;
 //Execute calback if registered
 if (dma_callback[active_channel] != NULL)
  dma_callback[active_channel]();
 //Clear interrupt flags
 DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; // clear
 DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR;
 DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP;
 //Enable global interrupts
 __enable_irq();
}
void DMAC_Init()
{
 if (!dmac_initialized)
 {
  PM->AHBMASK.reg |= PM_AHBMASK_DMAC;
  PM->APBBMASK.reg |= PM_APBBMASK_DMAC;
  NVIC_EnableIRQ(DMAC_IRQn);
  DMAC->BASEADDR.reg = (uint32_t)descriptor_section;
  DMAC->WRBADDR.reg = (uint32_t)descriptor_wrb;
  DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);
 }
}
void DMAC_End()
{
 if (dmac_initialized)
 {
  DMAC->CTRL.bit.DMAENABLE = 0;    // disable DMA controller
  DMAC->CTRL.bit.SWRST = 1;     // reset all DMA registers
  while (DMAC->CTRL.bit.SWRST) {};  // wait for reset to complete
 }
}
#ifdef __cplusplus
}
#endif
#endif //_DMA_HELPER_
My DMA_SPI class using the global stuff :
#ifndef _DMA_SPI_
#define _DMA_SPI_
#include "Arduino.h"
#include "project_DMA_Helper.h"
class DMA_SPI
{
 public:
  DMA_SPI(Sercom *s, uint32_t txc, uint32_t rxc, uint8_t txtrig, uint8_t rxtrig)
  {
   sercom = s;
   chnltx = txc;
   chnlrx = rxc;
   txTrigger = txtrig;
   rxTrigger = rxtrig;
  }
  Sercom *sercom;
  uint32_t chnltx, chnlrx; // DMA channels
  uint8_t txTrigger, rxTrigger;
  dmacdescriptor descriptor __attribute__((aligned(16)));
  void registerCallbacks(void(*tx)(), void(*rx)())
  {
   dma_callback[chnltx] = tx;
   dma_callback[chnlrx] = rx;
  }
  void registerRXCallbacks(void(*rx)())
  {
   dma_callback[chnlrx] = rx;
  }
  void registerTXCallbacks(void(*tx)())
  {
   dma_callback[chnltx] = tx;
  }
  void init()
  {
   DMAC_Init();
   txsrc[0] = 0xff;
   xfr(txsrc, rxsink, 1);
   while (!dmaChannelDone[chnltx] || !dmaChannelDone[chnlrx]);
   disable();
  }
  void transfer(void *txdata, void *rxdata, size_t n)
  {
   xtype = DoTXRX;
   xfr(txdata, rxdata, n);
  }
  void read(void *data, size_t n)
  {
   xtype = DoRX;
   xfr(txsrc, data, n);
  }
  void write(void *data, size_t n)
  {
   xtype = DoTX;
   xfr(data, rxsink, n);
  }
  bool transferDone()
  {
   return (dmaChannelDone[chnltx] || dmaChannelDone[chnlrx]);
  }
  void disable()
  {
   __disable_irq();
   DMAC->CHID.reg = DMAC_CHID_ID(chnltx); //disable DMA to allow lib SPI
   DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
   DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
   DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
   __enable_irq();
  }
 private:
  enum XfrType { DoTX, DoRX, DoTXRX };
  XfrType xtype;
  uint8_t rxsink[1], txsrc[1];
  void(*callbackTX)(void);
  void(*callbackRX)(void);
  void xfr(void *txdata, void *rxdata, size_t n)
  {
   __disable_irq();
   uint32_t temp_CHCTRLB_reg;
   // set up transmit channel
   DMAC->CHID.reg = DMAC_CHID_ID(chnltx);
   DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
   DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
   DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << chnltx));
   temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) |
            DMAC_CHCTRLB_TRIGSRC(txTrigger) | DMAC_CHCTRLB_TRIGACT_BEAT;
   DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
   DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK; // enable all 3 interrupts
   dmaChannelDone[chnltx] = 0;
   descriptor.descaddr = 0;
   descriptor.dstaddr = (uint32_t)&sercom->SPI.DATA.reg;
   descriptor.btcnt = n;
   descriptor.srcaddr = (uint32_t)txdata;
   descriptor.btctrl = DMAC_BTCTRL_VALID;
   if (xtype != DoRX) {
    descriptor.srcaddr += n;
    descriptor.btctrl |= DMAC_BTCTRL_SRCINC;
   }
   //copy all the descriptor data at once
   memcpy(&descriptor_section[chnltx], &descriptor, sizeof(dmacdescriptor));
   // rx channel
   DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
   DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE;
   DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST;
   DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << chnlrx));
   temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) |
            DMAC_CHCTRLB_TRIGSRC(rxTrigger) | DMAC_CHCTRLB_TRIGACT_BEAT;
   DMAC->CHCTRLB.reg = temp_CHCTRLB_reg;
   DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK; // enable all 3 interrupts
   dmaChannelDone[chnlrx] = 0;
   descriptor.descaddr = 0;
   descriptor.srcaddr = (uint32_t)&sercom->SPI.DATA.reg;
   descriptor.btcnt = n;
   descriptor.dstaddr = (uint32_t)rxdata;
   descriptor.btctrl = DMAC_BTCTRL_VALID;
   if (xtype != DoTX) {
    descriptor.dstaddr += n;
    descriptor.btctrl |= DMAC_BTCTRL_DSTINC;
   }
   memcpy(&descriptor_section[chnlrx], &descriptor, sizeof(dmacdescriptor));
   // start both channels
   DMAC->CHID.reg = DMAC_CHID_ID(chnltx);
   DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
   DMAC->CHID.reg = DMAC_CHID_ID(chnlrx);
   DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;
   __enable_irq();
  }
};
#endif //_DMA_SPI_