赞
踩
1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban
本章介绍APM32E103的DMA进行多通道的ADC采集。通过本章的学习,读者将学习到DMA、ADC的使用。
本章分为如下几个小节:
35.1 硬件设计
35.2 程序设计
35.3 下载验证
35.1 硬件设计
35.1.1 例程功能
/* 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通道传输完成中断 */ }
从上面的代码中可以看出,本章实验的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); } }
本章实验的应用代码与上一章实验中的引用代码基本一致,只不过本章实验要处理的是7个ADC通道的数据,因此DMA传输的目的存储器容量的定义变大了,随后便可在DMA传输完成后,采用与上一章实验一样的处理方式处理ADC采集并转换后的数据,最后将每个通道采集到电压的数字量和模拟量显示置LCD上。
35.3 下载验证
在完成编译和烧录操作后,可以看到LCD上不断地刷新显示ADC1通道1通道7(PA1引脚PA7引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给PA1至PA7中的任意一个或多个引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。