Tinyos学习笔记2

Tinyos学习笔记(2)

--ADC的使用

前言

这次的主要工作时搞清楚tinyos在CC2430平台下对ADC的操作。在CC2430下的ADC的工作方式主要有两种:一种为正常模式(包括轮询和中断);另外一种为DMA采集模式。在相应的tinyos实现中主要由AdcP module和AdcAdcDmaP module进行实现。由于本笔记只供自己记录使用,方便以后查询,所以内容比较杂也比较乱是在所难免的,但是笔者能够保证的是内容的足够详细与本人认真的态度。希望大家多提宝贵意见。

(支持原创,如需转载,请注明地址:http://blog.sina.com.cn/litianping0709作者:叶雨荫城(阿雨))

Tinyos中基于CC2430下的ADC的相关模块---普通方式

在应用中使用到的ADC的配置(configuration)模块为AdcC模块,提供的接口主要有AdcControl接口与Read接口,具体的configuration模块我们可以看如下的代码,对于AdcControl接口与Read接口的具体介绍在下面继续进行讲解。可以看到这个configuration模块利用到了generic关键字,generic关键字表明这个模块是一个通用模块,为什么要把ADC采用通用模块机制呢??我们知道,在tinyos中每个组件都是唯一的,每个组件的名字都是唯一的(组件可见性具有全局性质),每个组件完成特定的某个功能。对于ADC来说我们如果让其成为一个唯一的组件可不可以?这样是不行的,为什么,因为我们知道在应用程序中可能不止一个地方用到ADC的组件,并且ADC的端口也不是固定的,所以在这种情况下我们就需要采用通用组件的方法,达到的目的就是完成的功能一样,但是可以由不同的实体来完成这个功能。而对于不同的实体进行区分的话就需要用到相应的ID来进行区别,这个由tinyos的unique函数来进行完成。这样,不同的应用可以调用属于自己的ADC模块并且在signal相应的event时可以通过ID来进行识别。这是tinyos中特别有用的一种机制,在很多地方都会用到。

generic configuration AdcC(){

provides interfaceAdcControl;

provides interfaceRead<int16_t>; //表示特定的类型的接口

}

implementation {

components MainC,AdcP;

MainC.SoftwareInit-> AdcP.Init;//由主函数自动完成初始化

enum { ID =unique("UNIQUE_ADC_PORT"), };//唯一的ID

AdcControl =AdcP.AdcControl[ID];

Read =AdcP.Read[ID];

}

接下来看看相关的接口,首先看看AdcControl的接口定义:

interface AdcControl {

command voidenable(uint8_t reference, uint8_t resolution, uint8_tinput);

command voiddisable();

}

接口实现的功能我们从”Enables the ADC for the chosenchannel with the specified settings. Also, enables interrupts anddisables any current sampling.”中可以知道主要对ADC的功能进行配置及中断的控制,主要由两个功能函数进行实现:enable和disable。当然接口的具体实现在AdcP模块中。

再来看看Read接口定义(注:这个接口在tinyos的标准接口文件夹下,tinyos-2.1.1/tos/interfaces/下):

interfaceRead<val_t> {

command error_tread();

event void readDone(error_t result, val_t val );

}

接口的定义很简单,一个命令read用于读取和一个事件readDone在读取完成后进行触发。

下面就来看这几个接口的具体实现,所有的实现均在AdcP中可以找到。看下面的代码,具体的代码我将进行批注。其中的uniqueCount函数的解释我在tinyos programming中找到了这么一段话足以解释其用意” Because the calls to unique definethe set of valid client Ids, nesC has a second compile-timefunction, uniqueCount(). This function also takes a string key. Ifthere are n calls to unique with a given key (returning values0...n-1), then uniqueCount returns n, and this is resolved atcompile-time.”即在编译阶段决定出有多少个不同的ID用户。

module AdcP {

provides interfaceInit; //提供初始化接口

provides interfaceAdcControl[uint8_t id];

provides interfaceRead<int16_t>[uint8_t id];//Read是一个通用的接口,其中int16_t用于指明读取的数据类型

}

implementation

{

#include "Adc.h"

uint8_treferences[uniqueCount("UNIQUE_ADC_PORT")];

uint8_tresolutions[uniqueCount("UNIQUE_ADC_PORT")];

uint8_tinputs[uniqueCount("UNIQUE_ADC_PORT")];

boolinUse[uniqueCount("UNIQUE_ADC_PORT")];

uint8_tcounter;

uint8_t lastId =uniqueCount("UNIQUE_ADC_PORT");

command error_tInit.init() {

uint8_t i;

for (i = 0; i < uniqueCount("UNIQUE_ADC_PORT"); i++){

inUse[i] = FALSE; //表明未被使用

}

counter = 0;

return SUCCESS;

}

command voidAdcControl.enable[uint8_t id](uint8_t reference, uint8_tresolution, uint8_t input) {//参考电压分辨率 输入端口

if (counter == 0) {

ADCIE = 1;// ADC interrupt enable中断使能

ADC_STOP(); //停止采样

}

if (!inUse[id]) { //判断该id的ADC是否在使用过程中

inUse[id]= TRUE;

counter++;

ADC_ENABLE_CHANNEL(input);//默认为0,为何这里要这样搞这里有错误 inputs[id]应为input 否则不可能采集得到 具体是否正确等验证源程序再说

}

references[id] = reference; //第id个ADC的参考电压

resolutions[id] = resolution; //第id个ADC的分辨率

inputs[id] = input; //存储对应id号的输入通道

}

command voidAdcControl.disable[uint8_t id]() {

if (inUse[id]) {

inUse[id] = FALSE;

ADC_DISABLE_CHANNEL(inputs[id]); // 使用到了ADCCFG寄存器ADC input configuration. ADCCFG[7:0] //select P0_7 - P0_0 as ADCinputs AIN7 – AIN0

counter--;

if (counter == 0) {

ADCIE = 0; //如果没有ADC了就禁止ADC中断

}

}

}

command error_tRead.read[uint8_t id]() { //启动read过程

if (lastId < uniqueCount("UNIQUE_ADC_PORT")){

return FAIL;

} else {

uint8_t temp;

lastId = id; //记住调用方

temp = ADCH;//把旧值存储起来

temp = ADCL;

ADC_SINGLE_CONVERSION(references[id] | resolutions[id] |inputs[id]);

return SUCCESS;

}

}

task voidsignalReadDone();

int16_tvalue;

MCS51_INTERRUPT(SIG_ADC) {

value = (( (uint16_t) ADCH) <<8);

value |= ADCL; //得到存储值

post signalReadDone(); //表明读取完成

}

task voidsignalReadDone() {

uint8_t tmp;

tmp = lastId; //存储调用方

lastId = uniqueCount("UNIQUE_ADC_PORT"); //恢复lastId的值

value >>= (8 - (resolutions[tmp]>> 3));//根据分辨率转换值

//8bit

//value >>= 2;

//value |= 0xC000 * ((value & 0x2000)>> 13);

//#define ADC_8_BIT0x00// 64decimation rate

//#define ADC_10_BIT0x10// 128 decimation rate

//#define ADC_12_BIT0x20// 256 decimation rate

//#define ADC_14_BIT0x30// 512 decimation rate

signal Read.readDone[tmp](SUCCESS, value); // 将值传给调用者

}

default event voidRead.readDone[uint8_t id](error_t result, int16_t val) {

//默认的event handler

}

}

总结起来关于ADC用到的寄存器主要有如下几个:

ADCCFG:设置P0口的输入模式,如果这个寄存器的相应位置置1,说明P0口的对应位将会用作ADC的输入口,一般的宏使用为:

#defineADC_ENABLE_CHANNEL(ch)ADCCFG |=(0x01<<ch)

#defineADC_DISABLE_CHANNEL(ch) ADCCFG&=~(0x01<<ch)

ADCIE:中断控制位,置1启动中断

ADCCON1,ADCCON2及ADCCON3(主要用于连续AD转换,这里不介绍,配置与ADCCON2差不多,这里只介绍单个AD转换)三个不同的控制寄存器,在实际的使用中我们把其相应的功能(注:在ADCCON1中有个特殊的控制位为STSEL,复位状态默认为11,说明启动ADC的转换为手动转换。启动标志位为ADCCON1中的ST标志位置1。):

#define ADC_SINGLE_CONVERSION(settings)do{ADCCON3 = settings; }while(0)

这个宏的设置包括选择参考电压,分辨率及相应的ADC的输入口(注意这个宏在执行后也会自动启动相应的AD转换和设置ST的标志位具有相同的功能)

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

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

这个宏用于人工启动一个新的转换。由于是人工启动,所以在应用程序中在readDone中接收到采集到的数据后必须要记得调用read函数启动另外一次AD转换。

应用程序中ADC模块(直接方式中断)的使用

在应用程序中的module模块中我们主要需要使用的是AdcControl与Read<int16_t>;一个用于ADC的控制,一个用于ADC值的读取。我写了一个小例子,主要实现采集片内温度的值然后把采集到的值发送到串口进行显示。我写了一个简单的例子程序如下,注释采用英文注释,主要是为了养成一种习惯,这与看别人程序不同:

Module 文件

module TestAdcP{

uses interface Boot;

uses interface Leds;

uses interface SerialByteComm as uart0;

uses interface AdcControl;

uses interface Read<int16_t> asRead;

}

implementation{

#define TEMPSENOR 0x0E //temp sensor

#define ADC_REF_1_25_V 0x00

#define ADC_14_BIT 0x30

#define ADC14_TO_CELSIUS(ADC_VALUE) (((ADC_VALUE)>> 4)-315)

event voidBoot.booted()

{

call Leds.led2On();

callAdcControl.enable(ADC_REF_1_25_V,ADC_14_BIT,TEMPSENOR);

call Read.read();

}

task voiddelayTime()

{

int i=0,j=0;

for(i=0;i<3000;i++)

for(j=0;j<100;j++);;

}

uint8_ttempValue[4];

uint8_t index=0;

event voidRead.readDone(error_t result,int16_t val)

//why cant define a variablein an event??only can show two chars contiously??

{

val=ADC14_TO_CELSIUS(val);

tempValue[0]=val/10+'0';

tempValue[1]=val+'0';

tempValue[2]='C';

tempValue[3]='n';

call uart0.put(tempValue[index++]);

post delayTime();

call Leds.led2Toggle();

}

task void print() //I have todo this because sampling is faster than the sending process ofuart,so we must guaranteed that the sample value can be read againonly after the send process is over.

{

call uart0.put(tempValue[index++]);

index==4?(index=0):index;

}

async event voiduart0.putDone()

{

if(index==0)

call Read.read();

else

postprint();

//none

}

async event voiduart0.get(uint8_t data) {

return;

}

}

Configure文件

configuration TestAdcC{

}

implementation{

components MainC,UartC,TestAdcP,LedsC,new AdcC();

TestAdcP.Leds->LedsC;

TestAdcP.Boot->MainC.Boot;

TestAdcP.uart0->UartC. SerialByteComm;

TestAdcP.AdcControl->AdcC;

TestAdcP.Read->AdcC;

}

试验效果截图(图1)

图1

出现的主要问题:

(1)一定要注意需要人工启动AD转换

(2)必须确保在发送字符完成后再调用put函数,否则容易产生覆盖现象,这就需要一定的逻辑保证,在编程的过程中一定要注意

(3)原版下的ADC的module文件下的ADC_ENABLE_CHANNEL(inputs[id])是错误的,并且在源程序中他们在写的过程中只考虑了AN0-AN7的情况,温度传感器的情况他们没有考虑进来。这个需要一定的改进,例如可以将input进行判断看是否是采集内部还是外部,然后再相应的是否使能相应的AD端口。

Tinyos中基于CC2430下的ADC的相关模块---DMA方式

cc2430中提供给应用层的ADC(DMA)接口主要由AdcDmaC模块提供:

generic configuration AdcDmaC(){

provides interfaceAdcControl; //同样是两个接口

provides interfaceRead<int16_t>;

}

implementation {

components MainC,AdcDmaP;

MainC.SoftwareInit-> AdcDmaP.Init;

enum { ID =unique("UNIQUE_ADC_PORT"), };

AdcControl =AdcDmaP.AdcControl[ID];

Read =AdcDmaP.Read[ID];

components newDmaC(); //使用到了DmaC的功能

AdcDmaP.Dma-> DmaC;

}

可以看到,同样使用的generic关键字来表明这个模块是一个通用模块,提供的接口与前例相同,均为两个接口。只不过在实现的时候用到了DmaC模块提供的Dma接口,这个模块我不准备细说,在后续的学习DMA的过程中我会对DMA做详细的介绍。接下来看看具体的接口实现部分,在模块中AdcDmaP模块中进行实现。同样我会将一些关键部分进行注解。

module AdcDmaP {

provides interfaceInit;

provides interfaceAdcControl[uint8_t id];

provides interfaceRead<int16_t>[uint8_t id];

uses interface Dma;//使用到了Dma接口

}

implementation

{

#include "Adc.h"

#include "dma.h"

uint8_treferences[uniqueCount("UNIQUE_ADC_PORT")]; //这些定义与在AdcP模块中的相同

uint8_tresolutions[uniqueCount("UNIQUE_ADC_PORT")];

uint8_tinputs[uniqueCount("UNIQUE_ADC_PORT")];

boolinUse[uniqueCount("UNIQUE_ADC_PORT")];

uint8_tcounter;

uint8_t lastId =uniqueCount("UNIQUE_ADC_PORT");

int16_tvalue;

dma_config_t *dmaConfig; //dma配置

command error_tInit.init() { //初始化

uint8_t i;

for (i = 0; i < uniqueCount("UNIQUE_ADC_PORT"); i++){

inUse[i] = FALSE;

}

counter = 0;

dmaConfig = call Dma.getConfig(); //得到配置参数的指针,用于后续进行配置

dmaConfig->SRCADDR =(uint16_t) 0xDFBA;// address of source

dmaConfig->DESTADDR = (uint16_t)&value;// address of destination //目的地址

dmaConfig->LEN= 1;

dmaConfig->VLEN= VLEN_USE_LEN; // Using LEN todetermine how many bytes to transfer

dmaConfig->IRQMASK =TRUE;// Issue an IRQ upon completion.

dmaConfig->DESTINC =DESTINC_0;// The destination address is to be incremented by 1 after eachtransfer

dmaConfig->SRCINC= SRCINC_0;// The source address inremented by 1 byte after eachtransfer

dmaConfig->TRIG= DMATRIG_ADC_CHALL; // The DMA channelwill be started manually

dmaConfig->WORDSIZE = WORDSIZE_WORD; //One byte is transferred each time.

dmaConfig->TMODE= TMODE_SINGLE_REPEATED; //The number of bytes specified by LEN is transferred

return SUCCESS;

}

command voidAdcControl.enable[uint8_t id](uint8_t reference, uint8_tresolution, uint8_t input) {

if (counter == 0) {

ADC_STOP();

call Dma.armChannel(); //下面为该函数的具体实现

}

if (!inUse[id]) {

inUse[id] = TRUE;

counter++;

}

//中间省略了ADC_ENABLE_CHANNEL(inputs[id]);部分

references[id] = reference;

resolutions[id] = resolution;

inputs[id] = input;

}

command voidAdcControl.disable[uint8_t id]() {

if (inUse[id]) {

inUse[id] = FALSE;

counter--;

if (counter == 0) {

call Dma.stopTransfer(); //停止传输

}

}

}

command error_tRead.read[uint8_t id]() {

if (lastId < uniqueCount("UNIQUE_ADC_PORT")){

return FAIL;

} else {

uint8_t temp;

lastId = id;

ADC_ENABLE_CHANNEL(inputs[id]); //使能相应的输入通道,这里这样写才是正确的,在AdcP模块中的那样的逻辑是有错的。

ADC_SEQUENCE_SETUP(references[id] | resolutions[id] |inputs[id]);

ADC_SAMPLE_SINGLE(); //开始采样

return SUCCESS;

}

}

task voidsignalReadDone();

async event voidDma.transferDone() { //这时候不是利用ADC的中断来完成采用的signal,而是由Dma的传输完成来代替相应功能的实现,道理是一样的。

post signalReadDone();

}

task voidsignalReadDone() {

uint8_t tmp;

tmp = lastId;

lastId = uniqueCount("UNIQUE_ADC_PORT");

value >>= (8 - (resolutions[tmp]>> 3));

ADC_DISABLE_CHANNEL(inputs[tmp]);

Tinyos学习笔记(2)

signal Read.readDone[tmp](SUCCESS, value); //读取完成,并传送相应的值。

}

default event voidRead.readDone[uint8_t id](error_t result, int16_t val) {

}

}

应用程序中ADC模块(DMA方式)的使用

和直接方式基本一样,module模块其实是一样的,主要是configuare文件有点区别,因为此时提供AdcControl接口和Read<uint_16>接口的模块此时为AdcDmaC模块。具体的configure文件修改如下,很简单:

configuration TestAdcC{

}

implementation{

components MainC,UartC,TestAdcP,LedsC,newAdcDmaC();//利用到了AdcDmaC

TestAdcP.Leds->LedsC;

TestAdcP.Boot->MainC.Boot;

TestAdcP.uart0->UartC. SerialByteComm;

TestAdcP.AdcControl->AdcC;

TestAdcP.Read->AdcC;

}

至于连续采集的部分基本原理差不多,主要利用DMA来进行实现,这里就不再过多叙述。有兴趣的话大家可以试试做做实验然后做做总结,然后和大家分享学习的成果。

  

爱华网本文地址 » http://www.aihuau.com/a/25101011/98214.html

更多阅读

中小学教师职业道德规范学习笔记

中小学教师职业道德规范学习笔记一、依法执教。学习和宣传马列主义、毛泽东思想和邓--同志建设有中国特色社会主义理论,拥护党的基本路线,全面贯彻国家教育方针,自觉遵守《教师法》等法律法规,在教育教学中同党和国家的方针政策保持一

捞尸笔记2之黄河鬼侠 黄河捞尸人

题外话:我明早起程,离开兰州,素材已经全部找好。《擒灵Ⅱ》近一个月未更新,抱歉。主要是千里迢迢赶来,除了写稿,还要见一些提供素材的朋友,总不可能拿了素材就走,人情世故还是要的。然后就是四本新书陆续印刷,准备发行了,我在顶置博文更新了各

伤寒学习笔记(下

伤寒学习笔记---14,桂枝用量问题?学习仲景药法,涉及用量问题。深入考证,固然不必,简单了解,实属必要。仅以桂枝为例,整理如下:基本资料:《现代中医药应用与研究大系》1985年版,简称《大系》。药法:凡例里说:“将其折合今之用量---,并结合笔者临床

关于闪光灯TTL的学习笔记

关于闪光灯TTL的学习笔记(转)俺注:一直对加闪光灯的拍摄比较疑惑,要知道并不是加开个闪光就一了百了的。特别是现在的专业闪灯,有好几种模式,都需要配合相机的测光使用。今天无意之间看到一篇解疑的文章,赶紧收集下来仔细学习。想学习下闪

声明:《Tinyos学习笔记2》为网友庸亽氺分享!如侵犯到您的合法权益请联系我们删除