当STM8单片机使用ADC功能读取多个通道的值时,可以使用连续模式,但是连续模式一次只能采样一个通道的值,那么如果要采样多个通道时怎么办呢?STM8提供了一个多通道连续采样扫描模式。也就是说多个通道采样时才有扫描模式,从第0通道开始依次向后扫描,扫描完成后会自动开始第二次扫描。而不像单次扫描模式那样,依次结束后ADC就会自动关闭,需要手动开启第二次扫描。连续扫描模式只需要开启一次,就会一直重复的采样,直到手动关闭,才会结束。

连续扫描模式相比单次扫描模式,在初始化的时候,只是多了一行代码,就是设置COUNT位为1.

  下面开始分析要使用连续扫描模式,需要设置哪些寄存器的哪些位。

  首先要设置最大的采样通道。单次采样的时候,ADC_CSR寄存器中的通道号指的是要采样的通道号,要采样那个通道就设置为几,而在扫描模式下,这个通道号指的是要扫描的最大通道号,扫描都是从0通道开始。比如想要扫描通道2、和通道3。那么这里的通道值就要设置要采样的最大通道值,也就是要设置为3,那么系统就会从0通道开始扫描到通道3结束,就算通道0和通道1没有用到,系统依然会扫描。

  接下来看ADC_CR1寄存器,这个寄存器要设置的有两个,就是ADON位和COUNT位,ADON位用来控制ADC的转换开关,为1时启动转换功能,为0时关闭转换功能。COUNT位用来开启连续转换模式。预分频位默认值为0,刚好符合我们的需求,也就是默认2分频。

  接下来看ADC_CR2寄存器,这个寄存器必须设置的位其实只有一个,就是要要使用扫描模式必须设置SCAN位为1,其他位使用默认值就行。如果数据对齐方式使用左对齐的话,就不用设置。这里使用的是数据右对齐,所以需要将ALIGN位也设置为1.其他位默认为0.

  最后就是这个ADC_TDR寄存器,用来禁止施密特触发器,主要是用来降低单片机功耗。当然这个寄存器不用设置也可以。如果要设置的话,使用了哪几个ADC通道,就将对应的通道位置1就行。

  下面就可以开始编写代码了:

