如何使用STM8S单片机的多通道AD转换

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用STM8S单片机的多通道AD转换相关的知识,希望对你有一定的参考价值。

#include "adc.h"
 u16  TempADC;
void ADC_Config(void) 

    ADC_CR1 |= 0x01;    //开始启动初始化必须启动一次
                        //第一次写1,ADC从低功耗唤醒,第二次写1,启动ADC转换
   //ADC_CR1 |= 0x20; //使能转换结束中断,本例中采用的查询方式。

 
uint16_t Get_ADCCH_Value(ADC1_Channel_TypeDef ADC_Channel)
 
  ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS,ADC_Channel, ADC1_PRESSEL_FCPU_D2,
     ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_ALL, DISABLE);
    //你也可以用操作寄存器的方式代码如下,但未验证。
#if 0
ADC_CR2 = 0x00;             // A/D结果数据左对齐
CLK_PCKENR2 |= 0x08;        // 使能fMASTER与ADC的连接
ADC_CR1 = 0x00;// ADC时钟=主时钟/2=8MHz,单次转换模式
ADC_CSR = 0x01;             // 选择通道2
ADC_TDRL = 0x01;            // 禁止施密特触发功能 
ADC_TDRH = 0x01;
ADC_CR1 = 0x03; //2分频,连续转换,启动ADC
ADC_CR2 = 0x31; //外部事件保留,数据右对齐,禁止扫描模式,
ADC_CSR|= 0x01; //选择通道1
#endif
 ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, ADC_Channel,ADC1_ALIGN_RIGHT);
     ADC_CR1 |= 0x01;      //开始启动转换
     while(!(ADC_CSR & 0x80));   //等待转换结束
    return ADC1_GetConversionValue(); //返回ADC结果

//这个初始化代码很重要,不能省,每次切换通道都要初始化一次!
在main函数中直接调用即可:
  TempADC=Get_ADCCH_Value(ADC1_CHANNEL_1); //获取AD转化值
  WetADC=Get_ADCCH_Value(ADC1_CHANNEL_3; //获取AD转化值

参考技术A   过置位ADC_CR1寄存器的 ADON位来开启ADC。当首次置位ADON位时,ADC从低功耗模式唤醒。为了启动转换必须第二次使用写指令来置ADC_CR1寄存器的ADON 位。在转换结束时ADC会保持在上电状态,用户只需要置位ADON位一次来启动下一次的转换。
  如果长时间没有使用ADC,推荐将ADC模块切换到低功耗模式来降低功耗,这可以通过清零 ADON 位来实现。ADC模块上电后,所选通道对应的I/O口输出模块是被禁用的。因此推荐在ADC上电之前要选择合适的ADC转换通道。
  如果单次模式在单次转换模式中,ADC仅在由ADC_CSR寄存器的CH[3:0]选定的通道上完成一次转换。该模式是在当CONT位为0时通过置位ADC_CR1寄存器的ADON位来启动的。
  一旦转换完成,转换后的数据存储在ADC_DR寄存器中,EOC(转换结束)标志被置EOCIE 被置位将产生一个中断。
  代码如下:
  #include "adc.h"
  u16 TempADC;
  void ADC_Config(void)
  
  ADC_CR1 |= 0x01; //开始启动初始化必须启动一次
  //第一次写1,ADC从低功耗唤醒,第二次写1,启动ADC转换
  //ADC_CR1 |= 0x20; //使能转换结束中断,本例中采用的查询方式。
  
  
  uint16_t Get_ADCCH_Value(ADC1_Channel_TypeDef ADC_Channel)
  
  ADC1_Init(ADC1_CONVERSIONMODE_CONTINUOUS,ADC_Channel, ADC1_PRESSEL_FCPU_D2,
  ADC1_EXTTRIG_TIM, DISABLE, ADC1_ALIGN_RIGHT, ADC1_SCHMITTTRIG_ALL, DISABLE);
  //你也可以用操作寄存器的方式代码如下,但未验证。
  #if 0
  ADC_CR2 = 0x00; // A/D结果数据左对齐
  CLK_PCKENR2 |= 0x08; // 使能fMASTER与ADC的连接
  ADC_CR1 = 0x00;// ADC时钟=主时钟/2=8MHz,单次转换模式
  ADC_CSR = 0x01; // 选择通道2
  ADC_TDRL = 0x01; // 禁止施密特触发功能
  ADC_TDRH = 0x01;
  ADC_CR1 = 0x03; //2分频,连续转换,启动ADC
  ADC_CR2 = 0x31; //外部事件保留,数据右对齐,禁止扫描模式,
  ADC_CSR|= 0x01; //选择通道1
  #endif
  ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, ADC_Channel,ADC1_ALIGN_RIGHT);
  ADC_CR1 |= 0x01; //开始启动转换
  while(!(ADC_CSR & 0x80)); //等待转换结束
  return ADC1_GetConversionValue(); //返回ADC结果
  
  //这个初始化代码很重要,不能省,每次切换通道都要初始化一次!
  在main函数中直接调用即可:
  TempADC=Get_ADCCH_Value(ADC1_CHANNEL_1); //获取AD转化值
  WetADC=Get_ADCCH_Value(ADC1_CHANNEL_3; //获取AD转化值
  

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方式自动AD采样案例测试

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

来自: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 2
UINT16X	ADCbuf[ ADCCount ] _at_ 0x0040 ;                                       //存储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;
//	    	ADC_DMA_CN = ADCCount ;//DMA 剩余计数寄存器重新赋值,否则不重新数据转换
				mDelaymS(1000); 
	    
    

  • 串口打印的结果:

并未实现DMA自动采样并连续打印的效果,只有在复位后重启后才打印一次采用的数据。上面的打印数据是烧录官方所提供的Hex文件,打印的结果。本想自己来找出bug来补上,看了半天的CH559手册上的ADC寄存器真的很难看懂, 以前吐槽STC单片机手册都是1000+页的资料难翻阅,现在看到了WCH的70+页的数据手册,感觉不够看,关键是看不懂。感叹STC单片机手册详细,STM32的描述的明白。目前还没有找到清标志位的地方。

🎯手动采样案例


/********************************** (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

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

以上是关于如何使用STM8S单片机的多通道AD转换的主要内容,如果未能解决你的问题,请参考以下文章

怎样把51单片机读回来的AD值转换成电压值?

STC的51单片机自带有AD转换,和PCF8591T AD芯片相比的话,哪个更好一些?

STM8S AD转换

单片机AD采集回来的数值如何能显示为对应的电压值?

请问一下,我用PIC单片机的AD转换来测直流电压(0~5V),然后用数码管来显示(三位)

stm8s的iar如何和谐