这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战
STM8单片机的模拟看门狗功能不仅在单次模式和连续模式下可以使用,同时也可以在扫描模式下使用。
对于扫描模式可以通过ADC_AWCR寄存器来设置扫描的通道,但是设置上下限寄存器的只有一组寄存器,所以根据这个推断,模拟看门狗的功能每次只能用在一个通道中。
模拟看门狗的上限值通过下面两个寄存器设置
ADC_HTRH寄存器设置上限值的高8位,ADC_HTRL设置上限值的低2位。
ADC_LTRH寄存器设置下限值的高8位,ADC_LTRL设置下限值的低2位。
对于这四个寄存器的设置要以官方的英文资料为准,官方的STM8中文手册中对于这四个寄存器的介绍是错误的,这个问题在其他文章中也详细说明过,在这里就不具体说了。
设置完上下限值之后,还需要开启相应通道模拟看门狗功能
ADC_AWCRH寄存器设置通道8和通道9,ADC_AWCRL寄存器设置通道0到通道7,这里需要监控的是模拟通道4,所以只需要设置ADC_AWCRL寄存器的第四位为1就行。
下面直接通过代码来演示在连续扫描模式下开启通道4的模拟看门狗功能。
#include "adc.h"
#include "main.h"
#include "led.h"
_Bool ADC_flag = 0; //ADC转换成功标志
//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_HTRH = ( u8 )( 800 >> ( u8 )2 ); //存放10位数据的高8位
ADC_HTRL = ( u8 )800; //存放10位数据的低2位
//设置下限门槛值
ADC_LTRH = ( u8 )( 300 >> ( u8 )2 ); //存放10位数据的高8位
ADC_LTRL = ( u8 )300; //存放10位数据的低2位
ADC_AWCRH = 0x00;
ADC_AWCRL |= ( 1 << 4 ); //使能通道4的模拟看门狗功能
//看门狗中断和ADC中断一次只能使用一个,不能同时使用。
//ADC_CSR |= ( 1 << 4 ); // 使能看门狗中断
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;
}
return voltage;
}
//AD中断服务函数 中断号22
#pragma vector = 24 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void )
{
if( ADC_CSR & ( 1 << 7 ) ) // 不能通过位操作来清零 EOC 标志
{
ADC_CSR = ADC_CSR & 0x7F | 0x04; // 转换结束标志位清零 EOC
}
if( ADC_AWSRL & ( 1 << 4 ) )
{
ADC_AWSRL &= ~( 1 << 4 );
}
if( ADC_CSR & ( 1 << 6 ) )
{
ADC_CSR = ADC_CSR & ( ~( 1 << 6 ) ) | 0x04; // 模拟看门狗标志位清零 AWD
}
ADC_flag = 1; // ADC中断标志 置1
}
在初始化中首先开启连续扫描模式,最大扫描通道数为4,也就是从通道0开始依次扫描到通道4,然后设置模拟看门狗的上下限,开启通道4的模拟看门狗功能,然后开启采样功能。
在中断中清除标志位的时候要特别注意下面这一点:
注意:在扫描模式(连续扫描模式)中,不要使用位操作指令(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表示扫描到那个通道结束。
所以在中断中使用 ADC_CSR = ADC_CSR & 0x7F | 0x04; 来清除标志位。与上0x7F清除最高位,然后或上0x04用来来重新设置最大采样通道。 这样在清除标志位的时候就不会影响到扫描通道的值。因为在扫描模式下CH[3:0]寄存器中的值会不停的变,当前采样那个通道的值时,对应的位就会被硬件自动置1,当采样开始后CH[3:0]寄存器中的值是时时刻刻在发生着变化,如果在中断中直接使用位操作的方式对EOC标志位清零时,CH[3:0]寄存器中的值也会被置0,这样中断结束后,扫描模式就失效了,导致程序运行出错。
接下来在主函数中直接调用扫描函数读取结果
#include "iostm8s103F3.h"
#include "led.h"
#include "adc.h"
#include "stdio.h"
void SysClkInit( void )
{
CLK_SWR = 0xe1; //HSI为主时钟源 16MHz CPU时钟频率
CLK_CKDIVR = 0x00; //CPU时钟0分频,系统时钟0分频
}
void main( void )
{
SysClkInit();
__asm( "sim" ); //禁止中断
LED_GPIO_Init();
ADC_CH_Init( 4 );
__asm( "rim" ); //开启中断
while( 1 )
{
LED = !LED;
ReadVol_CHx();
}
}
主程序中循环调用ReadVol_CHx()函数读取采样结果,当通道4的采样电压值小于300或者大于800时,ADC_AWSRL寄存器的第4位就会置1,表示当前发生了一次模拟看门狗事件。
在这里还需要注意一个问题 ADC的采样中断和模拟看门狗中断不能同时使用。
代码使用的是ADC采样中断,没有使用模拟看门狗中断。如果要使用模拟看门狗中断就必须关掉ADC采样中断,当两个中断同时开启后,ADC的扫描模式好像就会被影响,导致进入中断一次后,扫描模式失效。按照官方手的中说的不使用位清零操作也不行,也许是这两个中断在芯片内部存在冲突,官方手册上没有具体的说明,所以真实原因还不知道。