APM32F407微控制器中的模数转换器(ADC)是一种12位逐次逼近型转换器,用于将模拟信号(如电压)转换为数字值,支持高精度数据采集。该模块具备以下核心特性:
主要特性
分辨率与精度:支持12位、10位、8位或6位可配置分辨率,最小量化误差低,可通过自校准提升精度。输入电压范围由VREF-至VREF+(典型值VREF+连接VDDA=3.3V)。
通道配置:
最多24个外部通道:每个 ADC 最多有 16 个外部通道、3个内部通道(温度传感器、内部参考电压VREFINT和备份电压)、以及VBAT电池监控通道。
通道分为规则组(最多16通道,用于常规转换)和注入组(最多4通道,支持高优先级中断式转换)。
转换模式:
支持单次转换、连续转换、扫描模式(自动顺序转换多个通道)和间断模式(分组触发)。
双重或三重ADC模式(ADC1与ADC2和ADC3可协同工作)。
数据处理:结果以左对齐或右对齐16位数据寄存器存储,支持DMA传输(减少CPU开销)和中断机制(用于转换结束或看门狗事件)。
其他功能:软件或外部触发启动转换,采样时间按通道可编程,模拟看门狗监控输入阈值超限。
采样速率:支持最高2.4 MSPS(每秒百万次采样)的单通道速度;通过交替模式,可提升至7.2 MSPS,满足高速数据应用需求。
APM32F407的有三个ADC,除了可以每个ADC单独工作外,还有双重ADC和三重ADC模式,可以实现更丰富的功能。接下来就测试下三重ADC的同时规则模式,三个ADC同时采各种的通道,实现多通道ADC采样有更快的速度。
首先是ADC和DMA的初始化
void ADC_Init(void) { GPIO_Config_T gpioConfig; ADC_Config_T adcConfig; ADC_CommonConfig_T adcCommonConfig; /* RCM Enable*/ RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA|RCM_AHB1_PERIPH_GPIOB|RCM_AHB1_PERIPH_GPIOC); /* GPIO Configuration */ GPIO_ConfigStructInit(&gpioConfig); gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6; gpioConfig.mode = GPIO_MODE_AN; gpioConfig.pupd = GPIO_PUPD_NOPULL; GPIO_Config(GPIOA, &gpioConfig); GPIO_ConfigStructInit(&gpioConfig); gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1; gpioConfig.mode = GPIO_MODE_AN; gpioConfig.pupd = GPIO_PUPD_NOPULL; GPIO_Config(GPIOB, &gpioConfig); GPIO_ConfigStructInit(&gpioConfig); gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; gpioConfig.mode = GPIO_MODE_AN; gpioConfig.pupd = GPIO_PUPD_NOPULL; GPIO_Config(GPIOC, &gpioConfig); RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1); RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC2); RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC3); ADC_Reset(); /* ADC Common Configuration */ adcCommonConfig.mode = ADC_MODE_TRIPLE_REGSIMULT; adcCommonConfig.prescaler = ADC_PRESCALER_DIV6; adcCommonConfig.accessMode = ADC_ACCESS_MODE_1; adcCommonConfig.twoSampling = ADC_TWO_SAMPLING_10CYCLES; ADC_CommonConfig(&adcCommonConfig); /* ADC Configuration */ ADC_ConfigStructInit(&adcConfig); /* Set resolution*/ adcConfig.resolution = ADC_RESOLUTION_12BIT; /* Set dataAlign*/ adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT; /* Set scanDir*/ adcConfig.scanConvMode = ENABLE; /* Set convMode continous*/ adcConfig.continuousConvMode = ENABLE; /* Set extTrigEdge*/ adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE; /* Set nbrOfConversion*/ adcConfig.nbrOfChannel = 4; ADC_Config(ADC1, &adcConfig); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_4, 4, ADC_SAMPLETIME_3CYCLES); ADC_Config(ADC2, &adcConfig); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_5, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_6, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_8, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_9, 4, ADC_SAMPLETIME_3CYCLES); ADC_Config(ADC3, &adcConfig); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_12, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_13, 4, ADC_SAMPLETIME_3CYCLES); ADC_EnableMultiModeDMARequest(); /* Enable ADC*/ ADC_Enable(ADC1); ADC_Enable(ADC2); ADC_Enable(ADC3); ADC_SoftwareStartConv(ADC1); }
主要的配置点:配置为3ADC同步规则模式
adcCommonConfig.mode = ADC_MODE_TRIPLE_REGSIMULT;
使能连续扫描模式,不使能触发
/* Set scanDir*/ adcConfig.scanConvMode = ENABLE; /* Set convMode continous*/ adcConfig.continuousConvMode = ENABLE; /* Set extTrigEdge*/ adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;
配置各ADC的通道顺序
ADC_Config(ADC1, &adcConfig); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_4, 4, ADC_SAMPLETIME_3CYCLES); ADC_Config(ADC2, &adcConfig); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_5, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_6, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_8, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_9, 4, ADC_SAMPLETIME_3CYCLES); ADC_Config(ADC3, &adcConfig); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_12, 3, ADC_SAMPLETIME_3CYCLES); ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_13, 4, ADC_SAMPLETIME_3CYCLES);
双重或三重 ADC 模式,ADC1 默认为主ADC,其他默认为从 ADC,只需要ADC1开始采样,其他ADC就也能工作。接下来是DMA的配置
查看用户手册,ADC1在DMA2的通道0 数据流0.根据这个进行配置
void DMA_Init(uint32_t* Buf) { /* DMA Configure */ DMA_Config_T dmaConfig; /* Enable DMA clock */ RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA2); /* size of buffer*/ dmaConfig.bufferSize = 12; /* set memory Data Size*/ dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD; /* Set peripheral Data Size*/ dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD; /* Enable Memory Address increase*/ dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE; /* Disable Peripheral Address increase*/ dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE; /* Reset Circular Mode*/ dmaConfig.loopMode = DMA_MODE_CIRCULAR; /* set priority*/ dmaConfig.priority = DMA_PRIORITY_HIGH; /* read from peripheral*/ dmaConfig.dir = DMA_DIR_PERIPHERALTOMEMORY; /* Set memory Address*/ dmaConfig.memoryBaseAddr = (uint32_t)Buf; /* Set Peripheral Address*/ dmaConfig.peripheralBaseAddr = (uint32_t)&ADC->CDATA; dmaConfig.channel = DMA_CHANNEL_0; dmaConfig.fifoMode = DMA_FIFOMODE_ENABLE; dmaConfig.fifoThreshold = DMA_FIFOTHRESHOLD_HALFFULL; dmaConfig.peripheralBurst = DMA_PERIPHERALBURST_SINGLE; dmaConfig.memoryBurst = DMA_MEMORYBURST_SINGLE; DMA_Config(DMA2_Stream0, &dmaConfig); NVIC_EnableIRQRequest(DMA2_STR0_IRQn, 0x01, 0x01); /* Clear DMA TF flag*/ DMA_ClearIntFlag(DMA2_Stream0, DMA_INT_TCI**0); /* Enable DMA Interrupt*/ DMA_EnableInterrupt(DMA2_Stream0,DMA_INT_TCI**); DMA_Enable(DMA2_Stream0); }
开启DMA中断,可以在每轮采样完成后进行数据处理
void DMA_IRQHandler(void) { if(DMA_ReadIntFlag(DMA2_Stream0,DMA_INT_TCI**0) != RESET) { APM_MINI_LEDToggle(LED2); ADC1_CH0_ConvertedValue = (float)DMA_DualConvertedValue[0]/4095*3.3f; ADC2_CH5_ConvertedValue = (float)DMA_DualConvertedValue[1]/4095*3.3f; ADC3_CH10_ConvertedValue = (float)DMA_DualConvertedValue[2]/4095*3.3f; ADC1_CH1_ConvertedValue = (float)DMA_DualConvertedValue[3]/4095*3.3f; ADC2_CH6_ConvertedValue = (float)DMA_DualConvertedValue[4]/4095*3.3f; ADC3_CH11_ConvertedValue = (float)DMA_DualConvertedValue[5]/4095*3.3f; ADC1_CH2_ConvertedValue = (float)DMA_DualConvertedValue[6]/4095*3.3f; ADC2_CH8_ConvertedValue = (float)DMA_DualConvertedValue[7]/4095*3.3f; ADC3_CH12_ConvertedValue = (float)DMA_DualConvertedValue[8]/4095*3.3f; ADC1_CH4_ConvertedValue = (float)DMA_DualConvertedValue[9]/4095*3.3f; ADC2_CH9_ConvertedValue = (float)DMA_DualConvertedValue[10]/4095*3.3f; ADC3_CH13_ConvertedValue = (float)DMA_DualConvertedValue[11]/4095*3.3f; DMA_ClearIntFlag(DMA2_Stream0,DMA_INT_TCI**0); } }
最后可以使用串口打印,输出采的电压。
int main(void) { USART_Config_T usartConfigStruct; /* USART configuration */ USART_ConfigStructInit(&usartConfigStruct); usartConfigStruct.baudRate = 115200; usartConfigStruct.mode = USART_MODE_TX_RX; usartConfigStruct.parity = USART_PARITY_NONE; usartConfigStruct.stopBits = USART_STOP_BIT_1; usartConfigStruct.wordLength = USART_WORD_LEN_8B; usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE; APM_MINI_LEDInit(LED2); /* COM1 init*/ APM_MINI_COMInit(COM1, &usartConfigStruct); DMA_Init((uint32_t*)DMA_DualConvertedValue); ADC_Init(); while (1) { Delay(0x1FFFFF); printf("\r\nADC CH0: ADC1 Voltage = %f V \r\n", ADC1_CH0_ConvertedValue); printf("ADC CH1: ADC1 Voltage = %f V \r\n", ADC1_CH1_ConvertedValue); printf("ADC CH2: ADC1 Voltage = %f V \r\n", ADC1_CH2_ConvertedValue); printf("ADC CH4: ADC1 Voltage = %f V \r\n", ADC1_CH4_ConvertedValue); printf("ADC CH5: ADC2 Voltage = %f V \r\n", ADC2_CH5_ConvertedValue); printf("ADC CH6: ADC2 Voltage = %f V \r\n", ADC2_CH6_ConvertedValue); printf("ADC CH8: ADC2 Voltage = %f V \r\n", ADC2_CH8_ConvertedValue); printf("ADC CH9: ADC2 Voltage = %f V \r\n", ADC2_CH9_ConvertedValue); printf("ADC CH10: ADC2 Voltage = %f V \r\n", ADC3_CH10_ConvertedValue); printf("ADC CH11: ADC2 Voltage = %f V \r\n", ADC3_CH11_ConvertedValue); printf("ADC CH12: ADC2 Voltage = %f V \r\n", ADC3_CH12_ConvertedValue); printf("ADC CH13: ADC2 Voltage = %f V \r\n", ADC3_CH13_ConvertedValue); } }
将PA0接到高电平,查看串口打印的就是正确的了。