CH559L单片机ADC介绍以及ADC采样案例

CH559L单片机ADC介绍以及ADC采样案例


  • ✨本案例基于Keil环境下开发。
  • 📌案例来源WCH官网地址:https://www.wch.cn/products/CH559.html
    在这里插入图片描述
根据ADC采样通道的选择分为:单通道采样和多通道采样,根据ADC数据的转换处理分:DMA方式自动AD采样和手动方式AD采样。

📑CH559 ADC简介

CH559 芯片提供 10 位或 11 可选的逐次逼近型模拟数字转换器。该转换器具有 8 个模拟信号输入通道,可以分时采集。ADC 主要特性:

  1. 可选 10 位或 11 位分辨率;
  2. ADC 模拟输入电压范围:0 到 VDD33;
  3. 最高 1MSPS 采样速率;
  4. 支持自动交替通道模式,用于在两个输入通道之间进行自动交替转换;
  5. 内置 2 级 FIFO,支持自动采样和 DMA。

ADC 相关寄存器介绍

  • P1_IE: P1 端口输入使能寄存器 。
    在这里插入图片描述

该寄存器为0-7bit设置位,P1.x 引脚输入使能,该位为 0 则引脚用于 ADC 模拟输入,禁止数字输入;该位为 1 则使能数字输入 。

  • ADC_EX_SW:ADC 扩展模拟开关控制寄存器
    在这里插入图片描述

  • ADC_SETUP:ADC 设置寄存器
    在这里插入图片描述

  • ADC_CHANN :ADC 通道选择寄存器
    在这里插入图片描述

  • ADC_CTRL:ADC 控制寄存器
    在这里插入图片描述

  • ADC_STAT :ADC 状态寄存器
    在这里插入图片描述

  • ADC_CK_SE:ADC 时钟分频设置寄存器
    在这里插入图片描述

  • ADC_DMA_CN:DMA 剩余计数寄存器,

当前 DMA 剩余计数,可预置初值,DMA 操作后自动减少。

  • ADC_DMA :ADC_DMA_AL 和 ADC_DMA_AH 组成 16 位 SFR
    在这里插入图片描述

📝ADC 采样模式配置步骤:

  1. 设置 ADC 设置寄存器 ADC_SETUP 中的 bADC_POWER_EN 位为 1,开启 ADC 模块。
  2. 设置时钟分频设置寄存器 ADC_CK_SE,选择时钟频率,频率最高 12MHz,建议不低于 1MHz。
  3. 清空 FIFO 中的已有数据;如果需要用到中断或者 DMA,那么在此处进行相关设置。
  4. 对于自动采样模式,那么应该先设置 ADC 通道选择寄存器 ADC_CHANN。
  5. 设置 ADC 控制寄存器 ADC_CTRL 中的 bADC_SAMPLE 和 MASK_ADC_CYCLE,如果 MASK_ADC_CYCLE
    设置为 0,则为手动采样模式;如果 MASK_ADC_CYCLE 设置为非 0 值,则为自动采样模式,此
    时 MASK_ADC_CYCLE 就是连续不断自动采样的时钟周期。
  6. 对于手工采样模式,设置 ADC 通道选择寄存器 ADC_CHANN,选择 ADC 模拟信号输入通道。
  7. 如果是手动采样模式,那么需要通过设置 bADC_SAMPLE 位为 1 并延时至少一个 ADC 时钟周期后
    再清 0,完成一次模拟信号采样并启动一次 ADC 转换。
  8. 等待 ADC 状态寄存器 ADC_STAT 的 bADC_IF_ACT 位为 1,表示 ADC 转换结束,可通过 ADC_FIFO
    读取结果数据。
  9. 或读取 ADC 状态寄存器 ADC_STAT 的 MASK_ADC_FIFO_CNT 获得 FIFO 计数,再通过 ADC_FIFO 读
    取若干数据,建议丢弃第一个 ADC 结果数据,因为有可能采样不完全。
  10. 如需 DMA 步骤:设置 ADC_DMA 为用户定义的数据缓冲区的起始地址值,设置 ADC_DMA_CN 为用
    户定义的 DMA 剩余计数,并设置 ADC_SETUP 中的 bADC_DMA_EN 位为 1,即开启 DMA 功能。
  11. ADC 结果数据共 12 位,其中位 0~位 10 为 ADC 数值,位 11 是标志位,位 12~位 15 始终为 0。
    对于手工选择通道模式,位 11 始终为 0;对于自动交替通道模式,位 11 表示该 ADC 数值的通
    道识别标志,参考 bADC_CHANN_ID 说明。

⛳DMA方式自动ADC采样案例测试

  • 官方所给的示例代码如下:

来自:ADCAUTODMA.C文件


/********************************** (C) COPYRIGHT *******************************
* File Name          : ADCAutoDMA.C
* Author             : WCH
* Version            : V1.3
* Date               : 2016/6/24
* Description        : CH559的ADC自动DMA方式采样操作,设置采样通道为0和1轮测,采样位数设置为11位               				   
*******************************************************************************/
#include "..\DEBUG.C"                                                          //调试信息打印
#include "..\DEBUG.H"

#define ADCCount 30
UINT16X	ADCbuf[ ADCCount ];// _at_ 0x0040 ;  //不选择存入xdata存储区,ADC采样数据
UINT8 Flag;

#pragma  NOAREGS

/*******************************************************************************
* Function Name  : InitADCInterrupt()
* Description    : ADC中断初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void InitADCInterrupt()
{
//  ADC_SETUP |= bADC_IE_FIFO_OV;                                             //使能FIFO溢出中断
//  ADC_SETUP |= bADC_IE_AIN7_LOW;                                            //使能AIN7低电平中断
//  ADC_SETUP |= bADC_IE_ACT;                                                 //ADC完成中断
    IE_ADC = 1;                                                               //使能ADC中断
}

/*******************************************************************************
* Function Name  : InitADCAuto()
* Description    : ADC自动采样初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void InitADCAuto()
{
	 //P10-P17对应AIN0~AIN7:8 通道 ADC 模拟信号输入。
	
    P1_IE = 0x00; //关闭P1口其他数据功能,如果只用部分通道,可根据需要设定,否则影响IO正常使用
    ADC_SETUP |= bADC_POWER_EN;  //ADC电源使能
    ADC_CK_SE |= (MASK_ADC_CK_SE & 0x0C);//设置12分频
    ADC_CTRL &= ~MASK_ADC_CYCLE;
    ADC_CTRL |= 0x0C;  //设置ADC自动采样周期
	//ADC channel control mode
    ADC_CTRL &= ~bADC_CHANN_MOD1; //1101 1111                              
    ADC_CTRL |= bADC_CHANN_MOD0;  //0001 0000  自动通道选择通道0和通道1

    ADC_EX_SW |= bADC_RESOLUTION;//采样位数11bit
//	ADC_EX_SW &= ~bADC_RESOLUTION; //采样位数10bit
    mDelayuS(100);  //确保ADC正常启动	
}

/*******************************************************************************
* Function Name  : InitADCDMA(UINT16 addr,UINT8 num)
* Description    : ADC的DMA初始化
* Input          : UINT16 addr,DMA起始地址
                   UINT8 num,DMA剩余计数
* Output         : None
* Return         : None
*******************************************************************************/
void InitADCDMA( )
{
    ADC_DMA = ADCbuf;                                                         //设置DMA起始地址
    ADC_DMA_CN = ADCCount;                                                    //设置DMA剩余计数
    ADC_SETUP |= bADC_DMA_EN;                                                 //使能DMA和中断
}

