cancel
Showing results for 
Search instead for 
Did you mean: 

H7 read temperature from ADC

Pilous Droip
Senior

H friends,

I'm trying to read adc3, a temperature sensor from stm32h753. But something is wrong. Because I didn't get any interruptions. Can anyone help?

void MX_ADC3_Init(void) {
    LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP);
 
    /* Peripheral clock enable */
    LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_ADC3);
 
    if (LL_ADC_IsDeepPowerDownEnabled(ADC3) != 0UL)
        LL_ADC_DisableDeepPowerDown(ADC3);
 
    if (LL_ADC_IsInternalRegulatorEnabled(ADC3) == 0UL) {
        LL_ADC_EnableInternalRegulator(ADC3);
        __IO uint32_t wait_loop_index = 0UL;
        wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US / 10UL) * (SystemCoreClock / (100000UL * 2UL))) + 1000;
        while (wait_loop_index != 0UL) {
            wait_loop_index--;
        }
    }
 
    LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(ADC3), LL_ADC_CLOCK_ASYNC_DIV2);
    LL_ADC_SetResolution(ADC3, LL_ADC_RESOLUTION_16B);
    LL_ADC_REG_SetSequencerLength(ADC3, LL_ADC_REG_SEQ_SCAN_ENABLE_10RANKS);
    LL_ADC_SetLowPowerMode(ADC3, LL_ADC_LP_MODE_NONE);
    LL_ADC_REG_SetContinuousMode(ADC3, LL_ADC_REG_CONV_CONTINUOUS);
    LL_ADC_REG_SetSequencerDiscont(ADC3, LL_ADC_REG_SEQ_DISCONT_DISABLE);
    LL_ADC_REG_SetTriggerSource(ADC3, LL_ADC_REG_TRIG_SOFTWARE);
    LL_ADC_REG_SetDataTransferMode(ADC3, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
    LL_ADC_REG_SetOverrun(ADC3, LL_ADC_REG_OVR_DATA_OVERWRITTEN);
    LL_ADC_SetOverSamplingScope(ADC3, LL_ADC_OVS_DISABLE);
 
    LL_ADC_REG_SetSequencerRanks(ADC3, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_TEMPSENSOR);
    LL_ADC_SetChannelSamplingTime(ADC3, LL_ADC_CHANNEL_TEMPSENSOR, LL_ADC_SAMPLINGTIME_387CYCLES_5);
    LL_ADC_SetChannelSingleDiff(ADC3, LL_ADC_CHANNEL_TEMPSENSOR, LL_ADC_SINGLE_ENDED);
 
    LL_ADC_SetOffset(ADC3, LL_ADC_OFFSET_1, LL_ADC_CHANNEL_TEMPSENSOR, 0);
 
    LL_ADC_EnableIT_EOS(ADC3); // End of regular sequence of conversions IR enable
    LL_ADC_EnableIT_EOC(ADC3); // End of conversion IR enable
}
 
void init_DMA_ADC(void) {
 
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
    LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
    LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE);
    LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_HALFWORD);
    LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_CIRCULAR);
    LL_DMA_SetPeriphBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_PBURST_SINGLE);
    LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_HALFWORD);
    LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_ADC3);
    LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_HIGH);
 
    LL_DMA_SetPeriphAddress(DMA1, LL_DMA_STREAM_1, (uint32_t) (&ADC3->DR));
    LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)tempSensor);
 
    LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_1);
}
 
