CC3000驱动移植中的几个陷阱

文章目录
  1. 1. 文档中的一个小错误
  2. 2. 两组中断控制函数的实现
  3. 3. TI提供的CC3000 Host Driver假设char类型是无符号的
  4. 4. 供电

文档中的一个小错误

wlan_ioctl_get_scan_results函数用于返回WLAN扫描的结果,每调用一次返回一个结果。返回的数据结构在文档中给出,但是这里文档写错了。。。Result entry前的56bytes应该是42bytes,而每次的结果总有有4+4+42=50(bytes)。把下面的结果加起来,也会发现各项的和是42而不是56. 这本来只是手册上的一个笔误,但在编程时,大家通常会使用一个结构体来接收返回的数据,如果这个字节数不对的话,就会影响内存对齐,从而导致返回的结果错误。这里,我使用的结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef struct _wlan_full_scan_results_args_t
{
/*
4 Bytes: number of networks found
4 Bytes: The status of the scan: 0 - agged results, 1 - results valid, 2 - no results
{
1 bit isValid - is result valid or not
7 bits rssi - RSSI value;
}
{
2 bits: securityMode - security mode of the AP: 0 - Open, 1 - WEP, 2 WPA, 3 WPA2
6 bits: SSID name length
}
2 bytes: the time at which the entry has entered into scans result table
32 bytes: SSID name
6 bytes: BSSID
*/
uint32_t ap_count;
uint32_t ap_state;
uint32_t ap_vaild : 1;
uint32_t ap_rssi : 7;
uint32_t ap_security : 2;
uint32_t ap_ssidlen : 6;
uint16_t ap_time;
char ap_ssid[32];
uint8_t ap_bssid[6];
uint8_t reserved[2]; //for memory align
} wlan_scan_result;

另外,这个函数读到最后一条结果时,再读会返回一个长度为0的结果,即前四个字节(number of networks found)为零,然后再读才会读出下一次扫描的结果。这一点手册上并没有指出,是我们在编程实践中自己发现的。

2014年3月19日注:目前最新版的CC3000官方API文档中这个错误已经得到了修正。

两组中断控制函数的实现

如果前面一条是一个小坑的话,这一条绝对是一个大坑。SpiPause - SpiResumeWlanInterruptDisable - WlanInterruptEnable这两组中断控制函数是CC3000整个驱动移植工作中最tricky的部分。目前为止,我见过的CC3000的驱动实现中,不论是开源的、产品正在开发而尚未开源的实现,除了官方的例程以外,还没有人将这两组函数写对。甚至有人能通过降低SPI速率、修改IO中断方式等等办法,使得整个程序得以正常运行,将错误带到了产品中。只要有人说:“我的CC3000驱动有点问题,调了很久都不行。。。”之类的话,不用等他说完,就可以猜想他是把这两组函数写错了。

这两组函数非常容易错,错了又非常难以发现。这两组函数的实现已经成为了CC3000驱动移植的主要障碍。其主要原因如下:

  1. 是官方的Porting Guide没有明确指出这两组函数的区别。很多人则想当然地认为这两组函数是一样的。甚至是一组中调用另一组。再看官方的实现,又有点不知所云,所以就忽略了这个问题。
  2. 忽略这个问题后,CC3000模块可以正常初始化,还可以正常扫描WiFi热点,有人还能正常进行Smart config,甚至有人还能正常打开socket并使用UDP协议发送数据!开发者根据CC3000驱动自下而上分层的架构来看,看到SPI已经可以进行正常通信,从而直接在心理上排除了SPI驱动实现有误的可能性,错误查来查去还是在原地兜圈子。
  3. 由于这两组函数实现有误导致的错误几乎成了一个人品问题,在不同速度MCU上的错误实现会导致不同的错误现象,以至于大家的描述不统一,很难从现象判断到底是哪儿错了,即使在网上根据错误现象搜索或者发帖求助,也很难得到有针对性的答案。

为什么这两组函数写错了,会有这么神奇的问题呢?答案就是,这里的错误会导致程序的“竞态问题”,类似于数字电路基础中的“竞争冒险”。SPI速率、AP的品质和信号强度、IO口翻转速度、延时的误差等无关紧要的问题,都可能让竞态问题出现不同的现象。因此,出现的错误现象千奇百怪,貌似跟人品有关就不难解释了。说了这么多,下面就来解释一下这两组函数到底有什么区别:

WlanInterruptDisable的作用是关闭IO口的外部中断,也就是IRQ的下降沿中断。关闭以后,IRQ下降沿的中断将被丢弃,在调用WlanInterruptEnable时,会重新使能中断,之后发生在该IO上的外部中断又再次可以响应。而中断被禁用期间,发生的中断都被丢弃,永远不再响应。这个函数一般人都写对了。

SpiPauseSpi函数是暂时挂起IO口外部中断,也就是IRQ下降沿中断,如果在SpiPauseSpi调用之后,IRQ线上再有下降沿,该中断将一直保持Pending状态,暂时不调用中断服务程序,该中断必须在调用SpiResumeSpi被重新响应,而不能被丢弃。这里,很多很多很多人,都,写错了。。。

这种中断的响应方式比较特殊,像STM32就没有在标准驱动库中实现相应的API,而必须通过操作寄存器的方式来手工实现。相应寄存器的定义,既不在芯片Datasheet里,也不在那份大家熟知的STM32F1系列Reference Manual(RM0008)里,而是在很多STM32开发者都没有听说过的一份文档PM0056——STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual里。有很多MCU,甚至包括TI的MSP430,在硬件上根本就不支持这种响应方式,因此只能用一些猥琐的方法来实现。MSP430该函数的代码居然是向一个配置为输入的IO口写数据,足以让不熟悉MSP430的开发者挠头了。

TI提供的CC3000 Host Driver假设char类型是无符号的

这个问题可能很多人都不会遇到,因为在嵌入式开发中char通常都是无符号的,基本所有的用于嵌入式平台的编译器默认情况下都会这样设定。而我则比较习惯PC上有符号的char,所以我手贱给GCC加了一个编译参数-fsigned-char……

TI的Host Driver就比较扯淡,它假设char是无符号的,并且在char有符号时就会因符号位在强制转换中的行为不同而出错。这个问题不容易排查,因为需要跟踪到TI实现的Host Driver里,而大家通常都会假设这个实现是没错的,能跟进去就是一种勇气了……我当时遇到这个问题时,不知道是哪儿的错误,就在很多回调函数中添加了串口打印的语句,通过串口输出,观察各个函数被调用的情况,结合现象,再加以大胆的猜想,才解决了这个问题。

供电

这个是一个硬件问题,一个不应该犯的错误。目前大家用的最多的CC3000模块还是Jorjin的WG1300,而Jorjin的文档中并未给出该模块工作时所需的电流。在一些追求体积最小化的应用中,大家也普遍使用了SOT23-5封装的小型LDO,这类LDO的电流通常只有100mA左右。一个单片机才几十毫安的电流,一个WiFi又能大到哪里去呢?

看看德州仪器官方WiFi模块(TI的WiFi模块封装尺寸和引脚定义与WG1300完全相同,本人咨询Jorjin总代理后得知TI的官方模块正是由Jorjin设计并制造的,与WG1300相比在硬件上也是完全一样的,只是外壳上的字不同而已)的文档后,不禁大吃一惊!发送电流峰值275mA,接收峰值103mA,合起来有接近400mA的峰值电流!正是因为如此,我120mA的LDO被瞬间秒杀,电压从3.3V拉到了2.6V……我居然还不知道!因为STM32可以在2.6V下正常工作,我的还是在各种加断点、各种单步跟踪,完全没有意识到模块已经因电压过低死机了。。。看了一下Sparkcore的电路图,发现它用的LDO型号是MIC5219,一个SOT23-5封装,500mA的LDO,哈哈~

另外还有一个不算是坑,或者说只是一个小坑~就是security.h文件最后少了一段代码,在用C++编译时可能导致编译出错,并且把错误报到别的文件里,让人很困惑。。。缺少的代码如下,大家一看就懂了~

1
2
3
#ifdef __cplusplus
}
#endif

如果您在CC3000软硬件方面还有什么其他的困扰,欢迎联系我,QQ:284388369

本人可以提供关于驱动移植、固件更新、硬件设计、量产优化等方面的有偿技术指导。

不调试具体网络通信代码,只负责跑通官方BasicWiFiApplication,不承接外包项目。



版权声明

The Bloom of Youth by KUANG Qi is licensed under a Creative Commons BY-NC-ND 4.0 International License.
况琪创作并维护的锦瑟华年博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证

本文首发于The Bloom of Youth | 锦瑟华年博客( http://kuangqi.me ),版权所有,侵权必究。

本文永久链接:http://kuangqi.me/embedded/cc3000-host-driver-porting-guide/