当前位置:   article > 正文

【正点原子STM32连载】第三十五章 多通道ADC采集(DMA读取)实验 摘自【正点原子】APM32E103最小系统板使用指南_rcm_readadcclkfreq

rcm_readadcclkfreq

1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三十五章 多通道ADC采集(DMA读取)实验

本章介绍APM32E103的DMA进行多通道的ADC采集。通过本章的学习,读者将学习到DMA、ADC的使用。
本章分为如下几个小节:
35.1 硬件设计
35.2 程序设计
35.3 下载验证

35.1 硬件设计
35.1.1 例程功能

  1. ADC1采集通道1~7上的电压,并在LCD上显示ADC转换后电压的数字量和换算后的模拟量
  2. LED0闪烁,指示程序正在运行
    35.1.2 硬件资源
  3. LED
    LED0 - PB5
  4. 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  5. ADC1
    通道1 - PA1
    通道2 - PA2
    通道3 - PA3
    通道4 - PA4
    通道5 - PA5
    通道6 - PA6
    通道7 - PA7
  6. DMA1
    通道1
    35.1.3 原理图
    本章实验使用的ADC1为APM32E103的片上资源,因此没有对应的连接原理图。
    35.2 程序设计
    35.2.1 Geehy标准库的DMA驱动
    本章实验中对DMA的操作与上一章实验中对DMA的操作完全一致,因此请见第34.2.1小节中Geehy标准库的DMA驱动的相关内容。
    35.2.2 Geehy标准库的ADC驱动
    本章实验与上一章实验中对ADC的操作十分类似,不过本章实验是配置、使能和读取ADC1的通道1~7,因此请见第34.2.2小节中Geehy标准库的ADC驱动的相关内容。
    35.2.3 ADC驱动
    本章实验的ADC驱动主要负责向应用层提供ADC的初始化和启动ADC的DMA采集函数,同时实现了DMA的中断回调函数。本章实验中,ADC的驱动代码包括adc.c和adc.h两个文件。
    ADC驱动中,对DMA、GPIO、ADC的相关宏定义,如下所示:
/* ADC通道定义 */
#define ADC_ADCX                    ADC1
#define ADC_ADCX_CHY_CLK_ENABLE()   do{ RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1); }while(0)

#define ADC_ADCX_DMAx               DMA1_Channel1
#define ADC_ADCX_DMAx_IRQn          DMA1_Channel1_IRQn
#define ADC_ADCX_DMAx_IRQHandler    DMA1_Channel1_IRQHandler
ADC驱动中,ADC的初始化函数,如下所示:
/**
 * @brief   初始化ADC和DMA
 * @note    公式:TCONV=采样时间+12.5个周期
 *          采样时间由 SMPCYCCFGx[2:0]位控制,最小采样周期为1.5个,当
 *          ADCCLK=14MHz,采样时间为1.5周期:TCONV=1.5l,周期+12.5,周期=14,周期=1us。
 * @param   无
 * @retval  无
 */
void adc_dma_init(uint32_t mar)
{
    ADC_Config_T adc_init_struct;
    GPIO_Config_T gpio_init_struct;
    DMA_Config_T dma_init_struct;
    
    /* 使能ADC时钟 */
ADC_ADCX_CHY_CLK_ENABLE();
/* 置ADC分频因子6,120MHz/6=20MHz,ADC时间频率不能超过20MHz */
RCM_ConfigADCCLK(RCM_PCLK2_DIV_6);
/* 使能ADC1通道1/2/3/4/5/6/7输入引脚端口时钟 */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
    
    if ((uint32_t)ADC_ADCX_DMAx > (uint32_t)DMA2)
    {   /* 使能DMA2时钟 */
        RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA2);
    }
    else
    {   /* 使能DMA1时钟 */
        RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
    }
    
    /* ADC1通道1/2/3/4/5/6/7输入引脚 */
    gpio_init_struct.pin = GPIO_PIN_1 |
                           GPIO_PIN_2 |
                           GPIO_PIN_3 |
                           GPIO_PIN_4 |
                           GPIO_PIN_5 |
                           GPIO_PIN_6 |
                           GPIO_PIN_7;
    gpio_init_struct.mode = GPIO_MODE_ANALOG;        /* 模拟输入 */
    GPIO_Config(GPIOA, &gpio_init_struct);           /* 配置ADC1通道输入引脚 */
    ADC_Reset(ADC_ADCX);                             /* 复位ADC1 */
    
    /* 配置ADC */
    adc_init_struct.mode = ADC_MODE_INDEPENDENT;     /* ADC工作模式:独立模式 */
    adc_init_struct.scanConvMode = ENABLE;           /* 使能扫描模式 */
