CC2530中adc的使用记录

本文主要是记录使用CC2530中adc过程中遇到的问题。同时分析ZigBee协议栈中实现的代码。

  • ADC的基本知识
  • CC2530中ADC相关特性
  • CC2530中和ADC相关的寄存器
  • ZigBee协议栈中的实现代码
  • 参考电压的问题
  • 实验过程中遇到的问题

1. ADC的基本知识

ADC是模数转换的简称,通常是指一个将模拟信号转变为数字信号的电子元件。一般的模数转换器是将一个输入电压信号转换为一个输出的数字信号,也可以看成将一个连续的模拟信号转换成离散的数字信号,主要有取样和保持,量化和编码这个几个步骤,详细的可以考考百度百科数模转换

下面就通过一个例子来说明:一个具有8位分辨率的模拟数字转换器可以将模拟信号编码成256个不同的离散值(因为2^8= 256),从0到255(即无符号整数)或从-128到127(即带符号整数),假设参考电压,输入的模拟电压是1.25v,那我们得到的数字值是多少呢? 
将5V分成256,同时1.25v是5V的1/4,则输出数字值为256/4=64。

2. CC2530中ADC相关特性

ADC支持多达14位的模拟数字转换与高达12位的有效位数。ADC包括一个达8个独立配置通道的模拟多路转换器,和一个参考电压发生器。转换结果通过DMA写入存储器。还具有若干运行模式。

ADC主要特性如下:
   1)、可在有效分辨率7—12字节中选择设置;
   2)、8个独立输入通道,可接受单端或差分信号;
   3)、参考电压可选为内部,外部单端,外部差分,或AVDD5;
   4)、产生中断请求;
   5)、转换结束时的DMA触发;
   6)、温度传感器输入;
   7)、电池测量功能。

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
ADC输入:

P0引脚上的信号可以作为ADC输入来使用。在下面,这些引脚叫做AIN0—AIN7引脚,输入脚AIN0—AIN7与ADC连接。 
入脚可配置成单端或差动输入。如选择差动输入,包含成对输入AIN0-AIN1,AIN2-AIN3,AIN4-AIN5和AIN6-AIN7;注意这些引脚既不能加载负电压,也不能加载大于VDD的电压。在差动模式中,成对脚转换是不同的。 
除了输入脚AIN0-AIN7外,片上的温度传感器也可以用来作为ADC温度测量的输入。如要实现这个功能,需设置寄存器TR0.ADCTM和ATEST.ATESTCTRL。 
也可以选择AVDD5/3电压作为一个ADC输入。这种输入允许执行,如应用中的电池测量功能。注意,这种参考不能依靠于电池电压,如AVDD5电压不能被用来做参考值。 
单端输入AIN0至AIN7可代表通道号0至7,通道号8至11分别代表差动输入AIN0-AIN1,AIN2-AIN3,AIN4-AIN5,AIN6-AIN7;通道12表示GND,通道13表示温度传感器,通道15表示AVDD5/3。这些值在ADCCON2.SCH和ADCCON3.SCH中设置。

ADC转换模式

ADC的转换模式主要有两个,分别是序列转换(连续转换)和单次转换。 
序列转换:无需CPU的参与,ADC能够完成一个序列的转换,并通过DMA把结果写入内存。 
单次转换:ADC可以通过编程执行单个转换。通过写入ADCCON3寄存器可以触发一个转换,转换立即启动,除非一个转换序列正在进行中,这种情况下,当序列完成后,马上执行单个转换。 
关于这边的详解可以阅读官方提供的CC2530 datasheet,也可以参看cc2530手册翻译(ADC)

3. CC2530中和ADC相关的寄存器

主要涉及的寄存器有:ADCL 、ADCH 、ADCCFG、ADCCON1 、ADCCON2 、ADCCON3 、TR0、P0DIR、P0SEL、APCFG。 
其中ADCL 、ADCH用来存放转换的得到的数据。ADCCFG用来配置ADC通道的选择。ADCCON1 、ADCCON2 、ADCCON3是ADC模式的配置寄存器,连续转换的时候需要配置ADCCON1和ADCCON2寄存器,单次转换的时候只需要配置寄存器ADCCON3即可。TR0是测试寄存器0,主要用于测试片内温度传感器的值,一般情况下用不到。P0DIR、P0SEL、APCFG这三个寄存器主要用于配置IO来输入外部模拟信号,其中APCFG可以将IO口配置成模拟输入,可以覆盖P0SEL的设置,也就是P0SEL可以不用设置。P0DIR用来配置IO输入输出方向的。

4. ZigBee协议栈中的实现代码

