Circular buffer update using DMA with SPI in SAM21DA1

The DMA controller rewrittes the first position of the buffer which holds the values of the first transfer with the values of the second transfer. After that it starts to work correctly.

Also the variable that holds the position of the last transmission does not update correctly.

I have configured the DMA to save the incoming data from the SPI MOSI (I am the slave) into a circular buffer of 500 bytes. SPI transmissions come in 50-byte frames so that is the size I have configured the descriptor to handle and thus the size of a burst transmission.

On each transmission complete interrupt I update the descriptor so that the value of the destination address is increased by 50 bytes (taking into account that it is a circular buffer).

I get the expected behaviour except for the second transmission, which rewrittes the first 50 bytes of the buffer corresponding with the first transmission. After that there is no more rewriting and the system behaves as expected.

Also, with the system behaving correctly, the value of the variable I use to keep track of the last position written dmaBufferPosition, which is used for data processsing gives a value that is 50-100 bytes higher than the actual value being written.

Please note that these DMA controllers use reverse access, so that if we want to write in position 0 we provide in the descriptor the size of the transfer (50) and the “last” destination adress (also 50)

#include "dma_transfer.h"
#include "sam.h"
#include "GlobalConfig.h"

#define PORTA 0

volatile uint8_t circular_buffer[DMA_BUFFER_SIZE];
volatile uint16_t dmaBufferPosition = DMA_BURST_SIZE;

volatile DmacDescriptor dmaDescriptorArray[1] __attribute__ ((aligned (16)));
volatile DmacDescriptor dmaDescriptorWritebackArray[1] __attribute__ ((aligned (16)));

