(转载)STM8 的 ADC 的五种工作模式
STM8 的 ADC 是 10 位的逐次比较型模拟数字转换器,多达 16 个多功能的输入通道。拥有 5 种转换模式,转换结束可产生中断。
STM8 ADC 的初始化顺序如下:
- AD 输入通道对应的 IO 设置为上拉输入;
- 配置 AD 参数,如:预分频系数、是否使用外部触发转换、是否使用施密特触发器、是否使用缓存以及是否使用扫描模式等;
- 开启 ADC;
- 开启转换;
注意!开启ADC和开启转换实际上都是置位 ADON,然后就可以通过 ADC_DR 寄存器读取转换后的值。
单次模式
在单次转换模式中,ADC 仅在由 ADC_CSR 寄存器的 CH[3:0] 选定的通道上完成一次转换。该模式是在当 CONT 位为 0 时通过置位 ADC_CR1 寄存器的 ADON 位来启动的。
一旦转换完成,转换后的数据存储在 ADC_DR 寄存器中,EOC (转换结束) 标志被置 EOCIE 被置位将产生一个中断。
注意!初始化的时候只能选择一个通道。转换多个通道只能通过反复重新初始化或扫描模式!
示例程序:
void adc_init(void)
{
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT); //AIN4 IO 设置为上拉输入
ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2); //预分频 2
ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE); //不使用外部触发
ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE); //禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗 PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_SINGLE, //单次转换
ADC1_CHANNEL_4, //只能选择一个通道!
ADC1_ALIGN_RIGHT); //右对齐
ADC1_Cmd(ENABLE); //开启 ADC
}
void main( void )
{
u16value=0;
adc_init();
while(1)
{
ADC1_StartConversion(); //开启一次转换一次
while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC)); //等待转换完成
ADC1_ClearFlag(ADC1_FLAG_EOC); //软件清除
value=(u16)ADC1_GetConversionValue(); //从 ADC_DR 中读取 ADC 值
}
}
连续模式与带缓存的连续模式
在连换模式中,ADC 在完成一次转换后就立刻开始下一次的转换。当 CONT 位被置位时即将 ADC 设为连续模式,该模式是通过置位 ADC_CR1 寄存器的 ADON 位来启动的。
如果缓冲功能没有被使能 (ADC_CR3 寄存器的 DBUF 位 = 0),那么转换结果数据保存在 ADC_DR 寄存器中同时 EOC 标志被置位。如果 EOCIE 位已被置位时将产生一次中断。然后开始下一次转换。
如果缓存功能被使能 (DBUF = 1),那么某个选定通道上的 8 个或者 10 个连续的转换结果会填满数据缓存(此时填满的是同一个通道的数据!!扫描模式时才是不同通道的数据!),当缓存被填满时,EOC (转换结束)标志被置位,如果 EOCIE 位已被置位,则会产生一个中断,然后一个新的转换自动开始。如果某个数据缓存寄存器在被读走之前被覆盖,OVR 标志将置 1。
如果要停止连续转换,可以复位清零CONT位来停止转换或者复位清零ADON位来关闭ADC的电源。
示例程序(不带缓存):
void adc_init(void)
{
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT); // AIN4 IO 设置为上拉输入
ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2); //预分频 2
ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE); //不使用外部触发
ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE); //禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗 PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换
ADC1_CHANNEL_4, //只能选择一个通道!
ADC1_ALIGN_RIGHT); //右对齐
ADC1_Cmd(ENABLE); //开启 ADC
ADC1_StartConversion(); //开启连续转换
}
void main( void )
{
u16value=0;
adc_init();
while(1)
{
while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC)); //等待转换完成
ADC1_ClearFlag(ADC1_FLAG_EOC); //软件清除
value=(u16)ADC1_GetConversionValue(); //从 ADC_DR 中读取 ADC 值
}
}
示例程序(带缓存):
void adc_init(void)
{
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT); // AIN4 IO 设置为上拉输入
ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2); //预分频 2
ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE); //不使用外部触发
ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE); //禁止 AIN2 AIN4 的施密特触发器,降低IO静态功耗 PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换
ADC1_CHANNEL_4, //只能选择一个通道!
ADC1_ALIGN_RIGHT); //右对齐
ADC1_Cmd(ENABLE); //开启 ADC
ADC1_StartConversion(); //开启连续转换
ADC1_DataBufferCmd(ENABLE); //开启缓存
}
void main( void )
{
u16value=0;
adc_init();
while(1)
{
while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC)); //等待转换完成
ADC1_ClearFlag(ADC1_FLAG_EOC); //软件清除
value=0;
for(u8 i=0;i<10;i++)
{
value+=ADC1_GetBufferValue(i); //将 10 个缓存中的值求和
}
value=value/10;//求出 ADC 的平均值
}
}
注意,只有在连续转换模式下 ADC_DB 寄存器才能称之为缓存,此时存储的是同一个通道多次转换的值。在单次扫描和连续扫描模式下该寄存器被用来存放不同通道的转换值。
单次扫描模式
该模式是用来转换从 AIN0 到 AINn 之间的一连串模拟通道,‘n’ 是在 ADC_CSR 寄存器的 CH[3:0] 位中指定的通道编号(即 CH[3:0] 里配置第 n 个通道,就从通道 0 顺序递增逐个通道进行转换,直到第 n 个通道结束。例如,CH[3:0] 里配置为 AIN4,则对 AIN0、AIN1、AIN2、AIN3、AIN4 进行转换,其他通道不转换)。在扫描转换的过程中,序号 CH[3:0] 位的值是被硬件自动更新的,它总保存当前正在被转换的通道编号。
单次转换模式可以在在 SCAN 位被置位且 CONT 位以被清零时通过置位 ADON 位来启动。
注意:当使用扫描模式时,不可以将 AIN0 到 AINn 之间通道对应的 I/O 口设为输出状态,因为 ADC 的多路选择器已经将这些 I/O口 的输出模块禁用了。
对于单次扫描模式,转换是从AIN0通道开始的,而且结果数据被存储在数据缓冲寄存器 ADC_DBxR 中(例如,CH[3:0] 里配置为 AIN4,则 ADC_DB0R 存放 AIN0 的转换结果,ADC_DB1R 存放 AIN1 的转换结果,以此类推 ),当最后一个通道(通道 ‘n’ )被转换完成后,EOC (转换结束) 标志被置位,当 EOCIE 位已被置位时将产生一个中断。
可以从缓冲寄存器中读取各个通道的转换结果值。如果某个数据缓存寄存器在被读走之前被覆盖,OVR 标志将置1。
在转换序列正在进行过程中不要清零 SCAN 位;单次扫描模式可通过清零 ADON 位来立即停止。为了开启一次新 SCAN 扫描转换,可以通过对 ADC_CR1 寄存器的 EOC 位清零和 ADON 位置位来实现。
示例程序:
void adc_init(void)
{
GPIO_Init(GPIOC,GPIO_PIN_4,GPIO_MODE_IN_PU_NO_IT); // AIN2 IO 设置为上拉输入
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT); // AIN4 IO 设置为上拉输入
ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2); //预分频 2
ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE); //不使用外部触发
ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE); //禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗 PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_SINGLE, //单次转换
ADC1_CHANNEL_4, //配置通道号最大的那个
ADC1_ALIGN_RIGHT); //右对齐
ADC1_Cmd(ENABLE); //开启 ADC
ADC1_ScanModeCmd(ENABLE); //开启扫描模式
}
void main( void )
{
u16value1=0;
u16value2=0;
adc_init();
while(1)
{
ADC1_StartConversion(); //开启一次转换
while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC)); //等待转换完成
ADC1_ClearFlag(ADC1_FLAG_EOC); //软件清除
value1=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL2) //读取 AIN2 的值
value2=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL4) //读取 AIN4 的值
}
}
连续扫描模式
该模式和单次扫描模式相近,只是每一次在最后通道转换完成时,一次新的从通道 0 到通道 n 扫
描转换会自动开始。如果某个数据缓存寄存器在被读走之前被覆盖,OVR 标志将置 1。连续扫描模式是在当 SCAN 位和 CONT 位已被置时,通过置位 ADON 位来启动的。在转换序列正在进行过程中不要清零 SCAN 位。
连续扫描模式可以通过清零 ADON 位来立即停止。另外一种选择就是当转换过程中清除 CONT 位那么转换会在下一次的最后一个通道转换完成时停止。
注意:在扫描模式(连续扫描模式)中,不要使用位操作指令( 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 表示扫描到那个通道结束。
示例程序:
void adc_init(void)
{
GPIO_Init(GPIOC,GPIO_PIN_4,GPIO_MODE_IN_PU_NO_IT); // AIN2 IO 设置为上拉输入
GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT); // AIN4 IO 设置为上拉输入
ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2); //预分频 2
ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE); //不使用外部触发
ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE); //禁止 AIN2 AIN4 的施密特触发器,降低 IO 静态功耗 PD5,PD6 上的通道如果施密特方式禁用会导致串口无法收发数据!
ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换
ADC1_CHANNEL_4, //配置通道号最大的那个
ADC1_ALIGN_RIGHT); //右对齐
ADC1_Cmd(ENABLE); //开启 ADC
ADC1_ScanModeCmd(ENABLE); //开启扫描模式
ADC1_StartConversion(); //开启转换
}
void main( void )
{
u16value1=0;
u16value2=0;
adc_init();
while(1)
{
while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC)); //等待转换完成
ADC1->CSR = (uint8_t)(ADC1->CSR &(~ADC1_FLAG_EOC)|ADC1_CHANNEL_4); //软件清除
value1=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL2) //读取 AIN2 的值
value2=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL4) //读取 AIN4 的值
}
}
至此,STM8 的 ADC 的 5 种工作模式全部介绍完毕。总结一下学习经验就是仔细对照芯片手册编写程序,然后进行仿真调试,观察寄存器中的值的变化,从中领悟手册中的意思。
本文来自 STM8 的 ADC 的五种工作模式

浙公网安备 33010602011771号