查看: 2718|回复: 0

[经验] cookie驱动开发备忘录-MPL115A

  [复制链接]
  • TA的每日心情
    郁闷
    半小时前
  • 签到天数: 1624 天

    连续签到: 2 天

    [LV.Master]伴坛终老

    发表于 2013-1-17 11:06:14 | 显示全部楼层 |阅读模式
    分享到:
      • 话说小时候总盼着放假,一年只有寒假暑假可放,而其中寒假因为带着春节,尤其热闹,更是盼望。小学班主任知道孩子贪玩,到了年底就更没心思好好上课了,提醒我们:“同学们,要是这几天不好好上课,那就是‘一年到头调皮捣蛋’了啊,注意啊!”所以后来我养成个习惯,就是每年末的最后一天也要学点、做点什么,这样可以跟人吹嘘“ouba一年到头刻苦style”。

        ——2012年12月31日 写完了I2C接口的飞思卡尔MPL115A2气压传感器cookie的驱动,第一版

        这还要从2012年末说起。当时CooCox还是非著名组织,推出了个叫做Cookie的开发板,用的是著名的新塘的32位mcu,兼容著名的Arduino。兴趣所致,上网狂搜,发现他们竟然用雷军卖小米的办法卖板子,饥饿营销啊。

        之后出于对开源的推崇和对CooCox的兴趣,参与了CooCox的开发开源驱动的活动,本来准备做PCD8544的驱动,把手册翻了n遍,觉得胸有成竹才去申请,没想到Walter竟然早了半天捷足先登,于是乎转而承揽了飞思卡尔MPL115A压力传感器。

        飞思卡尔MPL115A压力传感器是一款测量气压的传感器,这个系列有2个型号,分别是串口的MPL115A1和I2C的MPL115A2,拿到手册之后发现2款只有协议和引脚定义略有区别,使用流程和算法是一样的。

        先说使用流程,首先要保证!SHDN为高电平,然后读取几个参数,然后启动转换,再后面是读取气压、温度原始标量,最后是按照规定算法计算出实时气压值。这里后来发现有几个问题:一是使用I2C协议时,由于MPL115A2的地址固定为0x60,所以如果一条总线上要用多个MPL115A2,必须要有多个!SHDN才行;二是传感器参数并不是像我开始设想的那样,是出厂配置不会变,所以只好每次量气压前再读一次;三是得到的是格式不同的定点数,不好计算,这个后面会提到。


        再说算法,算式很简单:
        Pcomp= a0+(b1+c12×Tadc)×Padc+b2×Tadc
        Pressure(kPa) = Pcomp × (115-50)/1023+ 50

        但是做起来就费劲了。首先遇到的问题是:传感器上的6个数据是长度不等、小数位各异的。最开始用2个函数来做转换,实验代码如下:


        #define MY_DEBUG printf
        #include "stdio.h"

        float getFloatByBIN(signed short intB,unsigned short iShift)
        {
        float f=0.0;
        f+=intB;
        f/=(1<<ishift );
        return f;
        }
        float calcMACs(float a,float b,float x){
        return a+b*x;
        }
        void main()
        {
            float a0 ,b1 ,b2 ,c12 ;
            unsigned short padc,tadc;

            a0 = getFloatByBIN(0x3ECE,3);
            b1 = getFloatByBIN(0xB3F9,13);
            b2 = getFloatByBIN(0xC517,14);
            c12 = getFloatByBIN(0x33C8,2+9+13,2);
            padc=0x6680>>6;
            tadc=0x7EC0>>6;
            MY_DEBUG("%f,%f,%f,%f,%d,%d\n",a0 ,b1 ,b2 ,c12,padc,tadc);


            MY_DEBUG("%f\n",calcMACs(50, calcMACs( calcMACs( a0,calcMACs(b1,c12,tadc) ,padc) ,b2,tadc) ,(115-50)/1023.0));
        }

        其间发现手册有“印刷错误”(不知道这个典故的可以去找老罗语录),不过经认真比对之后还是不难搞定滴:p
        虽然实现了功能,但是效率不怎么样,而且浮点数是会损失精确度的,需要修改!
        正在苦B的写定点数转换算法时,发现飞思卡尔有一份应用笔记,里面有详细的算法,简直是定点数计算的极好教材,果断收下:

        /*********************************************************\
        *  Calculate   the   compensated  pressure  PComp  from the  last  set
        *  of  ADC  and   coefficient values.
        \*********************************************************/
        sint16   mpl115a1_CalculatePComp(void)
        {
           uint16  Padc, Tadc;
           sint16  a0,  b1, b2, c12;
           sint16  PComp;

           //  extract   adc outputs
           Padc  =   (mpl115a1_regs[0x00] <<   8)  |  mpl115a1_regs[0x01];
           Tadc   =  (mpl115a1_regs[0x02] <<  8)  |  mpl115a1_regs[0x03];

           //  extract   coefficients
           a0     =   (mpl115a1_regs[0x04]  <<   8)  |  mpl115a1_regs[0x05];
           b1    =  (mpl115a1_regs[0x06]  <<  8)   |  mpl115a1_regs[0x07];
           b2    =  (mpl115a1_regs[0x08]  <<  8)   |  mpl115a1_regs[0x09];
           c12    =   (mpl115a1_regs[0x0A] <<  8)  |  mpl115a1_regs[0x0B];
           //  calculate   internally compenstated  PComp  value  using either  version
           //PComp  =  calculatePCompLong(Padc, Tadc, a0, b1, b2, c12);
           PComp   =  calculatePCompShort(Padc,  Tadc, a0, b1, b2,  c12);
           return  ( sint16)PComp;
        }
        /*********************************************************\
        *  Calculate   the   pressure  in  1/16  kPa   from  a compensated
        *  PComp  value.
        \*********************************************************/
        uint16  mpl115a1_CalculatePressure(sint16 PComp)
        {
           sint32  Pressure;

           //  The  final   step  is  to   convert the  internal  PComp  value  into  units of  kPa.
           //     Pressure  =   PComp  ∙  ((115.0  ‐  50.0) /   1023.0)  +  50
           //
           //  The  us e  of   a  fl oating  point  divide  can  be  eliminated using  the   following  approximation:
           //     Pressure  =   ( (  PComp  ∙  1041 ) >> 14 )  +  50
           //
           //  Note  that  in  this  implementation  the  final  pressure  value is  reported   with  a  4   bit  fractional
           //  part. This  may  be eliminated by right  shifting  the  result  four   additional  bits.

           Pressure   =  ((((sint32)PComp) *   1041)  >>  14)  +  800;

           return  (uint16)Pressure;
        }

        /*********************************************************\
        *  Calculate   the   compensated  pressure  PComp  value us ing   th e  detailed  description.
        \*********************************************************/
        sint16   calculatePCompLong(uint16   Padc,  uint16  Tadc,  sint16  a0,  sint16   b1, sint16  b2, sint16  c12)
        {
           //  TEMPORARY  DATA VARIABLES:
           sint32  lt1,   lt2,  lt3;
           sint32  c12x2,   a1, a1x1,  y1, a2x2, PComp;
           //  Pressure   calculation   (long)
           //============================
           //  This   version of  the  pressure  calculation  function  has the  long   description  showing  ex aclty
           //  how  the  bit widths  of   the   coefficients  align through the  calculation.
           //
           //  Variables  used  to  do large  calculation  as  3  temp  variables   in  the  process below
           //    signed   long  (sint32) lt1,   lt2,  lt3;
           //
           //  Variables  used  for Pressure   and   Temperature Raw.
           //    unsigned   short (uint16)  Padc, Ta dc.
           //
           //  In  order  to  optimize  the  fixed point arithmetic,  each  value  is  annotated with  a  descriptor  x(N,F).
           //    x  is   's'   for  signed  or  'u'  for  unsigned
           //    N  is   the   number of  significant  digits in  the  value
           //    F  is   the  number of   fractional bits,  right  of   the   decimal point
           //
           //  Each  of   the   input  values  and   coefficients   are  identified below,  based  upon the  coefficient bit
           //  width table in   the   data  sheet:
           //     Padc    : u(10.0)
           //     Tadc    : u(10,0)
           //     a0      :  s(16,3)
           //     b1     :  s(16,13)
           //     b2     :  s(16,14)
           //     c12     :  s(16,24)  //  s(14,13) +  9   zero  pad   =  s(16,15+9) =>  s(16,24) left   justified
           //     PComp  :  s(16,4)   //  compensated   pressure  value  contains   a 8  bit  integer  part   and   a 4  bit  fractional part
           //
           //  The  compensation  formula is:
           //     PComp  =   a0   +  (b1  +   c12  ∙  Tadc) ∙  Padc  +  b2 ∙  Tadc

           //  The  calculation  can  be  broken  down into  individual  steps
           Padc>>=6;  //Note  that   the   Padc   is  the  raw  value  from  Pegasus  >>6  since its  10  bit  unsigned
           Tadc>>=6;   //Note  that   the  Tadc  is   the  raw  value from Pegasus >>6   since its   10 bit unsigned
           //******* STEP  1  :  c12x2 =   c1 2   *  Ta dc
           lt1  =  c12;            //  s(16,24) //   c12   is  s(14,13)+9 zero  pad  =  s(16,15)+9 =>   s(16,24) left  justified
           lt2  =  (sint16)Tadc;  //  u(10,0)
           lt3  =  lt1 *  lt2;      //  s(26,24) =  c12   *  Tadc
           c12x2  =  lt3 >>  11;   //  s(15,13) ‐  EQ  3  =  c12x2
           //******* STEP  2  :  a1   =  b1 +  c12x2
           lt1  =  (sint16)b1;     //  s(16,13)
           lt2  =  c12x2;            //  s(15,13)
           lt3  =  lt1 +  lt2;      //  s(16,13) =  b1 +   c12x2
           a1    =  lt3;            //  s(16,13) ‐  EQ 4  =   a1
           //******* STEP  3  :  a1x1  =  a1 *   Padc
           lt1  =  a1;            //  s(16,13)
           lt2  =  (sint16)Padc;  //   u( 10,0)
           lt3  =  lt1 *  lt2;      //  s(26,13) =  a1 *   Padc
           a1x1  =  lt3;          //  s(26,13) ‐  EQ  5  =  a1x1
           //******* STEP  4     y1  =   a0   +  a1x1
           lt1  =  ((sint32)a0)  <<  10; //  s(26,13) shifted   to  match a1x1   F value to   add.   So  s(16,3)<<10   =  s(26,13)
           lt2  =  a1x1;         //  s(26,13)
           lt3  =  lt1 +  lt2;      //  s(26,13) =  a0 +   a1x1
           y1    =  lt3;            //  s(26,13) ‐  EQ 6  =   y1
           //******* STEP  5  :  a2x2  =  b2 *   Tadc
           lt1  =  (sint32)b2;     //  s(16,14)
           lt2  =  (sint32)Tadc;  //  u(10,0)
           lt3  =  lt1 *  lt2;      //  s(26,14) =  b2 *   Tadc
           a2x2  =  lt3  >>  1;      //  s(25,13) ‐  EQ 7  =   a2x2
           //*******  STEP  6   : PComp  =   y1  +  a2x2
           lt1  =  y1;            //  s(26,13)
           lt2  =  a2x2;         //  s(25,13)
           lt3  =  lt1 +  lt2;      //  s(26,13) =  y1  +  a2x2
           PComp   =  lt3 >>   9;     //  s(17,4)  ‐  EQ 8  =   PComp  

           return  (sint16)PComp; //   By calibration  this  is  less  th an  16 bits
        }
        /*********************************************************\
        *  Calculate   the   compensated  pressure  PComp  value using  the   brief  version
        \*********************************************************/
        sint16   calculatePCompShort(uint16  Padc, uint16  Tadc, sint16   a0, sint16  b1,  sint16   b2, sint16  c12)
        {
           sint32  c12x2,   a1, a1x1,  y1, a2x2, PComp;
           //  Pressure   calculation   (short)
           //=============================
           //  This   version of  the  pressure  calculation  function  has the  sa me  func tion  as  the  long
           //  version  and   gets   exaclty   the  same result,  but is   implemented   more succinctly.
           Padc  >>=  6;  //Note  that  the  Padc  is   the   raw   value from Pegasus >>6   since its  10 bit unsigned
           Tadc   >>= 6;   //Note   that  the   Tadc  is  the   raw   value  from Pegasus  >>6  si nce it s  10  bit unsigned
           c12x2  =  (((sint32)c12)   *  Tadc)  >>  11;   //  c12x2 =  c12   *  Tadc
           a1      =  (sint32)b1  +  c12x2;              //  a1     =   b1   +  c12x2
           a1x1    =   a1  *  Padc;                      //  a1x1    =  a1   *  Padc
           y1      =  (((sint32)a0) <<  10) +   a1x1;    //   y1      =  a0   +  a1x1
           a2x2    =   (((sint32)b2) *   Tadc) >>   1;      //   a2x2    =   b2   *  Tadc
           PComp   =  (y1 +   a2x2)  >>  9;               //  PComp  =   y1    +   a2x2

           return  (sint16)PComp;
        }
        总结起来基本上是以下几点:
        1】正负问题交给语言去解决
        2】注意小数点对齐
        3】及时清理压缩数据的容器,当然,这需要准确掌握计算结果的长度和实际值域,比如[m,n]+[j,k]的长度是[max(m,j),max(n,k)],[m,n]×[j,k]的长度是[m+j,n+k]。
        4】把 65/1023 改成成 1041>>14 简直是位于牛A与牛C之间的极品,深受触动,大受启发。
        搞定算法后开始拿板子调试接口,先做MPL115A2,所以启用I2C——把板上跳线A4,A5跳到23,然后就开始写协议代码了。由于当时coIDE和coocox的库还在升级中,偶尔会出现示例不能用之类的情况,不过在coocox的liam帮助下,还是搞明白了I2C的用法,后来就是调试了。没想到启动转换的命令0x12发送后就飞了,后来发现,在命令后还要跟一个0x00——看手册不仔细害死人啊。 启动转换没问题了,可是取气压温度原始数据时又出问题了——虽然调试的时候有时能正常,但一旦开跑,每次取出来的都是0,这个问题直到31日晚上才解决:当时手册已经看到第n次方遍了,快崩溃了,正要关机歇业,下年再说,突然灵机一动,把等待adc的延时加大了,然后就洗具了。这才踏踏实实过了年。 2012年就这么过去了,2013年赶紧把驱动传给liam看,结果发现一堆问题,主要是不符合coocox的文档规范。coocox的文档除了在程序文本中要加入doxygen语法外,还对预定义的格式等有严格要求。虽然学习过doxygen,不过有些东西还是做得不到位啊。后来liam提出的问题全部解决,顺手着还发现、处理了几个结构上不够紧凑的毛病。 MPL115A2做完之后,该做MPL115A1了,这个是SPI协议,用的也是cox的库来做的,相比做MPL115A1容易了一些,但调底层时还是遇到了一个问题:必须要用xSPISSSet对spi进行使能。当时参考了Walter的方法,不成功,后来参数改用xSPI_SS_SOFTWARE才调通,又发现每次发送前都要重新做一次xSPISSSet才可以,不知是否MPL115A1会自动拉高?总之,MPL115A1也搞定了。 都搞定后,liam告诉我,还要上传一套用于介绍组件的主页,这样使用者就可以从主页上大概了解这个组件有什么功能、如何使用。手册翻了那么多遍,这个很好写了。写好后,问了问如何写sample文件、如何上传等等,最后就毫无悬念的上传了,这是上传之后的部分截图: 与非blog 01 a-1.png





    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-25 12:54 , Processed in 0.105768 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.