打印
[其他]

(分享)灵动微单片机MM32F013x-ADC任意通道工作模式

[复制链接]
4822|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
在MCU的应用场景中,处处都有用到ADC,比如电池电量的采集、温度采集、电机应用中电流检测等等。MM32F013x的ADC模块新增了任意通道工作模式,支持在多种应用场景中更灵活的应用;下面对任意通道工作模式,分享在MM32F013x上实现任意通道工作模式的使用与具体配置。

使用特权

评论回复
沙发
onlycook|  楼主 | 2021-7-20 15:21 | 只看该作者
任意顺序多通道功能
在MM32F013x系列的MCU中新增了ADC对任意通道的支持,在任意通道配置(ADC_ANY_CR. CHANY_MDEN)使能后,其优先级高于常规通道配置,后续的转换按任意通道配置的方式转换。

任意通道模式支持单次转换模式、单周期转换模式和连续扫描模式。

使用特权

评论回复
板凳
onlycook|  楼主 | 2021-7-20 15:22 | 只看该作者

A/D 转换开始条件:
软件启动
外部触发启动,且软件可配置外部触发延时
timer1/2/3 匹配或 TRGO 信号,外部 EXTI 信号源
相关的寄存器


具体功能与详细描述,请参考MM32F013x系列的用户手册。
任意通道工作模式

使用特权

评论回复
地板
onlycook|  楼主 | 2021-7-20 15:24 | 只看该作者

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)触发转换时序。


使用特权

评论回复
5
onlycook|  楼主 | 2021-7-20 15:25 | 只看该作者
本帖最后由 onlycook 于 2021-7-20 15:28 编辑

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。

在一些场景中,需要在执行一遍上述采样后,对采样顺序做调整;

或减少采样通道数,以减少采样总体时间,可以通过简单的配置一两个寄存器实现灵活的配置;


使用特权

评论回复
6
onlycook|  楼主 | 2021-7-20 15:29 | 只看该作者

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,硬件不会立即更新这些配置,只会在当前设置的通道都转换结束时更新,即下一个扫描周期开始新的通道转换。


使用特权

评论回复
7
onlycook|  楼主 | 2021-7-20 15:31 | 只看该作者

应用还可以结合外部触发功能与DMA传输功能,实现TIM触发多通道 ADC 转换,DMA装载数据的功能。
具体可以参考官方的Lib样例程序:


使用特权

评论回复
8
onlycook|  楼主 | 2021-7-20 15:32 | 只看该作者

下面通过寄存器配置多个通道,实现多路转换,多次切换任意通道,附上全部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;
}


使用特权

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

本版积分规则

438

主题

1656

帖子

3

粉丝