跳转到主要内容

MM32F0140学习笔记——ADC

<strong><font color="#4e5e9e">1、ADC简介</font> </strong>

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

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

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

<center><img src="http://mcu.eetrend.com/files/2022-06/wen_zhang_/100560924-256210-1.png&…; alt=“图1.ADC功能框图"></center><center><i>图1.ADC功能框图</i></center>

<strong><font color="#4e5e9e">2、ADC功能</font> </strong>

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

<strong>分辨率</strong>

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

<strong>通道采样时间</strong>

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

<center><img src="http://mcu.eetrend.com/files/2022-06/wen_zhang_/100560924-256211-2.png&…; alt=“图2.ADC采样时间"></center><center><i>图2.ADC采样时间</i></center>

<strong>时钟分频系数</strong>

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

<strong>数据对齐</strong>

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

<center><img src="http://mcu.eetrend.com/files/2022-06/wen_zhang_/100560924-256212-3.png&…; alt=“图3.ADC数据对齐"></center><center><i>图3.ADC数据对齐</i></center>

<strong>通道选择</strong>

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

<strong>普通工作模式</strong>

● 单次转换模式

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

● 连续扫描模式

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

<strong>任意通道工作模式</strong>

● 单次转换模式

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转换器进入空闲状态。

<strong><font color="#4e5e9e">3、实验</font> </strong>

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

<strong>启用ADC外设时钟 enable_clock()</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">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;
}</pre>

<strong>配置引脚 pin_init()</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">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 &lt;&lt; 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 &lt;&lt; GPIO_CRH_MODE10_SHIFT);
}</pre>

<strong>UART初始化 uart_init()</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">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 &lt;&lt; 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;
}</pre>

<strong>ADC初始化 adc_init()</strong>

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非注入通道组转换中断。

<pre style="overflow-x:auto; background-color:#e9e9e9;">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 &lt;&lt; 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 &lt;&lt; 4u)) | (ADC_SampleTime_Alt7 &lt;&lt; 4u ); /* Period is 240.5. */
/* Enable ADC interrupt. */
ADC1->ADCR |= ADC_ADCR_ADIE_MASK;
NVIC_EnableIRQ(ADC_COMP_IRQn);
}</pre>

<strong>启动A/D转换 adc_doswtrigger()</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">void adc_doswtrigger()
{
ADC1->ADCR |= ADC_ADCR_ADST_MASK;
}</pre>

<strong>编写中断服务程序 ADC_COMP_IRQHandler()</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">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. */
}</pre>

<strong>main()函数</strong>

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

<pre style="overflow-x:auto; background-color:#e9e9e9;">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");
}
}</pre>

<center><img src="http://mcu.eetrend.com/files/2022-06/wen_zhang_/100560924-256213-4.png&…; alt=“图4.实验现象"></center><center><i>图4.实验现象</i></center>

来源:<a href="https://mp.weixin.qq.com/s/YcYMlvFzWwhahHkdWZmdxQ">灵动MM32MCU</a&gt;
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。