配置 ADC
模式: 如果只启用了一个ADC, 这里只能配置为Independent mode
时钟分频: 这个选项是ADC的预分频器, 可设置为2/4/6/8, 决定了一个ADC时钟周期. 加入设置为2, 由于ADC是挂载在APB2总线(84M)上, 所以一个ADC时钟便是84 * M/2=42M
分辨率: 最高为12位分辨率, 分辨率越高转换时间越长
数据对齐方式: 如果选择12位分辨率, 右对齐, 得到的结果最大便是4096.
扫描模式: 转换完一个通道会不会继续转换下一个通道
连续转换模式: 使能的话转换将连续进行
不连续转换模式: 当使能多个转换通道时, 可单独设置不连续转换通道.
DMA连续请求: 是否连续请求DMA.
EOC标志设置: 当有多个转换通道时, 是每转换完一个通道设置一次EOC标志还是所有通道都转换完设置一次EOC标志.
转换的通道数:
触发模式: 可选择软件触发, 外部触发或定时器事件触发
秩序列表: 设置转换周期数和转换顺序
注入通道设置
窗口看门狗模式
配置 ADC 为主动请求模式
while (1)
{
/*##-1- Start the conversion process #######################################*/
HAL_ADC_Start(&hadc1);
/*##-2- Wait for the end of conversion #####################################*/
/* Before starting a new conversion, you need to check the current state of
the peripheral; if it’s busy you need to wait for the end of current
conversion before starting a new one.
For simplicity reasons, this example is just waiting till the end of the
conversion, but application may perform other tasks while conversion
operation is ongoing. */
HAL_ADC_PollForConversion(&hadc1, 50);
/* Check if the continous conversion of regular channel is finished */
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
/*##-3- Get the converted value of regular channel ######################*/
AD_Value = HAL_ADC_GetValue(&hadc1);
printf("MCU Temperature : %.1f¡ærn",((AD_Value*3300/4096-760)/2.5+25));
}
HAL_Delay(1000);
}
配置 ADC 为多通道连续扫描DMA模式
开ADC的IN0/IN1两个通道
在Pinout图上, 将PA0和PA1设为ADC1_IN0和ADC2_IN1
配置时钟
ADC1相关配置
ADCs_Common_Settings
Mode: Independent mode
ADC_Settings
Clock Prescaler: PCLK2 divided by 4 可以在时钟配置页看到PCLK2的值
Resolution: 12bits (15 ADC Clock cycles) 采样精度12bit, 此时每次采样需要15个时钟周期, 8bit对应11个时钟周期
Data Alignment: Right alignment
Scan Conversion Mode: Enabled
Continuous Conversion Mode: Enabled --> for DMA
Discontinuous Conversion Mode: Disabled
DMA Continuous Requests: Enabled
End Of Conversion Selection: EOC flag at the end of single channel conversion
ADC_Regular_ConversionMode
Number of Conversion: 2 --> 2 channels
External Trigger Conversion Source: Regular Conversion launched by software
External Trigger Conversion Edge: None
Rank: 1: Choose channel 0
Rank: 2: Choose channel 1
ADC_Injected_ConversionMode
Number of Conversions: 0
DMA相关配置
ADC1
Stream: DMA2 Stream 4
Direction: Peripheral To Memory
Priority: High
DMA Request Settings
Mode: Circular
Increment Address: Memory
Datawidth: Peripheral->Half Word, Memory->Half Word
NVIC Settings
ADC1 global interrupt: Enabled unchecked
DMA2 stream4 global interrupt: Enabled checked
ADC+DMA配置, 体现在代码上的变化
stm32f4xx_hal_conf.h 去掉了ADC的注释
#define HAL_ADC_MODULE_ENABLED
stm32f4xx_it.h 增加了方法声明
void DMA2_Stream4_IRQHandler(void);
stm32f4xx_it.c 增加了对应的typeDef和方法定义
extern DMA_HandleTypeDef hdma_adc1;
void DMA2_Stream4_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_adc1);
}
stm32f4xx_hal_msp.c
extern DMA_HandleTypeDef hdma_adc1;
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hadc->Instance==ADC1)
{
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream4;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance==ADC1)
{
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1);
/* ADC1 DMA DeInit */
HAL_DMA_DeInit(hadc->DMA_Handle);
}
}
main.c
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);
}
最后, 在main.c中增加用于存储DMA数据的数组, 将数组地址传给HAL_ADC_Start_DMA()开启DMA传输就可以得到数据了.
DMA数组大小和中断的问题
数组的大小与HAL_ADC_Start_DMA()方法第三个参数length一致, 这里length代表的是数据的个数. 在设置这个大小时, 如果开启了DMAx_Streamx_IRQn的中断, 要考虑sConfig.SamplingTime指定的采样时间不能太短, 太短的话会一直卡在中断里(因为中断什么都不做也需要时间). 这个与SYSCLK大小无关, 在两个通道采样时
如果这里指定的值为ADC_SAMPLETIME_3CYCLES, 这个数组大小至少为6, 如果等于4采样循环会卡住
如果指定的值为ADC_SAMPLETIME_15CYCLES, 这个数组大小至少为4
如果指定的值为ADC_SAMPLETIME_28CYCLES, 数组大小可以为2
如果不需要使用DMA中断, 可以在 MX_DMA_Init()方法中, 将HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);这句注释掉或者改成HAL_NVIC_DisableIRQ(DMA2_Stream4_IRQn);指定禁用它, 这个数组就可以设到最小(和采样通道数一致)了.
uint16_t ADC_Value[6];
main(void) {
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC_Value, 6); // Enable DMA transfer
while (1)
{
printf("%d %d %d %drn",
ADC_Value[0], ADC_Value[1], ADC_Value[2], ADC_Value[3]);
HAL_Delay(100);
}
}
DMA中断处理回调
查看代码可以看到, 在stm32f4xxx_hal_dma.h中, 定义的 DMA_HandleTypeDef 类型中, 包含了几个对应中断的处理方法
typedef struct __DMA_HandleTypeDef
{
DMA_Stream_TypeDef *Instance; /*!< Register base address */
DMA_InitTypeDef Init; /*!< DMA communication parameters */
HAL_LockTypeDef Lock; /*!< DMA locking object */
__IO HAL_DMA_StateTypeDef State; /*!< DMA transfer state */
void *Parent; /*!< Parent object state */
void (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA transfer complete callback */
上一篇:STM32F401+nRF24L01无线传输音频(对讲机原型)
下一篇:Keil MDK STM32系列(七) STM32F4基于HAL的PWM和定时器
设计资源 培训 开发板 精华推荐
- ADR525A 2.5 Vout 高精度并联模式基准电压源的典型应用
- LTC3859AIFE 双输出、降压/降压/升压同步控制器的典型应用电路,具有改进的突发模式操作
- Sg1825C 用于电压放大器连接的高速电流模式 PWM 的典型应用
- SPV2021-LQPF64核心板
- TEA1721ADB1060: TEA1721 Non-Isolated Buck/Boost Converter Demo Board
- 适用于MSO8封装的双运放系列裸PCB评估板
- STM32F411
- DC2242A,LT8494EFE SEPIC 演示板,3V = VIN = 60V VOUT = 5V @ 1A
- 用于 48V、500W 电动自行车/踏板车的 LTC2992CMS 功率监视器的典型应用
- 用于消费电子产品的 4W、4.98 至 5.03V DC 至 DC 单路输出电源