1.TinyOS提供的组件和接口
CC2430被广泛应用于无线传感器网络,其片上自带的ADC可以将传感器采集到的模拟信号转换为数字信号进行相应处理。
开源组织TinyOS 8051 working group 提供可以移植到CC2430EM平台上的TinyOS,该平台TinyOS含有可用于控制CC2430单片机ADC的组件AdcC:
components new AdcC(); // 用于控制CC2430 ADC
该组件可将P0口8路输入任一通道中的模拟信号转换为数字信号。
为调用该组件提供有如下接口:
provides interface AdcControl; // 用于控制和打开指定ADC端口
provides interface Read<int16_t>; //用于进行指定端口的ADC转换
2.AdcC使用实例分析
2.1 组成AdcC组件的内部组件连接关系定义如下:
generic configuration AdcC() {
provides interface AdcControl;
provides interface Read<int16_t>;
}
implementation {
components MainC, AdcP;
MainC.SoftwareInit -> AdcP.Init;
/*****该枚举变量是关键,ID =unique("UNIQUE_ADC_PORT"),表明ID值是一个常量函数的返回值,常量函数unique()的具体使用方法参考tinyos-programming.pdf文档中的介绍**********/
enum { ID = unique("UNIQUE_ADC_PORT"), };
AdcControl = AdcP.AdcControl[ID]; // ID值为0当unique只调用1次
Read = AdcP.Read[ID]; // 同上
}
2.2下面进入到组件(模块)AdcP中查看AdcControl[ID]、Read[ID]接口的具体实现:
module AdcP {
provides interface Init;
provides interface AdcControl[uint8_t id];
provides interface Read<int16_t>[uint8_t id];
}
implementation
{
#include "Adc.h"
uint8_t references[uniqueCount("UNIQUE_ADC_PORT")]; //uniqueCount()等于unique()函数在程序中的调用次数,该数组用于存放参考电压值
uint8_t resolutions[uniqueCount("UNIQUE_ADC_PORT")]; //该数组用于存放对应端口的转换精度:8bit/10bit/12bit/14bit
uint8_t inputs[uniqueCount("UNIQUE_ADC_PORT")]; //该数组用于存放对应的端口号(P0(0~7)口的任一端口)
bool inUse[uniqueCount("UNIQUE_ADC_PORT")]; // 端口是否使用FLAG
uint8_t counter;
uint8_t lastId = uniqueCount("UNIQUE_ADC_PORT");
// 一些用到的变量的初始化操作
command error_t Init.init() {
uint8_t i;
for (i = 0; i < uniqueCount("UNIQUE_ADC_PORT"); i++) {
inUse[i] = FALSE;
}
counter = 0;
return SUCCESS;
}
// 三个参数分别为参考电压、转换精度、端口号
command void AdcControl.enable[uint8_t id](uint8_t reference, uint8_t resolution, uint8_t input) {
/* enable interrupt when a channel is enabled (and stop any sampling in progress */
if (counter == 0) {
ADCIE = 1; // 使能ADC中断
ADC_STOP(); // start select,产生新的ADC转换序列,停止正在进行的转换
}
/* enable channel if not already enabled */
if (!inUse[id]) { // 查询对应ADC端口是否已经使用,否,使能
inUse[id] = TRUE;
counter++;
ADC_ENABLE_CHANNEL(inputs[id]); // ADC Input Configuration 对应端口输入使能,该宏定义在Adc.h中实现
}
/* save parameters */
references[id] = reference; //参考电压
resolutions[id] = resolution; //转换位数
inputs[id] = input; //端口号
}
// 对应端口ADC功能关闭
command void AdcControl.disable[uint8_t id]() {
/* disable channel if it has been enabled */
if (inUse[id]) {
inUse[id] = FALSE;
ADC_DISABLE_CHANNEL(inputs[id]);
counter--;
/* disable interrupts if no more channels are used by ADC */
if (counter == 0) {
ADCIE = 0;
}
}
}
/**
* Initiates a read of the value.
*
* @return SUCCESS if a readDone() event will eventually come back.
*/
command error_t Read.read[uint8_t id]() {
/* check if ADC is in use */
if (lastId < uniqueCount("UNIQUE_ADC_PORT")) {
return FAIL;
} else {
uint8_t temp;
/* remember caller */
lastId = id;
/* read out any old conversion value */
//temp = ADCH; //貌似没啥用,覆盖了 我给注释了结果是一样的
//temp = ADCL; //貌似没啥用,覆盖了 我给注释了结果是一样的
/* start conversion 根据数组中存储的对应端口的参数改变控制寄存器ADCCON3,进行ADC转换 */
ADC_SINGLE_CONVERSION(references[id] | resolutions[id] | inputs[id]);
return SUCCESS;
}
}
task void signalReadDone();
int16_t value;
/* Interrupt handler 中断服务函数 */
MCS51_INTERRUPT(SIG_ADC) {
/* read value from register */
value = (( (uint16_t) ADCH) << 8); // 高8位右移8为存储到16位value中
value |= ADCL; //ADC转换的低8位存到value的低8位
post signalReadDone(); // 通知上层组件ADC转化成功任务
}
task void signalReadDone() {
uint8_t tmp;
/* mark ADC as not in use */
tmp = lastId;
lastId = uniqueCount("UNIQUE_ADC_PORT");
/* map out value according to resolution */
value >>= (8 - (resolutions[tmp] >> 3)); // 左移2位,因为转化结果是14BIT的植
/* sign extend to int16_t */
//8bit
// value >>= 2;
// value |= 0xC000 * ((value & 0x2000) >> 13);
//#define ADC_8_BIT 0x00 // 64 decimation rate
//#define ADC_10_BIT 0x10 // 128 decimation rate
//#define ADC_12_BIT 0x20 // 256 decimation rate
//#define ADC_14_BIT 0x30 // 512 decimation rate
// 通知上层组件转换成功,value为传递的ADC转换的值
signal Read.readDone[tmp](SUCCESS, value);
}
default event void Read.readDone[uint8_t id](error_t result, int16_t val) {
}
}
通过以上对AdcP.nc文件的分析,可以看出CC2430单片机ADC转换的实现需要以下操作:
ADCIE // 端口使能,设置为输入
ADCCFG // 中断配置寄存器
ADCCON3 //ADC控制寄存器的操作
寄存器的具体定义参考CC2430 datasheet,内有使用详细说明。
2.3 ADC组件的使用实例分析
configuration TestAppC {
}
implementation {
components MainC, TestAppP;
MainC.SoftwareInit -> TestAppP.Init;
MainC.Boot <- TestAppP;
components LedsC;
TestAppP.Leds -> LedsC;
components StdOutC;
TestAppP.StdOut -> StdOutC;
#ifdef __cc2430em__
components new AdcC();
TestAppP.Read -> AdcC;
TestAppP.AdcControl -> AdcC;
#elif __micro4__
components new Msp430InternalTemperatureC();
TestAppP.Read -> Msp430InternalTemperatureC;
#endif
}
#define DEBUG
module TestAppP {
provides interface Init;
uses interface Boot;
uses interface Leds;
uses interface StdOut;
#ifdef __cc2430em__
uses interface Read<int16_t> as Read;
uses interface AdcControl;
#elif __micro4__
uses interface Read<uint16_t> as Read;
#endif
}
implementation {
#ifdef __cc2430em__
#define BUTTON_PUSH 0x07 // 修改ADC输入通道号,参考ADCCON3说明,由于我使用的是无线龙的C51RF-3-CS,P07连到外接的可调电阻,所以设置为0x07
#define ADC_INPUT_ACC_X 0x04
#define ADC_INPUT_ACC_Y 0x05
#define ADC_INPUT_JOYSTICK 0x06
#define ADC_INPUT_POT 0x07
#define ADC_REF_AVDD 0x00 // 参考电压方式 内部1.25V
#define ADC_14_BIT 0x30 // 14 BIT 转换精度
#endif
/*****************************************************************************/
bool ledOn = FALSE;
/**********************************************************************
** Init/Boot
**********************************************************************/
command error_t Init.init() {
return SUCCESS;
}
event void Boot.booted() {
call Leds.led0Off();
call Leds.led2Off();
call StdOut.print("Program initialized\n\r");
#ifdef __cc2430em__
call AdcControl.enable(ADC_REF_AVDD, ADC_14_BIT, BUTTON_PUSH); //使能ADC转换
#endif
}
/**************************************************************************
** Read comething
**************************************************************************/
uint8_t counter = 0;
#ifdef __cc2430em__
event void Read.readDone( error_t result, int16_t val ) // 底层ADC转换完成,事件处理
#elif __micro4__
event void Read.readDone( error_t result, uint16_t val )
#endif
{
if (counter != 0) {
call Read.read();
counter++;
} else {
counter = 0;
}
#ifdef DEBUG
call StdOut.print("Val: ");
#ifdef __cc2430em__
call StdOut.printBase10int16(val);
call StdOut.print("\n\r");
// call StdOut.printBase10uint8(unique("UNIQUE_ADC_PORT")); // 打印ADC转化值
// call StdOut.print("\n\r");
#elif __micro4__
call StdOut.printBase10uint16(val);
#endif
call StdOut.print("\n\r");
#endif
}
/**********************************************************************
**串口输入字符“u”,进行ADC转换
**********************************************************************/
uint8_t keyBuffer;
uint16_t i = 0;
task void consoleTask();
async event void StdOut.get(uint8_t data) {
keyBuffer = data;
#ifdef DEBUG
call Leds.led0Toggle();
#endif
post consoleTask();
}
task void consoleTask()
{
uint16_t j;
uint8_t * ptr;
uint8_t data[2], tmp;
atomic data[0] = keyBuffer;
switch (data[0]) {
case '\r':
call StdOut.print("\r\n");
break;
/*****************************************************************************/
case 'u':
#ifdef DEBUG
call StdOut.print("
#endif
call Read.read(); // 调用ADC接口Read进行ADC转换
break;
/*****************************************************************************/
default:
data[1] = '\0';
call StdOut.print(data);
break;
}
}
}
该程序运行流程如下:
初始化->打开ADC->串口接收到命令“u”->读取ADC转换植->返回到主调度函数
3. 实验结果
将上述程序编译下载到节点中,通过串口输入“u”并改变可调电阻值的大小,可以看到打印出如下信息:
Program initialized Reading adc // 可调电阻调到最小值时 Val: -48 Reading adc // 可调电阻调到最大值时 Val: 8191 // 14BIT,最高位为符号位,所以为213-1=8191 Reading adc Val: 3282 |
通过以上实验,可以验证CC2430 ADC的使用正常。