void enable_adc_interrupt(void) {
 
    NVIC_SetPriority(ADC3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_EnableIRQ(ADC3_IRQn);
}
 
void enable_dma_interrupt(void) {
 
    NVIC_SetPriority(DMA1_Stream1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
    NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}
 
void enable_ADC(void)
{
    LL_ADC_Enable(ADC3);
    while (LL_ADC_IsEnabled(ADC3) == 0UL);
}
 
void start_ADC(uint32_t size)
{
    LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, size);
    LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
    LL_ADC_REG_StartConversion(ADC3);
}
 
void DMA1_Stream1_IRQHandler(void) {
    if (LL_DMA_IsActiveFlag_TC1(DMA1)) {
        LL_DMA_ClearFlag_TC1(DMA1);
 
        dma_flag = 1;
    }
}
 
void ADC3_IRQHandler(void) {
 
    if(LL_ADC_IsActiveFlag_EOC(ADC3) || LL_ADC_IsActiveFlag_EOS(ADC3) ){
 
        LL_ADC_ClearFlag_EOC(ADC3);
        LL_ADC_ClearFlag_EOS(ADC3);
 
        adc_flag = 1;
    }
}

i call it in my main:

MX_ADC3_Init();
init_DMA_ADC();
enable_adc_interrupt();
enable_dma_interrupt();
enable_ADC();
start_ADC(2); // 2 for 2x half word

And in while ufnction I have condition:

if ((adc_flag == 1) || (dma_flag == 1)){
 
    adc_flag = 0;
    dma_flag = 0;
 
    // breakpoint here
}

my studing material is datasheet, reference manual and internet...

https://www.st.com/resource/en/datasheet/stm32h753ii.pdf

https://www.st.com/resource/en/reference_manual/dm00314099-stm32h742-stm32h743-753-and-stm32h750-value-line-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

25 REPLIES 25
Pilous Droip
Senior

I add calibration. This I read from this document: https://www.st.com/resource/en/user_manual/dm00392525-description-of-stm32h7-hal-and-lowlayer-drivers-stmicroelectronics.pdf

Now I get ADC interrupt. But DMA interrupt missing.

GwenoleB
ST Employee

Hi @Pilous Droip​,

First of all, can you tell me if in which RAM bank your variable "tempSensor" is stored. Indeed, DMA is working in D2 domain with ADC transfer. In this case, DMA cannot access to AXIRAM bank (0x20000000) or DTCM RAM. So you need to store tempSensor in D2-RAM by adding :

#define DMA_BUFFER \
	__attribute__((section(".dma_buffer")))
 
DMA_BUFFER uint32_t tempSensor

Don't forget to add this section in your linkerfile STM32H753xxx_FLASH.ld

I hope it will help.

Best Regards,

Gwénolé

Pilous Droip
Senior

Hello @Gwenole BROCHARD​ 

In Linker script I have this part of memory:

 
	/* Secondary RAM, domain D2, generic application use, secondary heap */
	SRAM1 (rw)		: ORIGIN = 0x30000000, LENGTH = 128K

 So my definition of my variable is:

#define DMA_BUFFER __attribute__ ((section (".sram1"),aligned(32)))
volatile DMA_BUFFER int32_t tempSensor = 0;

GwenoleB
ST Employee

Alright it's a good point to start. I've got some questions about the ADC3 configuration :

  • ADC is configured to convert 10 channels and I think one of them is TEMP_SENSOR. Isn't it ? if not you can try with only one channel which is more appropriate to your application.
  • In case you are converting ten channels, you need to create an array to store each conversion results
DMA_BUFFER uint32_t ADC3_Results[NUMBER_OF_CONVERSION];

  • Is D2SRAM clock enabled ?
RCC->AHB2ENR |= RCC_AHB2ENR_D2SRAM1EN;

Best regards,

Gwénolé

TDK
Guru

> volatile DMA_BUFFER int32_t tempSensor = 0;

> LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)tempSensor);

You're not passing the address of tempSensor. Instead, you're passing a null pointer as the memory address, so it is trying to write to memory address 0. On the H7, this is ITCM ram and is not accessible by the DMA. No doubt a DMA error flag is set.

If you want to store values in an array, create an array.

volatile DMA_BUFFER uint16_t tempSensor[2] = {0};
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_STREAM_1, (uint32_t)tempSensor);

If you feel a post has answered your question, please click "Accept as Solution".

In my SystemInit I have enable clock for D2SRAM.

RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);

I tried your solutin, and now, DMA and ADC is switching. I modif my code with print status to uart line:

  1. change
volatile DMA_BUFFER uint16_t tempSensor[2] = {0};
  1. next change
void DMA1_Stream1_IRQHandler(void) {
    if (LL_DMA_IsActiveFlag_TC1(DMA1)) {
        LL_DMA_ClearFlag_TC1(DMA1);
 
        dma_flag = 1;
    }
}
 
void ADC3_IRQHandler(void) {
 
    if (LL_ADC_IsActiveFlag_EOC(ADC3)) {
        LL_ADC_ClearFlag_EOC(ADC3);
 
        adc_flag = 1;
    }
 
    if (LL_ADC_IsActiveFlag_EOS(ADC3)) {
        LL_ADC_ClearFlag_EOS(ADC3);
 
        adc_flag = 2;
    }
}
  1. Enable is set with calibration
