S32K系列之ADC

简介

在S32K中,不同的芯片型号支持的ADC通道不同,S32K144UAVLL有两个ADC,每个ADC有12位、10位、8位和6位可选,每个ADC有16个外部通道。学习ADC的第一步是要掌握ADC的功能框图,对ADC的工作过程和编程过程有个整体的了解。

ADC功能框图

对ADC的配置主要涉及三种寄存器,控制状态寄存器、触发寄存器和数据寄存器。控制状态寄存器主要有SC2SC3CFG1CFG2;触发寄存器主要有SC1SC2;数据寄存器主要有Rn
1

电压输入范围

ADC的输入电压范围为:VREFLVINVREFHV_{REFL}≤V_{IN}≤V_{REFH},ADC的输入范围取决于4个参考电源VREFLVREFHVDDAVSSAV_{REFL}、V_{REFH}、V_{DDA}、V_{SSA}

​ADC的电压输入范围取决于4个参考电源​,我们在设计原理图的时候一般把​VREFLV_{REFL}VSSA​V_{SSA} 接地,把​VREFHV_{REFH}VDDA​V_{DDA} 接3.3V,可以得到ADC的输入范围是0~3.3V。在设计原理图时,应该把ADC设置为可以配置成外用输入电压参考源,以备不时之需,如下图所示。
2
如上图所示,​有三个选项。

  1. 焊接上R103,拆掉R99,R109和D7,电压参考源为VDD_MCU,一般为3.3V
  2. 焊接上D7和R99,拆掉R103和R109,R99进行分压之后为3V
  3. 焊接上R109,拆掉R103,R99和D7。这里的​是外部输入电压引脚,可以配置为外部电压参考源

时钟配置

可以选择4个时钟源ALTCLKx之一来作为ADC模块的时钟源。ADC具有多个时钟源。 选择取决于配置PCC_ADCn[PCS]。 分频器的配置应使ADC转换时钟频率在ADC要求的有效范围内(请参见数据手册)。如下图所示为ADC的时钟分配图。
3
如图所示,ADC的时钟源输入通过PCC_ADCn[PCS]来选择SOSCDIV2_CLKSIRCDIV2_CLKFIRCDIV2_CLKSPLLDIV2_CLK四种,然后经过ADC_CFG1[ADIV]来时钟分频,最后产生ADC的输入时钟ADCKADCK由数据手册可得,最大50MHZ,最小2MHZ,典型值为40MHZ,如果超过最大频率,ADC的转换结果会变得不可靠。ADC时钟设置流程如下:

  1. 设置PCC_ADCn[PCS]:选择时钟源
  2. 设置ADC_CFG1[ADICLK]:选择时钟输入
  3. 设置ADC_CFG1[ADIV]:选择时钟分频

转换时间

数据手册显示,ADC的转换时间取决于:

  • ADC_CFG2[SAMPLTS]确定的采样时间
  • MCU的Bus总线时钟频率
  • ADC_CFG1[MODE]确定的转换模式
  • fADCKf_{ADCK}即ADC的转换时钟频率

由数据手册可得,ADC的总转换时间=采样相位时间(SMPLTS设置的再+1)+保持相位(1个ADC周期)+比较相位时间(8位模式=20个ADC周期,10位模式=24个ADC周期,12位模式=28个ADC周期)+单个或第一个连续时间加法器(5个ADC周期+5个总线时钟周期)

例:设置BUS时钟为40MHZ,ADC时钟选择40MHZ,12位ADC,采样周期选择255个ADC周期,计算ADC的一次转换时间为:
Tconv=255+1+1+28+54×106+54×106=73.75μsT_{conv}=\frac{255+1+1+28+5}{4×10^6 }+\frac{5}{4×10^6}=73.75μs

触发设置

4
ADC触发设置主要包括寄存器SC2SC1。ADC支持软件触发和硬件触发两种,软件触发就是在ADC转换完毕之后产生中断;在硬件触发方面,ADC模块具有可选的异步硬件转换触发器ADHWT,主要通过PDB来触发。触发设置可见PDB触发ADC。设置ADC_SC2[ADTRG]来选择是硬件触发还是软件触发。触发方式的设置流程如下:

  1. 设置ADC_SC2[ADTRG]:选择触发方式
  2. 设置ADC_SC1n[ADCH]:选择ADC通道
  3. 如果上一步使用了具有硬件交错功能的通道,则需要设置SIM_CHIPCTL[ADC_INTERLEAVE_EN]
  4. 设置ADC_SC1n[AIEN]:开启中断触发(软件触发模式下)

获得ADC数据