#include "adc.h"
#include "main.h"
#include "led.h"_Bool ADC_flag = 0;                     //ADC转换成功标志u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};//AD通道引脚初始化
void ADC_GPIO_Init( void )
{PD_DDR &= ~( 1 << 2 );              //PD2 设置为输入PD_CR1 &= ~( 1 << 2 );              //PD2 设置为悬空输入PD_DDR &= ~( 1 << 3 );              //PD3 设置为输入PD_CR1 &= ~( 1 << 3 );              //PD3 设置为悬空输入PC_DDR &= ~( 1 << 4 );              //PC4 设置为输入PC_CR1 &= ~( 1 << 4 );              //PC4设置为悬空输入
}//设置为 连续扫描模式
//ch 为ADC通道 连续转换AIN0---AINch 通道的数据
void ADC_CH_Init( u8 ch )
{char l = 0;ADC_GPIO_Init();ADC_CR1 &= ~( 7 << 4 );   //预分频 2ADC_CR2 &= ~( 1 << 6 );   //不使用外部触发//禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗  PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!ADC_TDRL |= ( 1 << 2 );ADC_TDRL |= ( 1 << 3 );ADC_TDRL |= ( 1 << 4 );ADC_CR1 |= ( 1 << 1 );   //连续转换ADC_CSR |= 0x04;          //配置通道号最大的那个ADC_CR2 |= ( 1 << 3 );    //右对齐ADC_CR1 |= ( 1 << 0 );    //开启 ADCADC_CR2 |= ( 1 << 1 );    // SCAN = 1 开启扫描模式//当首次置位ADON位时,ADC从低功耗模式唤醒。为了启动转换必须第二次使用写指令来置位ADC_CR1寄存器的ADON位。for( l = 0; l < 10; l++ );  //延时,保证ADC模块的上电完成 至少7usADC_CR1 |= ( 1 << 0 );      //再次将CR1寄存器的最低位置1 使能ADC 并开始转换
}
/*
注意:在扫描模式(连续扫描模式)中,不要使用位操作指令(BRES)去清除EOC标志位,
这是因为该指令是对整个ADC_CSR寄存器的一个读-修改-写操作。
从CH[3:0]寄存器中读取当前的通道编号和写回该寄存器,将会改变扫描系列的最后通道编号。
在连续扫描模式中正确的清除EOC标志位的方法是 个RAM变量中载入一个字节到ADC_CSR寄存器,
这样来清除EOC标志位同时还重新载入扫描系列新的最后通道编号。实验发现,位操作指令只在连续扫描模式中会清除CH[3:0]寄存器中的值,但并不影响其他值。
因此将ADC_CSR中的值读出,再将CH[3:0]中原来通道号加入进去,最后重新写入ADC_CSR中即可。写法如下:ADC1->CSR = (uint8_t)(ADC1->CSR &(~ADC1_FLAG_EOC)|ADC1_CHANNEL_n);注:ADC1_CHANNEL_n表示扫描到那个通道结束。*/
u16 ain2_val = 0,ain3_val = 0,ain4_val = 0;
//读取采样电压值
u16 ReadVol_CHx( void )
{u16 voltage = 0;u16 temph = 0;u8 templ = 0;while( 1 ){LED = !LED;             //程序运行一圈耗时 10us    while( ( ADC_CSR & 0x80 ) == 0 );      //等待转换结束//ADC_CSR &= ~( 1 << 7 );               // 不能通过位操作来清零  EOC 标志ADC_CSR = ADC_CSR & 0x7F | 0x04;        // 转换结束标志位清零  EOC//读取 AIN2 的值templ = ADC_DB2RL;temph = ADC_DB2RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain2_val =  temph;//读取 AIN3 的值templ = ADC_DB3RL;temph = ADC_DB3RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain3_val =  temph;//读取 AIN4 的值templ = ADC_DB4RL;temph = ADC_DB4RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain4_val =  temph;}return voltage;
}

  这里要注意的地方有两个:

  一是在扫描模式时,采样的数据结果不是存放在ADC_DR寄存器中,而是存放在ADC_DBxR寄存器中,通道几就存放在对应的ADC_DBxR寄存器中,这个寄存器分为高位和低位两个。比如通道2的数据,就存放在ADC_DB2RL和ADC_DB2RH寄存器中,在读取数据的时候也要注意,如果数据是左对齐必须先读高8位,再读低位。如果数据是右对齐必须先读低8位,在读高8位。

数据对齐在官方手册中有详细的说明,在使用的时候要注意这一点。

  二是在数据读取完成后,清除EOC标志位的时候,千万不能使用位清除的方式来进行,比如在单次采样的时候,清除标志位的方式通常是 ADC_CSR &= 0x7F; 直接将最高位清0. 在连续扫描模式下,这样清除标志位就会出问题。因为位操作ADC_CSR寄存器时,系统会进行一个读 - 修改 - 写的操作,那么在读改写的过程中,会改变通道号。而通道在连续扫描模式下会自动变化,这样的话,位操作就会打乱扫描通道的通道号操作,导致采样错误。那么要如何清除这个标志位呢?
  官方文档中这样说道:在连续扫描模式中正确的清除EOC标志位的方法是 个RAM变量中载入一个字节到ADC_CSR寄存器,这样来清除EOC标志位同时还重新载入扫描系列新的最后通道编号。

  这样听起来有些抽象,简单的说,不要对寄存器进行读改写的操作,而是直接一次性的给ADC_CSR写入它需要的值。这里采用的方式就是

ADC_CSR = ADC_CSR & 0x7F | 0x04;

将寄存器的最高清0,然后重新设置一次转换通道号,将这些值一次性的写入寄存器中,这样就ADC_CSR就会在一轮扫描结束后重新设置它的值,然后从0开始继续扫描,这样就不会打断ADC的通道扫描过程了。

官方文档中对这里也有说明,在使用的时候要注意这一点。

上面的代码是通常查询标志位的方式读取数据的,也可以使用中断的方式来读取数据。

#include "adc.h"
#include "main.h"
#include "led.h"_Bool ADC_flag = 0;                     //ADC转换成功标志u16 ADC_DB[10] = {0};
u16 adc_data[5] = {0};//AD通道引脚初始化
void ADC_GPIO_Init( void )
{PD_DDR &= ~( 1 << 2 );              //PD2 设置为输入PD_CR1 &= ~( 1 << 2 );              //PD2 设置为悬空输入PD_DDR &= ~( 1 << 3 );              //PD3 设置为输入PD_CR1 &= ~( 1 << 3 );              //PD3 设置为悬空输入PC_DDR &= ~( 1 << 4 );              //PC4 设置为输入PC_CR1 &= ~( 1 << 4 );              //PC4设置为悬空输入
}//设置为 连续扫描模式
//ch 为ADC通道 连续转换AIN0---AINch 通道的数据
void ADC_CH_Init( u8 ch )
{char l = 0;ADC_GPIO_Init();ADC_CR1 &= ~( 7 << 4 );   //预分频 2ADC_CR2 &= ~( 1 << 6 );   //不使用外部触发//禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗  PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!ADC_TDRL |= ( 1 << 2 );ADC_TDRL |= ( 1 << 4 );ADC_CR1 |= ( 1 << 1 );   //连续转换ADC_CSR |= 0x04;          //配置通道号最大的那个ADC_CR2 |= ( 1 << 3 );    //右对齐ADC_CR1 |= ( 1 << 0 );    //开启 ADCADC_CR2 |= ( 1 << 1 );    // SCAN = 1 开启扫描模式ADC_CSR |= ( 1 << 5 );    //EOCIE 使能转换结束中断//当首次置位ADON位时,ADC从低功耗模式唤醒。为了启动转换必须第二次使用写指令来置位ADC_CR1寄存器的ADON位。for( l = 0; l < 10; l++ );  //延时,保证ADC模块的上电完成 至少7usADC_CR1 |= ( 1 << 0 );      //再次将CR1寄存器的最低位置1 使能ADC 并开始转换
}
/*
注意:在扫描模式(连续扫描模式)中,不要使用位操作指令(BRES)去清除EOC标志位,
这是因为该指令是对整个ADC_CSR寄存器的一个读-修改-写操作。
从CH[3:0]寄存器中读取当前的通道编号和写回该寄存器,将会改变扫描系列的最后通道编号。
在连续扫描模式中正确的清除EOC标志位的方法是 个RAM变量中载入一个字节到ADC_CSR寄存器,
这样来清除EOC标志位同时还重新载入扫描系列新的最后通道编号。实验发现,位操作指令只在连续扫描模式中会清除CH[3:0]寄存器中的值,但并不影响其他值。
因此将ADC_CSR中的值读出,再将CH[3:0]中原来通道号加入进去,最后重新写入ADC_CSR中即可。写法如下:ADC1->CSR = (uint8_t)(ADC1->CSR &(~ADC1_FLAG_EOC)|ADC1_CHANNEL_n);注:ADC1_CHANNEL_n表示扫描到那个通道结束。
*/
u16 ain2_val = 0, ain3_val = 0, ain4_val = 0;
u16 temph = 0;
u8 templ = 0;
//读取采样电压值
u16 ReadVol_CHx( void )
{u16 voltage = 0;if( ADC_flag == 1 ){ADC_flag = 0;//单通道扫描模式,转换结果存储在 ADC_DBxR 寄存器中//读取 AIN2 的值templ = ADC_DB2RL;temph = ADC_DB2RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain2_val =  temph;//读取 AIN3 的值templ = ADC_DB3RL;temph = ADC_DB3RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain3_val =  temph;//读取 AIN4 的值templ = ADC_DB4RL;temph = ADC_DB4RH;temph = ( u16 )( templ | ( u16 )( temph << ( u16 )8 ) );ain4_val =  temph;}return voltage;
}//AD中断服务函数 中断号22
#pragma vector = 24                     // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void )
{//ADC_CSR &= ~( 1 << 7 );           // 不能通过位操作来清零  EOC 标志ADC_CSR = ADC_CSR & 0x7F | 0x04;    // 转换结束标志位清零  EOCADC_flag = 1;                       // ADC中断标志 置1
}

当通道一次扫描完成后,会产生一个中断,然后去对应的ADC_DBxR寄存器中读取通道值就可以了,读取完成后,不需要手动开启ADON标志位,系统会自动启动下一次转换。

STM8单片机ADC连续扫描模式相关推荐

  1. STM8单片机ADC连续采样模式

     STM8S003单片机内部ADC为12位,A/D转换的各个通道可以执行单次和连续的转换模式.  单次转换模式的意思就是,ADC每次转换一次数据后,就会停止转换,如果还需要继续转换的话,就需要手动开启 ...

  2. STM8单片机ADC带缓存的连续采样模式

      在上一篇文章中说了STM8的ADC连续采样模式,为了提高采样的精度和速率,STM8单片机还提供了带缓存的连续采样模式,也就是说ADC会连续采集8个数据,放在缓存中,读取数据时可以一次从缓存中读取8 ...

  3. STM8单片机 ADC模拟看门狗中文资料错误

      在调试stm8单片机的adc模拟看门狗功能时,不管怎么设置ADC_HTR和ADC_LTR寄存器的值,然后通过IAR软件单步调试时观察这两个寄存器的值都不对.   根据中文手册上看,高位寄存器存储的 ...

  4. STM8单片机ADC采样功能通过定时器触发

      在使用STM8单片机的ADC功能时,读取ADC数据时一般有两种方式,一种是通常不断地读取采样标志位,来判断ADC采样是否结束,一种是通过中断的方式来通知系统采样是否结束.   有时候采样ADC数据 ...

  5. STM8单片机ADC单次采样模式

     STM8S003单片机内部ADC为12位,A/D转换的各个通道可以执行单次和连续的转换模式.  单次转换模式的意思就是,ADC每次转换一次数据后,就会停止转换,如果还需要继续转换的话,就需要手动开启 ...

  6. STM8单片机ADC模拟看门狗功能实现

      看门狗的功能大家都知道,而这个模拟看门狗又是什么东西呢?   简单的说就是这个模拟看门狗可以实时监控ADC采样的数据,当采样的数据值小于设置的最小值或者大于设置的最大值时,单片机就会触发ADC中断 ...

  7. stm8单片机例程下载链接

    在这篇文章中将自己上传的资源做一个下载链接的目录方便查找下载 STM8单片机编码器使用示例 STM8单片机ADC连续扫描模式并开启模拟看门狗功能 STM8单片机ADC单次扫描模式并开启模拟看门狗功能 ...

  8. STM8 ADC转换模式-------连续扫描模式

    STM8单片机ADC支持5种转换模式:单次模式,连续模式,带缓存的连续模式,单次扫描模式,连续扫描模式. 连续扫描模式         该模式和单次扫描模式相近,只是每一次在最后通道转换完成时,一次新 ...

  9. STM8单片机通过PWM触发ADC同步采样

      在做数字开关电源开发过程中使用最多的就是PWM功能和ADC采样功能.ADC采样时采样的时间点很重要,必须在PWM输出高电平的时候取采样,这样采样出来的数据才是最准确的.在STM8单片机中,可以通过 ...

最新文章

  1. linux上使用strace查看C语言级别的php源码【一种方法】
  2. 零基础参加软件测试培训需要学多长时间
  3. HTML的标签描述2
  4. 页面是可以这样设计的
  5. sap-通过定义物料组的评估类-设置无物料号的费用采购
  6. 所谓的二维背包Triangular Pastures POJ 1948
  7. matlab 倒数第二个位置_MATLAB中运行以下程序后倒数第二部分画图程序要怎么改??、、、...
  8. .net 垃圾回收机制
  9. Linux--iptables常用命令
  10. kvm虚拟机vnc和spice配置
  11. .NET Micro Framework开发板用户简明手册(v3.0)
  12. sass安装:webpack sass编译失败,node-sass安装失败的终极解决方
  13. 在VirtualBox安装Ubuntu虚拟机实现文件夹共享
  14. 木门锁孔合页综合加工机器
  15. 初学者:html中的表单详解(下面附有代码)
  16. 天上的街市Unity游戏场景制作案例(一)
  17. Cortex-M3 Bit-Banding
  18. Androi移动开发基础
  19. Python全局变量的隐藏“窍门”
  20. Excel常用的操作

热门文章

  1. Codeforces 402 and 403 (Div. 2 and 1)
  2. SpringBoot | 第三十二章:事件的发布和监听
  3. Keepalived相关参数说明
  4. BZOJ 4602: [Sdoi2016]齿轮 dfs
  5. MySQL - ODBC安装错误问题!
  6. Titanium系列--对Window和View的一点理解
  7. Unicode和UTF-8之间的转换详解
  8. 实现textbox输入时模糊查询
  9. nginx配置高可用的集群
  10. web测试与app测试异同