CC2430 片内AD使用详解——查询法

CC2430 ADC使用——查询法

1 目标

熟悉使用CC2430的ADC功能。根据我自己开发板的情况,我使用P07作为AD转换的输入口,使用一个旋转电位器来调整输入端口的电压,通过串口发送AD转换结果。在这里还是说说ADC的结构。

CC2430的ADC是基于sigma-delta原理,而不是常用的逐次比较式,通过不同的抽取率来实现不同的转换精度。

2 代码总览

还是老规矩,先列出所有的代码。在这里除了使用到ADC模块,还使用了定时器和串口模块,串口模块用来输出转换结果,定时器模块用来间隔调用ADC转换函数。具体的代码如下所示:

//包含头文件

#include "hal.h"

#include "stdio.h"

//函数申明

UINT8 UART0_Init();

UINT8 ADC_Init();

UINT8 Timer1_Init();

UINT16 ADC_Convert();

 

void main(){

  //使用外部晶振

  SET_MAIN_CLOCK_SOURCE(CRYSTAL);

  //设定IO口

  IO_DIR_PORT_PIN(0, 7, IO_IN);

  //初始化定时器

  Timer1_Init();

  //初始化串口

  UART0_Init();

  //初始化ADC

  ADC_Init();

  //输出提示

  printf("CC2430 ADC Test\n");

  //无线循环

  while(1){

  }

}

 

UINT8 ADC_Init(){

  //选择AD转换通道

  ADC_ENABLE_CHANNEL(ADC_AIN7);

  //选择参考电压,分辨率,通道

  ADC_SINGLE_CONVERSION(ADC_REF_AVDD | ADC_10_BIT | ADC_AIN7);

  return 0;

}

 

UINT16 ADC_Convert(){

  //转换结果高位和低位

  UINT8 adc_h; 

  UINT8 adc_l;

  UINT16 adc_value;

  //开始转换

  ADC_SAMPLE_SINGLE();

  //等待转化结束

  while(!ADC_SAMPLE_READY());

  //获得转换结果

  adc_h = ADCH;

  adc_l = ADCL;

  //获得AD转换结果,10位结果

  adc_value = (( adc_h << 8) | adc_l) >> 6;

  //输出转换结果

  printf("ADC 10bit = %d\n",adc_value);

  return adc_value ;

}

UINT8 UART0_Init(){

         //UART0 IO口定位

         IO_PER_LOC_UART0_AT_PORT0_PIN2345();

         //UART0参数9600 8 N 1

         UART_SETUP(0,9600,HIGH_STOP);

         UTX0IF = 1;

         return 0;

}

 

int putchar(int c){

    if(c== '\n'){

      while(!UTX0IF);

      UTX0IF = 0;

      U0DBUF = '\r';

    }

    while(!UTX0IF);

    UTX0IF = 0;

    U0DBUF = c;

           return c;

}

 

UINT8 Timer1_Init(){

         //定时器1复位

         TIMER1_INIT();        

         //设定定时器相关参数

         //溢出值低8位 

         T1CC0L=0x24; 

         //溢出值高8位

  T1CC0H=0xF4;      

  //128分频0000 1100

  T1CTL = 0x0c;   

         //定时器T1溢出中断使能

         TIMER1_ENABLE_OVERFLOW_INT(TRUE);

         //定时器T1中断使能

         INT_ENABLE(INUM_T1,INT_ON);

         //启动定时器1

         TIMER1_RUN(TRUE);    

         //全局中断使能,注意

         INT_GLOBAL_ENABLE(INT_ON);

 

  return 0;

}

 

//定时器1 中断函数

#pragma vector=T1_VECTOR

__interrupt void T1_ISR(void)

{

  //检查中断标志位

  if(T1CTL & 0x10){

    //ADC 通道参数初始化

    ADC_Init();

    //启动转换,通过串口输出结果

    ADC_Convert();      

  //清中断标志

  T1CTL &= ~0x10; 

  }

}


3 初始化其他内容

//使用外部晶振

SET_MAIN_CLOCK_SOURCE(CRYSTAL);

//设定IO口

IO_DIR_PORT_PIN(0, 7, IO_IN);

//初始化定时器

Timer1_Init();

//初始化串口

UART0_Init();


其他的不多说了,在使用AD转换功能之前,需要定义该IO口为输入状态。使用这个宏就可以了。IO_DIR_PORT_PIN(0, 7, IO_IN);

4 初始化ADC

  //初始化ADC

  ADC_Init();

         ADC_Init的函数如下所示:

UINT8 ADC_Init(){

  //选择AD转换通道

  ADC_ENABLE_CHANNEL(ADC_AIN7);

  //选择参考电压,分辨率,通道

  ADC_SINGLE_CONVERSION(ADC_REF_AVDD | ADC_10_BIT | ADC_AIN7);

  return 0;

}

初始化ADC可以分为2步,

第一步,使能ADC转换通道(IO特性),在这里我们选择通道7,使用了一个动作宏

#define ADC_ENABLE_CHANNEL(ch)   ADCCFG |=  (0x01<

该宏操作了ADCCFG寄存器,这个寄存器的说明位于IO部分,而不是ADC部分。

