ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MSP430F1101AIDWR维护指南:1KB Flash低功耗与软件外设

MSP430F1101AIDWR维护指南:1KB Flash低功耗与软件外设 采购单上出现 MSP430F1101AIDWR 这种料号多数人的第一反应是这不就是一片老掉牙的 MSP430 吗然后在群里问一句能不能换成新片子接着就被项目周期、BOM 冻结、老代码没人敢动这三件事劝退。我这两年接的维护型项目里用这颗 TI 的 MSP430F1101AIDWR 做过好几轮20 脚 SOIC 宽体封装1KB Flash、128B RAM一个 Timer_A3一个 Comparator_A没有串口外设没有 ADC没有硬件乘法器。它的价值不在性能而在够用且确定——3V 下满速跑在 200µA 量级待机 0.7µA从待机唤醒几微秒配两节干电池能撑好几年。下面这些内容是我从画板子、点亮、写软件串口、抠 Flash 空间一路踩过来的东西适合正在维护老设备、或者接了一颗 F1xx 老料号要把它用起来的人。1. 料号拆解MSP430F1101AIDWR 每个字符都在说什么1.1 前缀和后缀各代表什么为什么不要随手替换TI 的 MSP430 命名是一套很有规律的编码拆开看就知道自己买到了什么。MSP430是家族前缀F1101A是具体型号I是温度等级DW是封装R是包装形式。这四段里最容易出问题的是封装段和型号段。DW在 TI 的封装体系里是 20 脚 SOIC 宽体本体 7.5mm 左右对应的另一种常见封装是PW20 脚 TSSOP超薄窄体。同样一颗硅片DW 和 PW 的引脚间距、焊盘尺寸完全不同PCB 不能混用但从返修角度说DW 的 1.27mm 引脚间距对烙铁友好太多热风枪吹一片 TSSOP 容易连锡SOIC 基本是新手也能焊的水平。后缀的R只表示编带包装tape and reel跟电气特性没关系和管装无 R或用T在功能上完全一致但产线贴片机只吃编带手工打样买管装更划算。温度等级I是工业级覆盖 -40℃ 到 85℃ 这个区间。这一个字母决定了你冬天在户外机柜里能不能开机别为了省几毛钱换成商温档如果有的话。型号段F1101A里A 这个后缀值得单独说一句。带 A 的版本和早期不带 A 的 F11x1 在数据手册上是两份不同的文档低压工作范围和片上比较器这些地方有差异。我见过有人把 F1101 和 F1101A 当同一颗料互换结果在 2V 供电的电池设备上出现了一批莫名复位。替换前逐项对参数不要只看型号差一个字母。同家族还有 F1111A2KB Flash和 F1121A4KB Flash、256B RAM引脚和封装一致是后面讲替代方案时最省事的路径。1.2 1KB Flash 加 128B RAM意味着你的代码要换一种写法1KB Flash 是什么概念用 C 语言写一个带定时器中断、软件串口收发、PID 运算和几个状态机的小工程大概率落在 700 到 950 字节之间。听起来还剩一点余量但这还没算上位运算、除法库函数和任何调试代码。我在这颗片子上有个硬规矩永远不碰 printf/sprintf。一次格式化输出就能把几百字节的格式化引擎拉进来还要额外挂上浮点支持1KB 直接爆掉。要调试就打电平、翻 IO、或者用软件串口发几个原始字节让上位机解析。除法也尽量避开MSP430 的 GCC 和 CCS 编译器会为你插入除法和取模的库函数一次 32 位除法能吃掉上百字节代码空间能用移位就移位。128B RAM 更紧张。栈要占一部分全局变量要占一部分中断现场要占一部分。我曾经为了一个 15 字节的显示缓冲把全局变量一个一个按字节抠出来数。这里有个实用的自检方法编译完看 map 文件里 RAM 的占用分布如果栈的剩余空间不到 20 字节功能一多就会莫名跑飞而且这种跑飞很难复现因为只在你最不希望它发生的那条路径上触发。所以用这颗片子做设计思路是先规划数据再写代码。哪些量必须常驻、哪些可以复用同一块内存、哪些能算出来不需要存想清楚了再动手比写完再优化省事十倍。1.3 14 个 IO、Timer_A3 和 Comparator_A 的能力清单20 脚封装下实际可用 IO 是 14 个P1.0 到 P1.7 八个P2.0 到 P2.5 六个。剩下的 6 个引脚给了 XIN、XOUT、RST/NMI、TEST、VCC、VSS。P1 和 P2 大部分引脚都带复用功能P1 那一组的复用几乎全给 Timer_AP2 那一组则偏向时钟和比较器。资源数量/规格能做好的事做不到的事可用 IO14P1.0-P1.7、P2.0-P2.5按键、继电器、LED、软件串口大并行总线Timer_A3CCR0 CCR1 CCR2定时节拍、两路 PWM、输入捕获、软件串口位时钟四路以上独立 PWMComparator_A1 个比较器内部参考分压阈值检测、斜率型 ADC、欠压监测直接读模拟量时钟LFXT1 内部 DCO32.768kHz 精准定时DCO 约 1MHz 做高速缺失项无 USART、无 ADC、无乘法器、无 DMA需要自己用软件补指望库函数兜底Timer_A3 是三个捕获比较寄存器CCR0 管周期CCR1 和 CCR2 分别能在 P1.2/P1.3 或 P1.6/P1.7 上输出 PWM。两路 PWM 对一颗做温控、电机、调光的片子来说只要你不做三相驱动够用。它也支持输入捕获这一点在软件串口接收里是救命的——起始位那一下沿触发靠的就是捕获功能靠轮询去等下降沿误差能到几个位宽。Comparator_A 是最容易被忽略的宝藏。它有一个内部参考电压源可以用 VCC 分压出几档固定参考配一个定时器就能拼出简易 ADC也能拿来做电池欠压判断。这颗片子没有 ADC 不是缺陷是设计年代的问题解决办法在第三节展开。2. 从上电到点灯四件最容易在第一天卡住的事2.1 复位脚和 TEST 脚47k 加 10nF 不是随便写的RST/NMI 引脚内部有弱上拉但工程上不能只靠它。TI 在文档里推荐的外部复位电路是 47kΩ 上拉到 VCC 加一颗 10nF 电容到 VSS构成约 470µs 的 RC 时间常数。这个数值不是拍脑袋上拉电阻不能太小因为 RST 被拉低时电流会直接从 VCC 灌进引脚电容不能太大否则复位沿太缓芯片在电源还没稳的时候就被放出来运行容易出现有时候能启动有时候不能的玄学问题。RST/NMI 是双功能脚配成 NMI 后可以用来响应一个外部事件。但改这个配置要往 WDTCTL 里写特定序列的解锁值写错不会报错而是直接触发一次复位。我早期调这块的时候就是数组下标越界写到了 WDTCTL 附近现象是设备每隔几秒重启一次查了一下午才发现是代码跑飞写坏了看门狗控制寄存器。TEST 引脚的处理也一样不能随手来。它参与 JTAG 下载时序也参与 F1xx 的 BSL 进入序列。按数据手册的引脚功能表处理一般是接到 VSS 侧不要当普通 GPIO 悬空或者接负载否则会间歇性出现下载连不上、或者上电后进入非预期状态。还有一点跟存储有关这颗片子片内有欠压复位电路但阈值是固定的、不可配置。如果你要用 Flash 保存参数在电池缓慢跌落的场景下写入过程可能被打断导致参数区变成半截数据。对这种应用要么加一个外部复位监控芯片要么在写入前用比较器做一次电压判断宁可放弃这次保存。2.2 晶振和内部负载电容XCAP 位能帮你省两颗电容如果工程里需要精准的秒级定时比如数据记录、定时上报、低波特率串口那就老老实实装一颗 32.768kHz 表晶到 XIN/XOUT。不装也能跑但时基只能来自内部 DCO而 DCO 的频率随温度和电压漂动漂动幅度比晶振大一个量级还多。你要是拿它做每天记录一次的定时几个月下来偏差会很难看。好消息是负载电容可以省。控制寄存器里有 XCAP 位能选内部负载电容的几个档位大致是 0、6、10、12pF 这个量级。选对档位的前提是知道晶振的规格厂家给的 CL 标称值要和内部电容匹配得上。一般 CL 为 12.5pF 的晶振配上内部 6pF 那一档是比较稳的组合。如果你非要在外面再并两颗电容反而会把总负载电容推过头频率偏低、起振变慢。起步阶段还有一个经验先用内部 DCO 把程序跑通确认 IO、中断、存储都正常最后再上晶振。晶振起振失败会体现在两个地方一个是频率跑飞一个是有时候根本起不来这两种现象会掩盖掉你真正要调的软件问题。2.3 DCO 校准常量藏在信息存储区别顺手擦掉这颗片子在出厂时会把 DCO 的校准值烧进信息存储区的固定字节里。头文件里那两个宏CALDCO_1MHZ和CALBC1_1MHZ展开之后就是指向这些地址的指针你把它们的值直接赋给 DCOCTL 和 BCSCTL1在常温常压下就能拿到接近 1MHz 的主时钟。老版本头文件里这两个地址在信息存储区的高地址端两个字节挨着放。如果你手里的头文件版本不一样展开宏看一眼地址就明白了不要自己手抄地址。这里有个非常经典的坑为了给自己的参数腾地方直接对信息存储区做整段擦除把校准值一起擦了。擦掉之后芯片不会罢工DCO 照样输出只是频率完全靠 RSELx 和 DCOx 两个组合去凑而这两个组合值随温度漂得很厉害。表现出来就是串口波特率莫名其妙偏、定时的秒数不准、冬天和夏天的行为不一样。更麻烦的是这种故障不像死机那么显眼它是在指标上慢慢体现的查起来非常费劲。所以正确做法是先把信息存储区的段划分搞清楚确认校准值落在哪一段把这一段从你的参数存储计划里划掉参数一律放在别的段里。第二个坑跟 Flash 写入有关。Flash 的时序发生器要靠时钟分频产生一个固定的工作频率如果你在运行时把 DCO 的频率改了却没重新算分频写入时序就会超出数据手册允许的窗口范围。轻则这一笔写失败读回来是错的重则影响到相邻存储单元的数据完整性。我现在的习惯是凡是会改时钟配置的代码路径后面一定跟一句重新配置 Flash 时序分频哪怕当次不写 Flash。2.4 一个不依赖任何库的最小工程刚上手的板子先跑一个最小程序把所有中间环节都排除掉。下面这段是寄存器级的写法不需要任何 SDK 或驱动库头文件里定义的宏直接用。#include msp430.h int main(void) { WDTCTL WDTPW | WDTHOLD; /* 关键带密码关看门狗漏掉 WDTPW 会立刻复位 */ BCSCTL1 CALBC1_1MHZ; /* 先设 DCO 量程再设频率 */ DCOCTL CALDCO_1MHZ; /* 顺序不能反 */ P1DIR | BIT0; /* P1.0 推挽输出 */ P1OUT ~BIT0; for (;;) { volatile unsigned int i; P1OUT ^ BIT0; for (i 0; i 30000; i) { } /* 粗延时只是为了让灯看得出在闪 */ } }有几个点值得说明。第一WDTCTL的写入必须带WDTPW这个密码这是 TI 故意设计的防误写机制漏掉密码的那次写入会被当成非法访问直接触发复位现象就是程序跑到第一行就重启。第二BCSCTL1和DCOCTL的赋值顺序有讲究BCSCTL1 里的 RSELx 决定 DCO 的频率量程DCOCTL 里的 DCOx 决定量程内的细分先粗后细。反过来写要先看到一次量程跳变带来的频率冲击。建工程的时候器件选项要选容量最小的那个F1101A让链接器替你守住 1KB 这条线。万一工具里的器件列表没有 F1101A就选同家族的 F1121A 编译但必须打开 map 文件核对实际的 Flash 占用不超过 1KB——编译器只按你选的器件分配地址它不知道你要往更小的片子里烧。优化选项选优先代码尺寸能省下一两百字节。3. 没有串口、没有 ADC两个必须自己造的外设3.1 用 Timer_A 加 GPIO 拼一个软件串口这颗片子没有 USART 外设但这不代表不能有串口。做法是拿 Timer_A 当位时钟一个 GPIO 当发送线另一个 GPIO 加输入捕获当接收线。位时钟怎么来波特率的倒数就是一位的时间用定时器比较中断在每个位的时刻翻转 IO 电平就行。关键参数是定时间隔对应的计数值。用内部 DCO 跑到 1MHz一位的时间是 104 个时钟周期实际波特率是 1000000/104约 9615相对 9600 的误差大约 0.16%完全在可接受范围内一般控制住 2% 以内都比较安全。这里不建议用 32.768kHz 晶振做位时钟因为 32768 除以常用波特率基本都除不尽误差轻松超过 4%接收端会开始丢包。发送的骨架长这样示意用实际产品要改成中断驱动配低功耗模式#define BIT_TICKS 104 #define TXD BIT1 /* 用 P1.1 当发送脚 */ void tx_init(void) { P1DIR | TXD; P1OUT | TXD; /* 空闲时是高电平 */ TACTL TASSEL_2 | MC_0 | TACLR; /* 时钟源用 SMCLK先不计数 */ TACCTL0 0; /* CCR0 用作比较模式 */ } void tx_byte(unsigned char c) { unsigned char i; TACCR0 TAR BIT_TICKS; TACCTL0 ~CCIFG; TACTL | MC_1; /* 开始增计数 */ P1OUT ~TXD; /* 起始位 */ for (i 0; i 8; i) { while (!(TACCTL0 CCIFG)) { } TACCTL0 ~CCIFG; TACCR0 BIT_TICKS; /* 下一个边沿用累加而不是重设 */ if (c 0x01) P1OUT | TXD; else P1OUT ~TXD; c 1; } while (!(TACCTL0 CCIFG)) { } TACCTL0 ~CCIFG; P1OUT | TXD; /* 停止位回空闲态 */ TACTL ~MC_1; }用累加TACCR0 BIT_TICKS而不是每次重设成TAR BIT_TICKS这一点很关键累加方式不会把中断响应延迟累积到下一个位长帧传输的累积误差能控制在半个周期以内。接收比发送难得多。正确做法是把接收脚配成 Timer_A 的捕获输入捕捉下降沿起始位在捕获中断里把定时器对准位中心然后依次在每位的中点采样。整个过程要在低功耗模式下被捕获中断唤醒这样才能做到平均待机电流只有微安级。中断服务程序里只做移位和置标志数据处理留到主循环否则一个长中断会把位时序挤歪。还有两件工程上的事一是电平匹配3.3V 输出直接接 5V 的 USB 转串口模块要确认对方的输入高电平阈值足够低二是留调试口板子上留两个排针TX 加 GND成本几乎为零但省下来的时间非常可观。3.2 Comparator_A 加 Timer_A凑一个斜率型 ADC没有 ADC不等于读不了模拟量。比较器加定时器的组合可以搭出一个斜率型或者说积分型ADC用一个 GPIO 通过已知电阻给电容充电或放电同时用比较器把电容上的电压和内部参考比较一旦翻转就记下时刻。电容从固定起始电压充到参考电压所需的时间和被测电压成比例测时间就等于测电压。内部参考是 VCC 的几个固定分压档1/4 VCC、1/2 VCC 这个量级具体档位以数据手册的比较器章节为准同一批产品的参考点很稳定。这个方案的实测指标大概是 8 到 10 位有效分辨率测量一次要毫秒级时间。用来测电池电压、判断传感器通断、做简单的温度阈值检测完全够用。精度上不去的几个原因是电容本身的容差、比较器的输入失调几毫伏量级、以及充放电回路的漏电流。想再往上提就得用软件做多次测量取平均代价是时间和代码空间。如果你要的是 12 位以上、几十赫兹以上的采样率别硬扛。外挂一颗 I2C 的小 ADC成本几块钱12 到 16 位分辨率代码量反而比软件 ADC 更少用两个 IO 模拟 I2C 就能驱动。我现在的判断标准很简单采样率超过 10Hz 或者要求 10 位以上直接外挂只是偶尔测一下就内部方案。3.3 模拟前端的滤波用 FilterPro 这类工具时要留的余量信号调理这一环很多人是随手抄一个经典电路了事结果在现场被噪声教育。滤波器设计工具比如 TI 的 FilterPro老版本是本地软件后来 TI 把滤波器设计整合到在线工具里功能等价能帮你算出拓扑和元件值输入滤波器类型低通、高通、带通、增益、截止频率、通带纹波它输出 Sallen-Key 或者多反馈拓扑的电阻电容取值。工具给出的数值是按理想运放算的直接落地有四个坑。第一运放的增益带宽积要留够余量。经验值是截止频率的 50 到 100 倍比如你要做 10Hz 低通运放的增益带宽积最好在 1MHz 以上否则实际截止频率会明显低于设计值增益也被压。第二电阻电容的取值要落到能买到的系列上E24 或 E96算出来的 3.17kΩ 得换成 3.3kΩ换完之后截止频率就偏了十几二十个点要回过去确认这个偏移能接受。第三Sallen-Key 拓扑对 Q 值敏感高 Q 的设计里电阻容差会直接影响峰值响应。高 Q 场合电阻用 1% 精度电容用 5% 以内的别在这上面省。第四单电源供电一定要处理偏置。把运放的同相端用两个等值电阻抬到 VCC/2 附近否则信号摆不到负半周负半周直接被削平。同时记得在偏置点上并一颗电容到地否则电源噪声会直接串进信号通路。4. 1KB Flash 里塞参数段擦除、时序和向量的三重坑4.1 擦除单位比你想象的大得多Flash 的物理特性决定了写入只能把 1 变成 0要让 0 变回 1 只能整段擦除。所以改一个字节这件事实际上要经历读整段、改内容、擦整段、写回去四步。问题在于段的大小。1KB 主存储区的段划分通常在 512 字节量级不同子型号要查数据手册的 Memory Organization 表。512 字节远远超过这颗芯片 128B 的 RAM 容量意味着你没有足够的内存把整段搬出来改。这就把App 运行时修改自己的程序区这条路径彻底堵死了。存储位置段大小量级能否在线改主要风险主存储区数百字节不可行RAM 装不下擦写会波及代码区信息存储区几十字节量级可行别擦到校准常量所在段外部 I2C EEPROM按字节/页写可行多两个 IO需要软件 I2C结论是清楚的这颗片子上主存储区只放代码不放跑起来还要改的数据。4.2 三种参数存储方案的取舍信息存储区是最自然的落点。它的段更小几十字节量级一次擦除不会波及别的内容放十几个配置项不成问题。前提是先把出厂校准常量所在的那一段圈出来绝对不碰。写之前建议先把整个信息区的原始内容读出来存一份一是留底二是防止自己算错边界。外部 EEPROM 是更省心的选择。一颗小容量的 I2C EEPROM 成本几毛钱两个 IO 加一段软件 I2C 就能驱动写寿命和擦写粒度都比片内 Flash 友好得多。如果参数写得很频繁比如每隔几分钟记一次运行状态我建议直接上 I2C 接口的 FRAM 器件写入速度和寿命都不是一个量级唯一的代价是单价高一些。还有一种根本不在片上存的思路设定值由上位机每次下发。这在联网设备上很常见但离线设备用不了。我自己的分配习惯是出厂标定参数几乎不写放信息存储区运行中会改的设定值放外部 EEPROM同时加一个上电读一次、掉电前写一次的策略把擦写次数压到最低。4.3 写入时序和中断向量两处必须动的地方点亮工程之后第一次写 Flash有三件事必须确认。时序发生器的分频要落到合适的位置。Flash 需要的是一个固定的时序时钟目标是让它的频率落在 257kHz 附近太慢写入时间超标太快超出器件允许的窗口。举例来说主时钟 1MHz 时除以 4 得到 250kHz这个值就在窗口内。分频靠 Flash 控制寄存器的时钟源选择和分频位实现宏名以你用的器件头文件为准别自己拼位。擦写期间不能从 Flash 取指令。这一点是 F1xx 的老规矩Flash 在忙的时候不可读如果执行擦写的代码本身也在 Flash 里CPU 会取不到下一条指令。所以这段子程序必须搬到 RAM 里执行通常的做法是写一个 RAM 版本的函数启动时把它的机器码拷贝到 RAM 中运行。这里还有一个现实约束RAM 只有 128B擦写函数要写得极其精简我一般控制在三四十字节以内把循环和判断都放在调用者那边。/* 注意F1xx 擦写期间不能从 Flash 取指这段要放到 RAM 里执行 */ void flash_write_word(unsigned int *addr, unsigned int data) { FCTL2 FWKEY | FSSEL_2 | FN2; /* 时钟源与分频要落到 257kHz 附近 */ FCTL3 FWKEY; /* 解锁 */ FCTL1 FWKEY | WRT; /* 进入字写入模式 */ *addr data; while (FCTL3 BUSY) { } FCTL1 FWKEY; /* 退出写入模式 */ FCTL3 FWKEY | LOCK; /* 重新上锁 */ }第三件事是中断。擦写期间中断必须关掉因为中断向量表本身也在 Flash 里一次中断响应就要读向量直接和擦写冲突。段擦除的耗时是几十毫秒量级单字写入是几十微秒量级两者都不能在中断里做正确做法是用状态机把擦写拆成几步主循环任务。最后补一个数据完整性的小习惯每次保存参数时把数据和一份简单的校验累加和或 CRC8一起写上电读取时先校验校验失败就退回默认值并置一个标志位。这个小动作在电源不稳的现场能救回
返回列表