[Solved] Using DMA/Fast Communication on portentaH7

Has anyone had fun with the DMA function on PortentaH7. I am using a code where my ADC is converting based on a timer TRGO and the data is supposed to be sent to the buffer I defined in memory yet I do not read the data at the specified address even though the interrupt for the DMA transfer complete is triggered. Here is my code :

/*************Function prototype************/
void ADC_Init();
void ADC_Start();
void DMA_Config();
void VREFBUF_Init();
void AC_TIMER1_Init();
/*************Variables********************/
int updateEV = 0;
int DMABuffered = 0;
int Eosmp = 0;
int Eocie = 0;
int Eos = 0;
int DMAError=0;
int valADC=0;
bool TCpair=true;
/*************BUFFER***********************/
volatile uint16_t BufferADC1[10]; 
volatile uint16_t BufferADC2[10]; 
void setup() {
  // put your setup code here, to run once:
  //#ifdef CORE_CM7
  //bootM4();
  VREFBUF_Init();
  DMA_Config();
  AC_TIMER1_Init();
  ADC_Init();
  ADC_Start();
  //#endif
}
void loop() {
  
  //#ifdef CORE_CM7
  // put your main code here, to run repeatedly:
  Serial.print("TCpair: ");
  Serial.print(TCpair);
  Serial.print(" Eosmp : ");
  Serial.print(Eosmp);
  Serial.print(" valADC : ");
  Serial.print(valADC);
  //  Serial.print(" updateEV : ");
  //  Serial.print(updateEV);
  Serial.print(" DMA_Buffered : ");
  Serial.print(DMABuffered);
  Serial.print(" DMA_Error : ");
  Serial.println(DMAError);
  //#endif
  //#ifdef CORE_CM4
  if(TCpair){
    valADC=BufferADC1[1];
    digitalWrite(LEDG,LOW);
  }else{
    valADC=BufferADC2[1];
    digitalWrite(LEDG,HIGH);
  }
//  //#endif
}

void VREFBUF_Init(void) {
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_VREFEN_Msk);
  delay(1000);
  SET_BIT(VREFBUF->CSR, VREFBUF_CSR_ENVR_Msk);
  CLEAR_BIT(VREFBUF->CSR, VREFBUF_CSR_HIZ_Msk);
  while (VREFBUF_CSR_VRR & VREFBUF_CSR_VRR_Msk != 1) {
    //    dataFromRegister=READ_REG(VREFBUF->CSR);
    //    Serial.println(dataFromRegister,BIN);
  }
}

