STM8单片机ADC采样功能通过定时器触发

x33g5p2x  于2021-12-30 转载在 其他  
字(3.2k)|赞(0)|评价(0)|浏览(544)

在使用STM8单片机的ADC功能时,读取ADC数据时一般有两种方式,一种是通常不断地读取采样标志位,来判断ADC采样是否结束,一种是通过中断的方式来通知系统采样是否结束。

有时候采样ADC数据的时候,需要按照一定的时间间隔,定点的去采样数据。一般使用的方式就是通过定时器定时,然后在定时中断函数中再去读取ADC采样的数据。但是这种方式采样的时间是不固定的,比如进入定时器中断后,ADC采样刚结束,就可以直接使用当前采样到的数据。但是如果运气不好的话,进入定时中断后,ADC采样刚开始,那么此时就需要等到ADC采样结束后,才能使用数据。这样就好导致每次读取ADC数据时还会有随机的一段等到ADC数据的延时。

通常查阅STM8单片机的手册发现,ADC采样可以通过定时器来触发。

  通过定时器来触发ADC采样时,定时器的定时时间是固定的,采样时间也是固定的,这样采样数据的间隔也就是固定的。这样通过定时器来触发ADC的采样时间,就能完全保证每次读取ADC采样数据的时间间隔都是一样的,从而避免了数据的误差。

数据手册中对于开启ADC触发功能描述如下:

  对于如何通过代码来设置ADC触发,官方也没有详细的说明,在网上也没有找到相关例程。所以只能自己摸索,还好通过自己的一番摸索,成功的通过定时器的TRGO事件触发了ADC的启动。

关于ADC相关寄存器的设置,基本就是上面说的6条。接下来需要设置的就是定时器的相关寄存器。

  关于定时器只需要设置CR2寄存器中的 MMS位就可以了。

接下来就通过代码来实现。

#include "adc.h"
#include "main.h"
#include "led.h"

u16  DATAH = 0;                          //ADC转换值高8位
u16  DATAL = 0;                          //ADC转换值低8位
_Bool ADC_flag = 0;                      //ADC转换成功标志

u16 adc_cnt = 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 设置为悬空输入
}
//ch 为单片机的ADC 通道
//ADC输入通道初始化入口参数表示通道选择
void ADC_CH_Init( u8 ch )
{
    char l = 0;
    ADC_CR1  = 0x00;                    //fADC = fMASTER/2, 8Mhz 单次转换,禁止转换
    ADC_CR2  = 0x00;                    //默认左对齐 读数据时先读高在读低
    
    ADC_CR2 |= ( 1 << 6 );              //外部触发使能

    ADC_CSR  |= ch;                     //控制状态寄存器 选择要 AD输入通道 如:PD2(AIN3)
    ADC_TDRL = ( 1 << ch  );            //禁止相应通道 施密特触发功能 1左移ch位
    ADC_CR1 |= 0x01;                    //使能ADC并开始转换
    ADC_CSR |= ( 1 << 5 );              //EOCIE 使能转换结束中断 EOC中断使能
    for( l = 0; l < 100; l++ );         //延时,保证ADC模块的上电完成 至少7us
    ADC_CR1 = ADC_CR1 | 0x01;           //再次将CR1寄存器的最低位置1 使能ADC 并开始转换
}

u16 value = 0;
//AD中断服务函数 中断号22
#pragma vector = 24 // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void ADC_Handle( void )
{
    ADC_CSR &= ~0x80;                            // 转换结束标志位清零 EOC
    //默认左对齐 读数据时先读高高8位 再读低8位
    DATAH = ADC_DRH;                             // 读出ADC结果的高8位
    DATAL = ADC_DRL;                             // 读出ADC结果的低8位
    ADC_flag = 1;                                // ADC中断标志 置1
    value = ( DATAH << 2 ) + DATAL ; //得到十位精度的数据 0--1024
    adc_cnt++;
    LED = !LED;
}

在ADC代码中,相比普通的ADC初始化方式,这里只需要增加一句对ADC_CR2寄存器的设置。

ADC_CR2 |= ( 1 << 6 ); //外部触发使能

  在ADC_CR2寄存器中 使能外部触发转换功能,设置外部触发事件为 内部定时器1 TRG事件。

这里ADC使用的是单次触发模式,开启外部触发功能,开启ADC转换中断,当ADC转换完成之后,就会进入到ADC中断中,在中断中读取采样的数据,然后翻转LED的状态,通过示波器观察LED引脚电平的变化,就可以知道ADC中断进入的频率了。

接下来编写定时器初始化代码。

unsigned long time_cnt = 0;
// 使用 定时器触发 ADC采样
void tim1_init( void )
{
    TIM1_ARRH = ( unsigned char )( 1000 >> 8 );         //定时1ms
    TIM1_ARRL = ( unsigned char )( 1000 );
    TIM1_PSCRH = ( unsigned char )( 0x0F >> 8 );        // 16M / (1+15) =1M
    TIM1_PSCRL = ( unsigned char )( 0x0F );
    TIM1_RCR = 0x00;                                    //重复计数器值
    TIM1_SR1 = ( ~0x01 );                       		//清除更新中断标志

    TIM1_CR2 |= ( 2 << 4 );                             //使能信号,用于触发输出(TRGO)
    
    TIM1_CR1 |= 0x01;                                   //使能计数器
    TIM1_IER |= 0x01;                           		//更新中断使能
}

#pragma vector = 13 //IAR中的中断号,要在STVD中的中断号上加2
__interrupt void Timer1_Handle( void )         			//1ms 定时中断
{
    TIM1_SR1 = ( ~0x01 );                        		//清除更新中断标志
    time_cnt++;
}

定时器的初始化代码,也比正常情况下初始化代码多了一行。

TIM1_CR2 |= ( 2 << 4 ); //使能信号,用于触发输出(TRGO)

用来开启定时的的TRG功能。

  经过测试,这里定时器CR2寄存器中的值 只能设置为 010 或者 011,设置为其他值时,不能触发ADC采样。最开始测试的时候按照芯片资料上这个说明,MMS的值设置的是 001,ADC总是触发不了,还以为是方法的问题,最后才发现是MMS值设置的问题。

ADC和定时器初始化代码设置完成之后,接下来在主函数中初始化这两个函数就行了,按照资料上说的,首先初始化完ADC之后,再初始化定时器。

void main( void )
{

    __asm( "sim" );                             //禁止中断
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
     ADC_GPIO_Init();
    ADC_CH_Init(3);
    tim1_init();
    __asm( "rim" );                             //开启中断
    while( 1 )
    {
       
    }
}

接下来运行程序。

  分别在ADC中断中和定时器中断中用一个变量来统计中断执行的次数,通过变量变量观察窗口可以看到,ADC中断的次数比定时器中断的次数多了1次。这是因为ADC在初始化的时候,已经运行了一次。
  然后通过示波器观察LED口的电平。

  定时器的定时时间是1ms,LED的高低电平时间也是1ms,说明通过定时器触发ADC采样功能是正常运行的。
  为了减小系统进入中断的次数,可以将定时器的中断功能关闭掉。定时器中断功能关闭后,ADC的触发功能依然可以正常使用。

  这样只需要开启一个ADC中断,再加上定时器的TRG触发功能后,就可以实现ADC定时采样的功能了。

相关文章