工程下载地址: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];
}