void DMA_Config(void) {
  /************DMA CLock Enable******************/
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN_Msk);
  delay(1000);
  /***************P2M****************************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1);
  /******************Disable FIFO*************************/
  //LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
  //LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE);
  /******************Memory size**************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_0);
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_1);
  /******************MEM Increment**********************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MINC_Msk);
  /******************MEM Address to buffer**************/
  DMA1_Stream1->M0AR =(uint32_t)&BufferADC1;//0x30000000;
  DMA1_Stream1->M1AR =(uint32_t)&BufferADC2;//0x30000000;
  /*******************Number of data transfer***********/
  SET_BIT(DMA1_Stream1->NDTR,DMA_SxNDT_3 | DMA_SxNDT_1);
  /***********************DMA Mode**********************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_CIRC_Msk);//circular
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_DBM_Msk);//Double Buffer Mode
  /*******************Flow controller*******************/
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PFCTRL_Msk);//ADC in control
  /******************Periph size************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_0);
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_1);
  /******************Peripheral no Increment*************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PINCOS_Msk);
  /******************Periph request**********************/
  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_DMAREQ_ID_1 | DMAMUX_CxCR_DMAREQ_ID_3);//adc2_dma
  //  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_SYNC_ID_0 | DMAMUX_CxCR_SYNC_ID_1 | DMAMUX_CxCR_SYNC_ID_2);//TIM12_TRGO
  //  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_SPOL_0);//rising edge
  //  DMAMUX1_Channel1->CCR = 0; //Number of requests after synchro (add 1 to the reg value)
  //  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_EGE_Msk);//Event Generation end of synchro req
  //  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_SE_Msk);//Synchro Enable
  /******************Periph address***********************/
  DMA1_Stream1->PAR = (uint32_t)&ADC2->DR;
  /******************TC IT********************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_TCIE_Msk | DMA_SxCR_TEIE_Msk); //TC IT
  DMA1->LIFCR = DMA_LIFCR_CTCIF1;//Clear IT in LISR Register
  NVIC_SetVector(DMA1_Stream1_IRQn, (uint32_t)&DMA1_Stream1_IRQHandler);
  NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /*******************Enable DMA****************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_EN_Msk);
}
void DMA1_Stream1_IRQHandler(void) {
  if (DMA1->LISR & DMA_LISR_TCIF1 ) {
    DMABuffered = DMABuffered + 1;
    TCpair=!TCpair;
    //valADC=BufferADC[0];
    DMA1->LIFCR = DMA_LIFCR_CTCIF1;
  }
  if(DMA1->LISR & DMA_LISR_TEIF1 ){
    DMAError = DMAError +1;
    DMA1->LIFCR = DMA_LIFCR_CTEIF1;
  }
}
void ADC_Init(void) {
  /*******************Horloges**************************/
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_SYSCFGEN_Msk); //SYSCFG clock
  delay(1000);
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_RTCAPBEN_Msk); //RTCAPB clock
  delay(1000);
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_ADC12EN_Msk); //ADC12 clocks
  delay(1000);
  SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN_Msk); //GPIOA clock
  delay(1000);
  /********************Port config************************/
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_0);
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_1);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_0);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_1);
  SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_PA0SO_Msk);
  delay(1000);//PA0_C in analog mode
  /********************ADC voltage regulator***************/
  CLEAR_BIT(ADC2->CR, ADC_CR_DEEPPWD_Msk); //END DEEPPWD
  SET_BIT(ADC2->CR, ADC_CR_ADVREGEN_Msk); //ENABLE ADC VOLTAGE REG
  delay(1000);//WAIT VOLTAGE REG
  /********************ADC calibration*********************/
  CLEAR_BIT(ADC2->CR, ADC_CR_ADCALDIF_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCALLIN_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCAL_Msk);
  while (ADC_CR_ADCAL & ADC_CR_ADCAL_Msk != 0) {}
  /******************ADC clock*****************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1);
  /*******************ADC Prescaler************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_PRESC_0 | ADC_CCR_PRESC_1 );
  /*******************Input Mode***************************/
  CLEAR_BIT(ADC2->DIFSEL, ADC_DIFSEL_DIFSEL_0); //Single Ended
  /*******************ADC Enable***************************/
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADEN_Msk);
  while (ADC_ISR_ADRDY & ADC_ISR_ADRDY_Msk != 1) {}
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  /********************ADC RES*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_RES_2 | ADC_CFGR_RES_1);
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_RES_0);
  /********************ADC Data Management*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1);//DMA Circular mode
  /********************OVRMODE*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_OVRMOD_Msk); //Erase old data
  /********************CONT/Single/Discont*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DISCEN_Msk); // discontinuous mode
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_CONT_Msk); // | ADC_CFGR_DISCEN_Msk
  /********************Trigger Detection*******************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_3);//Trig rising edge TRGO2
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_1 | ADC_CFGR_EXTSEL_0 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_4);
  /********************INput Preselection******************/
  SET_BIT(ADC2->PCSEL, ADC_PCSEL_PCSEL_0);//Chan 0
  /********************Sample Time reg*********************/
  SET_BIT(ADC2->SMPR1, ADC_SMPR1_SMP0_0); //2.5 CLCK Cycles
  /********************ADC IT******************************/
  SET_BIT(ADC2->IER, ADC_IER_EOCIE_Msk | ADC_IER_EOSMPIE_Msk  );//| ADC_IER_EOSIE_Msk | ADC_IER_OVRIE_Msk
  NVIC_EnableIRQ(ADC_IRQn);
  NVIC_SetVector(ADC_IRQn, (uint32_t)&ADC_IRQHandler);
}
void ADC_IRQHandler(void) {
  if (ADC2->ISR & 0x02) {
    Eosmp += 1;
    SET_BIT(ADC2->ISR, ADC_IER_EOSMPIE_Msk);
  }
  if (ADC2->ISR & 0x04) {
    Eocie += 1;
    SET_BIT(ADC2->ISR, ADC_IER_EOCIE_Msk);
  } 
//  if (ADC2->ISR & 0x08) {
//    Eos += 1;
//    SET_BIT(ADC2->ISR, ADC_IER_EOSIE_Msk);
//  }
}
void ADC_Start(void) {
  SET_BIT(ADC2->CR, ADC_CR_ADSTART_Msk); //Start
}
void AC_TIMER1_Init(void) {
  //------------------------------Horloge Timer1----------------------------------------//
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM1EN_Msk);
  //------------------------------Select TRGO source-----------------------------------//
  TIM1->PSC = 8399; //PSC=1
  //----------------------------------------------------------TIM2 autoreload register--------------------------------------------//
  TIM1->ARR = 10000;
  //---------selectupdatevent-----------------//
  SET_BIT(TIM1->CR2, TIM_CR2_MMS2_1);
  //-----------IT-----------------------//
  //  SET_BIT(TIM1->DIER,TIM_DIER_UIE_Msk);
  //  NVIC_SetPriority(TIM1_UP_IRQn,0);
  //  NVIC_SetVector(TIM1_UP_IRQn, (uint32_t)&TIM1_UP_IRQHandler);
  //  NVIC_EnableIRQ(TIM1_UP_IRQn);
  SET_BIT(TIM1->CR1, TIM_CR1_CEN_Msk);//CEN=1
}

//void TIM1_UP_IRQHandler(){
//  if (TIM1->DIER & 0x01) {//a check les cd
//    if (TIM1->SR & 0x01) {
//      numit=numit+1;//all cd checked
//      //GPIOH_Port15_Toggle();
//      TIM1->SR &= ~(1U << 0);//reset IT
//    }
//  }
/******************************TIM12 SetUp*******************************/
//void TIM12_Init(void) {
//  /**********************Horloge TIM12***********************/
//  SET_BIT(RCC->APB1LENR, RCC_APB1LENR_TIM12EN_Msk);
//  delay(1000);
//  /**********************Select TRGO source******************/
//  SET_BIT(TIM12->CR2, TIM_CR2_MMS_1); //010 UPDATE EVENT
//  CLEAR_BIT(TIM12->CR2, TIM_CR2_MMS_0 | TIM_CR2_MMS_2);
//  /**********************TIM Update**************************/
//  //SET_BIT(TIM12->EGR,TIM_EGR_UG_Msk);//Update generation
//  /**********************TIM12 ARR**************************/
//  TIM12->ARR = 10000; //CNT TO ARR
//  /**********************TIM12 PSC**************************/
//  TIM12->PSC = 8399; //Random PSC
//  /**********************INTERRUPT**************************/
//  SET_BIT(TIM12->DIER, TIM_DIER_UIE_Msk); //IT UEV enabled
//  NVIC_SetPriority(TIM8_BRK_TIM12_IRQn, 0);
//  NVIC_SetVector(TIM8_BRK_TIM12_IRQn, (uint32_t)&TIM8_BRK_TIM12_IRQHandler);
//  NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
//  /**********************Enable*****************************/
//  SET_BIT(TIM12->CR1, TIM_CR1_CEN_Msk);//Timer ON
//}
//void TIM8_BRK_TIM12_IRQHandler(void) {
//  if (TIM12->SR & TIM_SR_UIF) {
//    updateEV = UpdateEV + 1;
//    TIM12->SR &= ~(1U << 0);
//  }
//}

Note : The ADC setup allows for conversion at a maximum rate of 900kHz all you need to change is the ARR and PSC register of TIM1 (and switch data management to the ADC Data Register) to check that the problem does not come from the ADC . To go any further than 900 kHz change the system clock and the ADC DELAY, SMP, RES (for it to end conversions before another TRGO2 is generated by the TIM1).

Update : I managed to have the DMA work this way :
-I declared a new structure within the shared memory of my two cores (SRAM3 in my case according to datasheet advices, look for AN5617) :

struct shared_data
{
  uint8_t sts_transfer;//status : 1=transfer complete 0=waiting
  uint8_t sts_buff1_or2; // status : 1=M7toM4_1, 0=M7toM4_2
  uint8_t sts_4to7; // status: 0 = no fluo, 1 = fluo, 2 = waiting for fluo
  //uint8_t sts_7to4; // status: 0 = empty, 1 = has data, 2 = locked (CM7-CM4)
  uint16_t M7toM4_1[10]; // 256 bytes from CM7 to CM4 DMA1
  uint16_t M7toM4_2[10]; // 256 bytes from CM7 to CM4 DMA2
};
volatile struct shared_data * const xfr_ptr = (struct shared_data *)0x38001000;

And I checked which one to read by using a variable declared in my structure and checking its state.

