电竞比分网-中国电竞赛事及体育赛事平台

分享

CC3000驅(qū)動(dòng)移植中的幾個(gè)陷阱

 細(xì)寒 2014-04-12

1. 文檔中的一個(gè)小錯(cuò)誤

CC3000驅(qū)動(dòng)移植中的幾個(gè)大坑 - 月下獨(dú)酌 - The Bloom of Youth
wlan_ioctl_get_scan_results 函數(shù)用于返回WLAN掃描的結(jié)果,每調(diào)用一次返回一個(gè)結(jié)果。返回的數(shù)據(jù)結(jié)構(gòu)在文檔中給出,但是這里文檔寫錯(cuò)了。。。Result entry前的56bytes應(yīng)該是42bytes,而每次的結(jié)果總有有4+4+42=50(bytes)。把下面的結(jié)果加起來(lái),也會(huì)發(fā)現(xiàn)各項(xiàng)的和是42 而不是56. 這本來(lái)只是手冊(cè)上的一個(gè)筆誤,但在編程時(shí),大家通常會(huì)使用一個(gè)結(jié)構(gòu)體來(lái)接收返回的數(shù)據(jù),如果這個(gè)字節(jié)數(shù)不對(duì)的話,就會(huì)影響內(nèi)存對(duì)齊,從而導(dǎo)致返回的結(jié)果錯(cuò)誤。這里,我使用的結(jié)構(gòu)體定義如下:
  1. typedef struct _wlan_full_scan_results_args_t 
  2.     /* 
  3.         4 Bytes: number of networks found 
  4.         4 Bytes: The status of the scan: 0 - agged results, 1 - results valid, 2 - no results 
  5.         { 
  6.             1 bit isValid - is result valid or not 
  7.             7 bits rssi - RSSI value; 
  8.         } 
  9.         { 
  10.             2 bits: securityMode - security mode of the AP: 0 - Open, 1 - WEP, 2 WPA, 3 WPA2 
  11.             6 bits: SSID name length 
  12.         } 
  13.         2 bytes: the time at which the entry has entered into scans result table 
  14.         32 bytes: SSID name 
  15.         6 bytes: BSSID 
  16.     */ 
  17.     uint32_t ap_count; 
  18.     uint32_t ap_state; 
  19.     uint32_t ap_vaild    : 1; 
  20.     uint32_t ap_rssi     : 7; 
  21.     uint32_t ap_security : 2; 
  22.     uint32_t ap_ssidlen  : 6; 
  23.     uint16_t ap_time; 
  24.     char     ap_ssid[32]; 
  25.     uint8_t  ap_bssid[6]; 
  26.     uint8_t  reserved[2]; //for memory align 
  27. } wlan_scan_result; 
另外,這個(gè)函數(shù)讀到最后一條結(jié)果時(shí),再讀會(huì)返回一個(gè)長(zhǎng)度為0的結(jié)果,即前四個(gè)字節(jié)(number of networks found)為零,然后再讀才會(huì)讀出下一次掃描的結(jié)果。這一點(diǎn)手冊(cè)上并沒(méi)有指出,是我們?cè)诰幊虒?shí)踐中自己發(fā)現(xiàn)的。

 

2. SpiPause - SpiResume與WlanInterruptDisable - WlanInterruptEnable的實(shí)現(xiàn)
如 果前面一條是一個(gè)小坑的話,這一條絕對(duì)是一個(gè)大坑。目前為止,我見過(guò)的CC3000的驅(qū)動(dòng)實(shí)現(xiàn)中,不論是開源的、產(chǎn)品正在開發(fā)而尚未開源的實(shí)現(xiàn),除了官方 的例程以外,還沒(méi)有人將這兩組函數(shù)寫對(duì)。甚至有人能通過(guò)降低SPI速率、修改IO中斷方式等等辦法,使得整個(gè)程序得以正常運(yùn)行,將錯(cuò)誤帶到了產(chǎn)品中。只要 有人說(shuō):“我的CC3000驅(qū)動(dòng)有點(diǎn)問(wèn)題,調(diào)了很久都不行。。?!敝惖脑?,不用等他說(shuō)完,就可以猜想他是把這兩組函數(shù)寫錯(cuò)了。
這兩組函數(shù)非常容易錯(cuò),錯(cuò)了又非常難以發(fā)現(xiàn)。這兩組函數(shù)的實(shí)現(xiàn)已經(jīng)成為了CC3000驅(qū)動(dòng)移植的主要障礙。其主要原因如下:
(1)是官方的Porting Guide沒(méi)有明確指出這兩組函數(shù)的區(qū)別。很多人則想當(dāng)然地認(rèn)為這兩組函數(shù)是一樣的。甚至是一組中調(diào)用另一組。再看官方的實(shí)現(xiàn),又有點(diǎn)不知所云,所以就忽略了這個(gè)問(wèn)題。
(2) 忽略這個(gè)問(wèn)題后,CC3000模塊可以正常初始化,還可以正常掃描WiFi熱點(diǎn),有人還能正常進(jìn)行Smart config,甚至有人還能正常打開socket并使用UDP協(xié)議發(fā)送數(shù)據(jù)!開發(fā)者根據(jù)CC3000驅(qū)動(dòng)自下而上分層的架構(gòu)來(lái)看,看到SPI已經(jīng)可以進(jìn)行 正常通信,從而直接在心理上排除了SPI驅(qū)動(dòng)實(shí)現(xiàn)有誤的可能性,錯(cuò)誤查來(lái)查去還是在原地兜圈子。
(3)由于這兩組函數(shù)實(shí)現(xiàn)有誤導(dǎo)致的錯(cuò)誤幾乎成了一個(gè)人品問(wèn)題,在不同速度MCU上的錯(cuò)誤實(shí)現(xiàn)會(huì)導(dǎo)致不同的錯(cuò)誤現(xiàn)象,以至于大家的描述不統(tǒng)一,很難從現(xiàn)象判斷到底是哪兒錯(cuò)了,即使在網(wǎng)上根據(jù)錯(cuò)誤現(xiàn)象搜索或者發(fā)帖求助,也很難得到有針對(duì)性的答案。
為什么這兩組函數(shù)寫錯(cuò)了,會(huì)有這么神奇的問(wèn)題呢?答案就是,這里的錯(cuò)誤會(huì)導(dǎo)致程序的“競(jìng)態(tài)問(wèn)題”,類似于數(shù)字電路基礎(chǔ)中的“競(jìng)爭(zhēng)冒險(xiǎn)”。SPI速率、AP的品質(zhì)和信號(hào)強(qiáng)度、IO口翻轉(zhuǎn)速度、延時(shí)的誤差等無(wú)關(guān)緊要的問(wèn)題,都可能讓競(jìng)態(tài)問(wèn)題出現(xiàn)不同的現(xiàn)象。因此,出現(xiàn)的錯(cuò)誤現(xiàn)象千奇百怪,貌似跟人品有關(guān)就不難解釋了。說(shuō)了這么多,下面就來(lái)解釋一下這兩組函數(shù)到底有什么區(qū)別:
WlanInterruptDisable的作用是關(guān)閉IO口的外部中斷,也就是IRQ的下降沿中斷。關(guān)閉以后,IRQ下降沿的中斷將被丟棄,在調(diào)用WlanInterruptEnable時(shí),會(huì)重新使能中斷,之后發(fā)生在該IO上的外部中斷又再次可以響應(yīng)。而中斷被禁用期間,發(fā)生的中斷都被丟棄,永遠(yuǎn)不再響應(yīng)。這個(gè)函數(shù)一般人都寫對(duì)了。
SpiPauseSpi函數(shù)是暫時(shí)掛起IO口外部中斷,也就是IRQ下降沿中斷,如果在SpiPauseSpi調(diào)用之后,IRQ線上再有下降沿,該中斷將一直保持Pending狀態(tài),暫時(shí)不調(diào)用中斷服務(wù)程序,該中斷必須在調(diào)用SpiResumeSpi被重新響應(yīng),而不能被丟棄。這里,很多很多很多人,都,寫錯(cuò)了。。。
       這種中斷的響應(yīng)方式比較特殊,像STM32就沒(méi)有在標(biāo)準(zhǔn)驅(qū)動(dòng)庫(kù)中實(shí)現(xiàn)相應(yīng)的API,而必須通過(guò)操作寄存器的方式來(lái)手工實(shí)現(xiàn)。相應(yīng)寄存器的定義,既不在芯 片Datasheet里,也不在那份大家熟知的STM32F1系列Reference Manual(RM0008)里,而是在很多STM32開發(fā)者都沒(méi)有聽說(shuō)過(guò)的一份文檔PM0056——STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual里。有很多MCU,甚至包括TI的MSP430,在硬件上根本就不支持這種響應(yīng)方式,因此只能用一些猥瑣的方法來(lái)實(shí)現(xiàn)。MSP430該函數(shù)的代碼居然是向一個(gè)配置為輸入的IO口寫數(shù)據(jù),足以讓不熟悉MSP430的開發(fā)者撓頭了。
STM32F103上的實(shí)現(xiàn)如下
  1. void SpiPauseSpi(void) 
  2. //    if(IRQ_INT_CHANNEL >= 0 && IRQ_INT_CHANNEL <= 31) 
  3. //    { 
  4. //        //NVIC->ICER[0] = (1 << IRQ_INT_CHANNEL); 
  5. //    } 
  6. //    else if(IRQ_INT_CHANNEL >= 32 && IRQ_INT_CHANNEL <= 63) 
  7. //    { 
  8.     NVIC->ICER[1] = (1 << (IRQ_INT_CHANNEL - 32)); 
  9. //    } 
  10. //    else if(IRQ_INT_CHANNEL >= 64 && IRQ_INT_CHANNEL <= 67) 
  11. //    { 
  12. //        //NVIC->ICER[2] = (1 << (IRQ_INT_CHANNEL - 64)); 
  13. //    } 
  14. //    else 
  15. //    { 
  16. //        while(1); //Should not be here. Macro IRQ_INT_CHANNEL might be invalid. 
  17. //    } 
  18.  
  19. void SpiResumeSpi(void) 
  20. //    if(IRQ_INT_CHANNEL >= 0 && IRQ_INT_CHANNEL <= 31) 
  21. //    { 
  22. //        //NVIC->ISER[0] = (1 << IRQ_INT_CHANNEL); 
  23. //    } 
  24. //    else if(IRQ_INT_CHANNEL >= 32 && IRQ_INT_CHANNEL <= 63) 
  25. //    { 
  26.     NVIC->ISER[1] = (1 << (IRQ_INT_CHANNEL - 32)); 
  27. //    } 
  28. //    else if(IRQ_INT_CHANNEL >= 64 && IRQ_INT_CHANNEL <= 67) 
  29. //    { 
  30. //        //NVIC->ISER[2] = (1 << (IRQ_INT_CHANNEL - 64)); 
  31. //    } 
  32. //    else 
  33. //    { 
  34. //        while(1); //Should not be here. Macro IRQ_INT_CHANNEL might be invalid. 
  35. //    } 
另外一組的實(shí)現(xiàn)是
  1. void WlanInterruptEnable() 
  2.     EXTI_ClearITPendingBit(IRQ_INT_LINE); 
  3.     EXTI_InitTypeDef exti; 
  4.     exti.EXTI_Line = IRQ_INT_LINE; 
  5.     exti.EXTI_Mode = EXTI_Mode_Interrupt; 
  6.     exti.EXTI_Trigger = EXTI_Trigger_Falling; 
  7.     exti.EXTI_LineCmd = ENABLE; 
  8.     EXTI_Init(&exti); 
  9.  
  10. void WlanInterruptDisable() 
  11.     EXTI_ClearITPendingBit(IRQ_INT_LINE); 
  12.     EXTI_InitTypeDef exti; 
  13.     exti.EXTI_Line = IRQ_INT_LINE; 
  14.     exti.EXTI_Mode = EXTI_Mode_Interrupt; 
  15.     exti.EXTI_Trigger = EXTI_Trigger_Falling; 
  16.     exti.EXTI_LineCmd = DISABLE; 
  17.     EXTI_Init(&exti); 
3. TI提供的CC3000 Host Driver假設(shè)char類型是無(wú)符號(hào)的
這個(gè)問(wèn)題可能很多人都不會(huì)遇到,因?yàn)樵?a href="http://www./a/index_em.html" target="_blank">嵌入式開發(fā)中char通常都是無(wú)符號(hào)的,基本所有的用于嵌入式平臺(tái)的編譯器默認(rèn)情況下都會(huì)這樣設(shè)定。而我則比較習(xí)慣PC上有符號(hào)的char,所以我手賤給GCC加了一個(gè)編譯參數(shù)-fsigned-char......
TI 的Host Driver就比較扯淡,它假設(shè)char是無(wú)符號(hào)的,并且在char有符號(hào)時(shí)就會(huì)因符號(hào)位在強(qiáng)制轉(zhuǎn)換中的行為不同而出錯(cuò)。這個(gè)問(wèn)題不容易排查,因?yàn)樾枰?蹤到TI實(shí)現(xiàn)的Host Driver里,而大家通常都會(huì)假設(shè)這個(gè)實(shí)現(xiàn)是沒(méi)錯(cuò)的,能跟進(jìn)去就是一種勇氣了……我當(dāng)時(shí)遇到這個(gè)問(wèn)題時(shí),不知道是哪兒的錯(cuò)誤,就在很多回調(diào)函數(shù)中添加了 串口打印的語(yǔ)句,通過(guò)串口輸出,觀察各個(gè)函數(shù)被調(diào)用的情況,結(jié)合現(xiàn)象,再加以大膽的猜想,才解決了這個(gè)問(wèn)題。
4. 供電
CC3000驅(qū)動(dòng)移植中的幾個(gè)大坑 - 月下獨(dú)酌 - The Bloom of Youth
這 個(gè)是一個(gè)硬件問(wèn)題,一個(gè)不應(yīng)該犯的錯(cuò)誤。目前大家用的最多的CC3000模塊還是Jorjin的WG1300,而Jorjin的文檔中并未給出該模塊工作 時(shí)所需的電流。在一些追求體積最小化的應(yīng)用中,大家也普遍使用了SOT23-5封裝的小型LDO,這類LDO的電流通常只有100mA左右。一個(gè)單片機(jī)才 幾十毫安的電流,一個(gè)WiFi又能大到哪里去呢?
看 看德州儀器官方WiFi模塊(TI的WiFi模塊封裝尺寸和引腳定義與WG1300完全相同,本人咨詢Jorjin總代理后得知TI的官方模塊正是由 Jorjin設(shè)計(jì)并制造的,與WG1300相比在硬件上也是完全一樣的,只是外殼上的字不同而已) 的文檔后,不禁大吃一驚!發(fā)送電流峰值275mA,接收峰值103mA,合起來(lái)有接近400mA的峰值電流!正是因?yàn)槿绱?,?20mA的LDO被瞬間秒 殺,電壓從3.3V拉到了2.6V……我居然還不知道!因?yàn)镾TM32可以在2.6V下正常工作,我的還是在各種加斷點(diǎn)、各種單步跟蹤,完全沒(méi)有意識(shí)到模 塊已經(jīng)因電壓過(guò)低死機(jī)了。。??戳艘幌耂parkcore的電路圖,發(fā)現(xiàn)它用的LDO型號(hào)是MIC5219,一個(gè)SOT23-5封裝,500mA的 LDO,哈哈~
另外還有一個(gè)不算是坑,或者說(shuō)只是一個(gè)小坑~就是security.h文件最后少了一段代碼,在用C++編譯時(shí)可能導(dǎo)致編譯出錯(cuò),并且把錯(cuò)誤報(bào)到別的文件里,讓人很困惑。。。缺少的代碼如下,大家一看就懂了~
#ifdef __cplusplus
}
#endif

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多