tag 标签: cc3000

相关博文
  • 热度 13
    2014-9-15 16:44
    1808 次阅读|
    1 个评论
    硬件平台: STM32F103+CC3000 软件平台: MDK Keil 4.22 现象描述:参考国产某开发板,将其提供的代码移植到自家平台上。开始以为只是引脚定义不同,对比电路图稍做修改后。最后程序似乎跑起来了,比如灯闪了。连续跑了几次,偶尔会能够 Smart Config ,感觉很奇怪;即使能 Config 成功,通过 PC 工具却收不到板子发出来的数据。 通过网络,终于看到了这样一篇博文《 CC3000 驱动移植中的几个陷阱》,我已经转到 EDNChina , 详见 . 其中的第二部分,那几个函数是关键。 SpiPauseSpi 函数是暂时挂起 IO 口外部中断,也就是 IRQ 下降沿中断,如果在 SpiPauseSpi 调用之后, IRQ 线上再有下降沿,该中断将一直 保持 Pending 状态,暂时不调用中断服务程序,该中断必须在调用 SpiResumeSpi 被重新响应,而不能被丢弃。 下面是 TI 提供的 MSP430+CC3000 例程中,对这部分功能的实现。乍一看去,感觉没什么,但仔细一看,不知所去。而且注释部分,也没什么有价值的信息。 //***************************************************************************** //! SpiPauseSpi //! //! @param none //! //! @return none //! //! @brief Spi pause operation //***************************************************************************** void SpiPauseSpi(void) { SPI_IRQ_PORT &= ~SPI_IRQ_PIN; } //***************************************************************************** //! SpiResumeSpi //! //! @param none //! //! @return none //! //! @brief Spi resume operation //***************************************************************************** void SpiResumeSpi(void) { SPI_IRQ_PORT |= SPI_IRQ_PIN; } 这两个函数实现的功能,比较另类,是让中断处于和跳出 Pending 状态。说白了, SpiPauseSpi() 实现的是中断来了,挂在那里,不去理会,不去调用中断服务子程序;等调用 SpiResumeSpi() 函数了,再去处理,注意,中断并没有被抛弃。正如博文《 CC3000 驱动移植中的几个陷阱》所言, STM32 的标准库里面并没有该中断功能的实现,所以参考文档 PM0056——STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual (该文档也附在后面),重新编写了函数,如下所示。 void SpiPauseSpi(void) { ISPR = (1 << (IRQ_INT_CHANNEL)); } void SpiResumeSpi(void) { ICPR = (1 << (IRQ_INT_CHANNEL)); } 这里 NVIC 为嵌套向量中断控制器( Nested Vectored Interrupt Controller )寄存器组的结构体。 struct { __IO uint32_t ISER ; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register*/ uint32_t RESERVED0 ;__IO uint32_t ICER ; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register*/ uint32_t RSERVED1 ;__IO uint32_t ISPR ; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register*/ uint32_t RESERVED2 ;__IO uint32_t ICPR ; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register*/ uint32_t RESERVED3 ;__IO uint32_t IABR ; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ uint32_t RESERVED4 ;__IO uint8_t IP ; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5 ;__O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register*/ }; 用到的是 ISPR 和 ICPR ,分别是 Interrupt Set Pending Register 和 Interrupt Clear Pending Register ,从名称就可以看出这两个寄存器组的功能,它们分别有三组寄存器 ISPR ~ 和 ICPR ~ . 在我们的产品中, SPI 的中断是通过外部中断 CC3000 的 SPI_IRQ 接在 STM32 的 GPIO PB1 上实现的。 PB1 对应的外部中断 1 ,在 STM3210X.h 中, EXTI1_IRQn = 7 ,即前面程序中 IRQ_INT_CHANNEL 为 7 ( EXTI1_IRQn ),注意, EXTI1_IRQn 是中 ISPR 和 ICPR 中的,如果用了比如用到 EXTI15_10_IRQn ,那应该是在 ISPR 和 ICPR 中了。 这两个函数应该是 CC3000 移植过程中的大坑,坑填好了,目前程序运转正常。 参考文献: 1. http://bbs.ednchina.com/BLOG_ARTICLE_3024084.HTM 2. PM0056——STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual (见附件)
  • 热度 2
    2014-9-15 14:10
    1398 次阅读|
    0 个评论
    1. 文档中的一个小错误 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 ; uint8_t ap_bssid ; uint8_t reserved ; //for memory align } wlan_scan_result; 另外,这个函数读到最后一条结果时,再读会返回一个长度为0的结果,即前四个字节(number of networks found)为零,然后再读才会读出下一次扫描的结果。这一点手册上并没有指出,是我们在编程实践中自己发现的。 2014年3月19日注:目前最新版的CC3000官方API文档中这个错误已经得到了修正。 2. 两组中断控制函数的实现 如果前面一条是一个小坑的话,这一条绝对是一个大坑。 SpiPause  -  SpiResume 与 WlanInterruptDisable  - WlanInterruptEnable 这两组中断控制函数是CC3000整个驱动移植工作中最tricky的部分。目前为止,我见过的CC3000的驱动实现中,不论是开源的、产品正在开发而尚未开源的实现,除了官方的例程以外,还没有人将这两组函数写对。甚至有人能通过降低SPI速率、修改IO中断方式等等办法,使得整个程序得以正常运行,将错误带到了产品中。只要有人说:“我的CC3000驱动有点问题,调了很久都不行。。。”之类的话,不用等他说完,就可以猜想他是把这两组函数写错了。 这两组函数非常容易错,错了又非常难以发现。这两组函数的实现已经成为了CC3000驱动移植的主要障碍。其主要原因如下: a. 是官方的Porting Guide没有明确指出这两组函数的区别。很多人则想当然地认为这两组函数是一样的。甚至是一组中调用另一组。再看官方的实现,又有点不知所云,所以就忽略了这个问题。 b. 忽略这个问题后,CC3000模块可以正常初始化,还可以正常扫描WiFi热点,有人还能正常进行Smart config,甚至有人还能正常打开socket并使用UDP协议发送数据!开发者根据CC3000驱动自下而上分层的架构来看,看到SPI已经可以进行正常通信,从而直接在心理上排除了SPI驱动实现有误的可能性,错误查来查去还是在原地兜圈子。 c. 由于这两组函数实现有误导致的错误几乎成了一个人品问题,在不同速度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的开发者挠头了。 3. TI提供的CC3000 Host Driver假设char类型是无符号的 这个问题可能很多人都不会遇到,因为在嵌入式开发中char通常都是无符号的,基本所有的用于嵌入式平台的编译器默认情况下都会这样设定。而我则比较习惯PC上有符号的 char ,所以我手贱给GCC加了一个编译参数 -fsigned-char …… TI的Host Driver就比较扯淡,它假设 char 是无符号的,并且在 char 有符号时就会因符号位在强制转换中的行为不同而出错。这个问题不容易排查,因为需要跟踪到TI实现的Host Driver里,而大家通常都会假设这个实现是没错的,能跟进去就是一种勇气了……我当时遇到这个问题时,不知道是哪儿的错误,就在很多回调函数中添加了串口打印的语句,通过串口输出,观察各个函数被调用的情况,结合现象,再加以大胆的猜想,才解决了这个问题。 4. 供电 这个是一个硬件问题,一个不应该犯的错误。目前大家用的最多的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   原文地址:http://kuangqi.me/embedded/cc3000-host-driver-porting-guide/
相关资源