历史上的今天
返回首页

历史上的今天

今天是:2024年09月16日(星期一)

2021年09月16日 | STM8单片机ADC连续扫描模式

2021-09-16 来源:eefocus

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

image.png?imageView2/2/w/550

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


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

image.png?imageView2/2/w/550

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

image.png?imageView2/2/w/550

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

image.png?imageView2/2/w/550

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

image.png?imageView2/2/w/550

  最后就是这个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 );   //预分频 2

    ADC_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 );    //开启 ADC

    ADC_CR2 |= ( 1 << 1 );    // SCAN = 1 开启扫描模式


      //当首次置位ADON位时,ADC从低功耗模式唤醒。为了启动转换必须第二次使用写指令来置位ADC_CR1寄存器的ADON位。

    for( l = 0; l < 10; l++ );  //延时,保证ADC模块的上电完成 至少7us

    ADC_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位。

image.png?imageView2/2/w/550

image.png?imageView2/2/w/550

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


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

image.png?imageView2/2/w/550

  官方文档中这样说道:在连续扫描模式中正确的清除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 );   //预分频 2

    ADC_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 );    //开启 ADC

    ADC_CR2 |= ( 1 << 1 );    // SCAN = 1 开启扫描模式


    ADC_CSR |= ( 1 << 5 );    //EOCIE 使能转换结束中断


    //当首次置位ADON位时,ADC从低功耗模式唤醒。为了启动转换必须第二次使用写指令来置位ADC_CR1寄存器的ADON位。

    for( l = 0; l < 10; l++ );  //延时,保证ADC模块的上电完成 至少7us

    ADC_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;    // 转换结束标志位清零  EOC

    ADC_flag = 1;                       // ADC中断标志 置1

}


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

推荐阅读

史海拾趣

Hifn Inc公司的发展小趣事

机顶盒,全称为数字视频变换盒,是现代家庭娱乐中不可或缺的重要设备。从广义上讲,任何与电视机连接的网络终端设备均可视为机顶盒,它们不仅限于接收和转换电视信号,还具备丰富的网络交互功能。

机顶盒的核心功能在于接收并解码来自有线电缆、卫星天线、宽带网络及地面广播的数字电视信号,将其转换成适合在电视机上播放的格式。这一过程包括接收、解码和显示三个主要步骤,确保了用户能够享受到高清乃至4K的超高清画质和震撼音效。此外,机顶盒还提供了诸如电子节目指南、因特网网页浏览等增值服务,极大地丰富了用户的观看体验。

随着技术的不断进步,机顶盒正朝着智能化、多功能化方向发展。例如,智能推荐算法能够根据用户的观看历史和偏好,提供个性化的节目推荐;语音识别技术则让用户能够通过语音指令控制机顶盒,实现更加便捷的操作体验。同时,机顶盒还支持家庭影院功能,让用户在家中就能享受到电影院般的视听效果。

总的来说,机顶盒作为连接电视与外部信号源的桥梁,不仅提升了电视节目的观看体验,还通过丰富的网络交互功能为用户提供了更加广泛的内容和娱乐选择。随着技术的不断发展和用户需求的日益多样化,机顶盒将继续在家庭娱乐领域发挥重要作用。

DLP Design公司的发展小趣事

DLP精工科技公司,一直秉承“品质至上”的经营理念,在DLP技术产品的设计和制造过程中严格把控品质。公司拥有一支专业的研发团队和先进的生产设备,确保每一件产品都符合高标准的质量要求。通过不断的技术创新和产品升级,DLP精工科技在电子行业中树立了良好的品牌形象,赢得了消费者的信赖和认可。

AMERICASEMI [America Semiconductor, LLC]公司的发展小趣事

面对日益严峻的环境问题和资源压力,AMERICASEMI积极响应可持续发展号召,推动绿色生产和技术创新。公司采用环保材料和节能技术,降低生产过程中的能耗和排放。同时,AMERICASEMI还关注未来技术的发展趋势,加大在人工智能、物联网等领域的研发投入,为公司的未来发展奠定坚实的基础。

这五个故事展示了AMERICASEMI在电子行业中的发展历程和取得的成就。从创立初期的艰难起步到如今的行业佼佼者,AMERICASEMI凭借技术创新、市场拓展、质量管理等方面的努力,不断推动自身的发展壮大。未来,随着电子行业的持续发展和技术的不断进步,AMERICASEMI有望继续保持领先地位,为行业的发展做出更大的贡献。

Advanced Thermal Solutions公司的发展小趣事

随着全球环保意识的日益增强,ATS积极响应国家号召,将环保理念融入公司的产品和服务中。公司开始研发和生产符合环保标准的散热器产品,采用环保材料和工艺,减少生产过程中的污染排放。同时,ATS还加强了对废旧产品的回收和处理工作,努力实现资源的循环利用。这些举措不仅提升了ATS的环保形象,也为公司的可持续发展奠定了坚实基础。

