当STM8单片机使用ADC功能读取多个通道的值时,可以使用单次模式,采样完一个通道之后,重新初始化另一个通道,然后采样,采样完成后继续重新初始化切换下一个通道。但是这样采样起来太麻烦。STM8单片机提供了一个扫描模式,可以依次按照顺序采样多个通道的值,多个通道全部采样完成后,会置位标志位,这样就可以一次性将多个通道的值全部读出来。
其中官方文档介绍如下:
从文档中可以看出,采样都是从0通道开始的,比如想采样3个通道值,那么采样的通道号就为0—3。如果想采样3、4通道,那么也得从0通道开始,也就是说要从0通道开始扫描到4通道,就算0、1、2通道不用,它也会扫描,就这一点不好。
单次采样的时候,ADC_CSR寄存器中的通道号指的是要采样的通道号,要采样那个通道就设置为几,而在扫描模式下,这个通道号指的是要扫描的最大通道号,扫描都是从0通道开始。这里设置时要注意。
单次扫描模式的配置其实也很简单,只是比单次模式多了一个开启扫描的设置。下面直接通过寄存器来说明单次扫描模式的设置方法。
首先看ADC_CSR寄存器,这个寄存器里面只需要设置一个通道转换位,这个通道指的是扫描的最大通道。在单次模式下这个通道指的是要读取的通道值,而在扫描模式下,这个通道指的是,最大扫描通道数。扫描都是从0通道开始。EOC位是转换结束标志位,这个在初始化的时候不用设置,只是在读取数据的时候通道判断这个位来读取。
接下来看ADC_CR1寄存器,这个寄存器要设置的只有一个,就是ADON位,用来控制ADC的转换开关,为1时启动转换功能,为0时关闭转换功能。预分频位和单次转换模式默认值都为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 ); //预分频 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转换的时候,需要开启两次。
系统启动后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位。
数据对齐在官方手册中有详细的说明,在使用的时候要注意这一点。
上一篇:STM8单片机ADC连续扫描模式
下一篇:STM8单片机ADC带缓存的连续采样模式
推荐阅读最新更新时间:2024-11-09 10:46