uint16 HalAdcRead (uint8 channel, uint8 resolution)
{
  int16  reading = 0;

#if (HAL_ADC == TRUE)
  uint8   i, resbits;
  uint8  adcChannel = 1;

  /*
   * If Analog input channel is AIN0..AIN7, make sure corresponing P0 I/O pin is enabled.  The code
   * does NOT disable the pin at the end of this function.  I think it is better to leave the pin
   * enabled because the results will be more accurate.  Because of the inherent capacitance on the
   * pin, it takes time for the voltage on the pin to charge up to its steady-state level.  If
   * HalAdcRead() has to turn on the pin for every conversion, the results may show a lower voltage
   * than actuality because the pin did not have time to fully charge.
   */
   //上面注释是提示通过P0口输入要注意配置相应的IO
   //同时在一次转换之后,尽量不要关闭IO的输入功能
   //因为每次转换的时候需要再次启用,这种转换需要一定的时间

  if (channel < 8)
  {
    for (i=0; i < channel; i++)
    {
      adcChannel <<= 1;
    }
  }

  /* Enable channel */
  //配置对应的通道
  ADCCFG |= adcChannel;

  /* Convert resolution to decimation rate */
  switch (resolution)
  {
    case HAL_ADC_RESOLUTION_8:
      resbits = HAL_ADC_DEC_064;
      break;
    case HAL_ADC_RESOLUTION_10:
      resbits = HAL_ADC_DEC_128;
      break;
    case HAL_ADC_RESOLUTION_12:
      resbits = HAL_ADC_DEC_256;
      break;
    case HAL_ADC_RESOLUTION_14:
    default:
      resbits = HAL_ADC_DEC_512;
      break;
  }
 /* Clear ADC interrupt flag */
  ADCIF = 0;
  ADCL = 0;
  ADCH = 0;
  /* writing to this register starts the extra conversion */
  ADCCON3 = channel | resbits | adcRef;

  /* Wait for the conversion to be done */
  while (!(ADCCON1 & HAL_ADC_EOC));

  /* Disable channel after done conversion */
  ADCCFG &= (adcChannel ^ 0xFF);

  /* Read the result */
  reading = (int16) (ADCL);
  reading |= (int16) (ADCH << 8);

  /* Treat small negative as 0 */
  if (reading < 0)
    reading = 0;

  switch (resolution)
  {
    case HAL_ADC_RESOLUTION_8:
      reading >>= 8;
      break;
    case HAL_ADC_RESOLUTION_10:
      reading >>= 6;
      break;
    case HAL_ADC_RESOLUTION_12:
      reading >>= 4;
      break;
    case HAL_ADC_RESOLUTION_14:
    default:
      reading >>= 2;
    break;
  }
#else
  // unused arguments
  (void) channel;
  (void) resolution;
#endif

  return ((uint16)reading);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

上面的代码很简单,同时英文注释也很详细,这里就不赘述了。

5. 参考电压的问题

首先看看参考电压的作用,下面一段是来自百度知道:

参考电压 
测量物体的长度要用卷尺,精确测量要用游标卡尺,同理,ADC 测量电压也要有标准电压做基准。 
如八位并行比较型 ADC ,把参考电压均分为 256 层,就好像卷尺的刻度,输入的待测电压与哪一个分层相等,数值就知道了。

CC2530可选择于内部产生电压,AVDD5脚电压,应用于AIN7输入脚的外部电压,或应用于AIN6-AIN7输入的差动电压。内部参考电压是1.25V(这个不是很确定),AVDD5应该是3.3v。

6. 实验过程中遇到的问题

a. 参考电压配置错误导致得到的结果只有0和4095.

最后总结下就是:

1、首先确定ADC用几位表示,最大数值是多少;

比如一个8位的ADC,最大值是0xFF,就是255。

2、然后确定最大值时对应的参考电压值;

一般而言最大值对应3.3V。这个你需要看这个芯片ADC模块的说明。寄存器中有对于输入信号参考电压的设置。有些是电压输入的1/3。

3、要计算电压,就把你的ADC数值除以刚才确定的最大数值再乘以参考电压值;

比如你ADC值为0x80,那么实际值就是0x80/(0xFF+1)*3.3V = 1.65V。

4、计算出来的电压值只是ADC管脚处的电压值;

你可以用电压表量一下,计算值和实际值是否一样。至于放大器等等,都是芯片外部的事情。外部电路怎么接,和芯片ADC的采样值无关。

5、如果你想知道芯片外部某处的电压,你需要从得出的ADC管脚处的电压(比如刚才的1.65V),再根据电路图进行计算。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值