4、利用Rc震荡电路,脉冲计数,测量外部温度 细节的一些思考。

前一段时间,有人问我怎么用单片机的io口加上一些简单的外部电路测量外部温度。我当时是莫名其妙,我承认我从来没有想过这个问题。所以当时我很肯定的回答不可能。(在我的印象中,测温一般用温度传感器,或者热敏电阻之类的。怎么也要用个ad转换器吧。)

       在坐公交车回去的路上,我仔细想了这个问题。单片机的io口能直接测到的只有电压,只能分辨高低电平嘛。单片机要想直接测温,只能是测外部脉冲的频率,然后再做映射(嘿嘿,其实我…..)。不管是利用温度传感器还是热敏电阻测温,其原理都是把一些参数的变化映射到温度。一个意思哦。

     还好我的电子电路不是很差,还晓得Rc震荡电路可以产生脉冲。而且它的频率和电阻以及电容有关系,是个确定表达式,具体是什么忘记了。

 

     这里只做软件的处理,电路嘛,以后再补上。哈哈

脉冲、温度映射包如下

 

/*

 * package that pulse to temperture

 *

 */

struct pulse_tem{

  unsigned int pulse;

  float  temp_value ;

};

 

这个映射数据包是自己经过试验得出来的。可以这样初始化:

struct pulse_tem  map_data[]={

    {100, 35},

    {110, 35.5},

    (120, 36),

    {125, 26.5},

    ......

    {200, 45 }

}

 

数据随便加咯。。不过有一点要特别注意,数据要从小到大排列。因为后面使用了二分搜索算法。 多点准确些,而且也不会对性能造成影响。长度也可以很容易计算出来。

map_data_len = sizeof(map_data) / sizeof(map_data[0]) ;

 

把测温问题转换成脉冲温度映射问题后,思路就很清晰了。无非就是两个步骤:数据的获得与数据的处理。

 

1、  数据的获得。

为了简单些,就用单片机意思意思哈。只需要使用单片机的一个外部中断,和一个定时器。(定时器有计数功能,不过这里貌似没有作用。因为算的是频率)。先定义外部中断为高优先级中断。

这里定义几个宏:

#define external_interrupt_enable( )  ………

#define external_interrupt_disable( )  ………

设置为边沿触发。

 

#define timer_interrupt_enable( )   …………

#define timer_interrupt_disable( )  …………

 

#define  pulse_temp_conversion_start( )   { timer_interrupt_enable( ) ;  external_ interrupt_enable( ) }

 

 

定义两个个全局变量 volatile unsigned int pulse_count volatile float tem_value;  (当然了,最好定义成全局静态变量。)

  

注意: #define timer_interrupt_enable( )   …………          应该把重置定时器初值,以及清零pulse_count

 

定时器中断响应函数如下:

 

_irq_timer_intrrupt_handle(void)

{

   int  id;

   external_interrupt_disable( ) ;

   timer_interrupt_disable( ) ;

id = mybinsearch(&pulse_coun, map_data, size_t  sizeof(struct pulse_tem), size_t map_data_len,  mycmp_s);  // this function is written by ourself like system provide .

 

if(id  == 0 ||  id ==  (map_data_len - 1) )

  tem_value = map_data[id].temp_value;   // obtain temperature directly

if(id > 0 && id < (map_data_len - 1))

  tem_value = ( map_data[id].temp_value  +  map_data[id - 1].temp_value ) / 2.0 ;   // obtain temperature in  average.

 

}

 

注:中断里做了浮点数运算,嘿嘿。

 

外部中断响应函数如下:

_irp external_ interrupt_handle(void)

{

  pulse_count++ ;

}

 

这种是最简单的数据获得了,没有做数据缓存。 使用的时候需要注意.。后面有一个做数据缓存的方法,不过代码增加了不少。

 

下面是一个改写了的二分搜索算法,它和系统提供的binsearch有一点点的不同。

1、  它返回的是一个索引号,而系统返回的是一个指向空类型的指针,个人习惯问题。

2、  由于特殊需求,这里返回的是第一个比它大或相等的对象的索引号。如果不在范围内,则返回-1.

 

int mybinsearch(void *key, void *base, size_t width, size_t size, int (*cmp)(void *a, void *b))

