2524|0

57

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【TI首届低功耗设计大赛】【超低功耗码表】完结篇 [复制链接]

本帖最后由 mark86739851 于 2014-12-31 12:13 编辑

    作品终于告一段落了,虽然没有多少技术难度,不过在这个过程当中熟悉了FR系列单片机,熟悉了CCS开发环境,熟悉了TI的430代码库,还是很不错的。
    我的超低功耗码表原理很简单,用32768晶体作为ACLK时钟源,不分频,直接给定时器使用。单片机P1.3管脚用做外部中断,检测自行车的触发信号,市场上几乎所有的码表都用的干簧管加磁铁,这样可以保证系统的低功耗要求。干簧管配上少量外部电路就能产生方波信号。每接收到一次下降沿就证明车轮转了一圈,累加车轮周长就是路程,计算两次中断间的时间差就可以得到瞬时速度,总路程除以总时间就是平均速度。下面是估算过程:
    速度计算考虑到自行车触发频率不是很高,采用测周期法测频率误差小:
    触发频率估算:普通人平路上骑车最高速度40km/h下山冲刺可能加速到60~70km/h,再高就相当危险了,达到100km/h随时都可能丧命,普通人是不可能做到的。当速度为100km/h假设自行车轮胎为26英寸(约660.4mm),那么干簧管的触发频率为(100/3.6)*1000/660.4≈42Hz,即便速度达到200km/h,也只上升到84Hz左右,这对单片机来说是很低的频率了,
这时用周期法测频率最准确了。当人推车步行时一般为3km/h,触发频率大概为1.6Hz,非常慢的情况下1km/h(女生散步也比这快吧),频率降到0.5Hz,大概每2s触发一次。如果速度再低是不太可能的现象(没有人骑车或推车走这么慢)不过还是照顾下“叫真儿”的群众们,超过2s不触发,就当作速度为0处理。
    接下来是测量精度问题。周期法的特点是频率越低测量越精准,设速度为SPD(km/h),显示精确到0.1km/h,定时器最小定时单位为 tick,则为了满足0.1km/h的精度,测量周期时精度要小于等于0.1/SPD:
                  tick                     0.1
      -----------------------   ≤  ---------  , 即:tick ≤ 42ms,用32768做定时器时钟源最小计数单位为30.5us,绝对够用,
      SPD x 1000 x    1             SPD
      3.6              660.4
同时,16位的计数器刚好能计量2s,作为低速溢出判断很合适。
      在管脚外部中断函数里判断定时器的状态:
  1. void Port_1(void)
  2. {
  3.         uint16_t TimerA_counterValue;//暂存定时器计数值
  4.         if(P1IFG&BIT3){
  5.                                 TimerA_counterValue = Timer_A_getCounterValue(TIMER_A1_BASE);
  6.                                 Timer_A_clear(TIMER_A1_BASE);
  7.                         //如果计数为0,说明之前定时器处于关闭状态(刚好溢出的情况非常小,即便出现了忽略掉影响甚微),应当开启定时器
  8.                                 if(TimerA_counterValue == 0){
  9.                                         //开启定时器
  10.                                         Timer_A_startCounter(TIMER_A1_BASE,TIMER_A_CONTINUOUS_MODE);
  11.                                         ridingFlag = true;
  12.                                         // 开启LED
  13.                                         GPIO_setOutputHighOnPin(GPIO_PORT_P1, GPIO_PIN0);
  14.                                 }
  15.                         //如果定时器值不等于0,说明这不是第一次进入该中断了,定时器里的值就是两次中断的时间差,开始计算相关参数
  16.                                 else{
  17.                                         ODO += WHEEL_SIZE;//总行程计数
  18.                                         singalTrip.DST += WHEEL_SIZE;//单次行程计数

  19.                         //                                      (WHEEL_SIZE/10^6) * 3600
  20.                         //        SPD=    ----------------------------------------------= 779039.5392/n
  21.                         //                                                                                    n
  22.                         //                                                                         ----------
  23.                         //                                                                         32768
  24.                                         singalTrip.SPD = (uint16_t)(((uint32_t)77903954/TimerA_counterValue)/100); //单位:百米/小时
  25.                                         //当首次开始计算时骑行时间为0s,无法计算平均速度,用此时的瞬时速度代替,防止出现分母为0的现象
  26.                                         if(singalTrip.TM.totalSeconds == 0){
  27.                                                 singalTrip.AVS = singalTrip.SPD;
  28.                                         }
  29.                                         else{//平均速度=骑行路程/骑行时间
  30.                         //                                        (DST/10^6)*3600                                DST*0.0036                                  DST
  31.                         //                                  ----------------------------- = -------------------------- =( ------------------- * 36 )/10000
  32.                         //                                                 Seconds                                          Seconds                               Seconds

  33.                                                 singalTrip.AVS = (uint16_t)(((singalTrip.DST/singalTrip.TM.totalSeconds)*36)/10000);
  34.                                         }
  35.                                 }
  36.         }

  37.         if(P1IFG&BIT1){         //清除单次骑行记录
  38.                 singalTrip.AVS = 0;
  39.                 singalTrip.DST = 0;
  40.                 singalTrip.TM.totalSeconds = 0;
  41.                 singalTrip.TM.Seconds = 0;
  42.                 singalTrip.TM.Minutes = 0;
  43.                 singalTrip.TM.Hours = 0;
  44.         }

  45.     P1IFG = 0;
  46.     //    GPIO_clearInterruptFlag(GPIO_PORT_P1, GPIO_PIN0+GPIO_PIN3);
  47. }

  48. <font size="4">定时器里要做的就简单多了:</font>
  49. <div class="blockcode"><blockquote>void TIMER1_A1_ISR(void)//最长2s中断一次
  50. {
  51.     //Any access, read or write, of the TAIV register automatically resets the
  52.     //highest "pending" interrupt flag
  53.     switch(__even_in_range(TA1IV,14))  //0~14内TA1IV为偶数才进switch
  54.     {
  55.     case  0: break;                              //No interrupt
  56.     case  2: break;                              //CCR1 not used
  57.     case  4: break;                              //CCR2 not used
  58.     case  6: break;                              //CCR3 not used
  59.     case  8: break;                              //CCR4 not used
  60.     case 10: break;                              //CCR5 not used
  61.     case 12: break;                              //CCR6 not used
  62.     case 14:
  63.             //如果定时器溢出,说明用户没有在骑自行车,停止计算速度,关闭定时器
  64.         timerA_OverFlowCnts++;//溢出一次加一次,相当于扩展成24位定时器
  65.         Timer_A_stop (TIMER_A1_BASE);
  66.         Timer_A_clear(TIMER_A1_BASE);
  67.         ridingFlag = false;
  68.         singalTrip.SPD = 0;
  69.         // 关闭LED
  70.         GPIO_setOutputLowOnPin(GPIO_PORT_P1, GPIO_PIN0);

  71.         break;
  72.     default: break;
  73.     }
  74. }
复制代码
          定时器溢出,说明速度太慢,只是偶尔触发,并没有骑行,将速度归零,骑行标志复位。
          整个过程都是用中断来实现的,系统实时性强,但显示操作相对来说比较费时,不过它的实时性要求低,因此可以放到主循环里执行:
整过过程无需全程开启CPU,只在RTC时钟1s中断到来后唤醒CPU,显示一下就行了,平时进入LPM3休眠模式。

  1. _Bool reflashFlag = true;//LCD刷新标志
  2. void main(void) {
  3.         SystemInit();
  4.         while(1){
  5.                 if(reflashFlag == true){
  6.                                 DispRealTime(¤tTime);
  7.                                 DispSpeed();
  8.                                 reflashFlag = false;//刷新过了标志失效
  9.                 }
  10.                 __bis_SR_register(LPM3_bits + GIE);
  11.         }
  12. }
复制代码
来看一下有方波信号和没方波信号时候的功耗差别吧,
有输入信号:


下面是无输入信号时候:

主要是因为有信号输入时每秒钟都会刷新一次LCD,刷新过程CPU全速运行。

      CCS6.0的功能还是很赞的,虽然速度上比不上IAR,但功能上超出了不少。

         回到正题,这个码表的创新点就是利用FRAM的非易失性,将要保存的变量定义在不用初始化的FRAM区,这样复位也好,掉电也好都不会对变量的值清零。我是看了一个老外发的帖子学来的:http://processors.wiki.ti.com/in ... Variables_Using_CCS
首先要修改下lnk_msp430fr5969.cmd文件,把原来的FRAM区域声明注释掉,重新开辟一个FRAM_VARS区域
  1. MEMORY
  2. {
  3.     SFR                     : origin = 0x0000, length = 0x0010
  4.     PERIPHERALS_8BIT        : origin = 0x0010, length = 0x00F0
  5.     PERIPHERALS_16BIT       : origin = 0x0100, length = 0x0100
  6.     RAM                     : origin = 0x1C00, length = 0x0800
  7.     INFOA                   : origin = 0x1980, length = 0x0080
  8.     INFOB                   : origin = 0x1900, length = 0x0080
  9.     INFOC                   : origin = 0x1880, length = 0x0080
  10.     INFOD                   : origin = 0x1800, length = 0x0080
  11.     <font color="Red">//FRAM                    : origin = 0x4400, length = 0xBB80
  12.     FRAM_VARS                                : origin = 0x4400, length = 0x0400 //1k byte
  13.     FRAM                                        : origin = 0x4800, length = 0xB780</font>
  14.     FRAM2                   : origin = 0x10000,length = 0x4000
  15.     JTAGSIGNATURE           : origin = 0xFF80, length = 0x0004, fill = 0xFFFF
  16.     BSLSIGNATURE            : origin = 0xFF84, length = 0x0004, fill = 0xFFFF
  17.     IPESIGNATURE            : origin = 0xFF88, length = 0x0008, fill = 0xFFFF
  18.     INT00                   : origin = 0xFF90, length = 0x0002
  19.     INT01                   : origin = 0xFF92, length = 0x0002
  20.     INT02                   : origin = 0xFF94, length = 0x0002
复制代码
将这个区域的性质定义成不用初始化,这样一来上电复位变量值就不会被清零了。
  1.     .infoA     : {} > INFOA              /* MSP430 INFO FRAM  MEMORY SEGMENTS */
  2.     .infoB     : {} > INFOB
  3.     .infoC     : {} > INFOC
  4.     .infoD     : {} > INFOD

  5.         <font color="Red">.fram_vars : {} > FRAM_VARS type=NOINIT</font>
复制代码
最后是定义变量了,一定要区分开是定义在SRAM中的变量还是定义在FRAM_VARS区中的变量。所以像平常那样声明是不行的。
IAR可以用__no_init关键字,但CCS里没有类似的关键字,好像只能修改 .cmd 文件开辟一个NOINIT空间来实现,然后在代码中这样声明

  1. <font color="Red">#pragma SET_DATA_SECTION(".fram_vars")</font>
  2. Calendar currentTime; //当前系统时间

  3. _Bool ridingFlag = false; //用户是否在骑行
  4. unsigned char timerA_OverFlowCnts = 0; //记录TimerA溢出次数
  5. singalTripData singalTrip; //单次骑行数据
  6. uint64_t ODO;   //总行驶路程,单位0.1mm(因为轮胎外径精确到0.5mm)
  7. <font color="Red">#pragma SET_DATA_SECTION()</font>
复制代码
把你要放在不用初始化的FRAM区域的变量包含在两条红色语句中就行了。个人感觉没有IAR的关键字方便。还有一个不方便的地方就是CCS没有@Address功能,不能在定义变量时就直接给定绝对地址(或许有但我没找到)。
         下面附上视频


由于其电路和原理简单,就没必要画原理图和PCB了
已发的相关贴子:
【TI首届低功耗设计大赛】【超低功耗码表】
【TI首届低功耗设计大赛】【超低功耗码表】--LCD驱动
【TI首届低功耗设计大赛】【超低功耗码表】--方便的Calendar

 
点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表