STM8单片机ADC单次扫描模式

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战markdown

  当STM8单片机使用ADC功能读取多个通道的值时,可使用单次模式,采样完一个通道以后,从新初始化另外一个通道,而后采样,采样完成后继续从新初始化切换下一个通道。可是这样采样起来太麻烦。STM8单片机提供了一个扫描模式,能够依次按照顺序采样多个通道的值,多个通道所有采样完成后,会置位标志位,这样就能够一次性将多个通道的值所有读出来。app

其中官方文档介绍以下:函数

image.png

  从文档中能够看出,采样都是从0通道开始的,好比想采样3个通道值,那么采样的通道号就为0---3。若是想采样三、4通道,那么也得从0通道开始,也就是说要从0通道开始扫描到4通道,就算0、一、2通道不用,它也会扫描,就这一点很差。post

image.png

  单次采样的时候,ADC_CSR寄存器中的通道号指的是要采样的通道号,要采样那个通道就设置为几,而在扫描模式下,这个通道号指的是要扫描的最大通道号,扫描都是从0通道开始。这里设置时要注意。ui

  单次扫描模式的配置其实也很简单,只是比单次模式多了一个开启扫描的设置。下面直接经过寄存器来讲明单次扫描模式的设置方法。url

image.png

  首先看ADC_CSR寄存器,这个寄存器里面只须要设置一个通道转换位,这个通道指的是扫描的最大通道。在单次模式下这个通道指的是要读取的通道值,而在扫描模式下,这个通道指的是,最大扫描通道数。扫描都是从0通道开始。EOC位是转换结束标志位,这个在初始化的时候不用设置,只是在读取数据的时候通道判断这个位来读取。spa

image.png

  接下来看ADC_CR1寄存器,这个寄存器要设置的只有一个,就是ADON位,用来控制ADC的转换开关,为1时启动转换功能,为0时关闭转换功能。预分频位和单次转换模式默认值都为0,恰好符合咱们的需求,也就是默认2分频,单次转换模式。3d

image.png

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

image.png

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

  下面就能够开始编写代码了:

#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 并开始转换
}

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;             //程序运行一圈耗时 15us 
        while( ( ADC_CSR & 0x80 ) == 0 );      //等待转换结束 等待时间为 4us
        ADC_CSR &= ~( 1 << 7 );               // 转换结束标志位清零 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;
        
        ADC_CR1 |= 0x01;                        //开启一次转换
    }
    return voltage;
}

复制代码

初始化流程就按照上面分析的流程进行,在启动ADC转换的时候,须要开启两次。

image.png

  系统启动后ADC默认是在低功耗模式下,第一次设置ADON位是将ADC从低功耗模式唤醒,而后须要延时1个转换周期,等待系统转换稳定。因此在初始化的时候须要设置两次ADON为1。因为这里使用的是单次触发模式,因此每次转换完成以后,ADC默认就关闭了,若是要继续转换,就须要手动将ADON位设置为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 << 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 并开始转换

}
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;

        ADC_CR1 |= 0x01;                        //开启一次转换
    }
    return voltage;
}
//AD中断服务函数 中断号22
#pragma vector = 24 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void ) {
    ADC_CSR &= ~( 1 << 7 );               // 转换结束标志位清零 EOC
    ADC_flag = 1;                       // ADC中断标志 置1
}
复制代码

中断的开启就是将ADC_CSR寄存器的EOCIE位设置为1,而后当全部的通道转换完成以后,就会产生一次中断,在中断中读取数据。

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

image.png

image.png

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

相关文章
相关标签/搜索