void enable_ADC(void)
{
    LL_ADC_Enable(ADC3);
    while (LL_ADC_IsEnabled(ADC3) == 0UL);
 
    LL_ADC_StartCalibration(ADC3 , LL_ADC_CALIB_OFFSET, LL_ADC_DIFFERENTIAL_ENDED );
    while (LL_ADC_IsCalibrationOnGoing(ADC3) == 0UL);
}

And in main is check variables...

if (adc_flag >= 1){
     UARTPrint("adc=%d, tempVal=0x%x 0x%x", adc_flag, tempSensor[0], tempSensor[1]);
     adc_flag = 0;
}
 
if (dma_flag == 1){
    UARTPrint("dma=%d, tempVal=0x%x 0x%x", dma_flag, tempSensor[0], tempSensor[1]);
    dma_flag = 0;
 
    start_ADC(2);
}

Output logs from uart line is here:

dma=1, tempVal=0x3943 0x3745
adc=2, tempVal=0x3943 0x3745
dma=1, tempVal=0x3943 0x3745
adc=2, tempVal=0x3943 0x3745
dma=1, tempVal=0x3943 0x3745

So now is DMA and ADC working. But how to measure only temperature?

And when I restart program, variables are changed.

dma=1, tempVal=0x2f35 0x306c
adc=2, tempVal=0x2f35 0x306c
dma=1, tempVal=0x2f35 0x306c
adc=2, tempVal=0x2f35 0x306c
dma=1, tempVal=0x2f35 0x306c

Something is wrong in my code.

GwenoleB
ST Employee

Hi @Pilous Droip​ 

I am glad ADC/DMA seem working now.

In fact, ADC is not giving you the temperature but a digital image of it. You need to convert your ADC Value using macro __LL_ADC_CALC_TEMPERATURE.

This macro uses calibration values stored in flash.

Note that the return value is expressed is degree Celsius.

I don't know in which condition you are measuring, but I think the ADC value is high...

Best Regards,

Gwénolé

Hello,

Definitely, I thought that ADC and DMA peripherals work. The value from the ADC is, in my opinion, incorrect. As you write @Gwenole BROCHARD​ . I think I'm measuring wrong. I should consider only one value in the calculation.

So I modif my code.

Init change:

	MX_ADC3_Init();
	init_DMA_ADC();
	enable_adc_interrupt();
	enable_dma_interrupt();
	enable_ADC();
	start_ADC(1);

Print...

			if (adc_flag >= 1){
                            temp = __LL_ADC_CALC_TEMPERATURE(3300, tempSensor[0], LL_ADC_RESOLUTION_16B);
			    UARTprint("adc=%d, tempVal=0x%02x; temp=%d", adc_flag, tempSensor[0], temp);
			    adc_flag = 0;
			}
 
			if (dma_flag == 1){
                             temp = __LL_ADC_CALC_TEMPERATURE(3300, tempSensor[0], LL_ADC_RESOLUTION_16B);
			    UARTprint("dma=%d, tempVal=0x%02x, temp=%d", dma_flag, tempSensor[0], temp);
                dma_flag = 0;
			}

To calculate the temperature, I should take the value only from tempSensor [0].

Here is output:

adc=2, tempVal=0x30e8, temp=34
dma=1, tempVal=0x311b, temp=35
adc=2, tempVal=0x311b, temp=35
dma=1, tempVal=0x311b, temp=35
adc=2, tempVal=0x311b, temp=35
dma=1, tempVal=0x311b, temp=35
..... many of 35 deg .......
adc=2, tempVal=0x3124, temp=35
dma=1, tempVal=0x3124, temp=35
adc=2, tempVal=0x3124, temp=35
dma=1, tempVal=0x33cd, temp=49
adc=2, tempVal=0x33ed, temp=50
dma=1, tempVal=0x33ed, temp=50
adc=2, tempVal=0x33ed, temp=50
dma=1, tempVal=0x33ed, temp=50
..... many of 50 deg .......
..... now break ..........
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x3090, temp=32
adc=2, tempVal=0x3090, temp=32
dma=1, tempVal=0x324a, temp=41
adc=2, tempVal=0x339f, temp=48
dma=1, tempVal=0x3429, temp=51
adc=2, tempVal=0x3429, temp=51
....... many of 51 deg.....
 

Something is wrong in my code. :beaming_face_with_smiling_eyes: