Within a project of mine I use DMA to share data between the two cores of the STM32H747, the data comes from an ADC controlled by the core M7. While testing my DMA I noticed that the data stored within the memory by the DMA wasn't varying and noticed that my ADC does not work as intended. I wrote a testing code for my ADC for it to work in single ended mode and continuous mode. So first I init my ADC :
void ADC_Init (void) {
uint32_t ISR = *ADC_ISR;
uint32_t IER = *ADC_IER;
uint32_t CR = *ADC_CR;
uint32_t CFGR = *ADC_CFGR;
uint32_t CFGR2 = *ADC_CFGR2;
uint32_t SMPR1 = *ADC_SMPR1;
uint32_t PCSEL = *ADC_PCSEL;
uint32_t DR = *ADC_DR;
uint32_t DIFSEL = *ADC_DIFSEL;
uint32_t SQR1 = *ADC_SQR1;
uint32_t SQR2 = *ADC_SQR2;
uint32_t SQR3 = *ADC_SQR3;
uint32_t SQR4 = *ADC_SQR4;
uint32_t CSR = *ADC1_CSR;
uint32_t CCR = *ADC1_CCR;
*RCC_AHB4ENR |= (1 << 0); //Enable clock GPIO port A
*GPIOA_MODER |= (3 << 0); //Analog mode port 0
/******Enable ADC Voltage regulator******/
CR = 0x00000000; //Fin du deep power down
*ADC_CR = CR;
CR = 0x10000000; //ADC voltage regulator Enable
*ADC_CR = CR;
while (*ADC_CR & (1 << 28) != 0) {} //check volatage enable (peut etre remplacé par un :
//delayMicroseconds(5); mais c'est moins safe)
//Petit interlude Differentiel ou single ended (a faire avant ADEN)---------
DIFSEL = 0x00000000;
*ADC_DIFSEL = DIFSEL;
//digitalWrite(LEDR, LOW);
//while (!(ADC1_ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Calibrate ADC*********/
/******Enable ADC CLOCK******/
*RCC_AHB1ENR |= (1 << 5); //ADC peripheral clock enable
//RCC_D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
digitalWrite(LEDB, LOW);
delay(2000);
digitalWrite(LEDB, HIGH);
delay(2000);
digitalWrite(LEDB, LOW);
delay(2000);
digitalWrite(LEDB, HIGH);
/******Set the prescalar******/
CCR = 0x000F0000;
*ADC1_CCR = CCR;
/******Set Scan Mode Data Management and resolution******/
CFGR |= (6 << 2); //RES[2:0]: Data resolution 110=12bits
CFGR &= ~(3 << 0); //DMNGT[1:0]: Data Management configuration 00 data stored in DR only
*ADC_CFGR = CFGR;
*ADC_CFGR2 |= (1 << 5);
//ADC regular sequence register 1
SQR1 = 0x0000040; //1st conv correspond au chan 0 (00000) et on réalise une conv par sequence de 1 conv(0000)
*ADC_SQR1 = SQR1;
/******Set the Continuous Conversion, ******/
CFGR |= (1 << 13); //CONT: Single / continuous conversion mode for regular conversions
*ADC_CFGR = CFGR;
/******Set the Sampling Time for the channels in ADC_SMPRx******/
SMPR1 = (7 << 0); //SMP0[2:0]: Channel 0 sampling time selection 011: 16.5 ADC clock cycles
*ADC_SMPR1 = SMPR1;
PCSEL |= (1 << 0); //PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
*ADC_PCSEL = PCSEL;
/******Set singleEnded Input******/
DIFSEL &= ~(1 << 0); //DIFSEL[19:0]: single ended mode for channel 0
*ADC_DIFSEL = DIFSEL;
}
And I start it with :
void ADC_start (void) {
uint32_t ISR = *ADC_ISR;
uint32_t IER = *ADC_IER;
uint32_t CR = *ADC_CR;
uint32_t CFGR = *ADC_CFGR;
uint32_t CFGR2 = *ADC_CFGR2;
uint32_t SMPR1 = *ADC_SMPR1;
uint32_t PCSEL = *ADC_PCSEL;
uint32_t DR = *ADC_DR;
uint32_t DIFSEL = *ADC_DIFSEL;
uint32_t SQR1 = *ADC_SQR1;
uint32_t SQR2 = *ADC_SQR2;
uint32_t SQR3 = *ADC_SQR3;
uint32_t SQR4 = *ADC_SQR4;
uint32_t CSR = *ADC1_CSR;
uint32_t CCR = *ADC1_CCR;
CR &= ~(1 << 30);
*ADC_CR = CR;
//On a deja ADCALDIF en single ended inputs mode par nos initialisations précédentes
CR = (1 << 16); //ADCALLIN calibration linéaire ET offset
*ADC_CR = CR;
//CR=0x90010001;
*ADC_CR |= (1 << 31); //Lancer une calibration ADCAL=1
//dataFromRegister=*ADC_CR;
while (*ADC_CR & (1 << 31) != 0) {
digitalWrite(LEDR, HIGH);
delay(1000);
digitalWrite(LEDR, LOW);
delay(1000);
} digitalWrite(LEDR, HIGH);
//On attends que la calibration soit complète par un reset de ADCAL
//Processus de calibration terminé (Serial.println(/*dataFromRegister,BIN*/"calibration");)
dataFromRegister=*ADC_CR;
Serial.println(dataFromRegister,BIN);
/******Enable ADC******/
*ADC_ISR |= (1 << 0); // Reset ADC ready flag
*ADC_CR |= (1 << 0); //Enable ADC
while (!(*ADC_ISR & (1 << 0))); //Wait for ready flag
*ADC_ISR |= (1 << 0);
//*ADC_CR |= (3 << 8);//11 boost if ADC Clock entre 25 et 50MHz
CR |= (1 << 2); //ADSTART
*ADC_CR = CR;
// if (*ADC_ISR & (1 << 1)) {
// CR |= (1 << 2); //ADSTART
// *ADC_CR = CR;
// }
//while(!(*ADC_ISR & (1 << 1)));
digitalWrite(LEDR, LOW);
}
Now the continuous conversion mode should start a new conversion continuously, and set the EOC (End Of Conversion) flag which I can check to go read ADC_DR (Data Register). That is what I do within my loop :
void loop() {
Serial.println("Begin Loop");
while ((*ADC_ISR & (1 << 2)) == 0) {
Serial.print(".");
delay(100);
}
dataFromADC = *ADC_DR;
*ADC_ISR|=(1<<2);
Serial.println(dataFromADC, BIN);
digitalWrite(LEDB, LOW);
}
According to the datasheet if I read the data from the ADC_DR register the EOC flag should reset himself yet this code does not work without forcing it to reset itself (and even with that it works randomly). I must admit I am quite lost here as to why it does not work, I am currently checking every bit of my code (mainly using digitalWrite to check where the code stop and without the EOC reset it stops within the EOC check but still gives me that same 100000000000 ADC_DR read no matter what signal I put on pin PA_0C). Here is how I defined my registers :
//GPIO REG----------------------------------------
volatile uint32_t* const GPIOA_MODER = (uint32_t *) 0x58020000;//Port GPIOA correspondant à l'ADC0
//--------------------ADC REGISTRES-------------------//
volatile uint32_t* const ADC_ISR = (uint32_t *) 0x40022000;//Interupt and status register
volatile uint32_t* const ADC_IER = (uint32_t *) 0x40022004;//Interupt enable register
volatile uint32_t* const ADC_CR = (uint32_t *) 0x40022008;//Control register
volatile uint32_t* const ADC_CFGR = (uint32_t *) 0x4002200C;//COnfiguration register
volatile uint32_t* const ADC_CFGR2 = (uint32_t *) 0x40022010;//2eme conf regrister
volatile uint32_t* const ADC_SMPR1 = (uint32_t *) 0x40022014; //Sample time reg (directement lié au temps de calc de l'ADC)
volatile uint32_t* const ADC_SMPR2 = (uint32_t *) 0x40022018;
volatile uint32_t* const ADC_PCSEL = (uint32_t*) 0x4002201C;//channel preselection register on choisis un chan pour la conv
volatile uint32_t* const ADC_DR = (uint32_t *) 0x40022040;//Registre où l'on stocke le resultat des conv
volatile uint32_t* const ADC_DIFSEL = (uint32_t *) 0x400220C0;
volatile uint32_t* const ADC_SQR1 = (uint32_t *) 0x40022030;
volatile uint32_t* const ADC_SQR2 = (uint32_t *) 0x40022034;
volatile uint32_t* const ADC_SQR3 = (uint32_t *) 0x40022038;
volatile uint32_t* const ADC_SQR4 = (uint32_t *) 0x4002203C;
volatile uint32_t* const ADC1_CSR = (uint32_t *) 0x40022300;//ADC1 common status register
volatile uint32_t* const ADC1_CCR = (uint32_t *)0x40022308; //ADC1 Common Control Register
//REG RCC-----------------------------------------------
volatile uint32_t* const RCC_APB4ENR = (uint32_t *) 0x580244F4;//to enable sysconf
volatile uint32_t* const RCC_AHB4ENR = (uint32_t *) 0x580244E0;
volatile uint32_t* const RCC_AHB1ENR = (uint32_t *) 0x580244D8;
volatile uint32_t* const RCC_CR = (uint32_t *) 0x58024400;
volatile uint32_t* const RCC_CFGR = (uint32_t *) 0x58024410;
volatile uint32_t* const RCC_D1CFGR = (uint32_t *) 0x58024418;
volatile uint32_t* const RCC_D2CFGR = (uint32_t *) 0x5802441C;
volatile uint32_t* const RCC_D3CFGR = (uint32_t *) 0x58024420;
volatile uint32_t* const RCC_PLLCKSELR = (uint32_t *) 0x58024428;
volatile uint32_t* const RCC_PLLCFGR = (uint32_t *) 0x5802442C;
volatile uint32_t* const RCC_PLL1DIVR = (uint32_t *) 0x58024430;
volatile uint32_t* const RCC_PLL1FRACR = (uint32_t *) 0x58024434;
volatile uint32_t* const RCC_PLL2DIVR = (uint32_t *) 0x58024438;
volatile uint32_t* const RCC_PLL2FRACR = (uint32_t *) 0x5802443C;
volatile uint32_t* const RCC_PLL3DIVR = (uint32_t *) 0x58024440;
volatile uint32_t* const RCC_PLL3FRACR = (uint32_t *) 0x58024444;
volatile uint32_t* const RCC_CIER = (uint32_t *) 0x58024460;
I must say I am no longer sure about the ADC1_CCR address, in the datasheet they say to add a 0x300 offset to the master base address, I considered the base address to be the address of ADC1-ADC2 as referenced page 140 of the datasheet : https://www.st.com/resource/en/reference_manual/dm00176879-stm32h745755-and-stm32h747757-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
However if I misunderstood what the master address for ADC1 and ADC2 is that would explain a lot of things as the clock would no longer be defined.
I will keep trying and I welcome any piece of advice.
Here the working code (I switched to CMSIS) there were quite a lot of errors in my first version it also seems to only work on PA0_C if I use ADC2 and not ADC1:
Thanks for the help somehow redefining everything with CMSIS highlighted some of my mistakes.