void configure_spi_slave() {

    // Configure SERCOM1 Pins for SPI Slave
    // PA16 (MOSI), PA17 (SCK), PA18 (SS), PA19 (MISO)

    // Set PA16, PA17, PA18, PA19 to peripheral function C (SERCOM1)
    //PORT->Group[0].PINCFG[16].reg = PORT_PINCFG_PMUXEN; // PA16 MOSI
    //PORT->Group[0].PMUX[8].bit.PMUXE = PORT_PMUX_PMUXE_C; // SERCOM1 PAD[0]

    PORT->Group[PORTA].PINCFG[16].bit.PMUXEN = 0x1; // Enable Peripheral Multiplexing for SERCOM1 SPI PA16, Arduino PIN11
    PORT->Group[PORTA].PMUX[8].bit.PMUXE = 0x2; // SERCOM1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)


    //PORT->Group[0].PINCFG[17].reg = PORT_PINCFG_PMUXEN; // PA17 SCK
    //PORT->Group[0].PMUX[8].bit.PMUXO = PORT_PMUX_PMUXO_C; // SERCOM1 PAD[1]

    // Set PA17 as input   (SCK)     1    PA17 corresponds to: PORTA, PMUX[8], Odd
    PORT->Group[PORTA].PINCFG[17].bit.PMUXEN = 0x1; // Enable Peripheral Multiplexing for SERCOM1 SPI PA17, Arduino PIN13
    PORT->Group[PORTA].PMUX[8].bit.PMUXO = 0x2; // SERCOM1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)

    //PORT->Group[0].PINCFG[18].reg = PORT_PINCFG_PMUXEN; // PA18 SS
    //PORT->Group[0].PMUX[9].bit.PMUXE = PORT_PMUX_PMUXE_C; // SERCOM1 PAD[2]

    // Set PA18 as input   (SS)      2    PA18 corresponds to: PORTA, PMUX[9], Even
    PORT->Group[PORTA].PINCFG[18].bit.PMUXEN = 0x1; // Enable Peripheral Multiplexing for SERCOM1 SPI PA18, Arduino PIN10
    PORT->Group[PORTA].PMUX[9].bit.PMUXE = 0x2; // SERCOM1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)

    //PORT->Group[0].PINCFG[19].reg = PORT_PINCFG_PMUXEN; // PA19 MISO
    //PORT->Group[0].PMUX[9].bit.PMUXO = PORT_PMUX_PMUXO_C; // SERCOM1 PAD[3]

    // Set PA19 as output  (MISO)    3    PA19 corresponds to: PORTA, PMUX[9], Odd
    PORT->Group[PORTA].PINCFG[19].bit.PMUXEN = 0x1; // Enable Peripheral Multiplexing for SERCOM1 SPI PA19, Arduino PIN12
    PORT->Group[PORTA].PMUX[9].bit.PMUXO = 0x2; // SERCOM1 is selected for peripheral use of this pad (0x2 selects peripheral function C: SERCOM)


    // Disable SPI 1
    SERCOM1->SPI.CTRLA.bit.ENABLE = 0; // page 481
    while (SERCOM1->SPI.SYNCBUSY.bit.ENABLE); // Wait until bit is enabled.
    // Enable SERCOM1 and Port Bus Clocks

    // Reset SPI 1
    SERCOM1->SPI.CTRLA.bit.SWRST = 1; // page 481
    while (SERCOM1->SPI.CTRLA.bit.SWRST || SERCOM1->SPI.SYNCBUSY.bit.SWRST); // Wait until software reset is complete.

    //PM->APBCMASK.reg |= PM_APBCMASK_SERCOM1;
    GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM1_GCLK_ID_CORE) |
                        GCLK_CLKCTRL_GEN_GCLK0 |
                        GCLK_CLKCTRL_CLKEN;

    
    while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); // Wait for synchronisation

     // Set up SPI control A register
  SERCOM1->SPI.CTRLA.bit.DORD = 0; // MSB is transferred first. // page 492
  SERCOM1->SPI.CTRLA.bit.CPOL = 0; // SCK is low when idle. The leading edge of a clock cycle is a rising edge, while the trailing edge is a falling edge. // page 492
  SERCOM1->SPI.CTRLA.bit.CPHA = 0; // Data is sampled on a leading SCK edge and changed on a trailing SCK edge. // page 492
  SERCOM1->SPI.CTRLA.bit.FORM = 0x0; // SPI frame // page 493
  SERCOM1->SPI.CTRLA.bit.DIPO = 0x0; // DATA PAD0 is used as slave input: MOSI // (slave mode) page 493
  SERCOM1->SPI.CTRLA.bit.DOPO = 0x2; // DATA PAD2 is used as slave output: MISO // (slave mode) page 493
  SERCOM1->SPI.CTRLA.bit.MODE = 0x2; // SPI slave operation. // page 494
  SERCOM1->SPI.CTRLA.bit.IBON = 0x1; // Immediate Buffer Overflow Notification. STATUS.BUFOVF is asserted immediately upon buffer overflow. // page 494
  SERCOM1->SPI.CTRLA.bit.RUNSTDBY = 1; // Wake on Receive Complete interrupt. // page 494

   // Set up SPI control B register
  //SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; // Enable Receiver // page 496
  SERCOM1->SPI.CTRLB.bit.SSDE = 0x1; // Enable Slave Select Low Detect // page 497
  SERCOM1->SPI.CTRLB.bit.CHSIZE = 0; // Character Size 8 bits // page 497
  //SERCOM1->SPI.CTRLB.bit.PLOADEN = 0x1; // Enable Slave Data Preload // page 497
  //while (SERCOM1->SPI.SYNCBUSY.bit.CTRLB); // Wait until receiver is enabled


  /*// Set up SPI interrupts
  SERCOM1->SPI.INTENSET.bit.SSL = 0x1; // Enable Slave Select Low interrupt. // page 501
  SERCOM1->SPI.INTENSET.bit.RXC = 0x1; // Enable Receive Complete interrupt. // page 501
  SERCOM1->SPI.INTENSET.bit.TXC = 0x1; // Enable Transmit Complete interrupt. // page 501
  SERCOM1->SPI.INTENSET.bit.ERROR = 0x1; // Enable Error interrupt. // page 501
  SERCOM1->SPI.INTENSET.bit.DRE = 0x1; // Enable Data Register Empty interrupt. // page 501*/
    
    // Enable SPI
  SERCOM1->SPI.CTRLA.bit.ENABLE = 1; // page 481
  while (SERCOM1->SPI.SYNCBUSY.bit.ENABLE); // Wait until bit is enabled.
  SERCOM1->SPI.CTRLB.bit.RXEN = 0x1; // Enable Receiver // page 496. This is done here rather than in section "Set up SPI control B register" due to an errate issue.
  while (SERCOM1->SPI.SYNCBUSY.bit.CTRLB); // Wait until receiver is enabled.
}