最后转换成功后,ADC的转换结果会被存放在ADC_Rn寄存器,ADC_Rn寄存器对应的是ADC_SC1n寄存器,比如ADC_SC1A的数据会存放在ADC_RAADC_SC1B的数据会存放在ADC_RB,以此类推。

在不同的转换位数下,ADC_Rn寄存器的数据位数也不同,如下图所示。
5
注:ADC_Rn寄存器是只读的

电压转换

模拟电压经过ADC转换后,是一个相对精度的数字值,如果通过串口以16进制打印出来的话,可读性比较差,故需要把数字电压转换为模拟电压,也可以根据实际的模拟电压(用万用表测)对比,看看转换是否准确。

在设计原理图时,一般会把ADC的输入电压范围定在0~3.3V,若设置的ADC为12位,那么12位满量程对应的就是3.3V,12位满量程对应的数字值是:2122^{12} 。数值0对应的就是0V。如果转换后的数值为X,X对应的模拟电压为Y,那么:
2123.3=XYY=3.3×X212\frac{2^{12}}{3.3}=\frac{X}{Y} → Y=\frac{3.3\times X}{2^{12}}

实例:ADC触发中断

功能概述

  • 平台:S32K144

现在使用S32K144的ADC0通道8,软件触发模式下,转换结束后触发中断。

编程顺序

  1. 初始化ADC0的时钟
  2. 初始化ADC0的位数
  3. 选择ADC0的通道,开启中断
    • 如果该通道有硬件交错功能,需要选择是哪个ADC通道
  4. 选择软件触发
  5. NVIC开启ADC中断
// ADC初始化函数
void ADC0_Init(void)  
{
	PCC->PCCn[PCC_PORTA_INDEX] |= PCC_PCCn_CGC_MASK;   /* Enable clock for PORTA */
	SIM->CHIPCTL |= (1 << 2);   /* 具有硬件交错功能,选择PTB13 */
	 
	PCC->PCCn[PCC_ADC0_INDEX] &= ~PCC_PCCn_CGC_MASK;  
	PCC->PCCn[PCC_ADC0_INDEX] |= PCC_PCCn_PCS(6);     /* PCS=6: 选择内核时钟80MHZ */
	PCC->PCCn[PCC_ADC0_INDEX] |= PCC_PCCn_CGC_MASK;  
	 
	ADC0->CFG1 = ADC_CFG1_ADIV(2) |     /* ADIV=2:输入内核时钟两分频=40MHZ */
	             ADC_CFG1_MODE(1) |     /* MODE=1: 12位ADC */
	             ADC_CFG1_ADICLK(0);    /* ADICLK=0: 默认选择PCC分配的时钟 */
		 
	ADC0->SC1[0] = ADC_SC1_AIEN_MASK |    /* AIEN=1: 开启中断触发 */
		          ADC_SC1_ADCH(8);     /* ADCH=8: 序号0选择通道8 */
	 
	ADC0->SC2 |= ADC_SC2_ADTRG(0);    /* ADTRG=0: 开启软件触发 */
	ADC0->SC3 = 0;       
		  
    S32_NVIC->ICPR[1] = 1 << (39 % 32);  /* 注册ADC0中断 */
	S32_NVIC->ISER[1] = 1 << (39 % 32);  /* 使能ADC0中断 */
	S32_NVIC->IP[39] = 0xA0;             /* 优先级为10 */
}
/*
 * \brief adcChan通道开启一次ADC转换
 *
 * \param adcChan 需要进行ADC转换的通道
 *
 */
void convertAdcChan(uint16_t adcChan) 
{   
	ADC0->SC1[0] &= ~ADC_SC1_ADCH_MASK;  /* Clear prior ADCH bits */
	ADC0->SC1[0] = ADC_SC1_ADCH(adcChan); /* Initiate Conversion*/
}
 
/*
 * \brief ADC转换判断函数
 *
 * \return >0 转换已完成
 * \return =0 转换未完成
 *
 */
uint8_t adc_complete(void)  
{
	return((ADC0->SC1[0] & ADC_SC1_COCO_MASK) >> ADC_SC1_COCO_SHIFT); 
}
 
/*
 * \brief ADC值转换函数
 *
 * \return 返回转换后的ADC电压值
 *
 */
uint32_t read_adc_chx(uint8_t ChanIndex)  
{
	uint16_t adc_result = 0;
	adc_result = ADC0->R[ChanIndex]; 
	/* For SW trigger mode, R[0] is used */
	return(uint32_t)((5000 * adc_result) / 0xFFF); 
	/* Convert result to mv for 0-5V range */
}
 
void ADC0_IRQHandler(void)
{
	adcRawValue1 = read_adc_chx(0);
	adcRawValue2 = read_adc_chx(1);
	adcConvDone = true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章