/*******************************************************************************
* Function Name  : ADCInterrupt(void)
* Description    : ADC 中断服务程序
*******************************************************************************/
void ADCInterrupt( void ) interrupt INT_NO_ADC using 1                        //ADC中断服务程序,使用寄存器组1
{
	printf("%02u  ",(UINT16)ADC_STAT);//ADC 状态寄存器 :11111000 
    if(ADC_STAT & bADC_IF_DMA_END)                                            //DMA完成中断
    {
    	Flag = 1;
    	ADC_STAT |= bADC_IF_DMA_END;                                          //清中断
    }
    if(ADC_STAT & bADC_IF_FIFO_OV)                                            //ADC的FIFO溢出中断
    {
    	ADC_STAT |= bADC_IF_FIFO_OV;                                          //清中断
    }
    if(ADC_STAT & bADC_IF_AIN7_LOW)                                           //AIN7低电平中断
    {
    	ADC_STAT |= bADC_IF_AIN7_LOW;                                         //清中断
    }
    if(ADC_STAT & bADC_IF_ACT)                                                //ADC完成中断
    {
    	ADC_STAT |= bADC_IF_ACT;                                              //清中断
    }
}

void main( ) 
{
    UINT8 i;
    mDelaymS(30);  //上电延时,等待内部晶振稳定,必加 
  CfgFsys( ); //CH559时钟选择配置
    mInitSTDIO( ); //串口0,可以用于调试
    printf("CH559L start ...\n"); 
    Flag = 0;
//ADC采样有8个通道,在P1口,通道n 分别ADC_CHANN对应 (0x01 << n) ,如通道0是0x01,通道1是0x02依次类推	
//	ADC_CHANN =0x01 << 5;
	InitADCInterrupt();  //ADC自动采样初始化
    InitADCAuto();//ADC中断初始化
    InitADCDMA();

    EA = 1; //开启全局中断
    while(1)
    { 
      if(Flag)
	    {
	    	for(i=1;i<29;i++)
	    	{
	    	    printf("%04u  ",ADCbuf[i]); //初次采样值建议丢弃
	    	}
	    	Flag = 0;
				mDelaymS(1000); 
	    }
    }
}
  • 串口打印的结果:
    在这里插入图片描述

DMA方式自动AD采样通道选择说明

  • 自动通道选择,配置方法如下:
  • 自动交替通道模式,在 AIN0 和 AIN1 之间自动交替切换
    ADC_CTRL &= ~bADC_CHANN_MOD1;   //1101 1111                                    
   ADC_CTRL |= bADC_CHANN_MOD0; //0001 0000自动通道选择:通道0和通道1

  • 自动交替通道模式,在 AIN6 和 AIN4 之间自动交替切换
    ADC_CTRL |= bADC_CHANN_MOD1;   //0001 0000                                    
   ADC_CTRL &= ~bADC_CHANN_MOD0; //1101 1111自动通道选择:通道6和通道4
  • 自动交替通道模式,在 AIN6 和 AIN7 之间自动交替切换
    ADC_CTRL |= bADC_CHANN_MOD1;   //0001 0000                                    
   ADC_CTRL |= bADC_CHANN_MOD0; //0010 0000 自动通道选择:通道6和通道7
  • 手工选择通道,配置方法如下:
	ADC_CTRL &= ~bADC_CHANN_MOD0; //	手工选择通道
	//ADC采样有8个通道,在P1口,通道n 分别ADC_CHANN对应 (0x01 << n) ,如通道0是0x01,通道1是0x02依次类推	
	ADC_CHANN =0x01 << 1;//手工指定通道1
//ADC_CHANN = 0x02;	//同上
并未实现DMA自动采样并连续打印的效果,只有在复位后重启后才打印一次采用的数据。上面的打印数据是烧录官方所提供的Hex文件,打印的结果。本想自己来找出bug来补上,看了半天的CH559手册上的ADC寄存器真的很难看懂, 以前吐槽STC单片机手册都是1000+页的资料难翻阅,现在看到了WCH的73页的数据手册,感觉不够看,关键是有些看不懂。感叹STC单片机手册详细,STM32的描述的明白。目前还没有找到清标志位的地方。

⛳DMA多次采样注意事项

  • 在官方所给出的DMA采样案例中,采样的30次的10/11位ADC模数转换的数据存进16位地址线的xdata 区域,直接进行存储是不对的,打印出来的数值是16位的数值,最大值为65536,而CH559L单片机8个通道只支持10/11位的ADC模数转换,如果选择10位的采样方式,那么最大采样值:1024,如果选择11位采样方式,最大采样值:2048,不是不可以这样存储,是需要在存储前需要对该存储地址位进行清零操作,否则读取的数据就不对了。
