ST67W611M1 T01 實作

開發環境與程式分工

元件設定
Host MCUSTM32H7R7
Wi-Fi ModuleST67W611M1
網路架構T01(LwIP / TLS in module)
MiddlewareX-CUBE-ST67W61 v1.3.0
RTOSFreeRTOS
Host – NCP 介面SPI,搭配必要控制 / 中斷 GPIO
Wi-Fi 帳密範例由 app_wifi_config.h 提供 WIFI_SSID、WIFI_PASSWORD
Appli/
├── Appli/App/
│ ├── main_app.c // Module Init、Scan、整體測試順序
│ └── wifi_connection.c // 指定 SSID / BSSID,連線並確認 IP
├── Core/Inc/
│ └── app_wifi_config.h // 測試設定(不公開原始帳密)
└── ST67W6X/Target/
└── spi_port.c // Host 與 Wi-Fi 模組的 SPI/IRQ 板級介面

Note:範例程式使用 LoggingInit()、LogInfo() 等專案 logging wrapper;複製到官方範例時,要將 logger 替換成自己的 UART logger 或原專案 logging

T01 模組初始化流程

FreeRTOS Task 啟動
↓
註冊 Wi-Fi / Network / Error callback
↓
確認 NCP Reset / CHIP_EN / SPI / IRQ 接線與啟動時序
↓
W6X_Init()
↓
W6X_SetPowerMode(0) [教學先關閉低功耗]
↓
W6X_WiFi_Init()
↓
W6X_Net_Init() [T01 使用]
↓
掃描並連線

初始化關鍵段落

#include "w6x_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "logging.h"

static void WifiEvent(W6X_event_id_t event, void *args)
{
    if (event == W6X_WIFI_EVT_DISCONNECTED_ID)
        LogWarn("[WIFI] disconnected\n");
    if (event == W6X_WIFI_EVT_REASON_ID && args)
        LogWarn("[WIFI] reason=%s\n", W6X_WiFi_ReasonToStr(args));
}

static void NetEvent(W6X_event_id_t event, void *args)
{
    (void)event;
    (void)args;
}

static void DriverError(W6X_Status_t status, const char *function)
{
    LogError("[NCP] %s status=%ld\n", function, (long)status);
}

static int T01_ModuleInit(void)
{
    W6X_App_Cb_t cb = {0};
    cb.APP_wifi_cb  = WifiEvent;
    cb.APP_net_cb   = NetEvent;
    cb.APP_error_cb = DriverError;

    if (W6X_RegisterAppCb(&cb) != W6X_STATUS_OK) return -1;

    /* 板級 NCP reset / SPI / IRQ 必須已完成初始化。 */
    if (W6X_Init() != W6X_STATUS_OK) return -1;
    if (W6X_SetPowerMode(0U) != W6X_STATUS_OK) return -1;
    if (W6X_WiFi_Init() != W6X_STATUS_OK) return -1;
    if (W6X_Net_Init() != W6X_STATUS_OK) return -1;

    LogInfo("[WIFI] T01 module init completed\n");
    return 0;
}

Note:T01 專案必須配合 mission_t01 firmware,否則 W6X_Init() 會檢查到架構不相符

掃描附近 Wi-Fi

使用 W6X_WiFi_Scan();掃描結果由 callback 非同步送回,所以不能呼叫完成後就假設列表立即可用

static volatile uint32_t scan_done;
static volatile int32_t scan_status;
static W6X_WiFi_Ap_t scan_aps[10];
static uint32_t scan_count;
static void CopyScanSsid(char *ssid, const W6X_WiFi_Ap_t *ap)
{
  memcpy(ssid, ap->SSID, sizeof(ap->SSID));
  ssid[sizeof(ap->SSID)-1U] = '\0';
  for (char *p = ssid; *p; ++p) if ((unsigned char)*p < 0x20U || *p == 0x7f) *p = '?';
}
static void ScanResult(int32_t status, W6X_WiFi_Scan_Result_t *result)
{
  /* The driver owns result/AP. Copy before returning; print in the app task. */
  scan_count = 0U;
  scan_status = status;
  if (status == 0 && result && result->AP) {
    scan_count = result->Count > 10U ? 10U : result->Count;
    memcpy(scan_aps, result->AP, scan_count * sizeof(scan_aps[0]));
  }
  __DMB();
  scan_done = 1U;
}
static int ScanOnce(void)
{
  W6X_WiFi_Scan_Opts_t options = {0};
  options.MaxCnt = 10U;
  scan_done = 0U;
  scan_status = -1;
  LogInfo("[STEP1] scan start (max 10)\n");
  if (W6X_WiFi_Scan(&options, ScanResult) != W6X_STATUS_OK) return 0;
  uint32_t start = HAL_GetTick();
  while (!scan_done) {
    if ((uint32_t)(HAL_GetTick() - start) >= 60000U) return 0;
    vTaskDelay(pdMS_TO_TICKS(20U));
  }
  __DMB();
  if (scan_status != 0) return 0;
  LogInfo("\n========== Wi-Fi SSID list (%lu found) ==========\n", (unsigned long)scan_count);
  for (uint32_t i = 0; i < scan_count; ++i) {
    char ssid[sizeof(scan_aps[i].SSID)];
    CopyScanSsid(ssid, &scan_aps[i]);
    LogInfo("%lu. %s\n", (unsigned long)(i+1U), ssid[0] ? ssid : "<hidden SSID>");
  }
  if (scan_count == 0U) LogInfo("No Wi-Fi networks found.\n");
  LogInfo("========== End Wi-Fi SSID list ==========\n\n");
  for (uint32_t i = 0; i < scan_count; ++i) {
    const W6X_WiFi_Ap_t *ap = &scan_aps[i];
    char ssid[sizeof(ap->SSID)];
    CopyScanSsid(ssid, ap);
    LogInfo("[AP %lu] SSID=%s RSSI=%d ch=%u sec=%u BSSID=%02X:%02X:%02X:%02X:%02X:%02X\n",
      (unsigned long)(i+1U), ssid, ap->RSSI, ap->Channel, ap->Security,
      ap->MAC[0],ap->MAC[1],ap->MAC[2],ap->MAC[3],ap->MAC[4],ap->MAC[5]);
  }
  LogInfo("[STEP1] scan done count=%lu\n", (unsigned long)scan_count);
  return 1;
}

Note: MaxCnt = 10 是這份教學的應用掃描數量設定,不是模組硬體最多只能掃描 10 個 AP

 Wi-Fi SSID 連線

掃描後,先比對 SSID;若同名 AP 多台,範例會選擇掃描結果中 RSSI 較高、MAC 有效的 BSSID,再把 MAC 寫進 W6X_WiFi_Connect_Opts_t。掃描結果找不到目標時,則保留只用 SSID 嘗試連線的備援路徑
wifi_connection.c,其中 WIFI_SSID / WIFI_PASSWORD 應由專案自己的設定檔提供

static const W6X_WiFi_Ap_t *FindTarget(const W6X_WiFi_Ap_t *aps, uint32_t count)
{
  const W6X_WiFi_Ap_t *best = NULL;
  if (!aps) return NULL;
  for (uint32_t i = 0U; i < count; ++i) {
    const W6X_WiFi_Ap_t *ap = &aps[i];
    uint8_t any = 0U;
    for (unsigned j = 0U; j < sizeof(ap->MAC); ++j) any |= ap->MAC[j];
    if (!any || (ap->MAC[0] & 1U) ||
        strncmp((const char *)ap->SSID, WIFI_SSID, sizeof(ap->SSID))) continue;
    if (!best || ap->RSSI > best->RSSI) best = ap;
  }
  return best;
}
bool APP_HotspotIsReady(void)
{
  W6X_WiFi_StaStateType_e state;
  W6X_WiFi_Connect_t info = {0};
  uint8_t ip[4], gw[4], mask[4];
  return W6X_WiFi_Station_GetState(&state, &info) == W6X_STATUS_OK &&
    state == W6X_WIFI_STATE_STA_GOT_IP && !strncmp((const char *)info.SSID, WIFI_SSID, sizeof(info.SSID)) &&
    W6X_Net_Station_GetIPAddress(ip, gw, mask) == W6X_STATUS_OK &&
    ip[0] != 0U && ip[0] != 127U && ip[0] < 224U;
}
bool APP_ConnectHotspotOnce(const W6X_WiFi_Ap_t *aps, uint32_t count)
{
  W6X_WiFi_Connect_Opts_t options = {0};
  _Static_assert(sizeof(WIFI_SSID) <= sizeof(options.SSID), "Fixed SSID exceeds SDK capacity");
  _Static_assert(sizeof(WIFI_PASSWORD) <= sizeof(options.Password), "Fixed password exceeds SDK capacity");
  strncpy((char *)options.SSID, WIFI_SSID, sizeof(options.SSID)-1U);
  strncpy((char *)options.Password, WIFI_PASSWORD, sizeof(options.Password)-1U);
  options.Reconnection_interval = 0U;
  /* Zero means unlimited attempts in this SDK. Match BWIFI's bounded setting. */
  options.Reconnection_nb_attempts = 1U;
  const W6X_WiFi_Ap_t *target = FindTarget(aps, count);
  LogInfo("[STEP2] connecting SSID=%s\n", WIFI_SSID);
  if (target) {
    memcpy(options.MAC, target->MAC, sizeof(options.MAC));
    LogInfo("[STEP2] scanned target BSSID=%02X:%02X:%02X:%02X:%02X:%02X ch=%u RSSI=%d\n",
      target->MAC[0], target->MAC[1], target->MAC[2], target->MAC[3], target->MAC[4], target->MAC[5],
      target->Channel, target->RSSI);
  } else {
    LogInfo("[STEP2] target not in retained scan results; SSID-only discovery\n");
  }
  W6X_Status_t status = W6X_WiFi_Connect(&options);
  volatile uint8_t *wipe = (volatile uint8_t *)&options;
  for (unsigned i=0; i<sizeof(options); ++i) wipe[i]=0;
  if (status != W6X_STATUS_OK) {
    LogError("[STEP2] connect command failed status=%ld\n", (long)status);
    return false;
  }
  uint32_t start = HAL_GetTick();
  while ((uint32_t)(HAL_GetTick()-start) < APP_WIFI_CONNECT_TIMEOUT_MS) {
    if (APP_HotspotIsReady()) {
      uint8_t ip[4], gw[4], mask[4];
      if (W6X_Net_Station_GetIPAddress(ip, gw, mask) != W6X_STATUS_OK) return false;
      LogInfo("[STEP2] connected IP=%u.%u.%u.%u\n", ip[0],ip[1],ip[2],ip[3]);
      return true;
    }
    vTaskDelay(pdMS_TO_TICKS(250U));
  }
  LogError("[STEP2] no matching SSID/GOT_IP within %lu ms\n", (unsigned long)APP_WIFI_CONNECT_TIMEOUT_MS);
  return false;
}

判斷真的連上網路

收到 AP 連線成功事件不等於能上傳到網際網路。 至少需要確認三件事:

  1. Station state 是 W6X_WIFI_STATE_STA_GOT_IP。
  2. 連上的 SSID 符合預期;若需強制指定基地台,另外檢查 BSSID。
  3. W6X_Net_Station_GetIPAddress() 能讀出有效的 IP;再用 DNS / socket 連線來確認 Internet 可用

Refer

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart