硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1241|回复: 10
收起左侧

[ADC] H7 调试ADC遇到问题

[复制链接]

3

主题

8

回帖

17

积分

新手上路

积分
17
发表于 2021-11-1 23:08:15 | 显示全部楼层 |阅读模式
  采用版上的V7-019_ADC定时器触发+DMA双缓冲实现例程,稍微加以改动,TIME1的触发时钟改为了1MHz,预期实现1MHz 16位的单路AD采样,AD接到PB1(CH5)上,调试了一周发现采样的AD值跳动非常大,现在不知道如何下手解决,求指点。

硬件用的是STM32H750V,100PIN的,硬件相关电路如下。目前测试接入一个电压,AD采样值跳动无规律,上百个字都有。





相关代码改动如下:
/* 选择ADC的时钟源 */
//#define ADC_CLOCK_SOURCE_AHB     /* 选择AHB时钟源 */
#define ADC_CLOCK_SOURCE_PLL     /* 选择PLL时钟源 */

/* 方便Cache类的API操作,做32字节对齐 */
uint16 temp[1024];
ALIGN_32BYTES(__attribute__((at(0x38000000))) uint16_t ADCxValues[1024]);

__IO uint8_t s_DmaFlag = 0;  /* 1表示进DMA半传输完成中断,2表示进入DMA传输完成中断 */
DMA_HandleTypeDef   DmaHandle = {0};       

static void TIM1_Config(void)
{
        TIM_HandleTypeDef  htim ={0};
        TIM_OC_InitTypeDef sConfig = {0};

        __HAL_RCC_TIM1_CLK_ENABLE();        
    HAL_TIM_Base_DeInit(&htim);
    //配置TIM1 1MHz
    htim.Instance = TIM1;
        htim.Init.Period            = 199;
        htim.Init.Prescaler         = 0;
        htim.Init.ClockDivision     = 0;
        htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
        htim.Init.RepetitionCounter = 0;
        HAL_TIM_Base_Init(&htim);

    sConfig.OCMode     = TIM_OCMODE_PWM1;
    sConfig.OCPolarity = TIM_OCPOLARITY_LOW;

    //占空比50%
    sConfig.Pulse = 500;
        HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1);
        HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1);
}

void InitADC(void)
{
    ADC_HandleTypeDef   AdcHandle = {0};
        ADC_ChannelConfTypeDef   sConfig = {0};
        GPIO_InitTypeDef         GPIO_InitStruct;
       
        /* ## - 1 - 配置ADC采样的时钟 ####################################### */
#if defined (ADC_CLOCK_SOURCE_PLL)
        /* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        PeriphClkInitStruct.PLL2.PLL2M = 25;
        PeriphClkInitStruct.PLL2.PLL2N = 504;
        PeriphClkInitStruct.PLL2.PLL2P = 7;
        PeriphClkInitStruct.PLL2.PLL2Q = 7;
        PeriphClkInitStruct.PLL2.PLL2R = 7;
        PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;
        PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;
        PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
        PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
        HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
#elif defined (ADC_CLOCK_SOURCE_AHB)

  /* 使用AHB时钟的话,无需配置,默认选择*/

#endif

        /* ## - 2 - 配置ADC采样使用的引脚 ####################################### */
        __HAL_RCC_GPIOB_CLK_ENABLE();

        GPIO_InitStruct.Pin = GPIO_PIN_1;
        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* ## - 3 - 配置ADC采样使用的时钟 ####################################### */
        __HAL_RCC_DMA1_CLK_ENABLE();
        DmaHandle.Instance                 = DMA1_Stream1;            /* 使用的DMA1 Stream1 */
        DmaHandle.Init.Request             = DMA_REQUEST_ADC1;            /* 请求类型采用DMA_REQUEST_ADC1 */  
        DmaHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;    /* 传输方向是从外设到存储器 */  
        DmaHandle.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */
        DmaHandle.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  
        DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  /* 外设数据传输位宽选择半字,即16bit */     
        DmaHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;  /* 存储器数据传输位宽选择半字,即16bit */   
        DmaHandle.Init.Mode                = DMA_CIRCULAR;            /* 循环模式 */   
        DmaHandle.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */  
        DmaHandle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;    /* 禁止FIFO*/
        DmaHandle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 禁止FIFO此位不起作用,用于设置阀值 */
        DmaHandle.Init.MemBurst            = DMA_MBURST_SINGLE;       /* 禁止FIFO此位不起作用,用于存储器突发 */
        DmaHandle.Init.PeriphBurst         = DMA_PBURST_SINGLE;       /* 禁止FIFO此位不起作用,用于外设突发 */
        HAL_DMA_Init(&DmaHandle);

    /* 开启DMA1 Stream1的中断 */
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);

    /* 关联ADC句柄和DMA句柄 */
        __HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);

        /* ## - 4 - 配置ADC ########################################################### */
        __HAL_RCC_ADC12_CLK_ENABLE();
        AdcHandle.Instance = ADC1;
#if defined (ADC_CLOCK_SOURCE_PLL)
        AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;          /* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */
#elif defined (ADC_CLOCK_SOURCE_AHB)
        AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      /* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */
#endif
        AdcHandle.Init.Resolution            = ADC_RESOLUTION_16B;            /* 16位分辨率 */
        AdcHandle.Init.ScanConvMode          = ADC_SCAN_DISABLE;              /* 禁止扫描,因为仅开了一个通道 */
        AdcHandle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;           /* EOC转换结束标志 */
        AdcHandle.Init.LowPowerAutoWait      = DISABLE;                       /* 禁止低功耗自动延迟特性 */
        AdcHandle.Init.ContinuousConvMode    = DISABLE;                       /* 禁止自动转换,采用的定时器触发转换 */
        AdcHandle.Init.NbrOfConversion       = 1;                             /* 使用了1个转换通道 */
        AdcHandle.Init.DiscontinuousConvMode = DISABLE;                       /* 禁止不连续模式 */
        AdcHandle.Init.NbrOfDiscConversion   = 1;                             /* 禁止不连续模式后,此参数忽略,此位是用来配置不连续子组中通道数 */
        AdcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIG_T1_CC1;            /* 定时器1的CC1触发 */
        AdcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;    /* 上升沿触发 */
        AdcHandle.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR; /* DMA循环模式接收ADC转换的数据 */
        AdcHandle.Init.BoostMode                = ENABLE;                          /* ADC时钟超过20MHz的话,使能boost */
        AdcHandle.Init.Overrun                  = ADC_OVR_DATA_OVERWRITTEN;        /* ADC转换溢出的话,覆盖ADC的数据寄存器 */
        AdcHandle.Init.OversamplingMode         = DISABLE;                         /* 禁止过采样 */
        HAL_ADC_Init(&AdcHandle);
        HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);

        /* 配置ADC通道  */
        sConfig.Channel      = ADC_CHANNEL_5;              /* 配置使用的ADC通道 */
        sConfig.Rank         = ADC_REGULAR_RANK_1;          /* 采样序列里的第1个 */
        sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES_5;     /* 采样周期 */
        sConfig.SingleDiff   = ADC_SINGLE_ENDED;            /* 单端输入 */
        sConfig.OffsetNumber = ADC_OFFSET_NONE;             /* 无偏移 */
        sConfig.Offset = 0;                                 /* 无偏移的情况下,此参数忽略 */
        HAL_ADC_ConfigChannel(&AdcHandle, &sConfig);

        /* ## - 5 - 配置ADC的定时器触发 ####################################### */
        TIM1_Config();
        HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *)ADCxValues, 1024);
}

void DMA1_Stream1_IRQHandler(void)
{
        /* 传输完成中断 */
        if((DMA1->LISR & DMA_FLAG_TCIF1_5) != RESET)
        {
               
                /*
                   1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
                   2、进入传输完成中断,当前DMA正在使用缓冲区的前半部分,用户可以操作后半部分。
                */
                SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[512]), 1024);               
                s_DmaFlag = 2;
               
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_TCIF1_5;
        }

        /* 半传输完成中断 */   
        if((DMA1->LISR & DMA_FLAG_HTIF1_5) != RESET)
        {
                /*
                   1、使用此函数要特别注意,第1个参数地址要32字节对齐,第2个参数要是32字节的整数倍。
                   2、进入半传输完成中断,当前DMA正在使用缓冲区的后半部分,用户可以操作前半部分。
                */
                SCB_InvalidateDCache_by_Addr((uint32_t *)(&ADCxValues[0]), 1024);               
                s_DmaFlag = 1;

                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_HTIF1_5;
        }

        /* 传输错误中断 */
        if((DMA1->LISR & DMA_FLAG_TEIF1_5) != RESET)
        {
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_TEIF1_5;
        }

        /* 直接模式错误中断 */
        if((DMA1->LISR & DMA_FLAG_DMEIF1_5) != RESET)
        {
                /* 清除标志 */
                DMA1->LIFCR = DMA_FLAG_DMEIF1_5;
        }
}