UINT16X	ADCbuf[ ADCCount ];// _at_ 0x0040 ; //存储ADC采样数据
    while(1)
    { 
      if(Flag)
	    {
				ADC_SETUP &= ~bADC_DMA_EN;//关闭DMA和中断
	    	for(i=1;i<5;i++)
	    	{

	    	    printf("%hu  ",ADCbuf[i]);//初次和最后一次采样值建议丢弃	
					total +=ADCbuf[i];					
	    	}
				total =total/4;
				printf("ADC_value=:%lu  \n",total);//初次和最后一次采样值建议丢弃				
//				memset(ADCbuf, 0, sizeof(ADCbuf));//清空数组
				total=0;
	    	Flag = 0;
				ADC_DMA = ADCbuf; //设置DMA起始地址
				ADC_DMA_CN=ADCCount;
				ADC_SETUP |= bADC_DMA_EN;//使能DMA和中断
//				InitADCDMA( );
	    }
			 mDelaymS(1000);			
    }

在这里插入图片描述

🎯手动采样案例


/********************************** (C) COPYRIGHT *******************************
* File Name          : ADCManual.C
* Author             : WCH
* Version            : V1.3
* Date               : 2016/6/24
* Description        : CH559的手动采样ADC操作,支持通道切换
*******************************************************************************/

#include "..\DEBUG.C"                                                          //调试信息打印
#include "..\DEBUG.H"

UINT8 ADCChannel[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};               //ADC通道1-8

// #define _INT_  1 //ADC中断方式


//#define mul_channel  //启用代表0-8个通道一起采集,不启用则单通道采集

#define Single_channel 2 //如果选择单通道数据采集,指定单通道号:0-7

#pragma  NOAREGS


/*******************************************************************************
* Function Name  : InitADCManual()
* Description    : ADC手动采用初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void InitADCManual()
{
    P1_IE = 0x00;                                                              //关闭P1口其他数据功能,如果只用到部分采样通道,其余置1,否则影响IO功能
    ADC_SETUP |= bADC_POWER_EN;                                                //ADC电源使能
    ADC_CK_SE = 0x02;                                                          //设置分频
    ADC_CTRL &= ~MASK_ADC_CYCLE;                                               //选择手动采样
    ADC_CTRL &= ~(bADC_CHANN_MOD1 | bADC_CHANN_MOD0);                          //手动选择通道
    ADC_CHANN = ADCChannel[0];                                                 //选通通道1
    ADC_EX_SW |= bADC_RESOLUTION;                                              //采样位数11bit
//	ADC_EX_SW &= ~bADC_RESOLUTION;                                             //采样位数10bit
    mDelayuS(100);                                                             //确保ADC正常启动
}

/*******************************************************************************
* Function Name  : ADCChanelChange()
* Description    : ADC通道切换
* Input          : UINT8 Chanel
* Output         : None
* Return         : UINT16 ADCValue
*******************************************************************************/
UINT16 ADCChanelChange(UINT8 Chanel)
{
  UINT16 ADCValue = 0;
	ADC_CHANN = Chanel;                                                          //切换ADC通道
// 	ADC_EX_SW |= bADC_RESOLUTION;                                              //ADC采集位数
	mDelayuS(10);                                                                //可选,等待通道切换成功
	ADC_CTRL |= bADC_SAMPLE;                                                     //手动产生采样脉冲
	mDelayuS(5);
	ADC_CTRL &= ~bADC_SAMPLE;	
	while((ADC_STAT & bADC_IF_ACT) == 0);                                        //非中断方式,等待采集完成
	ADC_STAT |= bADC_IF_ACT;
	ADCValue = ADC_FIFO;
  return ADCValue;                                                             //返回采样值
}