adc_init_struct.continuosConvMode = ENABLE;      /* 使能连续转换模式 */ 
/* 转换由软件而不是外部触发启动 */
    adc_init_struct.externalTrigConv = ADC_EXT_TRIG_CONV_None;
adc_init_struct.dataAlign = ADC_DATA_ALIGN_RIGHT;/* ADC数据右对齐 */


/* 顺序进行规则转换的ADC通道的数目 */
    adc_init_struct.nbrOfChannel = 7; 
    ADC_Config(ADC_ADCX, &adc_init_struct);      /* 初始化外设ADCx的寄存器 */
    ADC_EnableDMA(ADC_ADCX);                     /* 使能ADC发出DMA请求 */
    ADC_Enable(ADC_ADCX);                        /* 使能指定的ADC1 */
    ADC_ResetCalibration(ADC_ADCX);              /* 使能复位校准 */
    while(ADC_ReadResetCalibrationStatus(ADC1)); /* 读取指定的ADC校准重置状态 */
    ADC_StartCalibration(ADC1);                  /* 开始校验 */
    while(ADC_ReadCalibrationStartFlag(ADC1));   /* 获取指定的ADC校准开始标志 */
    
    /* 配置ADC通道 */
    ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道1 */
                             ADC_CHANNEL_1, 
                             1, 
                             ADC_SAMPLETIME_239CYCLES5);    
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道2 */
			                 ADC_CHANNEL_2, 
2, 
ADC_SAMPLETIME_239CYCLES5);
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道3 */
			                 ADC_CHANNEL_3, 
3, 
ADC_SAMPLETIME_239CYCLES5);
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道4 */
			                 ADC_CHANNEL_4, 
4, 
ADC_SAMPLETIME_239CYCLES5);
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道5 */
			                 ADC_CHANNEL_5, 
5, 
ADC_SAMPLETIME_239CYCLES5);
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道6 */
			                 ADC_CHANNEL_6, 
6, 
ADC_SAMPLETIME_239CYCLES5);
ADC_ConfigRegularChannel(ADC_ADCX, /* 配置指定ADC规则通道7 */
			                 ADC_CHANNEL_7, 
7, 
ADC_SAMPLETIME_239CYCLES5);

    DMA_Reset(ADC_ADCX_DMAx);                     /* 复位DMA1 */
    while (ADC_ADCX_DMAx->CHCFG_B.CHEN != RESET); /* 等待DMA可配置 */
    
    /* 外设基地址 */
dma_init_struct.peripheralBaseAddr = (uint32_t)&ADC1->REGDATA;
/* DMA内存基地址 */
dma_init_struct.memoryBaseAddr = mar;
/* 数据传输方向 */
dma_init_struct.dir = DMA_DIR_PERIPHERAL_SRC;
/* 传输的数据项数目 */
dma_init_struct.bufferSize = 0;
/* 外设增量模式 */
dma_init_struct.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
/* 存储器递增模式 */
dma_init_struct.memoryInc = DMA_MEMORY_INC_ENABLE;
/* 外设数据大小 */
dma_init_struct.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
/* 存储器数据大小 */
dma_init_struct.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
/* 正常工作模式 */
dma_init_struct.loopMode = DMA_MODE_NORMAL;
/* 优先级 */
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
/* DMA通道x没有设置为内存到内存传输 */
dma_init_struct.M2M = DMA_M2MEN_DISABLE; 
/* 配置DMA通道 */
    DMA_Config(ADC_ADCX_DMAx, &dma_init_struct);
    
    DMA_ClearStatusFlag(DMA1_FLAG_TC1);             /* 清除指定DMA通道的标志 */
    NVIC_EnableIRQRequest(ADC_ADCX_DMAx_IRQn, 3, 0);/* 配置DMA中断 */
    DMA_EnableInterrupt(ADC_ADCX_DMAx, DMA_INT_TC); /* 使能DMA通道传输完成中断 */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

从上面的代码中可以看出,本章实验的ADC初始化与上一章实验中的ADC初始化步骤基本一致,不过本章实验的ADC初始化函数中配置了ADC1的通道1~通道7,一共七个通道。
ADC驱动中,启动ADC的DMA采集的函数和DMA的中断回调函数均与上一章实验中的函数一致,请见第34.2.3小节中的相关内容。
35.2.4 实验应用代码
本章实验的应用代码,如下所示:

#define ADC_DMA_BUF_SIZE (7 * 50)                    /* ADC DMA缓冲区大小 */

static uint16_t g_adc_dma_buf[ADC_DMA_BUF_SIZE];     /* ADC DMA缓冲区 */
extern uint8_t g_adc_dma_sta;                        /* DMA传输完成标志 */

int main(void)
{
    uint16_t i;
    uint8_t j;
    uint32_t sum;
    uint16_t adcdata;
    uint16_t voltage;
    
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4); /* 设置中断优先级分组为组4 */
    sys_apm32_clock_init(15);                        /* 配置系统时钟 */
    delay_init(120);                                 /* 初始化延时功能 */
    usart_init(115200);                              /* 初始化串口 */
    led_init();                                      /* 初始化LED */
    lcd_init();                                      /* 初始化LCD */
    adc_dma_init((uint32_t)g_adc_dma_buf);           /* 初始化ADC多通道和DMA */
    adc_dma_enable(ADC_DMA_BUF_SIZE);                /* 使能一次DMA传输ADC数据 */
    
    lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "ADC DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 12, 12, "ADC1_CH1_VAL:", BLUE);
    lcd_show_string(30, 122, 200, 12, 12, "ADC1_CH1_VOL:0.000V", BLUE);
    lcd_show_string(30, 140, 200, 12, 12, "ADC1_CH2_VAL:", BLUE);
    lcd_show_string(30, 152, 200, 12, 12, "ADC1_CH2_VOL:0.000V", BLUE);
    lcd_show_string(30, 170, 200, 12, 12, "ADC1_CH3_VAL:", BLUE);
    lcd_show_string(30, 182, 200, 12, 12, "ADC1_CH3_VOL:0.000V", BLUE);
    lcd_show_string(30, 200, 200, 12, 12, "ADC1_CH4_VAL:", BLUE);
    lcd_show_string(30, 212, 200, 12, 12, "ADC1_CH4_VOL:0.000V", BLUE);
    lcd_show_string(30, 230, 200, 12, 12, "ADC1_CH5_VAL:", BLUE);
    lcd_show_string(30, 242, 200, 12, 12, "ADC1_CH5_VOL:0.000V", BLUE);
    lcd_show_string(30, 260, 200, 12, 12, "ADC1_CH6_VAL:", BLUE);
    lcd_show_string(30, 272, 200, 12, 12, "ADC1_CH6_VOL:0.000V", BLUE);
    lcd_show_string(30, 290, 200, 12, 12, "ADC1_CH7_VAL:", BLUE);
    lcd_show_string(30, 302, 200, 12, 12, "ADC1_CH7_VOL:0.000V", BLUE);
    
    while (1)
    {	  /* 等待DMA传输结束 */
         if (g_adc_dma_sta == 1) 
        {
/* 遍历处理7个通道的数据 */
            for (j = 0; j < 7; j++)
            {
                sum = 0; 
/* 对ADC的多次采样值进行均值滤波 */
                for (i = 0; i < (ADC_DMA_BUF_SIZE / 7); i++)
                {
                    sum += g_adc_dma_buf[(7 * i) + j];
                }
                
                adcdata = sum / (ADC_DMA_BUF_SIZE / 7);
                lcd_show_xnum(114, 110 + (j * 30), adcdata, 5, 12, 0, BLUE);
/* 计算实际电压值(扩大1000倍) */
                voltage = (adcdata * 3300) / 4095;
                lcd_show_xnum(108,
122 + (j * 30),
voltage / 1000,
1,
12,
0,
BLUE);
/* 显示电压值的小数部分(保留三位小数) */
                lcd_show_xnum(118, 
122 + (j * 30),
voltage % 1000,
3,
12,
0x80,
BLUE);
                g_adc_dma_sta = 0;                /* 清除DMA传输结束标志 */
                adc_dma_enable(ADC_DMA_BUF_SIZE); /* 使能下一次DMA传输ADC数据 */
            }
        }
        
        LED0_TOGGLE();
        delay_ms(100);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

本章实验的应用代码与上一章实验中的引用代码基本一致,只不过本章实验要处理的是7个ADC通道的数据,因此DMA传输的目的存储器容量的定义变大了,随后便可在DMA传输完成后,采用与上一章实验一样的处理方式处理ADC采集并转换后的数据,最后将每个通道采集到电压的数字量和模拟量显示置LCD上。
35.3 下载验证
在完成编译和烧录操作后,可以看到LCD上不断地刷新显示ADC1通道1通道7(PA1引脚PA7引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA1至PA7中的任意一个或多个引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/172530
推荐阅读
相关标签
  

闽ICP备14008679号