{

   int left,right,mid;

   char *pbase =(char *)base                          

 

   left = 0;

   right = size -1;

if(cmp(pbase,  key) < 0  ||  cmp(pbase + right*width,  key) < 0

  return -1 ;

 

while(left <= right)

   {

     mid = left + (right-left)/2;

     if( cmp(pbase + mid * width, key) >= 0 )

         right = mid-1;

     else if(cmp(pbase + mid * width, key) < 0 )

         left = mid+1;

    }

 

  return  (left + 1 );

}

 

提供的cmp函数如下:

int  mycmp_s(void *a, void *b)

{

   return  ((struct pulse_tem *)a)->pulse -  *(unsigned int *)b ;

}

 

注意:我开始是写成return  (struct pulse_tem *)a ->pulse -  *(unsigned int *)b ;他娘的老是提示出错,找了好久,在印象中()的优先级比->高呀。。妈呀,书上的东西不能全信呢,害死人。

 

提供的函数写完了。至于操作嘛,嘿嘿。

需要测温度的时候:

  pulse_temp_conversion_start( )

 

至于温度的提取嘛,直接取咯,。还能咋样啊。读tem_value就可以了。

 

 

上面的方法比较直接,不过它有一个缺陷。只能读到某个时间的温度,不能读到一个时间段的温度。 就是没有做数据缓存啦。  数据缓存可以用循环栈或者循环队列来搞定。

 

先声明一个温度获得数据包:

struct temperature_data {

 float  temperature ;

 char  msg[4];  // reserve .

};

 

typedef struct q_msg {                   /* QUEUE CONTROL BLOCK  */                                     

    struct temperature_data   *QStart;            /* Pointer to start of queue data */                            

   struct temperature_data    *QEnd;              /* Pointer to end   of queue data  */                           

    struct temperature_data   *QIn;         /* Pointer to where next message will be inserted in the q*/

   struct temperature_data    *QOut;       /* Pointer to where next message will be extracted from the*/

    unsigned short   QSize;             /* Size of queue (maximum number of entries)                   */

    unsigned short   QEntries;          /* Current number of entries in the queue                      */

} Q_MSG;

 

操作就是初始化,入队,出队咯。。把入队或者出队稍微改一下就变成栈了。嘿嘿

Q_MSG *queue_init(Q_MSG *pq, struct temperature_data  *start, size_t size)

{

    pq->QStart    = start;                  /* Initialize the queue */

    pq->QEnd      = &start[size];

    pq->QIn     = start;

    pq->QOut    = start;

    pq->QSize   = size;

    pq->QEntries    = 0;

 

    return pq ;

}

 

bool queue_in(Q_MSG *pq, struct temperature_data  msg)

{

   if (pq->QEntries >= pq->QSize)

       return false;

 

   *pq->QIn++ = msg;            /* Insert message into queue */

    pq->QEntries++;             /* Update the nbr of entries in the queue*/

   

    if (pq->QIn == pq->QEnd)

    {                    /* Wrap IN ptr if we are at end of queue         */

            pq->QIn = pq->QStart;

        }

 

    return true ;

 

}

 

struct temperature_data  *queue_out(Q_MSG *pq)

{

    struct temperature_data *msg ;

 

    if (pq->QEntries > 0)

    {          /* See if any messages in the queue   */

                msg = pq->QOut++;                    

                /* Yes, extract oldest message from the queue         */

                pq->QEntries--;                      

                /* Update the number of entries in the queue          */

                if (pq->QOut == pq->QEnd)

                {          /* Wrap OUT pointer if we are at the end of the queue */

                                 pq->QOut = pq->QStart;

                            }

         return (msg);

        }

 

    return (NULL) ;

}

 

/*

 *  *the message is posted at the front instead of the end of the queue.

 *   *like stack to use.

 *    */

bool queue_in_front(Q_MSG *pq,  struct temperature_data  msg)

{

   if (pq->QOut == pq->QStart)

   {                 /* Wrap OUT ptr if we are at the 1st queue entry */

           pq->QOut = pq->QEnd;

      }

    pq->QOut--;

    *pq->QOut = msg;               /* Insert message into queue    */

    pq->QEntries++;

 

    return true ;

}

 

代码一大堆,怎么用到上面的程序中去呢?还需要做一些工作啦。

定义一个队列直接用的数组,全局变量:

struct temperature_data   temperature_data_queue[10] ;

定义一个 队列以及一个指向队列的指针,都是全局变量:

Q_MSG q_msg, *pq_msg = &q_msg ;

 

定时器中断函数如下:

_irq  timer_intrrupt_handle(void)

{

   int  id;

   struct temperature_data  temp_data;

 

   external_interrupt_disable( ) ;

   timer_interrupt_disable( ) ;

 

  id = mybinsearch(&pulse_count, map_data,  sizeof(struct pulse_tem),  map_data_len,  mycmp_s);  // this function is written by ourself like system provide .

 

if(id  == 0 ||  id ==  (map_data_len - 1) )

  tem_value = map_data[id].temp_value;   /* obtain temperature directly  */

if(id > 0 && id < (map_data_len - 1))

      tem_value = ( map_data[id].temp_value  +  map_data[id - 1].temp_value ) / 2.0 ;  /* obtain temperature in  average. */

 

  memset(&temp_data, 0, sizeof(struct temperature_data));

  temp_data.temperature = tem_value ;

  queue_in(pq_msg,  temp_data);

}

 

队列初始化函数的使用:

  queue_init(pq_msg,  temperature_data_queue ,  sizeof(temperature_data_queue) / sizeof(temperature_data_queue[0])) ;

 

温度数据的获得:

 temperature = (queue_out(pq_msg)) -> temperature

 

我靠!,头都晕了。搞什么咯。。。娘的!。。问题不少啊,还得好好瞧瞧。。花一些时间做测试。中断里直接做了浮点数运算,偷懒啦。偷笑。。。没有直接做测试,也不知道对不对。帮忙看看哈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值