#ifdef _INT_
/*******************************************************************************
* Function Name  : InitADCInterrupt()
* Description    : ADC中断初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void InitADCInterrupt()
{
    ADC_SETUP |= bADC_IE_FIFO_OV;                                              //使能FIFO溢出中断
//	ADC_SETUP |= bADC_IE_AIN7_LOW;                                             //使能AIN7低电平中断
    ADC_SETUP |= bADC_IE_ACT;                                                  //ADC完成中断
    IE_ADC = 1;                                                                //使能ADC中断
}

/*******************************************************************************
* Function Name  : ADCInterrupt(void)
* Description    : ADC 中断服务程序
*******************************************************************************/
void	ADCInterrupt( void ) interrupt INT_NO_ADC using 1                       //ADC中断服务程序,使用寄存器组1
{ 
    UINT16 ADCValue = 0;
    if(ADC_STAT & bADC_IF_ACT)                                                //ADC完成中断
    {  
    	ADC_STAT |= bADC_IF_ACT;                                                //清中断                                         
    }
    ADCValue = ADC_FIFO;
	  printf("FIFOCnt:%02u  ADC_DATA:%04u  \n",(UINT16)(ADC_STAT&3),(UINT16)ADC_FIFO);
/* 如果需要继续采样,则使能下面程序,产生手工采样脉冲 */
#if 1
    ADC_CTRL |= bADC_SAMPLE;                                                  //手动产生采样脉冲
    mDelayuS(2);
    ADC_CTRL &= ~bADC_SAMPLE;	
#endif
}
#endif

void main( ) 
{
    UINT16 ADCDat;
    UINT8 i = 0;
  CfgFsys( );                                                                //CH559时钟选择配置 
    mDelaymS(5);                                                               //等待内部晶振稳定
    
    mInitSTDIO( );                                                             //串口0,可以用于调试
    printf("start ...\n"); 
//ADC采样有8个通道,在P1口,通道n 分别ADC_CHANN对应 (0x01 << n) ,如通道0是0x01,通道1是0x02依次类推		
    InitADCManual();                                                           //ADC手动采样初始化
		ADCDat=ADC_FIFO;                                                           //ADC开启后,FIFO中有一个无效的采样值,取出丢弃
	  printf("FIFOCnt:%02u  InvalidADC_DATA:%04u  \n",(UINT16)(ADC_STAT&3),(UINT16)ADCDat);	
#ifdef _INT_
    InitADCInterrupt();                                                        //ADC中断初始化
    EA = 1;                                                                    //开启全局中断
	
    ADC_CTRL |= bADC_SAMPLE;                                                   //手动产生采样脉冲
    mDelayuS(2);
    ADC_CTRL &= ~bADC_SAMPLE;	
#endif	
    while(1)                                                                   //可以如果不用中断方式,可以查询bADC_IF_ACT位,为1代表手动采样成功
    {
#ifndef _INT_
		    ADC_SETUP|=bADC_POWER_EN;  			//开启ADC电源
#ifdef mul_channel			
        for(i=0;i<8;i++){
          ADCDat = ADCChanelChange(ADCChannel[i]);                             //通道i采样
	        printf("FIFOCnt:%02u  ADC_DATA_P1%u:%04u  \n",(UINT16)(ADC_STAT&3),(UINT16)i,(UINT16)ADCDat);			
        }
#else
   ADCDat = ADCChanelChange(ADCChannel[Single_channel]);  
				printf("FIFOCnt:%02u  ADC_DATA_P1%u:%04u  \n",(UINT16)(ADC_STAT&3),(UINT16)1,(UINT16)ADCDat);	
#endif			
	      ADC_SETUP&=~bADC_POWER_EN;                                             //关闭ADC电源
        printf("-----我是分隔符------\n");
        mDelaymS(1000);				
//如果同时采样多个通道,可以在采样完成后通过bADC_CHANN_ID区分采样值所属的通道
#endif
    }                                                                 
}
  • 指定单通道P1_2采样数据串口数据打印的结果
    在这里插入图片描述
  • 将上面的代码改成多通道一起采用并打印;

启用和注释对应的宏定义即可

#define mul_channel  //启用代表0-8个通道一起采集,不启用则启用下面的单通道采集
//#define Single_channel 2 //如果选择单通道数据采集,指定单通道号:0-7

在这里插入图片描述

在这里源码就不提供了,根据上面提供的官方的参考源码以及上面附的代码修改对应内容即可验证。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值