2021-11-19 12:45 AM
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...
2021-11-19 01:53 AM
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.
2021-11-19 02:31 AM
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é
2021-11-19 03:03 AM
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;
2021-11-19 05:05 AM
Alright it's a good point to start. I've got some questions about the ADC3 configuration :
DMA_BUFFER uint32_t ADC3_Results[NUMBER_OF_CONVERSION];
RCC->AHB2ENR |= RCC_AHB2ENR_D2SRAM1EN;
Best regards,
Gwénolé
2021-11-19 05:07 AM
> 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);
2021-11-20 04:01 AM
In my SystemInit I have enable clock for D2SRAM.
RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);
2021-11-20 04:17 AM
I tried your solutin, and now, DMA and ADC is switching. I modif my code with print status to uart line:
volatile DMA_BUFFER uint16_t tempSensor[2] = {0};
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;
}
}
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.
2021-11-21 11:46 PM
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é
2021-11-22 12:04 AM
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: