1. 程式人生 > >Marvell 88W8686/88W8782/88W8801 WiFi模組驅動程式的編寫

Marvell 88W8686/88W8782/88W8801 WiFi模組驅動程式的編寫

本文以STM32F1系列的微控制器為例,詳細講解Marvell公司的88W8686/88W8782/88W8801 WiFi模組驅動程式的編寫。編寫程式時為了程式碼簡短起見,直接用暫存器操作,不使用STM32庫函式。IDE採用Keil uVision5。為了儲存下WiFi模組龐大的韌體,以及方便lwip的移植,請儘量採用較大SRAM和Flash容量的微控制器(如High-density或XL-density系列的),這裡筆者用的是STMF103RET6(引腳數64,SRAM容量64KB,Flash容量512KB)。因為Connectivity line系列的STM32F105/107微控制器沒有SDIO介面,所以請不要使用這兩種微控制器來測試。STM32F103C8T6容量太小,雖然Flash程式儲存空間有64KB,但SRAM執行記憶體只有20KB,不方便lwip協議棧的移植,所以最好也不要使用(當然這種情況改用uip協議棧也行,uip在SRAM容量才1KB的ATMega16微控制器上都能執行)。

88W8686已經是比較老的晶片了,其資料手冊(datasheet)的釋出時間是2007年2月20日。淘寶網上可以買到晶片組(Chip Set)為88W8686的WM-G-MR-09模組,價格比較貴,85元一個。其韌體(firmware)及Fedora Linux下的驅動程式可直接在Marvell的官方網站上下載到,壓縮包名稱為SD-8686-LINUX26-SYSKT-9.70.3.p24-26409.P45-GPL.zip,裡面有兩個韌體:helper_sd.bin和sd8686.bin,最後修改日期都是2008年2月29日。

賣家世訊電子提供了STM32103RET6驅動該網絡卡的程式。但該程式可靠性很差,程式碼既亂又複雜而且很難看懂,掃描熱點時經常出現problem fetching packet from firmware, rewhile的錯誤,連線熱點時有時候會出現認證失敗的錯誤type=0x888e!,一連線失敗就直接重啟微控制器,而且與WPA2-PSK認證有關的程式碼被封裝到了wap_wpa2_lib.lib檔案中,不開放原始碼。這也是筆者寫本教程的原因:自己編寫出高可靠性的驅動程式!


較新的88W8782和88W8801模組價格更便宜,大概20~30塊錢一個,支援建立AP模式的熱點,安卓手機可以不打補丁直接連線,但很多暫存器的地址和88W8686不一樣。其韌體和Linuxl驅動程式無法在Marvell的官方網站上下載到,但可在淘寶的賣家那裡獲得,還可以獲得資料手冊PDF文件。資料手冊的釋出時間分別為2011年4月6日和2013年8月19日。壓縮包的名稱分別為SD-UAPSTA-8782-FC13-MMC-14.69.12.p35-M2614336_B0-GPL_new.zip和SD-UAPSTA-8801-FC18-MMC-14.76.36.p61-C3X14090_B0-GPL.zip。每個模組只有一個韌體,分別是sd8782_uapsta.bin和sd8801_uapsta.bin,最後修改日期分別為2012年8月16日和2015年2月25日。

 

本文主要講解88W8686模組,同時也會順帶說明如何在88W8782/8801這兩款晶片上執行所寫的程式。

這些WiFi模組都是SD I/O卡,而我們平常在手機裡面插的記憶體卡屬於SD memory卡。SDIO card和SD memory card都可以用STM32微控制器的SDIO介面來操作,但它們所支援的命令不一樣。後者支援很多命令,比如復位命令CMD0、寫資料塊命令CMD24~25、讀資料塊命令CMD17~18等等。但前者就只支援CMD0、3、5、7、15、52、53這幾個命令,並且CMD0不是復位命令,而是從SDIO模式切換到SPI模式的命令。並且,兩者的初始化時序也不一樣。

其中,Part1_Physical_Layer_Simplified_Specification_Ver6.00是SD memory卡的文件,PartE1_SDIO_Simplified_Specification_Ver3.00是SDIO卡的文件。PartE7_Wireless_LAN_Simplified_Addendum_Ver1.10雖然是介紹SDIO WiFi卡的文件,但和本文所講的WiFi模組沒有任何關係,因此不必下載。

