这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
STM8单片机的模拟看门狗功能不仅在单次模式和连续模式下可以使用,同时也可以在扫描模式下使用。但是在扫描模式下看门狗只能监测一个通道。
在扫描模式下需要通过ADC_AWCR寄存器来设置需要开启模拟看门狗功能的通道,通道最多有10个,但是不同的单片机具体通道数不一样,需要根据实际使用的型号来确定。
在扫描模式下模拟看门狗中断后,会在ADC_AWSR寄存器中对相应的通道标志置位,在中断中通过相应的标志位来判断对应的通道是否有中断发生。
在扫描模式下模拟看门狗的上下限值同样是通过ADC_HTR寄存器和ADC_LTR寄存器来设置的。
扫描模式下使用到的寄存器如下图所示
这里使用单次扫描模式,同时开启通道4的模拟看门狗功能。下面直接通过代码来演示如何设置:
#include "adc.h"
#include "main.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_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_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 )
{
//扫描模式中不能使用位清零
if( ADC_CSR & ( 1 << 7 ) )
{
ADC_CSR = ADC_CSR & ( ~( 1 << 7 ) ) | 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
}
在初始化代码中关于模拟看门狗的设置如下:
//设置上限门槛值
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的模拟看门狗功能
首先设置模拟看门狗的上下限值,这里设置的上限值是800,下限值是300,然后开启通道4的模拟看门狗功能。 这里设置的需要扫描的通道依次为2、3、4通道,只有当通道4的采样值低于300或者高于800的时候,模拟看门狗才会触发中断,而通道2和3的值不论是什么,模拟看门狗都不会触发中断,也就是说模拟看门狗只对通道4起作用。如果需要监控通道2或者通道3的值时,重新设置ADC_AWCRL寄存器的值就行。
模拟看门狗中断和ADC采样中断入口函数是同一个函数,所以在中断函数中需要判断3个标志位,一个是采样中断标志,一个是模拟看门狗中断标志,一个是模拟看门狗通道标志。
通过中断中的代码可以发现一个问题,那就是对ADC_CSR寄存器清除中断标志位的方法有点奇怪,不是直接通过位操作来实现,而是搞了一大堆东西,这是为什么呢?
首先来看一下官方文档中的介绍
在扫描模式中不能通道位操作的指令来清除采样中断标志位。
在ADC_CSR寄存器中可以看到最高位EOC为转换结束标志位,最低4位为转换通道位。不能直接位操作的原因就是,在扫描模式下最低4位的这个通道值是会变的。由于需要对2、3、4折三个通道依次扫描,所以在需要扫描那个通道的时候,对应的通道位就会被单片机硬件自动置位。当使用位操作清除EOC位的时候,会把CH为也清除为0,这时候当中断结束后,扫描模式就不会继续扫描了,相当于扫描通道数被设置为0,程序就会出现异常。所以在扫描模式下对EOC标志位进行清零的时候,就需要使用一次性写入的方法。在写入的同时,还需要重新设置一下采样通道值。 ADC_CSR = ADC_CSR & ( ~( 1 << 7 ) ) | 0x04;
这里将1左移动7位然后取反,也就是对最高位EOC位取反,相当于将EOC位清零。最后0x04做或运算,就是重新设置扫描的最大通道值。因为这里采样的最大通道号是4,所以最大通道值也设置为4。通过这种方法对EOC位清零后,就不会影响扫描通道的设置了。
接下来在主函数中直接读取这三个通道的采样值就行了。
#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; //程序运行一圈耗时 10us
ReadVol_CHx();
}
}
在主函数中循环读取通道采样函数,当通道4的采样电压值低于300或者高于800的时候,就会模拟看门狗的标志位就会置位。这里还需要注意一点。
ADC的采样中断和模拟看门狗中断不能同时使用。
代码使用的是ADC采样中断,没有使用模拟看门狗中断。如果要使用模拟看门狗中断就必须关掉ADC采样中断,当两个中断同时开启后,ADC的扫描模式好像就会被影响,导致进入中断一次后,扫描模式失效。按照官方手的中说的不用位清零操作也不行,也许是这两个中断在芯片内部存在冲突,官方手册上没有具体的说明,所以真实原因还不知道。