void DMA1_Stream1_IRQHandler(void) {
  if (DMA1->LISR & DMA_LISR_TCIF1 ) {
    TCpair = !TCpair;
    xfr_ptr->sts_buff1_or2 = DMABuffered % 2;
    //valADC=BufferADC[0];
    DMABuffered = DMABuffered + 1;
    xfr_ptr->sts_transfer = 1;
    DMA1->LIFCR = DMA_LIFCR_CTCIF1;
  }
  if (DMA1->LISR & DMA_LISR_TEIF1 ) {
    DMAError = DMAError + 1;
    DMA1->LIFCR = DMA_LIFCR_CTEIF1;
  }
}

Somehow using this method is probably the fastest (and easiest) way to 'communicate' (it is not really communication since it is written in both memory but it is a good way to exchange buffers) between the two cores instead of using some RPC communication or murata.

Hi, I used your code for ADC with DMA in portenta H7 lite. The DMA is working only once. Do you know why?. I have enabled DMA in circular mode and disabled double buffer. Once this "BufferADC1[10]" is filled, it does not restart.

Can you share some code? (mainly the DMA config in circular mode)

Thanks for the reply. I have used your same code. I have only deleted ADC interrupts

/*************Function prototype************/
void ADC_Init();
void DMA_Config();
void VREFBUF_Init();

/*************BUFFER***********************/
uint16_t BufferADC1[100];
uint16_t *temp_address = BufferADC1;


void setup()
{
  Serial.begin(9600);
  delay(100);
  ADC_Init();
  VREFBUF_Init();
  SET_BIT(ADC2->CR, ADC_CR_ADSTART_Msk); //Start ADC
  
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN_Msk);
  delay(1000);
  DMA_Config();
  
}
void loop() 
{
      for(int i=0;i<100;i++)
      {
        Serial.println(BufferADC1[i]);
        BufferADC1[i]=0;
      }
}

void VREFBUF_Init(void)
{
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_VREFEN_Msk);
  delay(1000);
  SET_BIT(VREFBUF->CSR, VREFBUF_CSR_ENVR_Msk);
  CLEAR_BIT(VREFBUF->CSR, VREFBUF_CSR_HIZ_Msk);
  while (VREFBUF_CSR_VRR & VREFBUF_CSR_VRR_Msk != 1) {}
}

void DMA_Config(void)
{
  delay(100);
  DMA1_Stream1->CR = 0x00000000; //clear register, reset
  while((DMA1_Stream1->CR & 0x00000001 == 0x00000000));
  /***************P2M****************************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1);
  
  /******************Memory size**************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_0);
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_1);
  /******************MEM Increment**********************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MINC_Msk);

  //LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
  //LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE);
  
  /******************MEM Address to buffer**************/
  DMA1_Stream1->M0AR =(uint32_t)&BufferADC1;
  /*******************Number of data transfer***********/
  DMA1_Stream1->NDTR = (DMA1_Stream1->NDTR | (0x00000064));
  /***********************DMA Mode**********************/
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_CIRC_Msk);
  DMA1_Stream1->CR = (DMA1_Stream1->CR | (0x00000100)); //circular mode
  
  /*******************Flow controller*******************/
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PFCTRL_Msk);//ADC in control
  /******************Periph size************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_0);
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_1);
  /******************Peripheral no Increment*************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PINCOS_Msk);
  /******************Periph request**********************/
  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_DMAREQ_ID_1 | DMAMUX_CxCR_DMAREQ_ID_3);//adc2_dma

  /******************Periph address***********************/
  DMA1_Stream1->PAR = (uint32_t)&ADC2->DR;
  /******************TC IT********************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_TCIE_Msk | DMA_SxCR_TEIE_Msk); //TC IT
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_HTIE_Msk); //HT IT
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_TCIE_Msk | DMA_SxCR_TEIE_Msk| DMA_SxCR_DMEIE_Msk | DMA_SxCR_HTIE_Msk ); //TC 
  DMA1_Stream1->CR = (DMA1_Stream1->CR | (0x00000010)); //Half transfer interrupt
  DMA1->LIFCR = DMA_LIFCR_CTCIF1;//Clear IT in LISR Register
  NVIC_SetVector(DMA1_Stream1_IRQn, (uint32_t)&DMA1_Stream1_IRQHandler);
  NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /*******************Enable DMA****************************/
  //Serial.println("DMA_Set");
  //Serial.println(DMA1_Stream1->CR);
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_EN_Msk);

  //while(1);
}
void DMA1_Stream1_IRQHandler(void)
{
  if (DMA1->LISR & DMA_LISR_TCIF1 ) 
  {
    DMA1->LIFCR = DMA_LIFCR_CTCIF1;
  }
  if (DMA1->LISR & DMA_LISR_TEIF1 ) 
  {
    DMA1->LIFCR = DMA_LIFCR_CTEIF1;
  }
}
void ADC_Init(void)
{
  /*******************Horloges**************************/
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_SYSCFGEN_Msk); //SYSCFG clock
  delay(1000);
  //SET_BIT(RCC->APB4ENR, RCC_APB4ENR_RTCAPBEN_Msk); //RTCAPB clock
  //delay(1000);
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_ADC12EN_Msk); //ADC12 clocks
  delay(1000);
  SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN_Msk); //GPIOA clock
  delay(1000);
  /********************Port config************************/
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_0);  //PA0_C in analog mode
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_1);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_0);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_1);
  SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_PA0SO_Msk);
  delay(1000);
  /********************ADC voltage regulator***************/
  CLEAR_BIT(ADC2->CR, ADC_CR_DEEPPWD_Msk); //END DEEPPWD
  SET_BIT(ADC2->CR, ADC_CR_ADVREGEN_Msk); //ENABLE ADC VOLTAGE REG
  delay(1000);//WAIT VOLTAGE REG
  /********************ADC calibration*********************/
  CLEAR_BIT(ADC2->CR, ADC_CR_ADCALDIF_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCALLI  Serial.println("volt_reg_on");N_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCAL_Msk);
  while (ADC_CR_ADCAL & ADC_CR_ADCAL_Msk != 0) {}
  /******************ADC clock*****************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1);
  /*******************ADC Prescaler************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_PRESC_0 | ADC_CCR_PRESC_1 );
  /*******************Input Mode***************************/
  CLEAR_BIT(ADC2->DIFSEL, ADC_DIFSEL_DIFSEL_0); //Single Ended
  /*******************ADC Enable***************************/
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADEN_Msk);
  while ((ADC_ISR_ADRDY & ADC_ISR_ADRDY_Msk) != 1) {}
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  /********************ADC RES*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_RES_2 | ADC_CFGR_RES_1);
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_RES_0);
  /********************ADC Data Management*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1);//DMA Circular mode
  /********************OVRMODE*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_OVRMOD_Msk); //Erase old data
  /********************CONT/Single/Discont*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_CONT_Msk); // | ADC_CFGR_DISCEN_Msk continuos mode
  /********************INput Preselection******************/
  SET_BIT(ADC2->PCSEL, ADC_PCSEL_PCSEL_0);//Chan 0
  /********************Sample Time reg*********************/
  SET_BIT(ADC2->SMPR1, ADC_SMPR1_SMP3_0); //2.5 CLCK Cycles
  /********************ADC IT******************************/
  //SET_BIT(ADC2->IER, ADC_IER_EOCIE_Msk | ADC_IER_EOSMPIE_Msk  );//| ADC_IER_EOSIE_Msk | ADC_IER_OVRIE_Msk
  //NVIC_EnableIRQ(ADC_IRQn);
  //NVIC_SetVector(ADC_IRQn, (uint32_t)&ADC_IRQHandler);
}

Works fine :
image
Here code modified :

/*************Function prototype************/
void ADC_Init();
void DMA_Config();
void VREFBUF_Init();

/*************BUFFER***********************/
struct shared_data
{
  uint16_t BufferADC1[10];
};
volatile struct shared_data * const xfr_ptr = (struct shared_data *)0x38001000;
//uint16_t *temp_address = BufferADC1;
volatile uint32_t TComplete=0;
volatile uint32_t Eosmp=0;
volatile uint32_t Eocie=0;
void setup()
{
  Serial.begin(9600);
  delay(100);
  DMA_Config();
  VREFBUF_Init();
  ADC_Init();
  AC_TIMER1_Init();
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADSTART_Msk); //Start ADC
  
  
  delay(1000);
  
  
}
void loop() 
{
        Serial.print("TCDMA : ");
        Serial.print(TComplete);
        Serial.print(" SequenceConv : ");
        Serial.println(Eocie);
        
     
}

void VREFBUF_Init(void)
{
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_VREFEN_Msk);
  delay(1000);
  SET_BIT(VREFBUF->CSR, VREFBUF_CSR_ENVR_Msk);
  CLEAR_BIT(VREFBUF->CSR, VREFBUF_CSR_HIZ_Msk);
  while (VREFBUF_CSR_VRR & VREFBUF_CSR_VRR_Msk != 1) {}
}