接下來,筆者將參考Part E1文件,講解SDIO WiFi卡的初始化方法。

工程的建立:在Keil中新建一個STM32F103RE的工程,並勾選上啟動程式碼CMSIS/CORE和Device/Startup:

如果要使用STM32的庫函式,則還需要勾選Device/StdPeriph Drivers中的專案(本文不使用庫函式,因此不必勾選):

工程建好後,Keil已經幫我們自動新增好了啟動檔案startup_stm32f10x_hd.s,其中hd是指high-density。

在其中建立main.c檔案,以及存放WiFi驅動程式的檔案WiFi.h和WiFi.c。

以下是88W8686 WiFi模組與STM32微控制器的連線。

SDIO_CLK接PC12,SDIO_CMD接PD2,資料線D0~D3接PC8~11。

在筆者所用的的開發板上,VCC3V3引腳不是直接連線到電源的,而是通過一個場效電晶體接到PB12上的。當開發板外接了5V的電源插頭,並且PB12為低電平時,WiFi模組才通電工作。下載程式時,PB12輸出高阻態,此時WiFi模組斷電。每次微控制器復位時,WiFi模組也就跟著自動復位。自己焊的板子可以用一個PNP三極體來代替場效電晶體Q1。


  1. RCC->AHBENR = RCC_AHBENR_SDIOEN;  
  2. RCC->APB1ENR = RCC_APB1ENR_TIM6EN;  
  3. RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN| RCC_APB2ENR_USART1EN;  
  4. GPIOA->CRH = 0x000004b0;  
  5. GPIOB->CRH = 0x00030000; // PB12為WiFi模組電源開關, PB12=0時開啟WiFi模組
  6. GPIOC->CRH = 0x000bbbbb;  
  7. GPIOD->CRL = 0x00000b00;  
  8. USART1->BRR = 0x271;  
  9. USART1->CR1 = USART_CR1_UE | USART_CR1_TE;  
在main函式中,首先開啟SDIO、GPIOA~D、定時器TIM6和串列埠USART1的時鐘,需要先開啟時鐘才能使用這些STM32外設。這些外設分別在AHB、APB1和APB2總線上。AHB和APB2的時鐘頻率為72MHz,而APB1的頻率為36MHz。STM32外接的外部高速晶振HSE只有8MHz,這些頻率都是RCC上的PLL倍頻器產生的。
接下來配置PA~PD的I/O口。CRL負責Px0~Px7,CRH負責Px8~Px15。每個十六進位制數位配置一個埠,最低位為Px8或Px0,最高位為Px15或Px7。

對於USART1串列埠,傳送埠USART1_TX為PA9,設定為複用推輓輸出,速度為50MHz:b,接收埠USART1_RX為PA10,設定為浮空輸入:4。

PB12為外接的電源開關,最開始為高阻態,WiFi模組為斷電狀態。當設定CRH為3(推輓輸出,速度為50MHz)後,因為GPIOB->ODR為0,所以PB12輸出低電平,WiFi模組通電。

PC8~11為SDIO資料埠D0~D3,PC12為SDIO時鐘引腳,PD2為SDIO命令埠。這些都應該設定為複用推輓輸出50MHz,因此都設為b。

由STM32F103的參考手冊(Reference manual)的9.1.11 GPIO configurations for device peripherals中的表格可知,所有的SDIO引腳都應該設為複用推輓輸出。

推輓和開漏輸出的區別:推輓輸出可以輸出低電平(ODR=0)和高電平(ODR=1)。開漏輸出可以輸出低電平(ODR=0),但不能輸出高電平,當ODR=1時輸出高阻態,高阻態相當於斷開了埠與電源的連線。

GPIO_CRH/CRL配置方法(加粗的表示常用):

每1位16進位制數表示一個I/O埠。
1為10MHz推輓輸出(推輓輸出適合直接驅動)(複用為9)
2為2MHz推輓輸出(複用為a)
3為50MHz推輓輸出(複用為b)

