STM32CubeMX ADC

ADC转换时间:

采样周期+12.5个时钟周期;
例如:采样使用1.5个时钟周期+12.5个时钟周期 = 14个时钟周期

ADC配置为最高时钟周期:14Mhz(f103zet6),那么转换时间为:14/(14*10^6)=1us.

DMA方式:使用ADC1/3测试

1. 选择使用的通道

 

 2. 参数配置 独立模式、数据右对齐、扫描转换(多通道默认配置)、连续扫描、使能常规转换组、6通道、软件触发、Rank单独配置,否则全部默认一样的,到时候采样值全是第一个通道的

 

 3.DMA

 

 

 

 F103板子12位AD,半字足够了。

同样的配置,配置了ADC3的3个通道。

4. 中断配置(看一眼,全局中断是否开启是否影响DMA中断,待测试(反正开启没问题))

 

 5.代码修改:在生成的代码中

main.c 在系统初始化函数后面,初始化ADC1、ADC3:

    HAL_ADCEx_Calibration_Start(&hadc1);//ADC校准
    HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC1_convert,adc1_convert_num);
    HAL_ADCEx_Calibration_Start(&hadc3);//ADC校准
    HAL_ADC_Start_DMA(&hadc3,(uint32_t *)ADC3_convert,adc3_convert_num);

adc.h 添加:全局定义DMA转存采样值地址与采样数据量、ADC通道数

#define adc1_convert_num 30  //ADC1转化数据量,,每转换30个数据 DMA进行一次数据转移
#define adc1_channel 6            //ADC1使用的通道数 6个
#define adc3_convert_num 9    //ADC3转化数据量,每转换9个数据 DMA进行一次数据转移
#define adc3_channel 3            //ADC3使用的通道数 3个

//extern uint32_t ADC1_convert[10][6];//转换数据缓存,定义60个,用了一半
extern uint16_t ADC1_convert[10][6];//转换数据缓存
//extern uint32_t ADC3_convert[6][3];//转换数据缓存,定义18个,只用了一半
extern uint16_t ADC3_convert[6][3];//转换数据缓存

ADC配置的通道按从小到大的顺序依次写入pData中,例如 channel1,2,3,11,12,13,对应在pData[0] 、pData[1]  、pData[2]  、pData[3]  、pData[4]  、pData[5] 中 ,函数详细说明在任务函数段。

HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)

adc.c 添加:

/* 对应CUbemx里DMA设置的转存数据位宽
** Word: uint32_t
** HalfWord: uint16_t, 在开启ADC_DMA时,只需强制转化地址指针,例如/
** HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC1_convert,24);
*/
//uint32_t ADC1_convert[10][6] = {0};//ADC1数据转存,每次可存储10组X6通道数据,每次刷新定义的数据量adc1_convert_num 个数
uint16_t ADC1_convert[10][6] = {0};//ADC1数据转存,每次可存储10组X6通道数据,每次刷新定义的数据量adc1_convert_num 个数

//uint32_t ADC3_convert[6][3];//ADC3数据转存,每次可存储6组X3通道数据,每次刷新定义的数据量adc3_convert_num 个数
uint16_t ADC3_convert[6][3];//ADC3数据转存,每次可存储6组X3通道数据,每次刷新定义的数据量adc3_convert_num 个数
/* USER CODE END 0 */

添加标志位与转存区域(区别于使用DMA将ADC转换值转存区,这里是从转存区再进行一次转移):

uint8_t ADC1_Rec_Flag;//ADC1转换完成标志
uint8_t ADC2_Rec_Flag;//ADC2转换完成标志
uint8_t ADC3_Rec_Flag;//ADC3转换完成标志

uint16_t ADC1_temp[10][6] = {0};//ADC1转化数据转存
//uint32_t ADC1_temp[10][6] = {0};//
uint16_t ADC3_temp[6][3] = {0};//ADC3转化数据转存
//uint32_t ADC3_temp[6][3] = {0};//

任务函数添加:

void Start_Task2(void const * argument)
{
  /* USER CODE BEGIN Start_Task2 */
    uint16_t task2_ram_availa =0;
    uint8_t loop1 = 0,loop2 = 0;//循环次数计数
  /* Infinite loop */
  for(;;)
  {
        if(ADC1_Rec_Flag ==1)
        {
            ADC1_Rec_Flag = 0;
            /*停止DMA转存    HAL_ADC_Stop_DMA();
            ** 1. 用于将采样数据从缓冲区转移至指定区域,后再开启DMA,期间数据不更新,避免同时读写
            ** 2. 处理数据错位问题(经测试,不停止DMA对数据连续性没有影响,该用法保留)
            */
            HAL_ADC_Stop_DMA(&hadc1);
            for(loop1 = 0;loop1<adc1_convert_num/adc1_channel;loop1++)
            {
                for(loop2 =0;loop2<adc1_channel;loop2++)
                {
                    ADC1_temp[loop1][loop2] = ADC1_convert[loop1][loop2];
                }                
            }
            /* HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
            ** hadc: 当前使用的ADC编号
            ** pData:ADC采样后,DMA转存采样值的保存地址,
            ** Length:数据量,为配置的通道数n的倍数,假设为K,每次完成Length=N*k个数据的转换后,调用回调函数/
            ** void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc); 
            */
            HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADC1_convert,adc1_convert_num);
        }
        if(ADC3_Rec_Flag ==1)
        {
            ADC3_Rec_Flag = 0;
                
            /*停止DMA转存    HAL_ADC_Stop_DMA();
            ** 1. 用于将采样数据从缓冲区转移至指定区域,后再开启DMA,期间数据不更新,避免同时读写
            ** 2. 处理数据错位问题(经测试,不停止DMA对数据连续性没有影响,该用法保留)
            */
            HAL_ADC_Stop_DMA(&hadc3);
            for(loop1 = 0;loop1<adc3_convert_num/adc3_channel;loop1++)
            {
                for(loop2 =0;loop2<adc3_channel;loop2++)
                {
                    ADC3_temp[loop1][loop2] = ADC3_convert[loop1][loop2];
                }                
            }
            /* HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)
            ** hadc: 当前使用的ADC编号
            ** pData:ADC采样后,DMA转存采样值的保存地址,
            ** Length:数据量,为配置的通道数n的倍数,假设为K,每次完成Length=N*k个数据的转换后,调用回调函数/
            ** void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
            */
            HAL_ADC_Start_DMA(&hadc3,(uint32_t *)ADC3_convert,adc3_convert_num);
        }
        count_task2++;
        task2_ram_availa = uxTaskGetStackHighWaterMark(Task2Handle);//获取最高任务栈剩余量        
        printf("TASK2!__ %d,task2_ram_availa: %d\r\n",count_task2,task2_ram_availa);
    osDelay(5000);
  }
  /* USER CODE END Start_Task2 */
}

重定义AD转换完成回调函数:void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

每个ADC在所有数据量采集完毕后,调用转换完成回调函数;可以将部分数据转移或处理事件放在这里,主函数值关心总数据集处理。

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{    //判断转换完成的AD号,同时判断转换完成标志位是否设置 EOC :End Of Conversion
    if((hadc == &hadc1)&&(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),HAL_ADC_STATE_REG_EOC)))
    {
        ADC1_Rec_Flag = 1;
    }
//    if((hadc == &hadc2)&&(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2),HAL_ADC_STATE_REG_EOC)))
//    {
//        ADC2_Rec_Flag = 1;
//    }
    if((hadc == &hadc3)&&(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc3),HAL_ADC_STATE_REG_EOC)))
    {
        ADC3_Rec_Flag = 1;
    }
}
在hal库中定义的有,右键查看:
//只能用于判断单独一位,除需要判断的标志位外,其余为0 如 REG=0x0200 BIT=0x0200
 #define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != 0U)//REG标志位1 BIT标志位 1,该Flag 有效(无效,自定义)

 #define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == 0U)//REG标志位1 BIT标志位 0,该Flag 无效(有效,自定义)

普通模式与中断模式:使用ADC2测试

参考自:Stm32cubeMx配置ADC多通道采集 – 电子创客营 (eemaker.com)

问题测试记录:

1. 不使用dma,使用中断模式,多通道,扫描转化模式,既不连续,也不不连续;

结果:只采样一次,多个通道全部完成后,调用回调函数与中断函数,HAL_ADC_GetValue(&hadcx)只采到 最后一个通道值。

正常配置:

1.CubeMX配置,扫描转换模式,非连续模式、非连续模式下,每次转换 1个,通道口、转换时间配置

 开启全局中断:

 2.  添加函数:

普通方式函数也可以参考: STM32HAL库CubeMX配置ADC多通道选择读取(非DMA)_支吾小白的博客-CSDN博客

#define Adc_Ready_Time_out 8 //等待ADC DR寄存器写入时间

#define Ad_Use_IT        //ADC2使用中断方式采样
//#define Ad_Use_Normal//ADC2使用一般模式采样
......
uint16_t ADC2_temp[3] = {0}; //ADC2转化数据转存
//uint32_t ADC2_temp[3] = {0}; //ADC2转化数据转存

uint16_t timeout = Adc_Ready_Time_out;
uint8_t  loop3 = 0; //控制循环次数    
......
while(1)
{
/************** 非DMA方式\中断方式,存在问题:数据错位,前两个被抛弃问题 **************************************
        ** 使用HAL_ADC_Start(),HAL_ADC_Start()写在for循环中,循环次数为使用的通道数量,转换数据按照从下到大的顺序依次写入到地址中/
        **数据错位原因:for循环中,使用HAL_ADC_Start();之后直接跟HAL_ADC_GetValue()函数,会导致转换还未完成就直接取值,最终导致循环取值数据错位;/
            /但使用 HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),HAL_ADC_STATE_REG_EOC);判断,转换完成标志确实被置位了,数据依旧是错位的,所以使用标志位判断:/
            /while(!(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),HAL_ADC_STATE_REG_EOC)));不起作用。
        **解决: 加一小段延时解决,等待ADC的DR寄存器写入后再取值。测试后,对于f103的板子,最小延时时间应该大于等于0.124ms。
        ** 使用HAL_ADC_Start(),不会触发转换完成回调函数 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc);
        ** Cubemx配置:
        ** Scan Conversion Mode :Enable
        ** Continuous Conversion Mode: Disable
        ** Discontinuous Conversion Mode: Enable
        ** Number Of Discontinuous Conversions: 1
*/    
#ifdef Ad_Use_Normal        
        for(loop3=0;loop3<3;loop3++)
        {        
            HAL_ADC_Start(&hadc2);
            //while(!(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1),HAL_ADC_STATE_REG_EOC)));
            while(timeout--);
            timeout = Adc_Ready_Time_out;            
            ADC2_temp[loop3] = HAL_ADC_GetValue(&hadc2);
        }
        HAL_ADC_Stop(&hadc2);
#endif
        
/************** 中断方式;存在问题:数据错位,前两个被抛弃问题(与普通方式一致)********************************************************
        ** 使用HAL_ADC_Start_IT(),HAL_ADC_Start_IT()写在for循环中,循环次数为使用的通道数量,转换数据按照从下到大的顺序依次写入到地址中/
            / 在完成所有通道的数据采集与转换后先调用中断函数: void ADCx_IRQHandler(void),再调用回调函数 /
            / void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc); 所以说对于单个通道而言,可以采取在中断函数或者回调函数里定义flag来判断是否转换/
       / 进而主函数或任务根据flag来读取值,但对于多通道而言,使用这种方法只能得到最后一通道的值,之前的值被覆盖。还未解决,对于多通道,还是考虑用DMA方式、普通模式、定时器方式
**数据错位解决: 加一小段延时解决,等待ADC的DR寄存器写入后再取值。测试后,对于f103的板子,最小延时时间应该大于等于0.124ms。 ** Cubemx配置: ** Scan Conversion Mode :Enable ** Continuous Conversion Mode: Disable ** Discontinuous Conversion Mode: Enable ** Number Of Discontinuous Conversions: 1
*/ #ifdef Ad_Use_IT for(loop3=0;loop3<3;loop3++) { HAL_ADC_Start_IT(&hadc2); while(timeout--); timeout = Adc_Ready_Time_out; ADC2_temp[loop3] = HAL_ADC_GetValue(&hadc2); } HAL_ADC_Stop_IT(&hadc2); #endif }

 

posted @ 2022-08-19 20:05  百叶集  阅读(1503)  评论(0编辑  收藏  举报