void DMA_Config(void)
{
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN_Msk);
  delay(1000);
//  DMA1_Stream1->CR = 0x00000000; //clear register, reset
//  while((DMA1_Stream1->CR & 0x00000001 == 0x00000000));
  /***************P2M****************************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_DIR_0 | DMA_SxCR_DIR_1);
  
  /******************Memory size**************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_0);
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MSIZE_1);
  /******************MEM Increment**********************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_MINC_Msk);

  //LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
  //LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE);
  
  /******************MEM Address to buffer**************/
  DMA1_Stream1->M0AR =(uint32_t)&xfr_ptr->BufferADC1;
  /*******************Number of data transfer***********/
  DMA1_Stream1->NDTR = 10;
  /***********************DMA Mode**********************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_CIRC_Msk);
  //DMA1_Stream1->CR = (DMA1_Stream1->CR | (0x00000100)); //circular mode
  
  /*******************Flow controller*******************/
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PFCTRL_Msk);//ADC in control
  /******************Periph size************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_0);
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PSIZE_1);
  /******************Peripheral no Increment*************/
  CLEAR_BIT(DMA1_Stream1->CR, DMA_SxCR_PINCOS_Msk);
  /******************Periph request**********************/
  SET_BIT(DMAMUX1_Channel1->CCR, DMAMUX_CxCR_DMAREQ_ID_1 | DMAMUX_CxCR_DMAREQ_ID_3);//adc2_dma

  /******************Periph address***********************/
  DMA1_Stream1->PAR = (uint32_t)&ADC2->DR;
  /******************TC IT********************************/
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_TCIE_Msk | DMA_SxCR_TEIE_Msk); //TC IT
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_HTIE_Msk); //HT IT
  //SET_BIT(DMA1_Stream1->CR, DMA_SxCR_TCIE_Msk | DMA_SxCR_TEIE_Msk| DMA_SxCR_DMEIE_Msk | DMA_SxCR_HTIE_Msk ); //TC 
  DMA1_Stream1->CR = (DMA1_Stream1->CR | (0x00000010)); //Half transfer interrupt
  DMA1->LIFCR = DMA_LIFCR_CTCIF1;//Clear IT in LISR Register
  NVIC_SetVector(DMA1_Stream1_IRQn, (uint32_t)&DMA1_Stream1_IRQHandler);
  NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  /*******************Enable DMA****************************/
  //Serial.println("DMA_Set");
  //Serial.println(DMA1_Stream1->CR);
  SET_BIT(DMA1_Stream1->CR, DMA_SxCR_EN_Msk);

  //while(1);
}
void DMA1_Stream1_IRQHandler(void)
{
  if (DMA1->LISR & DMA_LISR_TCIF1 ) 
  {
    TComplete+=1;
    DMA1->LIFCR = DMA_LIFCR_CTCIF1;
    
  }
  if (DMA1->LISR & DMA_LISR_TEIF1 ) 
  {
    DMA1->LIFCR = DMA_LIFCR_CTEIF1;
  }
}
void ADC_Init(void) {
  /*******************Horloges**************************/
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_SYSCFGEN_Msk); //SYSCFG clock
  delay(1000);
  SET_BIT(RCC->APB4ENR, RCC_APB4ENR_RTCAPBEN_Msk); //RTCAPB clock
  delay(1000);
  SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_ADC12EN_Msk); //ADC12 clocks
  delay(1000);
  SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN_Msk); //GPIOA clock
  delay(1000);
  /********************Port config************************/
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_0);
  SET_BIT(GPIOA->MODER, GPIO_MODER_MODE1_1);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_0);
  CLEAR_BIT(GPIOA->PUPDR, GPIO_PUPDR_PUPD1_1);
  SET_BIT(SYSCFG->PMCR, SYSCFG_PMCR_PA0SO_Msk);
  delay(1000);//PA0_C in analog mode
  /********************ADC voltage regulator***************/
  CLEAR_BIT(ADC2->CR, ADC_CR_DEEPPWD_Msk); //END DEEPPWD
  SET_BIT(ADC2->CR, ADC_CR_ADVREGEN_Msk); //ENABLE ADC VOLTAGE REG
  delay(1000);//WAIT VOLTAGE REG
  /********************ADC calibration*********************/
  CLEAR_BIT(ADC2->CR, ADC_CR_ADCALDIF_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCALLIN_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADCAL_Msk);
  while (ADC_CR_ADCAL & ADC_CR_ADCAL_Msk != 0) {}
  /******************ADC clock*****************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1);
  /*******************ADC Prescaler************************/
  SET_BIT(ADC12_COMMON->CCR, ADC_CCR_PRESC_0 | ADC_CCR_PRESC_1 );
  /*******************Input Mode***************************/
  CLEAR_BIT(ADC2->DIFSEL, ADC_DIFSEL_DIFSEL_0); //Single Ended
  /*******************ADC Enable***************************/
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  SET_BIT(ADC2->CR, ADC_CR_ADEN_Msk);
  while (ADC_ISR_ADRDY & ADC_ISR_ADRDY_Msk != 1) {}
  SET_BIT(ADC2->ISR, ADC_ISR_ADRDY_Msk);
  /********************ADC RES*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_RES_2 | ADC_CFGR_RES_1);
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_RES_0);
  /********************ADC Data Management*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1);//DMA Circular mode
  /********************OVRMODE*****************************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_OVRMOD_Msk); //Erase old data
  /********************CONT/Single/Discont*****************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_DISCEN_Msk); // discontinuous mode
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_CONT_Msk); // | ADC_CFGR_DISCEN_Msk
  /********************Trigger Detection*******************/
  SET_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_3);//Trig rising edge TRGO2
  CLEAR_BIT(ADC2->CFGR, ADC_CFGR_EXTEN_1 | ADC_CFGR_EXTSEL_0 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_4);
  /********************INput Preselection******************/
  SET_BIT(ADC2->PCSEL, ADC_PCSEL_PCSEL_0);//Chan 0
  /********************Sample Time reg*********************/
  SET_BIT(ADC2->SMPR1, ADC_SMPR1_SMP0_0); //2.5 CLCK Cycles
  /********************ADC IT******************************/
  SET_BIT(ADC2->IER, ADC_IER_EOCIE_Msk | ADC_IER_EOSMPIE_Msk  );//| ADC_IER_EOSIE_Msk | ADC_IER_OVRIE_Msk
  NVIC_EnableIRQ(ADC_IRQn);
  NVIC_SetVector(ADC_IRQn, (uint32_t)&ADC_IRQHandler);
}
void ADC_IRQHandler(void) {
  if (ADC2->ISR & 0x02) {
    Eosmp += 1;
    SET_BIT(ADC2->ISR, ADC_IER_EOSMPIE_Msk);
  }
  if (ADC2->ISR & 0x04) {
    Eocie += 1;
    SET_BIT(ADC2->ISR, ADC_IER_EOCIE_Msk);
  } 
//  if (ADC2->ISR & 0x08) {
//    Eos += 1;
//    SET_BIT(ADC2->ISR, ADC_IER_EOSIE_Msk);
//  }
}
void ADC_Start(void) {
  SET_BIT(ADC2->CR, ADC_CR_ADSTART_Msk); //Start
}
void AC_TIMER1_Init(void) {
  //------------------------------Horloge Timer1----------------------------------------//
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM1EN_Msk);
  //------------------------------Select TRGO source-----------------------------------//
  TIM1->PSC = 8399; //PSC=1
  //----------------------------------------------------------TIM2 autoreload register--------------------------------------------//
  TIM1->ARR = 10000;
  //---------selectupdatevent-----------------//
  SET_BIT(TIM1->CR2, TIM_CR2_MMS2_1);
  //-----------IT-----------------------//
  //  SET_BIT(TIM1->DIER,TIM_DIER_UIE_Msk);
  //  NVIC_SetPriority(TIM1_UP_IRQn,0);
  //  NVIC_SetVector(TIM1_UP_IRQn, (uint32_t)&TIM1_UP_IRQHandler);
  //  NVIC_EnableIRQ(TIM1_UP_IRQn);
  SET_BIT(TIM1->CR1, TIM_CR1_CEN_Msk);//CEN=1
}

Be carefull with the way you read data, if you read and at the same time the DMA writes at this place you ll crash your board (main reason why I did go for double buffer it is easier to setup the reading part).

EDIT : you can place the struct wherever you want just declare it not forcibly where I put it :slight_smile:

1 Like

Also if you want to code using registers ,CMSIS is the way you will find all the notations here :
https://raw.githubusercontent.com/arduino/ArduinoCore-mbed/master/cores/arduino/mbed/targets/TARGET_STM/TARGET_STM32H7/STM32Cube_FW/CMSIS/stm32h747xx.h
Couple it with the datasheet (RM0399 from stm32) and you will be able to face any issue.

1 Like

Thanks for the quick reply and the tip :slight_smile: . I think I didn't frame my question properly. The DMA works fine but the values are not updated for every iteration.

BufferADC value: 283
BufferADC value: 149
BufferADC value: 144
BufferADC value: 151
BufferADC value: 157
BufferADC value: 157
BufferADC value: 157
BufferADC value: 155
BufferADC value: 156
BufferADC value: 155
ADC->DR value:144 
BufferADC value: 283
BufferADC value: 149
BufferADC value: 144
BufferADC value: 151
BufferADC value: 157
BufferADC value: 157
BufferADC value: 157
BufferADC value: 155
BufferADC value: 156
BufferADC value: 155
ADC->DR value:4095 

void loop() 
{
        for(int i=0;i<10;i++)
        {
          Serial.print("BufferADC value: ");
          Serial.println(xfr_ptr->BufferADC1[i]);
        }
        Serial.print("ADC->DR value:");
        Serial.print(ADC2->DR);
        Serial.println(" ");
        delay(1000);
        //Serial.print(TComplete);
        //Serial.print(" SequenceConv : ");
        //Serial.println(Eocie);
      
}

Initially when no voltage is given, the value is around 100(looks fine) . but when i give 3.3 v to ADC pin. The ADC2->DR is changed to 4095 but DMA does not update it in BufferADC1. Its vice versa. It is updated only the first time. I even tried to reinitialize dma everytime, updating the BUfferADC1 address to peripheral address register.

An hypothesis :


To verify it add the Overrun interrupt on the ADC and add a counter if it goes above zero that might explain the transfer failure, I already added that part in my previous code, I repost it here :

void ADC_IRQHandler(void) {
  if (ADC2->ISR & 0x10) {
    Ovrun += 1;
    SET_BIT(ADC2->ISR, ADC_IER_OVRIE_Msk);
  }
}

In the ADC part don't forget to enable the interrupt associated. You can also check the transfer error directly with DMA interrupts.

Thanks for the reply. I found the issue. It seems to be cache coherency issue with DMA. Disabling cache seems to work or clearing cache works.

It is not mentioned anywhere in the reference manual or arduino documentation. I had to check cortex m7 RM.

SCB_DisableDCache(); - Using this command seems to work.

1 Like

Good job, makes sense! (I also use this line for data access between the two cores

SCB_CleanInvalidateDCache_by_Addr(buff, sizeof(*buff));
    if (buff->datReady1 == 1) {
      readyM4 = 1;
    } SCB_CleanDCache_by_Addr(buff, sizeof(*buff));

)

Yes, DMA and Caches - called "Cache Coherency".
DMA is like another master - but it writes directly to final memory.

MCU with caches enabled - reads from memory via caches. If cache has already the data (e.g. from previous read) - not a new cache fill.

And these "simple" MCUs do not have means so that DMA engine could tell MCU: "hey - update your cached - I have written to memory new stuff" (like a "Cache Coherency Interconnect" - not there in this MCU).

Yes, do the Cache Maintenance yourself - or program MPU and disable caches (therefore it might work on another RAM where cache is already disabled).

Hi,

I think I've found an error in the program you've shared in post #1. During the ADC calibration stage within the ADC_Init(); function, you use the line:

while (ADC_CR_ADCAL & ADC_CR_ADCAL_Msk != 0) {}

to wait for the calibration to complete. This line doesn't really do anything since in the CMSIS definitions ADC_CR_ADCAL and ADC_CR_ADCAL_Msk are the same thing. However, if you replace this line with:

while(ADC2->CR & ADC_CR_ADCAL_Msk);

the program gets hung up. To fix this, you just need to set up the ADC clock before you begin the calibration.

So, to get the calibration to work, place the following line before the while loop I mentioned previously:

SET_BIT(ADC12_COMMON->CCR, ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1);

1 Like

I've been struggling a bit with a CMSIS-based ADC implementation but its quite tricky to find out the sequence of events required to setup the ADC. Where can you find this? I've looked in the reference manual but its a bit of a maze!

Can't help you about sequencing to be honest I kinda read the whole manual RM0399 and also I took a look at the HAL library open source code to check which and when registers where called. You can also look for application note on STM website (stm32>ressources>stm32h747ai

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.