我们在本次ADC检测篇,使用的是DMA传输方式,DMA的使用可以减少CPU将外设数据搬运到RAM中,可以减少CPU资源的使用。首先是ADC1的初始化部分。
uint16_t adc11_value[10]; //ADC数据缓冲区
ADC_HandleTypeDef hadc1; //ADC1的结构体
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0}; //规则组配置
hadc1.Instance = ADC1; //ADC1
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV12; //时钟分频
hadc1.Init.Resolution = ADC_RESOLUTION_12B; //12位转换
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; //数据右对齐
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; //扫描模式开启
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; //关闭eoc中断
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE; //连续转换模式
hadc1.Init.NbrOfConversion = 1; //转换通道数量
hadc1.Init.DiscontinuousConvMode = DISABLE; //禁止不连续转换
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //外部触发设为NONE
hadc1.Init.DMAContinuousRequests = ENABLE; //DMA请求
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED; //ADC过载
hadc1.Init.OversamplingMode = DISABLE;
sConfig.Channel = ADC_CHANNEL_11; //通道11
sConfig.Rank = ADC_REGULAR_RANK_1; //规则组通道一
sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; //转换周期
sConfig.SingleDiff = ADC_SINGLE_ENDED; //单端模式
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //ADC自校准
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc11_value,10); //DMA传输
}
初始化部分根据cubemx设置好,选择连续转换,DMA请求。单端模式转换一个通道的电压值。
hdma_adc1.Instance = DMA1_Channel1; //DMA1
hdma_adc1.Init.Request = DMA_REQUEST_ADC1; //ADC1请求DMA
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; //DMA循环传输
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW; //优先级低
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); //外设连接
轮询方法
轮询方法的关键点在于要在while循环中进行ADC转换,由于是软件触发,可以在我们需要的时候,开启ADC转换,避免了在不需要知道电压的情况下,ADC不停地转换。
uint32_t get_adc(ADC_HandleTypeDef *hadc)
{
HAL_ADC_Start(hadc); //开启ADC转换
while(HAL_ADC_PollForConversion(hadc,1)) //判断ADC是否转换完成
return HAL_ADC_GetValue(hadc); //返回ADC值
}
float get_adc_adverage(ADC_HandleTypeDef *hadc,uint8_t ch, uint8_t time)
{
uint16_t ADC_PRIO = 0; //静态变量
uint32_t ADC_SUM = 0;
for(int i=0;i<time;i++)
{
ADC_PRIO = get_adc(hadc); //保存上一回的ADC的值
ADC_SUM += ADC_PRIO; //均值滤波算法
}
return ADC_SUM*3.3/4096/time; //返回电压值
}
DMA中断模式
uint32_t adc_sum_filter,adc_sum=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1)
{
for(int i=0;i<10;i++)
{
adc_sum += adc11_value[i]; //DMA存放数据的地址
}
adc_sum_filter = adc_sum; //保存转换值
adc_sum = 0;
}
}
DMA中断结束的标志是在HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc11_value,10);10个数据发送完毕的时候,进入DMA中断,也就是进入ADC中断回调函数。处理我们收到的数据组,均值滤波。