打印
[MM32软件]

【灵动微电子MM32F5330测评】04 ADC+DMA

[复制链接]
346|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 怀揣少年梦 于 2024-8-3 09:43 编辑

一、先来看一下MM32F5330的ADC功能介绍与特性
1、ADC介绍
ADC 是 12 位的逐次逼近型(SAR)模拟数字转换器,可以将模拟信号转换成数字信号。ADC 有可测量内部或外部信号源,其中 ADC1 有 19 路外部输入通道,ADC2 有 17 路外部输入通道和 2 路内部通道。这些 ADC 的通道可以单次、单周期和连续进行转换。根据不同的方式又可以选择普通通道转换、任意通道转换。ADC 的最大输入时钟为 48MHz,它是由 PLL2 和 APB2 时钟(PCLK2)分频产生。
2、特性
  • 高达 3Msps 转换速率;
  • 支持普通通道转换;
  • 单次转换模式:在指定通道完成一次转换;
  • 单周期扫描模式:对所有指定通道(从低序号通道到高序号通道,或从高序号通道到低序号通道)完成一个周期转换;
  • 连续扫描模式:连续执行单周期扫描模式直到软件停止 A/D 转换。若需要修改转换通道只能停止A/D 转换,等待完成寄存器配置再重新开启转换;
  • 支持任意通道转换;
  • 单次转换模式:在指定通道完成一次转换; 单周期扫描模式:在所有指定通道按照通道设置完成一个周期转换;连续扫描模式:连续执行单周期扫描模式直到软件停止 A/D 转换。若需要转换期间修改通道,用户不必停止转换,可配置相应通道寄存器,配置的新通道将在下一个扫描周期进行转换;
  • 注入通道转换自动注入:在任意通道转换方式下,完成任意通道转换后自动开始进行注入通道转换
  • 可编程通道采样时间 最高 12 位可编程分辨率 SAR 支持 DMA 传输 A/D 转换开始条件
  • 软件启动;
  • 触发启动,可配置触发延时。触发源包括:Timer 和 EXTI 模拟看门狗功能。转换结果与指定的阈值区间进行比较,当转换值超出设定的阈值区间时,如果ADC_ADCR.AWDIE 置位,则产生中断;
  • 支持自校准功能,输入时钟 1.5MHz 支持单端、差分、伪差分转换;这个还是很强大的。
  • 支持过采样


二、ADC+DMA编程
1、编写ADC代码
void ADC_Configure(void)
{
    ADC_InitTypeDef  ADC_InitStruct;
    DMA_InitTypeDef  DMA_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);// 使能GPIOA时钟

    ADC_CalibrationConfig(ADC1, 0x1FE);//校准

    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;//12bit模式
    ADC_InitStruct.ADC_Prescaler  = ADC_Prescaler_16; //时钟分频
    ADC_InitStruct.ADC_Mode       = ADC_Mode_Continue;//连续采样
    ADC_InitStruct.ADC_DataAlign  = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_DMACmd(ADC1, ENABLE);//使能DMA

    ADC_SampleTimeConfig(ADC1, ADC_Channel_1, ADC_SampleTime_79_5);//配置
    ADC_SampleTimeConfig(ADC1, ADC_Channel_4, ADC_SampleTime_79_5);
    ADC_SampleTimeConfig(ADC1, ADC_Channel_5, ADC_SampleTime_79_5);

        //使能ADC通道 1 4 5
    ADC_ChannelCmd(ADC1, ADC_Channel_1, ENABLE);
    ADC_ChannelCmd(ADC1, ADC_Channel_4, ENABLE);
    ADC_ChannelCmd(ADC1, ADC_Channel_5, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    /* PA1(RV1) PA4(RV2) PA5(RV3) */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5; // 使能GPIOA时钟,配置为模拟输入模式

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
        //使能ADC
    ADC_Cmd(ADC1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
        
    DMA_DeInit(DMA1_Channel1);
        //初始化DMA参数
    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->ADDATA);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)ADC_Buffer;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStruct.DMA_BufferSize         = 30;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_VeryHigh;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_Reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStruct);
        //配置DMA中断
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
        //使能DMA通道1
    DMA_Cmd(DMA1_Channel1, ENABLE);
        //配置DMA中断
    NVIC_InitStruct.NVIC_IRQChannel = DMA1_CH1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

void ADC_NormalChannel_ContinuousScan_DMA_Interrupt_Sample(void)
{
    uint8_t  i = 0, j = 0;
    uint32_t RVxSum = 0;
    float    RVxAverage[3], RVxVoltage[3];

    printf("\r\nTest %s", __FUNCTION__);

    ADC_Configure();

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    while (1)
    {
        if (0 != ADC_DMA_InterruptFlag)
        {
            ADC_DMA_InterruptFlag = 0;

            for (i = 0; i < 3; i++)
            {
                RVxSum = 0;

                for (j = 0; j < 10; j++)
                {
                    RVxSum += ADC_Buffer[i + j * 3];
                }

                RVxAverage[i] = (float)RVxSum / (float)10.0;
            }

            printf("\r\n");


            RVxVoltage[0] = RVxAverage[0] * (float)3.3 / (float)4096.0;
                        log_d("RV%d Voltage = %0.2f\t", 1, RVxVoltage[0]);
            RVxVoltage[1] = RVxAverage[1] * (float)3.3 / (float)4096.0;
                        log_i("RV%d Voltage = %0.2f\t", 2, RVxVoltage[1]);
                        RVxVoltage[2] = RVxAverage[2] * (float)3.3 / (float)4096.0;
                        log_w("RV%d Voltage = %0.2f\t", 3, RVxVoltage[2]);
                        PLATFORM_DelayMS(500);
        }
    }
}

2、验证
1)ADC三个通道的电压,如图所示。


2)旋转RV1的电位器,如图所示。


3)与万用表对比
使用UT39E+万用表对比测试。
万用表的精度如下图:

实测数据对比
            
万用表
(V)
ADC
(V)
差值(MV)
RV1
3.198
3.21
2
RV2
1.646
1.65
4
RV3
1.742
1.74
2

总结:
1、检测10分钟,ADC数值没有变化,还是比较稳定。
2、与万用表对比,基本相差2-4mv;在要求不是特别高的情况,精度还是不错的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:一切皆有可能

28

主题

382

帖子

2

粉丝