这五个故事展示了ATS公司在电子行业中的发展历程和取得的成就。从创业初期的技术突破,到从咨询到制造的转型,再到全球化战略的实施和研发创新能力的持续提升,ATS始终保持着敏锐的市场洞察力和创新精神。同时,公司还积极践行环保理念,为电子行业的可持续发展做出了积极贡献。

General Electric Solid State公司的发展小趣事

ATS始终坚持以创新为驱动力,不断提升研发创新能力。公司设立了专门的研发团队,负责新技术和新产品的研发工作。通过与高校和研究机构的合作,ATS不断引入前沿的科技成果和人才资源,为公司的创新发展提供了有力支持。此外,ATS还建立了完善的研发流程和质量控制体系,确保每一款产品都能达到最高的性能和质量标准。

AEM [ American Electronic Materials]公司的发展小趣事

AEM深知人才是企业发展的核心竞争力。因此,公司一直注重人才培养和团队建设。AEM建立了完善的人才培养和激励机制,吸引和培养了一批高素质、专业化的员工队伍。同时,公司还为员工提供了良好的工作环境和发展空间,鼓励员工不断创新和进取。

在团队建设方面,AEM注重团队合作和沟通协作能力的培养。通过定期的团队建设活动和内部培训,AEM打造了一支团结、高效、富有战斗力的团队,为公司的持续发展提供了有力保障。

这五个故事从不同角度展示了AEM公司在电子行业中的发展历程和取得的成就。通过不断创新、合作与拓展,AEM已经成为电子行业中具有重要影响力的企业之一。未来,随着技术的不断进步和市场的不断变化,AEM将继续保持其领先地位,为电子行业的发展贡献更多力量。

问答坊 | AI 解惑

4-20MAIC 现场外接保护方案参考

ISO 系列隔离放大器IC 现场外接保护方案参考联系人:陈志娟 13480868230 QQ:215596524 MSN:chenzhijuan00@hotmail.com…

查看全部问答>

开创移动互联网未来

动互联网蓬勃兴起,将移动通信产业带入新的发展时期 整个移动互联网的兴起,给移动通信产业带来了一个蓬勃发展的新时期,不管是从欧洲3G的数据发展情况(每年接近600%的发展),还是从移动终端技术的发展趋势(越来越多的互联网服务,包括社区网站 ...…

查看全部问答>

有点复古有点雷人的概念摇棒手机

这个“时髦”的电话拥有一个曲柄的摇棒,还有一个滑出的数字键盘,听筒和话筒基本上移植了古老的电话形式,但是这个手机需要一个50磅重的电池提供能量,看完之后小编觉得超级的雷人啊!…

查看全部问答>

【FPGA代码】模为60 的BCD 码加法计数器

module count60(qout,cout,data,load,cin,reset,clk);output[7:0] qout;output cout;input[7:0] data;input load,cin,clk,reset;reg[7:0] qout;always @(posedge clk) //clk 上升沿时刻计数beginif (reset) qout<=0; //同步复位else if(load) qou ...…

查看全部问答>

最新语音交通广播

PVS适用于各设有禁止临时停车标志、标线处所。应用范围非常广,包括机场、车站、捷运、码头、学校、博物馆、展览场、竞技场、市场或其它公共场所出入口、交叉路口、公共汽车招呼站等地点,或是严重并排临时停车路段。未来更可依据单位需求,扩充 ...…

查看全部问答>

做wince mobile方面的技术交流与合作 高级群 68387065

做wince mobile方面的技术交流与合作 高级群 68387065…

查看全部问答>

EVC

EVC 下的dialog based的app没有最小化方法,前面发了一个帖子,有人建议处理ACTIVE消息,但是我在ACTIVE中使用showwindow来最小化,还是不行。 我还修改了dialog的属性,加入了WS_MINIMIZEBOX,dialog出现了一个_图标,但是点击之后不能实现最小化 ...…

查看全部问答>

ADS编译问题

ADS编译问题: 我定义了一个中断函数   void __irq time0Up(void); 编译是报错如下:__irq FUNCTION need no argument and no return 这是为什么呢?…

查看全部问答>

请教怎么关闭STM32外围模块电源

                                   是这么函数吗? ADC_Cmd(ADC1,DISABLE);  ADC_Cmd(ADC2,DISABLE); 主要我是我关 ...…

查看全部问答>

请教香主:sd写文件总是失败,结果电脑不识别

最近在做一个录音系统,想法录音文件存在sd卡上,参考st IAR-STM32-SKMP3_player的范例,发现只有读取文件没有写入文件,按照自己的理解增加代码,搞了好几天结果总是写不成功,仿真代码运行又都正常,请香主指导一下。我的代码如下:&nb ...…

查看全部问答>