第二步,选择ADC的参考电压,转换分辨率和ADC通道。在这里使用了另一个宏定义:

#define ADC_SINGLE_CONVERSION(settings) \

   do{ ADCCON3 = settings; }while(0)

// Reference voltage:

#define ADC_REF_1_25_V      0x00     // Internal 1.25V reference

#define ADC_REF_P0_7        0x40     // External reference on AIN7 pin

#define ADC_REF_AVDD        0x80     // AVDD_SOC pin

#define ADC_REF_P0_6_P0_7   0xC0     // External reference on AIN6-AIN7 differential input

// Resolution (decimation rate):

#define ADC_7_BIT           0x00     //  64 decimation rate

#define ADC_9_BIT           0x10     // 128 decimation rate

#define ADC_10_BIT          0x20     // 256 decimation rate

#define ADC_12_BIT          0x30     // 512 decimation rate

// Input channel:

#define ADC_AIN0            0x00     // single ended P0_0

#define ADC_AIN1            0x01     // single ended P0_1

#define ADC_AIN2            0x02     // single ended P0_2

#define ADC_AIN3            0x03     // single ended P0_3

#define ADC_AIN4            0x04     // single ended P0_4

#define ADC_AIN5            0x05     // single ended P0_5

#define ADC_AIN6            0x06     // single ended P0_6

#define ADC_AIN7            0x07     // single ended P0_7

#define ADC_GND             0x0C     // Ground

#define ADC_TEMP_SENS       0x0E     // on-chip temperature sensor

#define ADC_VDD_3           0x0F     // (vdd/3)

所有的参数都可以在数据手册中,ADCCON3部分找到,这里不多做说明。需要说明的一点是,原书代码中(ZigBee技术实践教程)转换分辨率的定义为8,10,12,14,通过我个人的多次试验和资料查证,分辨率实际为7,9,10,12,数据左对齐,以补码的形式保存。所以这里定义为10位分辨率时,最大的结果为511,最小的结果为-512。但是这里是不会有负结果出现的。(这个和实验的结果也是吻合的)

5 进行AD转换

UINT16 ADC_Convert(){

  //转换结果高位和低位

  UINT8 adc_h; 

  UINT8 adc_l;

  UINT16 adc_value;

  //开始转换

  ADC_SAMPLE_SINGLE();

  //等待转化结束

  while(!ADC_SAMPLE_READY());

  //获得转换结果

  adc_h = ADCH;

  adc_l = ADCL;

  //获得AD转换结果,10位结果

  adc_value = (( adc_h << 8) | adc_l) >> 6;

  //输出转换结果

  printf("ADC 10bit = %d\n",adc_value);

  return adc_value ;

}

这里使用了最简单的等待方法,ADC还可以使用其他方法,比如说中断或者DMA传送等。先从简单的来,完成一次ADC转换可以分为3步:

第一步:启动AD转换

#define ADC_SAMPLE_SINGLE() \

  do { ADC_STOP(); ADCCON1 |= 0x40;  } while (0)

#define ADC_STOP() \

  do { ADCCON1 |= 0x30; } while (0)

第二步:等待AD转换结束

只需要查询标志位就可以了。

#define ADC_SAMPLE_READY()  (ADCCON1 & 0x80)

ADCCON1的7位置位时代表AD转换完成,否则不断等待。

第三步:结果处理

AD转换的结果保存在ADCH和ADCL寄存器中,先把两个寄存器组成一个16位长度的整形数据,然后使用移位算法获得相应的结果,本例中使用了10位数据,所以右移6位即可。

转换完成后使用printf输出结果,这里就体现了串口的好处了。最大的作用方便调试。

6 定时输出结果

由于完成单次的转换没有意义,所以需要定时完成转换。在这里使用了定时器1,定时器1的使用前面的文章给你已经说过,这里不再重复。中断服务函数如下所示:

#pragma vector=T1_VECTOR

__interrupt void T1_ISR(void)

{

  //检查中断标志位

  if(T1CTL & 0x10){

    //ADC 通道参数初始化

    ADC_Init();

    //启动转换,通过串口输出结果

    ADC_Convert();      

  //清中断标志

  T1CTL &= ~0x10; 

  }

}

还请大家注意,每次转换完成后必须重新选择通道。数据手册中关于ADCCON3有明确提到。我一开始也以为只要设定好了,这个通道号是不会变化的,但是后面发现这样做转换的结果总是不变,打印了数据手册一页一页看才明白过来。

7 输出结果

没有实验结果的示例是站不住脚的。我把旋转定位器的先拧到最大,使7通道的输入电压最大,也就是达到VDD(3.3V),然后再慢慢减小。测试的结果如下图所示:

通过转动电位器使电压不断减小,转换的结果也随之不断减小。还可以看出,转换的最大结果为511,符合预计结果。幸好没有出现512,否则我又要郁闷好久。为了这个AD转换我还做了很多实验,在不同的抽取率情况下取出ADC结果,然后画图分析,这个以后再写文章说明。

CC2430串口使用说明

CC2430定时器使用说明

CC2430 hal.h

你可能感兴趣的:(CC2430 片内AD使用详解——查询法)