STM8单片机ADC单次扫描模式开启模拟看门狗

342 阅读5分钟

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

  STM8单片机的模拟看门狗功能不仅在单次模式和连续模式下可以使用,同时也可以在扫描模式下使用。但是在扫描模式下看门狗只能监测一个通道。

image.png

  在扫描模式下需要通过ADC_AWCR寄存器来设置需要开启模拟看门狗功能的通道,通道最多有10个,但是不同的单片机具体通道数不一样,需要根据实际使用的型号来确定。

  在扫描模式下模拟看门狗中断后,会在ADC_AWSR寄存器中对相应的通道标志置位,在中断中通过相应的标志位来判断对应的通道是否有中断发生。

  在扫描模式下模拟看门狗的上下限值同样是通过ADC_HTR寄存器和ADC_LTR寄存器来设置的。

  扫描模式下使用到的寄存器如下图所示

image.png

这里使用单次扫描模式,同时开启通道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寄存器清除中断标志位的方法有点奇怪,不是直接通过位操作来实现,而是搞了一大堆东西,这是为什么呢?

  首先来看一下官方文档中的介绍

image.png

在扫描模式中不能通道位操作的指令来清除采样中断标志位。

image.png

  在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的扫描模式好像就会被影响,导致进入中断一次后,扫描模式失效。按照官方手的中说的不用位清零操作也不行,也许是这两个中断在芯片内部存在冲突,官方手册上没有具体的说明,所以真实原因还不知道。