5為10MHz開漏輸出(開漏輸出適合接三極體基極)(複用為d)
6為2MHz開漏輸出(複用為e)
7為50MHz開漏輸出(複用為f)

0為模擬輸入/輸出
4為浮空輸入
8為帶上/下拉電阻的輸入
(ODR=0為下拉,1為上拉),上拉輸入表示IDR的預設值為1,下拉輸入表示IDR的預設值為0

當使用帶上下拉電阻的輸入模式時,該埠對應的ODR位的值就表示預設的輸入電平。當該埠懸空時,IDR=ODR。否則IDR就等於輸入的電平值。

一般情況下,PNP型三極體的發射極都是接的+5V,如果基極通過電阻接到微控制器的I/O口上並配置為開漏輸出,則當ODR=0時,三極體飽和導通,發射極與基極間的電壓為0.7V,基極電阻兩端的電壓為4.3V;當ODR=1時輸出高阻態,相當於基極直接懸空,三極體截止。所以開漏輸出特別適合接三極體的基極。如果配置為推輓輸出,那麼當ODR=1時,I/O口輸出的是3.3V的高電平,5V-3.3V=1.7V高於導通電壓0.7V,因此三極體還是導通的,且基極電阻兩端的電壓為1V。

所以,如果WiFi模組的電源上接的是PNP三極體,那麼只有將PB12配置為開漏輸出(7)後,才能通過ODR暫存器控制WiFi模組電源的通斷。

接著配置串列埠USART1,BRR為波特率。USART1是在APB2總線上的外設,該匯流排的時鐘為72MHz,也就是72000000Hz。欲設定的波特率為115200,72000000 ÷ 115200 = 625 = 0x271,因此,BRR=0x271。CR1為控制暫存器,UE表示啟動該外設,TE表示允許傳送。

為了在程式中使用printf函式向串列埠輸出資訊,需要引入stdio.h標頭檔案,然後實現fputc函式。printf函式使用的是C語言標準輸出流stdout,因此fp=stdout。ch為要輸出的每個字元。若輸出的是換行符\n,為了正確換行,需要先輸出一個回車符\r組成\r\n。向USART1的DR暫存器寫入資料前,必須先等待SR暫存器中的TXE位(傳送緩衝區空)變為1。寫入資料後,串列埠外設將自動傳送資料。最後函式必須返回ch的原有內容。

  1. #include <stdio.h>
  2. int fputc(int ch, FILE *fp)  
  3. {  
  4.     if (fp == stdout)  
  5.     {  
  6.         if (ch == '\n')  
  7.         {  
  8.             while ((USART1->SR & USART_SR_TXE) == 0);  
  9.             USART1->DR = '\r';  
  10.         }  
  11.         while ((USART1->SR & USART_SR_TXE) == 0);  
  12.         USART1->DR = ch;  
  13.     }  
  14.     return ch;  
  15. }  

此外還需在專案屬性裡勾選上“Use MicroLIB”選項。

J-Link下載器配置:在Debug選項卡中選擇J-Link作為除錯工具,並在設定對話方塊裡勾選上Reset and Run複選框,以便下載完成後程式能自動開始執行。



STM32 SDIO外設的初始化

串列埠初始化完畢後便開始執行Card_Init函式:SDIO卡初始化函式。

  1. printf("Initialization begins...\n");  
  2. SDIO->POWER = SDIO_POWER_PWRCTRL;  
  3. SDIO->CLKCR = SDIO_CLKCR_CLKEN | 178; // 初始化時最高允許的頻率: 72MHz/(178+2)=400kHz
  4. delay(5); // 延時可防止CMD5重發
首先將SDIO_POWER暫存器中的PWRCTRL位置位,啟動SDIO外設。以下是STM32F1參考手冊中的SDIO_POWER暫存器的說明:

只有當該暫存器的第1~0位同時為1時才能啟動該外設。

在STM32F10x.h標頭檔案中,有如下的定義:

  1. /******************  Bit definition for SDIO_POWER register  ******************/
  2. #define  SDIO_POWER_PWRCTRL                  ((uint8_t)0x03)               /*!< PWRCTRL[1:0] bits (Power supply control bits) */
  3. #define  SDIO_POWER_PWRCTRL_0                ((uint8_t)0x01)               /*!< Bit 0 */
  4. #define  SDIO_POWER_PWRCTRL_1                ((uint8_t)0x02)               /*!< Bit 1 */
