STM32F1單片機通過SPI方式驅動88W8801的代碼

工程下載地址:https://pan.baidu.com/s/1myogwFS687nCnsPEzU9ySQ

代碼:

/* 定義與單片機寄存器操作和模塊接口相關的函數, 方便在不同平臺間移植 */
// 單片機: STM32F103VE, 模塊接口: SPI (GPIO模擬)

#include <stdio.h>
#include <stdlib.h>
#include <stm32f1xx.h>
#include "common.h"
#include "WiFi.h"

#if WIFI_USEDMA
#error "DMA is not supported in this version!"
#endif

#define PDN_0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET)
#define PDN_1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET)
#define CS_0 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET)
#define CS_1 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET)
#define SCK_0 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET)
#define SCK_1 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET)
#define MISO (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_8) == GPIO_PIN_SET)
#define MOSI_0 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET)
#define MOSI_1 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET)
#define INT (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) == GPIO_PIN_SET)

#define CMD52_WRITE _BV(31)
#define CMD52_READAFTERWRITE _BV(27)
#define CMD53_WRITE _BV(31)
#define CMD53_BLOCKMODE _BV(27)
#define CMD53_INCREMENTING _BV(26)

#define POLYNOMIAL_CRC7 0x89ul
#define POLYNOMIAL_CRC16 0x11021ul

static uint8_t WiFi_LowLevel_CalcCRC7(const void *data, int len);
static uint16_t WiFi_LowLevel_CalcCRC16(const void *data, int len);
static void WiFi_LowLevel_ChangeToSPI(void);
static int WiFi_LowLevel_CheckError(uint8_t cmd, int ret, uint8_t resp, const char *msg_title);
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags);
static void WiFi_LowLevel_GPIOInit(void);
static void WiFi_LowLevel_Receive(void *data, int len);
static int WiFi_LowLevel_ReceiveResponse(uint8_t *resp, uint8_t resp_len);
static void WiFi_LowLevel_SDIOInit(void);
static void WiFi_LowLevel_Send(const void *data, int len);
static uint8_t WiFi_LowLevel_SendByte(uint8_t data);
static int WiFi_LowLevel_SendCommand(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len);
static void WiFi_LowLevel_SendCommandEx(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len, const char *msg_title);
static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags, uint8_t *resp, uint8_t resp_len, const char *msg_title);
static int WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags, uint8_t *resp, uint8_t resp_len);
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void);
#endif

CRC_HandleTypeDef hcrc;
static uint8_t sdio_func_num = 0; // 功能區總數 (0號功能區除外)
static uint16_t sdio_block_size[2]; // 各功能區的塊大小, 保存在此變量中避免每次都去發送CMD52命令讀SDIO寄存器

/* 計算CRC7校驗碼 */
static uint8_t WiFi_LowLevel_CalcCRC7(const void *data, int len)
{
  const uint8_t *p = data;
  int i, j;
  uint16_t temp = 0;

  if (len != 0)
    temp = p[0] << 8;

  for (i = 1; i <= len; i++)
  {
    if (i != len)
      temp |= p[i];
    for (j = 0; j < 8; j++)
    {
      if (temp & 0x8000)
        temp ^= POLYNOMIAL_CRC7 << 8;
      temp <<= 1;
    }
  }
  return temp >> 9;
}

/* 計算CRC16校驗碼 */
static uint16_t WiFi_LowLevel_CalcCRC16(const void *data, int len)
{
  const uint8_t *p = data;
  int i, j;
  uint32_t temp = 0;

  if (len & 1)
  {
    printf("%s: data size %d is odd!\n", __FUNCTION__, len);
    len--; // 不允許出現奇數長度
  }
  if (len != 0)
    temp = (p[0] << 24) | (p[1] << 16); // 填充前二分之一
  if (len > 2)
    temp |= p[2] << 8; // 填充到四分之三

  for (i = 3; i <= len + 2; i++)
  {
    if (i < len)
      temp |= p[i]; // 每次都填充最後四分之一的空間
    
    // 從左數第0~7位計算到左數第16~23位
    for (j = 0; j < 8; j++)
    {
      if (temp & 0x80000000)
        temp ^= POLYNOMIAL_CRC16 << 15;
      temp <<= 1;
    }
  }
  return temp >> 16;
}