void GetAdcValues(void)
{
        uint16  i;
        uint8 Byte0,Byte1,Byte2;
        //当前DMA操作是后半部分缓冲,发送前半部分缓冲
        if(s_DmaFlag == 1)
        {
                s_DmaFlag = 0;
                if(StartChannelCodeEn)
                {
                        uint8 Response[1042]= {0x80,0x00,0x70};
                        ControlBox01.StdCastByts = 1025;
                        IntToBytes(ControlBox01.StdCastByts,&Byte0,&Byte1,&Byte2);               
                        Response[15] = Byte1;
                        Response[16] = Byte2;                       
                        Response[17]= StdRollCounter++;
                        for(i=0;i<512;i++)
                        {
                                Response[i*2+18] = (uint8)(ADCxValues>>8);
                                Response[i*2+19] = (uint8)ADCxValues;
               
                        }
                        EthRequestResp(udppcb,Response);                                               
                }
        }
        //当前DMA操作是后前部分缓冲,发送后半部分缓冲
        else if(s_DmaFlag == 2)
        {
                s_DmaFlag = 0;
                if(StartChannelCodeEn)
                {
                        uint8 Response[1042]= {0x80,0x00,0x70};
                        ControlBox01.StdCastByts = 1025;
                        IntToBytes(ControlBox01.StdCastByts,&Byte0,&Byte1,&Byte2);               
                        Response[15] = Byte1;
                        Response[16] = Byte2;                       
                        Response[17]= StdRollCounter++;
                        for(i=512;i<1024;i++)
                        {
                                Response[(i-512)*2+18] = (uint8)(ADCxValues>>8);
                                Response[(i-512)*2+19] = (uint8)ADCxValues;
               
                        }
                        EthRequestResp(udppcb,Response);                                               
                }
        }
}


回复

使用道具 举报

3

主题

8

回帖

17

积分

新手上路

积分
17
 楼主| 发表于 2021-11-1 23:14:52 | 显示全部楼层
采样时间用的是ADC_SAMPLETIME_8CYCLES_5,图上我在调整增加采样时间有没有效果,还是同样表现
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2021-11-2 09:13:42 | 显示全部楼层
求是虫 发表于 2021-11-1 23:14
采样时间用的是ADC_SAMPLETIME_8CYCLES_5,图上我在调整增加采样时间有没有效果,还是同样表现

方便的话,发个开启过采样和没有开启过采样的效果对比图。
回复

使用道具 举报

5

主题

61

回帖

76

积分

初级会员

积分
76
发表于 2021-11-2 12:39:03 | 显示全部楼层
AD采样稳不稳,一方面是软件问题,但大部分为硬件设计部分未设计好
回复

使用道具 举报

3

主题

8

回帖

17

积分

新手上路

积分
17
 楼主| 发表于 2021-11-2 13:01:23 | 显示全部楼层
稍等,我得处理下。目前没有考虑用过采样,1MHz已经是这个100PIN极限了,感觉是AD的配置没对,采集的AD值就是杂乱无章的。检查代码又没发现问题。

硬件电路图好像不能上传,REF+接的+3.3VA,采用外部基准电压,采样电路就是用了分压电阻,分别测了5V和GND,跳动很大,不是过采样能覆盖的。是否需要试试8字节的?
回复

使用道具 举报

3

主题

8

回帖

17

积分

新手上路

积分
17
 楼主| 发表于 2021-11-2 13:01:57 | 显示全部楼层
谢谢老大回复!
回复

使用道具 举报

5

主题

61

回帖

76

积分

初级会员

积分
76
发表于 2021-11-4 09:15:40 | 显示全部楼层
硬汉兄
    HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
    HAL_ADCEx_Calibration_Start(&hadc1,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
这两条语句后, 每次上电这个ADC采样不是固定的偏置,这个比较麻烦,对我做的东西精度有影响,您是如何
解决的,是上电后自校准吗?
回复

使用道具 举报

3

主题

8

回帖

17

积分

新手上路

积分
17
 楼主| 发表于 2021-11-4 11:18:11 | 显示全部楼层
dghwjh 发表于 2021-11-4 09:15
硬汉兄
    HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
    HA ...

您好!你有用0.5us的采样吗,跳动有多少个字吗
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2021-11-5 08:18:41 | 显示全部楼层
dghwjh 发表于 2021-11-4 09:15
硬汉兄
    HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
    HA ...

线性校准的话,注意这个问题没有

STM32H7的HAL库ADC线性度校准溢出时间设计小了,否则校准不成功,要特别注意!
http://www.armbbs.cn/forum.php?m ... 1436&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

5

主题

61

回帖

76

积分

初级会员

积分
76
发表于 2021-11-5 13:15:32 | 显示全部楼层
可能,我的意思没表答清楚, 再表达一下,就是每次下电再上电,经过校准函数,读取AD值不是固定的值,上下波动,是在另一个台阶上下波动
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107049
QQ
发表于 2021-11-6 09:18:49 | 显示全部楼层
dghwjh 发表于 2021-11-5 13:15
可能,我的意思没表答清楚, 再表达一下,就是每次下电再上电,经过校准函数,读取AD值不是固定的值,上下 ...

这个问题没遇到过。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-15 22:12 , Processed in 0.380475 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表