STM32H7之ADC使用问题(DMA、校准、准确度、频率)

您所在的位置:网站首页 geng格里芬 STM32H7之ADC使用问题(DMA、校准、准确度、频率)

STM32H7之ADC使用问题(DMA、校准、准确度、频率)

2023-10-13 08:35| 来源: 网络整理| 查看: 265

一、ADC+DMA (1)无法进入ADC采集完成中断

DMA采用半字传输16位ADC值,用于存储ADC数据的数组一定是采集数的两倍,否则会产生ADC溢出的错误中断HAL_ADC_ErrorCallback,从而无法进入ADC采集完成中断HAL_ADC_ConvCpltCallback。这里感觉是HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef hadc, uint32_t pData, uint32_t Length)函数传参是uint32_t类型导致的,因为使用的数组是uint16_t类型的,传参的时候强制转换成了uint32_t,没有去看库,有兴趣的可以去看一下。中间还遇见过一个奇怪的现象,就是只要在程序的任何一个地方再申请一个全局变量,ADC就会出现错误中断,觉得可能是哪个数组溢出了,但是找了一圈也没发现有溢出的地方,然后用了STM32CubeMx配置ADC+DMA,看了一下初始化顺序,发现有比较大的差距,修改后就不会再出现这种问题了。

(2)初始化代码 void ADC_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; AdcHandle.Instance = ADC1; AdcHandle.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2; AdcHandle.Init.Resolution = ADC_RESOLUTION_16B; AdcHandle.Init.ScanConvMode = ADC_SCAN_ENABLE; AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV; AdcHandle.Init.LowPowerAutoWait = DISABLE; AdcHandle.Init.ContinuousConvMode = ENABLE; AdcHandle.Init.NbrOfConversion = 3; AdcHandle.Init.DiscontinuousConvMode = DISABLE; AdcHandle.Init.NbrOfDiscConversion = 1; AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START; AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; AdcHandle.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; AdcHandle.Init.OversamplingMode = DISABLE; /* 禁止过采样 */ /* 初始化 ADC */ if (HAL_ADC_Init(&AdcHandle) != HAL_OK) { } /* 校准 ADC,采用偏移校准 */ if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) { } /* 校准 ADC,采用线性度校准 */ if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET_LINEARITY, ADC_SINGLE_ENDED) != HAL_OK) { } /*黄灯对应采集通道*/ sConfig.Channel = ADC_CHANNEL_3; /* 配置使用的 ADC 通道 */ sConfig.Rank = ADC_REGULAR_RANK_1; /* 采样序列里的第 1 个 */ sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5; /* 采样周期 */ sConfig.SingleDiff = ADC_SINGLE_ENDED; /* 单端输入 */ sConfig.OffsetNumber = ADC_OFFSET_NONE; /* 无偏移 */ sConfig.Offset = 0; /* 无偏移的情况下,此参数忽略 */ sConfig.OffsetRightShift = DISABLE; /* 禁止右移 */ sConfig.OffsetSignedSaturation = DISABLE; /* 禁止有符号饱和 */ HAL_ADC_ConfigChannel(&AdcHandle, &sConfig); sConfig.Channel = ADC_CHANNEL_7; /* 配置使用的 ADC 通道 */ sConfig.Rank = ADC_REGULAR_RANK_2; /* 采样序列里的第 1 个 */ HAL_ADC_ConfigChannel(&AdcHandle, &sConfig); sConfig.Channel = ADC_CHANNEL_4; /* 配置使用的 ADC 通道 */ sConfig.Rank = ADC_REGULAR_RANK_3; /* 采样序列里的第 1 个 */ HAL_ADC_ConfigChannel(&AdcHandle, &sConfig); sConfig.Channel = ADC_CHANNEL_9; /* 配置使用的 ADC 通道 */ sConfig.Rank = ADC_REGULAR_RANK_4; /* 采样序列里的第 1 个 */ HAL_ADC_ConfigChannel(&AdcHandle, &sConfig); /* 启动 ADC 的 DMA 方式传输 */ if (HAL_ADC_Start_DMA(&AdcHandle, (uint32_t*)adc_buf, 3) != HAL_OK) { } } void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(adcHandle->Instance==ADC1) { /* ADC1 clock enable */ __HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_4|GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); ADC1DMA_Handler.Instance = DMA1_Stream1; ADC1DMA_Handler.Init.Request = DMA_REQUEST_ADC1; ADC1DMA_Handler.Init.Direction = DMA_PERIPH_TO_MEMORY; ADC1DMA_Handler.Init.PeriphInc = DMA_PINC_DISABLE; ADC1DMA_Handler.Init.MemInc = DMA_MINC_ENABLE; ADC1DMA_Handler.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; ADC1DMA_Handler.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; ADC1DMA_Handler.Init.Mode = DMA_CIRCULAR; ADC1DMA_Handler.Init.Priority = DMA_PRIORITY_LOW; ADC1DMA_Handler.Init.FIFOMode = DMA_FIFOMODE_DISABLE; ADC1DMA_Handler.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; ADC1DMA_Handler.Init.MemBurst = DMA_MBURST_SINGLE; ADC1DMA_Handler.Init.PeriphBurst = DMA_PBURST_SINGLE; if(HAL_DMA_Init(&ADC1DMA_Handler) != HAL_OK) { } __HAL_LINKDMA(&AdcHandle, DMA_Handle, ADC1DMA_Handler); /* DMA interrupt init */ /* DMA1_Stream0_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { /* Peripheral clock disable */ __HAL_RCC_ADC12_CLK_DISABLE(); __HAL_RCC_DMA1_CLK_DISABLE(); HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_4|GPIO_PIN_5); } } void DMA1_Stream1_IRQHandler(void) { HAL_DMA_IRQHandler(&ADC1DMA_Handler); } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle) { //AD采集完成中断可以处理完成后的数据。 } (3)DMA数据不更新

导致这个问题可能有两点:第一点就是RAM使用不正确,因为DMA并不能访问所有的RAM,可以通过更改keil5 RAM来解决,如下图所示,更改选择0x24000000开始的RAM即可,也可以使用__attribute__来指定内存空间。第二是因为H7系列的单片机有cache,如果使能了cache又不使用MPU保护,就会导致DMA数据不更新

1.png

二、校准

之前编写ADC初始化都只是增加了偏移校准,无论是官方例程还是正点原子的例程都是只使用了偏移校准,但是在使用STM32H7芯片的时候发现个别的板子会出现线性度极差的情况,芯片出厂ADC应该都是校准过的,可能因为某种情况导致校准参数不再适用,才会出现这种情况。上面的代码中已经使用了偏移校准和线性度校准。使用校准时注意返回值,会返回超时的问题,这个问题在较旧的库中会出现,解决方法有两个:一就是更新罪行的库,二就是更改库里定义的超时时间。#define ADC_CALIBRATION_TIMEOUT (633600000U) /*!< ADC calibration time-out value */找到这个宏定义,更改为这个数据就可以了。

三、准确度

STM32H7系列的单片机片内集成了16位的ADC,但是在使用过程中发现了许多的问题,下面详细说明一下。

(1)真的是16位精度吗

其实STM32H7的ADC的有效位达不到16位,这个在手册中也是可以查询到的,使用单端方式的输入有效位只有12位多一点,使用差分方式有效位有13位多一点。下图ENOB写了有效位数,在DS13195手册中可以查到,这个手册可以到ST官网搜索的到。 ​

四、频率

ADC的时钟频率看了很多教程发现都说是不能够超过36M,但是其实不是完全正确的。ADC分为直接通道、快速通道、慢速通道,不同通道对应不同的ADC时钟频率。下图就是不同通道对应的ADC时钟频率,在使用的时候需要注意自己使用的是直接通道、快速通道还是慢速通道。至于时钟频率使用不当会产生哪些问题,我就没有再测试了,最好根据手册中来写。 ​ 下图就是ADC通道对应的是快速还是慢速,至于直接通道,只有四个引脚。这个Figure 157是RM0455手册中的,ST官网也可以搜索到。 ​ 下面是DS13195手册中说的四个直接通道 ​



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3