/* 切換到SPI模式 */
static void WiFi_LowLevel_ChangeToSPI(void)
{
  int i = 0, ret;
  uint8_t resp;
  
  do
  {
    if (i == WIFI_LOWLEVEL_MAXRETRY)
      abort();
    i++;
    
    // 復位
    PDN_0;
    delay(10); // 延時一段時間, 使WiFi模塊能夠正確復位
    PDN_1;
    
    delay(100);
    WiFi_LowLevel_SendByte(0xff); // 在CS=1的情況下發送8個空時鐘
    
    // 發送CMD0
    ret = WiFi_LowLevel_SendCommand(0, 0, &resp, 1);
    if (ret == -1)
      printf("CMD0 failed!\n");
    else
      printf("CMD0, R1_%02X\n", resp);
  } while (ret != 1 || resp != 0x01);
}

/* 檢查R1中的錯誤標誌位 */
static int WiFi_LowLevel_CheckError(uint8_t cmd, int ret, uint8_t resp, const char *msg_title)
{
  int err = 0;
  
  if (ret == -1)
  {
    printf("%s: CMD%d timeout!\n", msg_title, cmd);
    return 1;
  }
  if (resp & 0x80)
  {
    err++;
    printf("%s: invalid start bit of response!\n", msg_title);
  }
  if (resp & 0x40)
  {
    err++;
    printf("%s: invalid command parameter!\n", msg_title);
  }
  if (resp & 0x10)
  {
    err++;
    printf("%s: function number error!\n", msg_title);
  }
  if (resp & 0x08)
  {
    err++;
    printf("%s: CMD%d CRC failed!\n", msg_title, cmd);
  }
  if (resp & 0x04)
  {
    err++;
    printf("%s: an illegal command code was detected!\n", msg_title);
  }
  return err;
}

/* 判斷應該採用哪種方式傳輸數據 */
// 返回值: 0爲多字節模式, 否則表示塊傳輸模式的數據塊數
// *psize的值會做適當調整, 有可能大於原值
static uint16_t WiFi_LowLevel_GetBlockNum(uint8_t func, uint32_t *psize, uint32_t flags)
{
  uint16_t block_num = 0;
  
  if ((flags & WIFI_RWDATA_ALLOWMULTIBYTE) == 0 || *psize > 512) // 大於512字節時必須用數據塊方式傳輸
  {
    // 塊傳輸模式 (DTMODE=0)
    block_num = *psize / sdio_block_size[func];
    if (*psize % sdio_block_size[func] != 0)
      block_num++;
    *psize = block_num * sdio_block_size[func]; // 塊數*塊大小
  }
  else
  {
    // 多字節傳輸模式 (DTMODE=1)
    *psize = (*psize + 3) & ~3; // WiFi模塊要求寫入的字節數必須爲4的整數倍
  }
  
  return block_num;
}

/* 獲取WiFi模塊支持SDIO功能區個數 (0號功能區除外) */
uint8_t WiFi_LowLevel_GetFunctionNum(void)
{
  return sdio_func_num;
}

/* 判斷是否觸發了網卡中斷 */
int WiFi_LowLevel_GetITStatus(uint8_t clear)
{
  return !INT;
}

/* 初始化WiFi模塊有關的所有GPIO引腳 */
static void WiFi_LowLevel_GPIOInit(void)
{
  GPIO_InitTypeDef gpio = {0};
  
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  
  // 復位引腳(PDN)
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Pin = GPIO_PIN_2;
  gpio.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &gpio);
  
  // SDIO相關引腳
  // PC9: INT, 設爲帶下拉電阻的輸入 (不可以設爲浮空或帶上拉電阻)
  gpio.Mode = GPIO_MODE_INPUT;
  gpio.Pin = GPIO_PIN_9;
  gpio.Pull = GPIO_PULLDOWN;
  HAL_GPIO_Init(GPIOC, &gpio);

  // PC11: CS, PC12: SCK, 設爲推輓輸出
  CS_1;
  gpio.Mode = GPIO_MODE_OUTPUT_PP;
  gpio.Pin = GPIO_PIN_11 | GPIO_PIN_12;
  gpio.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &gpio);
  
  // PD2: MOSI, 設爲推輓輸出
  gpio.Pin = GPIO_PIN_2;
  HAL_GPIO_Init(GPIOD, &gpio);
}

void WiFi_LowLevel_Init(void)
{
  // 在此處打開WiFi模塊所需要的除GPIO和SDIO外所有其他外設的時鐘
  __HAL_RCC_CRC_CLK_ENABLE();
  
  hcrc.Instance = CRC;
  HAL_CRC_Init(&hcrc);
  
  // 檢查Flash中保存的固件內容是否已被破壞
#ifdef WIFI_FIRMWAREAREA_ADDR
  if (!WiFi_LowLevel_VerifyFirmware())
  {
    printf("Error: The firmware stored in flash memory is corrupted!\n");
    printf("Either run flash_saver program, or remove the definition of WIFI_FIRMWAREAREA_ADDR in WiFi.h\n");
    abort();
  }
#endif
  
  WiFi_LowLevel_GPIOInit();
  WiFi_LowLevel_SDIOInit();
}

/* 接收數據 */
// size爲要接收的字節數, bufsize爲data緩衝區的大小
// 若bufsize=0, 則只讀取數據, 但不保存到data中, 此時data可以爲NULL
int WiFi_LowLevel_ReadData(uint8_t func, uint32_t addr, void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  int err = 0, i, ret;
  uint8_t *p = data;
  uint8_t resp;
  uint16_t block_num; // 數據塊個數
  uint16_t crc[2], curr;
  uint32_t cmd53_flags = 0;
  uint32_t realsize = size;
  
  if ((uintptr_t)data & 3)
  {
    // DMA每次傳輸多個字節時, 內存和外設地址必須要對齊, 否則將不能正確傳輸且不會提示錯誤
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return 2; // 不重試
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return 2;
  }
  
  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size)
  {
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
    return 2;
  }
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
    ret = WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE, &resp, 1);
  else
    ret = WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags, &resp, 1);
  ret = WiFi_LowLevel_CheckError(53, ret, resp, __FUNCTION__);
  if (ret)
    return 0;
  
  // CMD53執行成功後, CS爲低電平
  // 現在開始接收數據
  for (i = 0; size > 0; i++)
  {
    if (i != 0)
    {
      resp = WiFi_LowLevel_SendByte(0xff);
      if (resp != 0xfe)
      {
        err = 1;
        printf("%s: invalid start of block!\n", __FUNCTION__);
        break;
      }
    }
    
    curr = (block_num) ? sdio_block_size[func] : size;
    if (bufsize != 0)
    {
      WiFi_LowLevel_Receive(p, curr);
      if (i == 0)
        p[0] = realsize & 0xff;
    }
    else
      WiFi_LowLevel_Receive(NULL, curr);
    WiFi_LowLevel_Receive(&crc[0], 2);
    
    // 校驗數據
    if (bufsize != 0)
    {
      crc[0] = ntohs(crc[0]);
      crc[1] = WiFi_LowLevel_CalcCRC16(p, curr);
      if (crc[0] != crc[1])
      {
        err = 1;
        printf("%s: data CRC failed!\n", __FUNCTION__);
        break;
      }
      p += curr;
    }
    
    size -= curr;
  }
  
  CS_1;
  return err == 0;
}

/* 讀SDIO寄存器 */
uint8_t WiFi_LowLevel_ReadReg(uint8_t func, uint32_t addr)
{
  uint8_t resp[2];
  
  WiFi_LowLevel_SendCMD52(func, addr, 0, 0, resp, sizeof(resp), __FUNCTION__);
  return resp[1];
}

/* 在SPI總線上接收數據 */
static void WiFi_LowLevel_Receive(void *data, int len)
{
  uint8_t *p = data;
  uint8_t rcv;
  
  while (len--)
  {
    rcv = WiFi_LowLevel_SendByte(0xff);
    if (p != NULL)
      *p++ = rcv;
  }
}

/* 接收指定長度的SDIO命令迴應 */
// 7.3.2.1 Format R1
// 7.5.1.1 Host Command to Card Response - Card is ready
// 7.5.4 Timing Values
static int WiFi_LowLevel_ReceiveResponse(uint8_t *resp, uint8_t resp_len)
{
  int i = 0;
  uint8_t temp;
  
  // 等待迴應期間 (NCR), MISO始終爲高電平
  // Modified R1迴應的最高位爲0, 當接收到的字節的最高位爲0時退出循環
  do
  {
    if (i == 200)
      return -1;
    i++;
    
    temp = WiFi_LowLevel_SendByte(0xff);
  } while (temp & 0x80);
  
  resp[0] = temp;
  for (i = 1; i < resp_len; i++)
    resp[i] = WiFi_LowLevel_SendByte(0xff);
  return i;
}

static void WiFi_LowLevel_SDIOInit(void)
{
  uint8_t resp[5];
  
  /* 切換到SPI模式 (CMD0: Used to change from SD to SPI mode) */
  WiFi_LowLevel_ChangeToSPI();
  
  /* 發送CMD5: IO_SEND_OP_COND */
  WiFi_LowLevel_SendCommandEx(5, 0, resp, sizeof(resp), __FUNCTION__);
  printf("CMD5, R1_%02X, RESP1_%02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3], resp[4]);
  
  /* 設置參數VDD Voltage Window: 3.2~3.4V, 並再次發送CMD5 */
  WiFi_LowLevel_SendCommandEx(5, 0x300000, resp, sizeof(resp), __FUNCTION__);
  printf("CMD5, R1_%02X, RESP1_%02x%02x%02x%02x\n", resp[0], resp[1], resp[2], resp[3], resp[4]);
  if (resp[1] & _BV(7))
  {
    // Card is ready to operate after initialization
    sdio_func_num = (resp[1] >> 4) & 7;
    printf("Number of I/O Functions: %d\n", sdio_func_num);
    printf("Memory Present: %d\n", (resp[1] & _BV(3)) != 0);
  }
  
  // SPI模式不支持CMD3和CMD7, 所以不需要發送這兩個命令
}

/* 在SPI總線上發送數據 */
static void WiFi_LowLevel_Send(const void *data, int len)
{
  const uint8_t *p = data;
  
  while (len--)
    WiFi_LowLevel_SendByte(*p++);
}

/* 在SPI總線上發送並接收1字節數據 */
static uint8_t WiFi_LowLevel_SendByte(uint8_t data)
{
  int i;
  
  for (i = 0; i < 8; i++)
  {
    if (data & 0x80)
      MOSI_1;
    else
      MOSI_0;
    data <<= 1;
    
    SCK_1;
    if (MISO)
      data |= 1;
    SCK_0;
  }
  return data;
}

/* 發送SD命令 */
static int WiFi_LowLevel_SendCommand(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len)
{
  int ret;
  uint8_t crc;
  uint8_t data[6];
  
  data[0] = index | 0x40; // 最高位爲起始位, 始終爲0, SD卡從起始位開始接收命令
  data[1] = argument >> 24;
  data[2] = argument >> 16;
  data[3] = argument >> 8;
  data[4] = argument;
  crc = WiFi_LowLevel_CalcCRC7(data, 5);
  data[5] = (crc << 1) | 1;
  
  CS_0;
  WiFi_LowLevel_Send(data, sizeof(data));
  ret = WiFi_LowLevel_ReceiveResponse(resp, resp_len);
  if (ret == -1 || index != 53)
    CS_1;
  return ret;
}

/* 發送SD命令, 如果出錯則自動重試 */
static void WiFi_LowLevel_SendCommandEx(uint8_t index, uint32_t argument, uint8_t *resp, uint8_t resp_len, const char *msg_title)
{
  int i = 0, ret;
  
  do
  {
    if (i == WIFI_LOWLEVEL_MAXRETRY)
      abort();
    i++;
    
    ret = WiFi_LowLevel_SendCommand(index, argument, resp, resp_len);
    ret = WiFi_LowLevel_CheckError(index, ret, resp[0], msg_title);
  } while (ret);
}

static void WiFi_LowLevel_SendCMD52(uint8_t func, uint32_t addr, uint8_t data, uint32_t flags, uint8_t *resp, uint8_t resp_len, const char *msg_title)
{
  uint32_t arg = (func << 28) | (addr << 9) | data | flags;
  
  WiFi_LowLevel_SendCommandEx(52, arg, resp, resp_len, msg_title);
}

static int WiFi_LowLevel_SendCMD53(uint8_t func, uint32_t addr, uint16_t count, uint32_t flags, uint8_t *resp, uint8_t resp_len)
{
  uint32_t arg = (func << 28) | (addr << 9) | (count & 0x1ff) | flags;
  
  return WiFi_LowLevel_SendCommand(53, arg, resp, resp_len);
}

/* 設置WiFi模塊功能區的數據塊大小 */
int WiFi_LowLevel_SetBlockSize(uint8_t func, uint32_t size)
{
  sdio_block_size[func] = size;
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x10, size & 0xff);
  WiFi_LowLevel_WriteReg(0, (func << 8) | 0x11, size >> 8);
  return 1;
}

/* 檢查Flash中保存的固件內容是否完整 */
#ifdef WIFI_FIRMWAREAREA_ADDR
static int WiFi_LowLevel_VerifyFirmware(void)
{
  uint32_t crc, len;
  
  if (WIFI_FIRMWARE_SIZE != 255536)
    return 0;
  
  len = WIFI_FIRMWARE_SIZE / 4 + 2; // 固件區(包括CRC)總大小的1/4
  crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)WIFI_FIRMWAREAREA_ADDR, len);
  return crc == 0;
}
#endif

/* 發送數據 */
// size爲要發送的字節數, bufsize爲data緩衝區的大小, bufsize=0時禁用緩衝區檢查
int WiFi_LowLevel_WriteData(uint8_t func, uint32_t addr, const void *data, uint32_t size, uint32_t bufsize, uint32_t flags)
{
  const uint8_t *p = data;
  int err = 0, ret;
  uint8_t resp[2];
  uint16_t block_num; // 數據塊個數
  uint16_t crc, curr;
  uint32_t cmd53_flags = CMD53_WRITE;
  
  if ((uintptr_t)data & 3)
  {
    printf("%s: data must be 4-byte aligned!\n", __FUNCTION__);
    return 2; // 不重試
  }
  if (size == 0)
  {
    printf("%s: size cannot be 0!\n", __FUNCTION__);
    return 2;
  }
  
  block_num = WiFi_LowLevel_GetBlockNum(func, &size, flags);
  if (bufsize != 0 && bufsize < size) // 只讀緩衝區越界不會影響數據傳輸, 所以這只是一個警告
    printf("%s: a buffer of at least %d bytes is required! bufsize=%d\n", __FUNCTION__, size, bufsize);
  
  if (flags & WIFI_RWDATA_ADDRINCREMENT)
    cmd53_flags |= CMD53_INCREMENTING;
  if (block_num)
    ret = WiFi_LowLevel_SendCMD53(func, addr, block_num, cmd53_flags | CMD53_BLOCKMODE, resp, sizeof(resp));
  else
    ret = WiFi_LowLevel_SendCMD53(func, addr, size, cmd53_flags, resp, sizeof(resp));
  ret = WiFi_LowLevel_CheckError(53, ret, resp[0], __FUNCTION__);
  if (ret)
    return 0;
  
  // CMD53執行成功後, CS爲低電平
  // 現在開始發送數據
  WiFi_LowLevel_SendByte(0xff); // NWR
  while (size > 0 && err == 0)
  {
    curr = (block_num) ? sdio_block_size[func] : size;
    WiFi_LowLevel_SendByte(0xfc); // start block token
    WiFi_LowLevel_Send(p, curr);
    
    crc = WiFi_LowLevel_CalcCRC16(p, curr);
    WiFi_LowLevel_SendByte(crc >> 8);
    WiFi_LowLevel_SendByte(crc & 0xff);
    
    resp[0] = WiFi_LowLevel_SendByte(0xff);
    switch (resp[0])
    {
      case 0xe5:
        // Data accepted
        p += curr;
        size -= curr;
        break;
      case 0xeb:
        err = 1;
        printf("%s: data CRC failed!\n", __FUNCTION__);
        break;
      case 0xed:
        err = 1;
        printf("%s: data transfer error!\n", __FUNCTION__);
        break;
      default:
        err = 1;
        printf("%s: data transfer error! resp=%#x\n", __FUNCTION__, resp[0]);
    }
  }
  
  CS_1;
  return err == 0;
}

/* 寫寄存器, 返回寫入後寄存器的實際內容 */
uint8_t WiFi_LowLevel_WriteReg(uint8_t func, uint32_t addr, uint8_t value)
{
  uint8_t resp[2];
  
  WiFi_LowLevel_SendCMD52(func, addr, value, CMD52_WRITE | CMD52_READAFTERWRITE, resp, sizeof(resp), __FUNCTION__);
  return resp[1];
}

 

發佈了78 篇原創文章 · 獲贊 39 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章