
在MCU的应用场景中,处处都有用到ADC,比如电池电量的采集、温度采集、电机应用中电流检测等等。MM32F013x的ADC模块新增了任意通道工作模式,支持在多种应用场景中更灵活的应用;本文针对任意通道工作模式,分享在MM32F013x上实现任意通道工作模式的使用与具体配置。
任意顺序多通道功能
在MM32F013x系列的MCU中新增了ADC对任意通道的支持,在任意通道配置(ADC_ANY_CR. CHANY_MDEN)使能后,其优先级高于常规通道配置,后续的转换按任意通道配置的方式转换。
任意通道模式支持单次转换模式、单周期转换模式和连续扫描模式。
A/D 转换开始条件:
① 软件启动
② 外部触发启动,且软件可配置外部触发延时
③ Timer1/2/3 匹配或 TRGO 信号,外部 EXTI 信号源
相关的寄存器

具体功能与详细描述,请参考MM32F013x系列的用户手册。
任意通道工作模式
01、单次转换模式
在单次转换模式下,A/D 转换相应通道上只执行一次,具体流程如下:
软件设置寄存器ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,设置转换通道,置位CHANY_MDEN。(单次转换模式,只需设置CHANY_SEL0)
通过软件、外部触发输入及定时器溢出置位ADCR 寄存器的ADST,开始A/D转换。
A/D 转换完成时,A/D 转换的数据值将存储于数据寄存器ADDATA 和ADDRn 中。
A/D 转换完成时,状态寄存器ADSTA 的ADIF 位置1。若此时控制寄存器ADCR 的ADIE位置1,将产生AD 转换结束中断请求。
A/D 转换期间,ADST 位保持为1。A/D 通道采样结束后,ADST 位自动清0,A/D 转换器进入空闲模式。
若在A/D 转换过程中,软件更新ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,硬件不会立即更新这些配置,只会在当前设置的通道都转换结束时更新,然后等待下一次软件置位ADST。

该模式仍然支持通过过配置当外部事件(比如TIM Trig或EXTI)触发转换时序。
02、单周期扫描模式
在单周期扫描模式下,A/D 转换相应通道上执行一遍按配定顺序的转换,具体流程如下:
软件设置寄存器ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,将需要转换的通道、数量设置好,然后置位CHANY_MDEN。
通过软件、外部触发置位ADCR 寄存器的ADST,外部触发可软件配置触发延时,A/D转换方向从CHANY_SEL0 到CHANY_SEL15,转换通道数量由CHANY_NUM 配置,且CHANY_SEL0 到CHANY_SEL15 是任意配置的,可以完全相同,或完全不相同。
每路A/D 转换完成时,A/D 转换的数据值将有序装载到相应通道的数据寄存器中,ADIF转换结束标志被设置,若此时控制寄存器ADCR 的ADIE 位置1,将产生AD 转换结束中断请求。
A/D 最后一个通道采样结束后,ADST 位自动清0,A/D 转换器进入空闲模式。
若在A/D 转换过程中,软件更新ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,硬件不会立即更新这些配置,只会在当前设置的通道都转换结束时更新,然后等待下一次软件软件置位ADST。

在一些场景中,需要在执行一遍上述采样后,对采样顺序做调整;或减少采样通道数,以减少采样总体时间,可以通过简单的配置一两个寄存器实现灵活的配置;
03、连续扫描模式
在连续扫描模式下,A/D 转换通道依软件配置一直执行,直到软件禁止。具体流程如下:
软件设置寄存器ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,将需要转换的通道、数量设置好,然后置位CHANY_MDEN。
通过软件、外部触发置位ADCR 寄存器的ADST,外部触发可软件配置触发延时,A/D转换方向从CHANY_SEL0 到CHANY_SEL15,转换通道数量由CHANY_NUM 配置,且CHANY_SEL0 到CHANY_SEL15 是任意配置的,可以完全相同,或完全不相同。
每路A/D 转换完成时,A/D 转换的数据值将有序装载到相应通道的数据寄存器中,ADIF转换结束标志被设置,若此时控制寄存器ADCR 的ADIE 位置1,将产生AD 转换结束中断请求。
通过软件、外部触发置位ADCR 寄存器的ADST,外部触发可软件配置触发延时,A/D转换方向从CHANY_SEL0 到CHANY_SEL15,转换通道数量由CHANY_NUM 配置,且CHANY_SEL0 到CHANY_SEL15 是任意配置的,可以完全相同,或完全不相同。
只要ADST 位保持为1,持续进行A/D 转换。当ADST 位被清0,当前A/D 转换完成后停止,A/D 转换器进入空闲状态。
若在A/D 转换过程中,软件更新ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,硬件不会立即更新这些配置,只会在当前设置的通道都转换结束时更新,即下一个扫描周期开始新的通道转换。

应用还可以结合外部触发功能与DMA传输功能,实现TIM触发多通道 ADC 转换,DMA装载数据的功能。
具体可以参考官方的Lib样例程序:
http://www.mindmotion.com.cn/getfile.aspx?id=1219
下面通过寄存器配置多个通道,实现多路转换,多次切换任意通道,附上全部Reg版本Demo代码:
// Define to prevent recursive inclusion #define _ADC_C_ // Files includes #include "delay.h" #include "sys.h" #include "uart.h" #include "adc.h" #define ADCSCANNUM 4 #define RESULTLEN 4 vu8 ADCflag = 0; u16 ADC_flag; u16 ADCValue[ADCSCANNUM]; u16 varADC_ResultList[RESULTLEN+10][ADCSCANNUM]; void ADC_AnyChanChangeDefault(void) { MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL0, 0<<ADC1_CHANY0_SEL0_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL1, 2<<ADC1_CHANY0_SEL1_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL2, 5<<ADC1_CHANY0_SEL2_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL3, 7<<ADC1_CHANY0_SEL3_Pos); } void ADC1_AnyChanMultiChannelInit(void) { SET_BIT(RCC->AHBENR,RCC_AHBENR_GPIOA); //enable GPIOA clock SET_BIT(RCC->APB2ENR, RCC_APB2ENR_ADC1EN); //enable ADC1clock //set PA0,2,5,7 as Analog Input MODIFY_REG(GPIOA->CRL, (GPIO_CNF_MODE_MASK << GPIO_CRL_CNF_MODE_0_Pos), GPIO_CNF_MODE_AIN << GPIO_CRL_CNF_MODE_0_Pos); MODIFY_REG(GPIOA->CRL, (GPIO_CNF_MODE_MASK << GPIO_CRL_CNF_MODE_2_Pos), GPIO_CNF_MODE_AIN << GPIO_CRL_CNF_MODE_2_Pos); MODIFY_REG(GPIOA->CRL, (GPIO_CNF_MODE_MASK << GPIO_CRL_CNF_MODE_5_Pos), GPIO_CNF_MODE_AIN << GPIO_CRL_CNF_MODE_5_Pos); MODIFY_REG(GPIOA->CRL, (GPIO_CNF_MODE_MASK << GPIO_CRL_CNF_MODE_7_Pos), GPIO_CNF_MODE_AIN << GPIO_CRL_CNF_MODE_7_Pos); SET_BIT(RCC->APB2RSTR,RCC_APB2RSTR_ADC1RST); //ADC1reset CLEAR_BIT(RCC->APB2RSTR,(RCC_APB2RSTR_ADC1RST)); //reset end //ADC configure soft trigger, single period mode //8 fractional frequency MODIFY_REG(ADC1->ADCFG, ADC_CFGR_PRE, ADCFG_ADCPRE_8); MODIFY_REG(ADC1->ADCR, \ ADCR_ADMD_PERIOD | ADCR_ADMD_CONTINUE | ADCR_ALIGN_LEFT, \ ADCR_ADMD_PERIOD); SET_BIT(ADC1->ADCHS, ADCHS_CHEN0|ADCHS_CHEN2|ADCHS_CHEN5|ADCHS_CHEN7); //single PERIOD mode , Data right-ALIGNED, discontinue //enable 4 channels WRITE_REG(ADC1->ANYCFG, 4); //Enable chan 0,2,5, 7 ADC_AnyChanChangeDefault(); SET_BIT(ADC1->ADCR, ADC_CR_DMAEN); SET_BIT(ADC1->ADCFG,ADCFG_ADEN);//ADC1 enable } void ADC_AnyChanChangeFirst(void) { MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL0, 2<<ADC1_CHANY0_SEL0_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL1, 0<<ADC1_CHANY0_SEL1_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL2, 5<<ADC1_CHANY0_SEL2_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL3, 7<<ADC1_CHANY0_SEL3_Pos); } void ADC_AnyChanChangeSecond(void) { MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL0, 5<<ADC1_CHANY0_SEL0_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL1, 0<<ADC1_CHANY0_SEL1_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL2, 2<<ADC1_CHANY0_SEL2_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL3, 7<<ADC1_CHANY0_SEL3_Pos); } void ADC_AnyChanChangeThird(void) { MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL0, 0<<ADC1_CHANY0_SEL0_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL1, 7<<ADC1_CHANY0_SEL1_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL2, 5<<ADC1_CHANY0_SEL2_Pos); MODIFY_REG(ADC1->CHANY0,ADC1_CHANY0_SEL3, 2<<ADC1_CHANY0_SEL3_Pos); } void M0_NVIC_Init(u32 NVIC_IRQChannelPriority, IRQn_Type NVIC_IRQChannel, FunctionalState NVIC_IRQChannelCmd) { if (NVIC_IRQChannelCmd != DISABLE) { NVIC->IP[NVIC_IRQChannel >> 0x02] = (NVIC->IP[NVIC_IRQChannel >> 0x02] & (~(((u32)0xFF) << ((NVIC_IRQChannel & 0x03) * 8)))) | ((((u32)NVIC_IRQChannelPriority << 6) & 0xFF) << ((NVIC_IRQChannel & 0x03) * 8)); NVIC->ISER[0] = 0x01 << (NVIC_IRQChannel & 0x1F); } else { NVIC->ICER[0] = 0x01 << (NVIC_IRQChannel & 0x1F); } } void DMA1_Channel1_IRQHandler() { if(DMA1->ISR & DMA_ISR_TCIF1) { DMA1->IFCR = DMA_IFCR_CTCIF1; ADCflag = 1; } } void DMAcheckStatus(u32 DMA_FLAG) { while(1) { if(DMA1->ISR & DMA_FLAG) { DMA1->IFCR = DMA_FLAG; break; } } } void DMAdisable(DMA_Channel_TypeDef* DMAy_Channelx) { //disable DMA_EN DMAy_Channelx->CCR &= 0xFFFFFFFE; } void DMA_AdctoM16_Init(void) { DMA_Channel_TypeDef* dma_channel; dma_channel = DMA1_Channel1; RCC->AHBENR |= RCC_AHBENR_DMA1EN ; DELAY_Ms(5); //wait DMAclock stabilization DMAdisable(dma_channel); dma_channel->CPAR = (u32) & (ADC1->DR);; //DMA1 external address dma_channel->CMAR = (u32)ADCValue; //DMA1,memory device address dma_channel->CCR &= ~DMA_CCR1_DIR; dma_channel->CNDTR = ADCSCANNUM; dma_channel->CCR &= ~DMA_CCR1_PINC; dma_channel->CCR |= DMA_CCR1_MINC; dma_channel->CCR |= DMA_CCR1_PSIZE_0; //external data 16bit dma_channel->CCR |= DMA_CCR1_MSIZE_0; //memory device data 16bit dma_channel->CCR |= DMA_CCR1_PL_0; //Medium priority dma_channel->CCR |= DMA_CCR_CIRC; dma_channel->CCR &= ~DMA_CCR1_MEM2MEM; //register memory device to memory device mode M0_NVIC_Init(0, DMA1_Channel1_IRQn, ENABLE); dma_channel->CCR |= DMA_CCR1_TCIE; ADCflag = 0x0; dma_channel->CCR |= DMA_CCR1_EN; //start DMA transmission } void Get_ResultListFun(u16 list_number) { u16 chan = 0; for(chan = 0; chan < ADCSCANNUM; chan++) { varADC_ResultList[list_number][chan] = ADCValue[chan]; } } void ADC_ConvertSoftwareStart(ADC_TypeDef* adc, FunctionalState state) { (state) ? (adc->ADCR |= ADC_CR_ADST) : (adc->ADCR &= ~ADC_CR_ADST); } u16 ADC1_MultiChanAnyDemo(void) { ADC1_AnyChanMultiChannelInit(); DMA_AdctoM16_Init(); ADC_ConvertSoftwareStart(ADC1, ENABLE); ADCflag = 0; while(1) { if(ADCflag == 1) { ADCflag = 0; Get_ResultListFun(0); break; } } ADC_AnyChanChangeFirst(); ADC_ConvertSoftwareStart(ADC1, ENABLE); while(1) { if(ADCflag == 1) { ADCflag = 0; Get_ResultListFun(1); break; } } ADC_AnyChanChangeSecond(); ADC_ConvertSoftwareStart(ADC1, ENABLE); while(1) { if(ADCflag == 1) { ADCflag = 0; Get_ResultListFun(2); break; } } ADC_AnyChanChangeThird(); ADC_ConvertSoftwareStart(ADC1, ENABLE); while(1) { if(ADCflag == 1) { ADCflag = 0; Get_ResultListFun(3); break; } } return 0; }
转自:灵动微电子