SDIO_POWER_PWRCTRL表示PWRCTRL的全部位,即第1~0位。SDIO_POWER_PWRCTRL_0表示第0位,SDIO_POWER_PWRCTRL_1表示第1位。

接著配置SDIO_CLKCR暫存器。該暫存器的CLKEN位決定是否啟用時鐘引腳SDIO_CK,即是否向PC12引腳輸出時鐘訊號。CLKDIV為時鐘分頻係數。

PC12引腳上的頻率為:SDIO外設的頻率 ÷ (CLKDIV + 2)

因為SDIO是AHB總線上的外設,所以SDIO外設的頻率等於AHB匯流排的頻率(記為HCLK),為72MHz。

程式中配置的是CLKDIV=178,因此分頻後,在PC12引腳上輸出的時鐘頻率就是400kHz。這是SD卡在初始化時所允許的最高頻率。只有當SDIO總線上掛接的所有SD卡都初始化完畢了之後,這一頻率才允許提高。

然後呼叫delay函式延時5毫秒。延時的目的是上電後使器件做好準備,降低CMD5命令重發的可能性,但這不能完全防止CMD5重發。delay函式的實現如下:

  1. // 延時n毫秒
  2. void delay(uint16_t n)  
  3. {  
  4.     TIM6->ARR = 10 * n - 1; // nms
  5.     TIM6->PSC = 7199; // 72MHz/7200=10kHz
  6.     TIM6->CR1 = TIM_CR1_URS | TIM_CR1_OPM; // UG不置位UIF, 非迴圈模式
  7.     TIM6->EGR = TIM_EGR_UG; // 儲存設定
  8.     TIM6->CR1 |= TIM_CR1_CEN; // 開始計時
  9.     while ((TIM6->SR & TIM_SR_UIF) == 0); // 等待計時完畢
  10.     TIM6->SR &= ~TIM_SR_UIF; // 清除溢位標誌
  11. }  
該函式使用了STM32中的基本定時器TIM6。ARR為計數量,這裡n=5,ARR=49,PSC為分頻係數,PSC=7199為7200分頻。這裡要注意的是,雖然TIM6是APB1總線上的外設,但提供的時鐘卻是2×36MHz=72MHz(見下圖),所以分頻後是10kHz。OPM=1表示定時器溢位後自動關閉,即只計時一次。UG=1表示使ARR和PSC中的值立即生效。URS=1表示執行UG=1時不將溢位標誌UIF置位。接著CEN置1開始計時,用while迴圈等待溢位標誌UIF置位,起到延時的目的。當CNT的值從49跳變到0的瞬間,UIF置位,CEN自動清零關閉定時器,跳出while迴圈。然後清除UIF標誌位,以便於下次延時。

CEN剛置位時,CNT=0。經過1/(10kHz)=0.1ms後,CNT從0跳變到1。經過4.9ms後,CNT從48跳變到49。經過5ms後,CNT剛好從49跳變到0。


SDIO卡的初始化流程見Part E1文件的圖3-2。首先以空引數(ARG=0)傳送一個CMD5命令,檢查有無迴應。若有迴應,則設定引數ARG後再次傳送CMD5,檢查迴應中的MP(Memory Present)位後決定之後的流程。

在STM32 SDIO外設中,使用SDIO_CMD暫存器傳送命令,使用SDIO_ARG暫存器設定命令引數。

在SDIO_CMD暫存器中,CMDINDEX決定命令號,CPSMEN=1時傳送命令(該位不會自動清零,只要寫完暫存器後該位為1,就傳送命令)。WAITRESP=00時不等待迴應,WAITRESP=01時等待48位的短迴應,WAITRESP=11時等待136位的的長迴應。迴應的內容儲存在RESP1~4暫存器中。


  1. /* 傳送CMD5: IO_SEND_OP_COND */
  2. SDIO->CMD = SDIO_CMD_CPSMEN | SDIO_CMD_WAITRESP_0 | 5;  
  3. while (SDIO->STA & SDIO_STA_CMDACT);  
  4. Card_CheckCommand(); // 為了保險起見還是要檢查一下是否要重發命令
  5. if (SDIO->STA & SDIO_STA_CMDREND)  
  6. {  
  7.     SDIO->ICR = SDIO_ICR_CMDRENDC;  
  8.     Card_ShowShortResponse();  
  9. }  
因為上電後ARG暫存器的預設值為0,所以這裡沒有寫SDIO->ARG=0;。這裡以空引數傳送CMD5,只將WAITRESP的第0位置1,等待短迴應。SDIO_STA_CMDACT=1表示命令正在傳送。由於之前延時5ms並不能100%保證命令不會出現超時,所以呼叫Card_CheckCommand函式檢查一下等待迴應是否超時。如果超時就重發命令。若收到了迴應,則SDIO_STA_CMDREND自動置1,對SDIO_ICR_CMDRENDC寫1清除該位,然後呼叫Card_ShowShortResponse函式顯示命令的迴應內容。
  1. // 檢查命令是否收到了迴應, 若沒收到則重發命令
  2. void Card_CheckCommand(void)  
  3. {  
  4.     while (SDIO->STA & SDIO_STA_CTIMEOUT)  
  5.     {  
  6.         SDIO->ICR = SDIO_ICR_CTIMEOUTC; // 清除標誌
  7.         SDIO->CMD = SDIO->CMD; // 重發
  8.         printf("Timeout! Resend CMD%d\n", SDIO->CMD & SDIO_CMD_CMDINDEX);  
  9.         while (SDIO->STA & SDIO_STA_CMDACT);  
  10. 相關推薦

    Marvell 88W8686/88W8782/88W8801 WiFi模組驅動程式編寫

    本文以STM32F1系列的微控制器為例,詳細講解Marvell公司的88W8686/88W8782/88W8801 WiFi模組驅動程式的編寫。編寫程式時為了程式碼簡短起見,直接用暫存器操作,不使用STM32庫函式。IDE採用Keil uVision5。為了儲存下

    程式】STM32F407VE微控制器驅動Marvell 88W8801 WiFi模組程式(20181010版)

    本程式所用的微控制器型號為:STM32F407VE PD14埠為復位引腳(PDN),請務必連線! 晶振用的是8MHz,請注意檢查自己的開發板,看看晶振是不是8MHz。如果是25MHz,請修改system_stm32f4xx.c檔案! 程式支援連線無密碼的熱點以及WEP、

    程式Marvell 88W8782/88W8801 WiFi模組的韌體下載程式碼

    本程式所用的微控制器型號為:STM32F103VEPB12接WiFi模組的復位引腳(RST),上電時RST=0先復位,然後RST=1開始工作。所用的WiFi模組為:88W8801。雖然綠色的板子上面寫的是88W8782,但實際上中間的黑色晶片用的是88W8801。【sd880

    程式Marvell 88W8801 WiFi模組連線路由器,並使用lwip2.0.3建立http伺服器(20180807版)

    本程式所用的微控制器型號為:STM32F103ZE 可以用STM32F103RE或STM32F103RD,但是STM32F103RC不行! PB12埠為復位引腳(PDN),請務必連線! 晶振用的是8MHz,請注意檢查自己的開發板,看看晶振是不是8MHz。如果是12MHz,請修

    用一個例項來理解驅動程式編寫流程 (自用)

    #include <linux/module.h> #include <linux/kernel.h> #include <asm/io.h> #include <linux/miscdevice.h> #include <linu

    Linux驅動程式編寫&&應用程式對她的呼叫

            Linux驅動程式的開發,我相信這是很多致力於嵌入式學習的騷年的終極夢想,不管是技術含量,還是薪金待遇,她都一一完美的體現了出來!當然,crk_13也一樣!不過,越是誘人的東西往往也越是可望而不可即,或許大家都對驅動開發的難度之大,要求之高有所耳聞!以我個人

    STM32驅動Marvell 88W8686 WiFi模組程式碼說明(20180129版)

    一、概述88W8686是Marvell公司2007年推出的一款SDIO Wi-Fi晶片,使用簡單的SPI或SDIO協議就可以與微控制器連線起來,操作方便,具有建立無密碼或帶有WEP密碼的Ad-Hoc熱點的功能,以及連線無密碼或帶有WEP、WPA/WPA2密碼的路由器的功能。不過有一點要注意,安卓手機預設是不能

    88w8686 wifi模組的linux裝置驅動的測試

    執行指令碼檔案wifisetup.sh來掃描並連線wifi熱點“cctest”,併為無線網絡卡分配IP地址192.168.43.25。如下所示。(注意,應事先把wifi_driver\FwImage目錄下的韌體程式helper_gspi.bin和gspi8686.bin放在

    linux 核心模組程式設計之LED驅動程式(六)

    我使用的是tiny6410的核心板,板子如下,淘寶可以買到 為了不與板子上的任何驅動發生IO衝突,我使用CON1那一排沒用到的IO口,引腳如下   LED1 LED2 LED3 LED4

    wifi驅動的理解(4)——usb介面在wifi模組中的角色

             轉載請註明出處:http://blog.csdn.net/Righthek 謝謝!          還有1天就到2017年了,回顧整個2016年至此,都沒發表過一篇技

    wifi驅動的理解(3)——usb介面在wifi模組中的角色

    轉載請註明出處:http://blog.csdn.net/Righthek 謝謝!                 上一篇文章已經提到USB介面在wifi模組中的最重要兩個函式是usb_

    wifi驅動的理解(2)——usb介面在wifi模組中的角色

    轉載請註明出處:http://blog.csdn.net/Righthek 謝謝!        上一篇文章我們已經通過三條線索簡單地描述了wifi驅動的框架,現在我們開始深入到每條線索中。首先我們從USB裝置這條線

    [Linux驅動煉成記] 06-博通WIFI模組AP6212配置

    Buildroot 配置 Kernel -> wifi modle -> AP6212 Kernel -> wifi modle -> wifi fw local path (wifi韌體路徑) 相關安裝包主要是: buildroot/

    13-編寫WIFI模組連線MQTT程式,和除錯助手測試通訊

      直接上程式吧 local SubscribeTopic = "wifi/user".."/"..clientid PublishTopic = "wifi/device".."/"..clientid local UsartReceiveData=""; lo

    wifi模組rtl8723b的驅動移植

    一、前言       在除錯wifi驅動的時候會遇到很多坑,相信每個除錯驅動的工程師都深有體會吧。wifi驅動涉及到linux和android兩個大層面,任意一個環節出錯都有可能導致wifi驅動不能正常工作,現在我總結一下我在移植wifi驅動的時候所遇到的坑。 1.sdio

    Tcar:智慧車之基於rtl8188eu驅動wifi模組

    2、WIFI模組 2.1 WIFI模組的驅動程式      http://www.realtek.com.tw      https://github.com/lwfinger/rtl8188eu      // wifi_hostapd_dnsmasq.rar/rtl81

    STM32 超聲波測距模組HCSR-04 驅動程式

    超聲波測距原理見:超聲波測距原理 超聲波測距模組:HC-SR04 採用定時器及外部中斷方式 /*******************************************************************************

    如何解決 Windows 7 中安裝印表機驅動程式時,出現錯誤資訊"找不到指定模組"或"XXX.dll 檔案丟失"

    文章簡介 在Windows7中安裝驅動程式時,出現錯誤資訊“找不到指定模組”或“XXX.dll 檔案丟失”,您可以參考本文的步驟嘗試解決問題 應用軟體執行時一般都是優先從自己的安裝目錄中呼叫所需的模組(.dll)檔案,如沒有所需檔案再到 Windows\System

    基於ARM的模組方式驅動程式

    基於ARM的模組方式驅動程式 作者:毛蘢瑋 / Saint 掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a 微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&a

    Linux 下wifi 驅動開發(二)—— WiFi模組淺析

    一、什麼是wifi 模組         百度百科上這樣定義:         Wi-Fi模組又名串列埠Wi-Fi模組,屬於物聯網傳輸層,功能是將串列埠或TTL電平轉為符合Wi-Fi無線網路通訊標準的嵌入式模組,內建無線網路協議IEEE802.11b.g.n協議棧以及TCP