灵动微电子 · 2022年08月22日 · 北京市

灵动微课堂 | MM32F0140学习笔记——ADC

1 ADC简介

ADC全称为analog to digital converter,是12位的逐次逼近型(SAR)模拟数字转换器,能够将模拟信号转换为数字信号。

MM32F0140的ADC拥有高达1MSPS转换速率,支持最大输入时钟为15MHz,ADC1多达14路外部输入通道和2路内部通道,ADC2、ADC3多达16路外部输入通道。

ADC功能框图如图1所示,数据通过模拟输入信号(AIN)进行输入,将数据传输到12位的逐次逼近型模拟数字转换器,转换后将结果按指定方向对齐并存储到对应的通道数据寄存器中。

image.png

2 ADC功能

ADC进行数据转换需要配置ADC的分辨率、采样时间、时钟分频系数及工作模式,可选择通道0 ~ 通道15转换数据,存储转换后的数据需提前配置数据对齐方向。

分辨率

ADC转换数据分辨率决定了ADC的转换精度,设置的有效位数越多,量化单位越小,对输入信号的分辨能力就越高。可通过操作配置寄存器(ADC_ADCCFG)的RSLTCTL[2:0]位控制ADC转换数据分辨率,能够配置8/9/10/11/12位有效。

通道采样时间

ADC转换采样时间可通过采样配置寄存器(ADC_SMPRx)中的SAMPCTLx位配置每个通道的采样时间,通道0到通道7的采样时间由ADC_SMPR1控制,通道8到通道15的采样时间由ADC_SMPR2控制,可选择的周期如图2所示。

image.png

时钟分频系数

ADC时钟的分频系数可通过操作配置寄存器(ADC_ADCFG)的ADCPRE位来选择,令ADC外设时钟的(ADCPRE+2)分频作为ADC时钟。

数据对齐

通过配置控制寄存器(ADCR)中的ALIGN位,可选择转换后数据储存为左对齐或右对齐,如图3所示,当采用右对齐,A/D转换结果的最低位与数据寄存器中DATA位的最低位对齐;当采用左对齐,A/D转换结果的最高位与数据寄存器中DATA位的最高位对齐。

image.png

通道选择

ADC的每个外部输入通道都具有独立的使能位,可通过设置通道选择寄存器(ADC_ADCHS)的CHENx位使能对应的模拟输入通道。

普通工作模式

单次转换模式

单周期扫描模式下,可通过配置控制寄存器(ADC_ADCR)的SCANDIR位选择扫描通道方向,运行时将按使能的通道顺序进行一次A/D转换;软件/外部触发置位ADST位,开始A/D转换,外部触发可软件配置触发延时,方向设置默认从最小序号通道到最大序号通道的A/D转换,也可按照程序设置,从最大序号通道到最小序号通道的A/D转换。A/D转换完成后,转换数值将有序装载到对应通道的数据寄存器中。当最后一个A/D通道采样结束后,ADST位硬件清零,进入空闲状态。

连续扫描模式

连续扫描模式下,A/D转换连续执行单周期扫描模式直到软件停止A/D转换。若想修改转换通道,不必停止转换,可配置通道选择寄存器(ADC_ADCHS)的CHENx位选择需要使能的新通道,在下一个扫描周期开始将进行新通道转换。

任意通道工作模式

单次转换模式

A/D转换在指定通道完成一次转换,然后进入空闲模式。

连续扫描模式

在连续扫描模式下,A/D转换连续执行单周期扫描模式直到软件停止A/D转换。通过软件/外部触发置位ADC_ADCR寄存器的ADST,外部触发可软件配置触发延时,A/D转换方向从CHANY_SEL0到CHANY_SELx,软件设置寄存器ADC_ANY_CFG、ADC_CHANY0、ADC_CHANY1,将需要转换的通道、数量设置好,然后置位CHANY_MDEN。

若在A/D转换过程中,软件更新ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,硬件不会立即更新这些配置,只会在当前设置的通道都转换结束时更新,即下一个扫描周期开始新的通道转换。

每路A/D转换完成时,A/D转换的数据值将有序装载到相应通道的数据寄存器中。只要ADST位保持为1,就持续进行A/D转换。当ADST位被清0,当前A/D转换完成后停止,A/D转换器进入空闲状态。

3 实验

本实验演示ADC串口接收中断服务的使用,ADC通道1对应引脚连接VCC,按下任意按键,A/D转换开始,串口打印转换值。初始化ADC,配置ADC精度为12位有效,ADC时钟分频为16分频,使用单次转换模式,转换后数据右对齐;使用模拟输入通道1,设置扫描通道的顺序为从低到高的顺序扫描,配置采样时间为240.5周期,使能A/D非注入通道组转换中断,在中断处理中获取转换后的数值并清除中断标志。

启用ADC外设时钟 enable_clock()

实验使用ADC1,使用的ADC通道1对应引脚为PA1,串口打印输出结果,串口使用引脚属于GPIOA组,因此需要启用ADC1、UART1及GPIOA的外设时钟。

void enable_clock()
{
    /* Enable ADC1 clock. */
    RCC->APB2ENR |= RCC_APB2_PERIPH_ADC1;
    /* Enable GPIOA clock. */
    RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA;
    /* Enable GPIOB clock. */
    RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOB;
    /* Enable UART1 clock. */
    RCC->APB2ENR |= RCC_APB2_PERIPH_UART1;
}

配置引脚 pin_init()

ADC1使用通道1,对应引脚为PA1,设置为模拟输入;由于实验现象通过串口显示,故配置UART的TX(PA9)与RX(PA10)引脚。

void pin_init()
{
    /* PA1 - ADC1 channel 1. */
    GPIOA->CRL &= ~GPIO_CRL_MODE1_MASK;
    GPIOA->CRL &= ~GPIO_CRL_CNF1_MASK;
    GPIOA->CRL |= GPIO_CRL_MODE1(GPIO_Speed_50MHz);
    GPIOA->CRL |= GPIO_CRL_CNF1(GPIO_PinMode_In_Analog);

    /* PA9 - UART_TX. */
    GPIOA->CRH &= ~GPIO_CRH_MODE9_MASK;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_MASK;
    GPIOA->CRH |= GPIO_CRH_MODE9(GPIO_Speed_50MHz);
    GPIOA->CRH |= GPIO_CRH_CNF9(GPIO_PinMode_AF_PushPull);
    GPIOA->AFRH &= ~GPIO_AFRH_AFRY_MASK
    GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT);

    /* PA10 - UART_RX. */
    GPIOA->CRH &= ~GPIO_CRH_MODE10_MASK;
    GPIOA->CRH &= ~GPIO_CRH_CNF10_MASK;
    GPIOA->CRH |= GPIO_CRH_CNF10(GPIO_PinMode_In_Floating);
    GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT);
}

UART初始化 uart_init()

初始化UART,配置时钟频率、波特率、数据长度、停止位、传输模式及是否使用校验。

void uart_init()
{
    /* Clear the corresponding bit to be used. */
    UART1->CCR &= ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK );
    UART1->GCR &= ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK );
    /* WordLength. */
    UART1->CCR |= UART_CCR_CHAR_MASK;
    /* XferMode. */
    UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT);
    /* Setup baudrate, BOARD_DEBUG_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */
    UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u;
    UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u;
    /* Enable UART1. */
    UART1->GCR |= UART_GCR_UARTEN_MASK;
}

ADC初始化 adc_init()

ADC初始化操作配置寄存器(ADC_ADCFG)的RSLTCTL位,配置ADC精度为12位有效,配置ADCPRE位为16分频;操作控制寄存器(ADC_ADCR)的ADMD位,选择转换模式为单次转换,配置ALIGN位为数据右对齐;设置ADC_ADCFG寄存器的ADEN位为1,使能A/D转换;将任意通道控制寄存器(ADC_ANY_CR)的CHANY_MDEN位清零,禁止任意通道配置模式;操作通道选择寄存器(ADC_ADCHS)的CHEN1位置1,使能模拟输入通道1;配置ADC_ADCR寄存器的SCANDIR位,ADC通道选择寄存器按从低到高的顺序扫描,配置ADIE位为1,使能A/D非注入通道组转换中断。

void adc_init()
{
    /* Setup the converter. */
    uint32_t cfg;
    cfg = ADC1->ADCFG & ~(    ADC_ADCFG_ADCPREH_MASK
                            |  ADC_ADCFG_ADCPREL_MASK
                            |  ADC_ADCFG_RSLTCTL_MASK
                            |  ADC_ADCR_ALIGN_MASK )
            ;
    /* Prescaler & Resolution. */
    cfg |= ADC_ADCFG_ADCPREL(ADC_ClockDiv_16)  /* ADC_ClockDiv_16 = 14u. */
         | ADC_ADCFG_ADCPREH((ADC_ClockDiv_16)>>1)
         | ADC_ADCFG_RSLTCTL(ADC_Resolution_12b)
         ;
    ADC1->ADCFG = cfg;
    /* ADC conversion mode and conversion data result align. */
    ADC1->ADCR = (ADC1->ADCR & ~( ADC_ADCR_ADMD_MASK | ADC_ADCR_ALIGN_MASK) )
               | ADC_ADCR_ADMD(ADC_ConvMode_SingleSlot)
               | ADC_ADCR_ALIGN(ADC_Align_Right)
               ;
    ADC1->ADCFG |= ADC_ADCFG_ADEN_MASK;  /* Enable ADC1. */
    /* Setup one regular channel. */
    ADC1->ANYCR &= ~ADC_ANYCR_CHANYMDEN_MASK;

    /* Enable regular channels. */
    ADC1->ADCHS = 1u << ADC_CHN_NUM;  /* ADC_CHN_NUM = 1u. */
    ADC1->ADCR = (ADC1->ADCR & ~ADC_ADCR_SCANDIR_MASK)
               | ADC_ADCR_SCANDIR(ADC_RegSeqDirection_LowFirst)
               ;
    /* Set channel sample time. */
    ADC1->SMPR1 = (ADC1->SMPR1 & ~(0xF << 4u)) | (ADC_SampleTime_Alt7 << 4u );  /* Period is 240.5. */
    /* Enable ADC interrupt. */
    ADC1->ADCR |= ADC_ADCR_ADIE_MASK;
    NVIC_EnableIRQ(ADC_COMP_IRQn);
}

启动A/D转换 adc_doswtrigger()

本实验设置按下任意按键后,软件触发A/D转换开始,配置控制寄存器(ADC_ADCR)的ADST位为1。

void adc_doswtrigger()
{
    ADC1->ADCR |= ADC_ADCR_ADST_MASK;
}

编写中断服务程序 ADC_COMP_IRQHandler()

中断服务程序中,读状态寄存器(ADC_ADSTA)获取当前ADC传输状态,当ADIF位置1,即A/D转换完成,将adc传输完成标志adc_conv_done设置为true,读数据寄存器(ADC_ADDATA)的DATA位获取转换结果,放入conv_val变量中,向ADIF位写1,清零标志。

void ADC_COMP_IRQHandler()
{
    if ( 0u != (ADC_ADSTA_EOSIF_MASK & ADC1->ADSTA) )  /* ADC convert complete. */
    {
        adc_conv_done = true;
        conv_val = (ADC1->ADDATA & ADC_ADDATA_DATA_MASK ) >> ADC_ADDATA_DATA_SHIFT;
    }
    ADC1->ADSTA |= ADC_ADSTA_EOSIF_MASK;  /* Clear flag. */
}

main()函数

main()函数结合上述操作,ADC通道1对应引脚连接VCC,运行程序,串口打印"adc_interrupt example.",初始化ADC,串口打印"press any key to start the conversion.",按下任意按键,A/D转换开始,ADC一个通道转换完成后,进入ADC中断服务函数,获取转换数值,串口打印转换值,实验结果如图4所示。

int main()
{
    enable_clock();
    pin_init();
    uart_init();
    printf("adc_interrupt example. \r\n");
    adc_init();
    printf("press any key to start the conversion.\r\n");
    while (1)
    {
        getchar();
        printf("adc conversion start...\r\n");
        adc_doswtrigger();
        adc_conv_done = false;
        while (!adc_conv_done)
        {}
        printf("value=%u\r\n", (unsigned)(conv_val & 0xFFF));
        printf("adc interrupt done...\r\n");
    }
}

image.png

图4.实验现象

作者:灵动MM32
文章来源:灵动MM32MCU

推荐阅读

更多MM32F5系列资料请关注灵动MM32 MCU专栏。如想进行MM32相关芯片技术交流,请添加极术小姐姐微信(id:aijishu20)加入微信群。
推荐阅读
关注数
6144
内容数
276
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息