// Set up the DMA dmaDescriptorArray[]
void configure_dma_descriptor() {
    dmaDescriptorArray[0].BTCTRL.bit.VALID = 1;             // Descriptor is valid
    dmaDescriptorArray[0].BTCTRL.bit.EVOSEL = 0;            // No event output
    dmaDescriptorArray[0].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val; // Interrupt after block transfer
    dmaDescriptorArray[0].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val; // 8-bit beats
    dmaDescriptorArray[0].BTCTRL.bit.SRCINC = 0;            // Source does not increment (fixed)
    dmaDescriptorArray[0].BTCTRL.bit.DSTINC = 1;            // Destination increments (circular buffer)
    dmaDescriptorArray[0].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val; // Step size applied to destination
    dmaDescriptorArray[0].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val; // Step size of 1

    dmaDescriptorArray[0].BTCNT.reg = DMA_BURST_SIZE;          
    dmaDescriptorArray[0].SRCADDR.reg = (uint32_t)&SERCOM1->SPI.DATA.reg; // Source address (SPI DATA register)
    dmaDescriptorArray[0].DSTADDR.reg = (uint32_t)circular_buffer + dmaBufferPosition; // Destination address + buffer size (for reverse access)
    dmaDescriptorArray[0].DESCADDR.reg = (uint32_t)&dmaDescriptorArray[0];  // Next dmaDescriptorArray[] address (self-reload)
}

void configure_dma() {
    // Enable the DMAC controller
    PM->AHBMASK.reg |= PM_AHBMASK_DMAC;
    PM->APBBMASK.reg |= PM_APBBMASK_DMAC;

    // Reset the DMA controller
    DMAC->CTRL.reg = DMAC_CTRL_SWRST;

    // Configure the DMA channel (e.g., channel 0)
    DMAC->CHID.reg = DMAC_CHID_ID(0); // Select channel 0
    DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; // Reset the channel

    DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL; //Enable transfer complete interrupt

    // Assign the dmaDescriptorArray[] to the DMA channel
    DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0) |   // Priority level 0
                        DMAC_CHCTRLB_TRIGSRC(SERCOM1_DMAC_ID_RX) | // Trigger on SERCOM1 RX
                        DMAC_CHCTRLB_TRIGACT_BEAT; // Trigger action: beat

    DMAC->CHCTRLA.reg = DMAC_CHCTRLA_ENABLE; // Enable the DMA channel
    DMAC->BASEADDR.reg = (uint32_t)&dmaDescriptorArray[0]; // Set the base dmaDescriptorArray[] address
    DMAC->WRBADDR.reg = (uint32_t)&dmaDescriptorWritebackArray[0];  // Write-back memory section address

    // Enable the DMA controller
    DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); // Enable the DMA controller
}

void dma_init() {
    configure_dma_descriptor();
    configure_dma();
    NVIC_EnableIRQ(DMAC_IRQn);
    NVIC_SetPriority(DMAC_IRQn, 1);
}

// DMA interrupt handler for handling 50-byte chunk completion
void DMAC_Handler(void) {
    // Check if Transfer Complete interrupt occurred for channel 0
    if (DMAC->CHINTFLAG.bit.TCMPL) {
        // Clear the interrupt flag
        DMAC->CHID.reg = DMAC_CHID_ID(0);  // Select Channel 0
        DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL;  // Clear Transfer Complete interrupt flag

        // Reset DMA descriptor for the next 50-byte block
        dmaDescriptorArray[0].BTCNT.reg = DMA_BURST_SIZE;  // Set the next block transfer size to 50 bytes
        dmaBufferPosition = (dmaBufferPosition % DMA_BUFFER_SIZE)  + DMA_BURST_SIZE; //Move position by 50 bytes
        dmaDescriptorArray[0].DSTADDR.reg = (uint32_t)(circular_buffer) + dmaBufferPosition;// The destination address wraps within the 500-byte buffer
    }
}```


Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật