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];
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值