1、ADC简介
ADC全称为analog to digital converter,是12位的逐次逼近型(SAR)模拟数字转换器,能够将模拟信号转换为数字信号。
MM32F0140的ADC拥有高达1MSPS转换速率,支持最大输入时钟为15MHz,ADC1多达14路外部输入通道和2路内部通道,ADC2、ADC3多达16路外部输入通道。
ADC功能框图如图1所示,数据通过模拟输入信号(AIN)进行输入,将数据传输到12位的逐次逼近型模拟数字转换器,转换后将结果按指定方向对齐并存储到对应的通道数据寄存器中。
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所示。
时钟分频系数
ADC时钟的分频系数可通过操作配置寄存器(ADC_ADCFG)的ADCPRE位来选择,令ADC外设时钟的(ADCPRE+2)分频作为ADC时钟。
数据对齐
通过配置控制寄存器(ADCR)中的ALIGN位,可选择转换后数据储存为左对齐或右对齐,如图3所示,当采用右对齐,A/D转换结果的最低位与数据寄存器中DATA位的最低位对齐;当采用左对齐,A/D转换结果的最高位与数据寄存器中DATA位的最高位对齐。
通道选择
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"); } }
来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。