WiFi模块的固件很大,如果直接放到程序中,每次下载程序都会浪费很长的时间。只要我们预先将这些内容写入到Flash的固定区域中,调试程序就不用再烧写固件到Flash中,从而大大提高开发效率。
Marvell 88W8686 WiFi模块的固件数据:http://blog.csdn.net/zlk1214/article/details/76166140
Marvell 88W8686 WiFi模块创建或连接热点,并使用lwip建立http服务器的程序: http://blog.csdn.net/zlk1214/article/details/75410647STM32F103RE单片机属于High-density型,Flash容量为512KB,一共有256页,每页2KB。
新建一个工程,运行下面的程序:
#include <stdio.h>
#include <stm32f10x.h>
typedef struct
{
uint8_t U_ID[12];
} DeviceID_TypeDef;
#define FLASH_SIZE (*(uint16_t *)0x1ffff7e0)
#define DEVICE_ID ((DeviceID_TypeDef *)0x1ffff7e8) // 12 bytes
extern const unsigned char firmware_helper_sd[];
extern const unsigned char firmware_sd8686[];
const void *data_addr[] = {firmware_helper_sd, firmware_sd8686};
const uint32_t data_size[] = {2516, 122916};
// Flash地址范围
#define FLASH_ADDR_START 0x08000000
#define FLASH_ADDR_END 0x0807ffff
#define FLASH_PAGESIZE 2048 // Flash页大小
#define DATAEND FLASH_ADDR_END // 希望写入的数据所在的最后一页的任意地址
void dump_data(const void *data, uint32_t len)
{
const uint8_t *p = data;
while (len--)
printf("%02X", *p++);
printf("\n");
}
int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, '\r');
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
return ch;
}
FLASH_Status program(uint8_t n, uint32_t addr)
{
const uint32_t *p;
uint8_t ch;
uint8_t i = 1;
uint32_t j, page;
FLASH_Status ret = FLASH_COMPLETE;
printf("Press Y to start programming: ");
do
{
if (i == 0)
printf("\a");
else
i = 0;
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
ch = USART_ReceiveData(USART1);
} while (ch != 'Y' && ch != 'y');
printf("%c\n", ch);
FLASH_Unlock();
for (page = addr; page < DATAEND; page += FLASH_PAGESIZE)
{
printf("Erase page 0x%08x\n", page);
ret = FLASH_ErasePage(page);
if (ret != FLASH_COMPLETE)
goto end;
}
CRC_ResetDR();
printf("Program data...\n");
for (i = 0; i < n; i++)
{
CRC_CalcCRC(data_size[i]);
ret = FLASH_ProgramWord(addr, data_size[i]);
if (ret != FLASH_COMPLETE)
goto end;
addr += 4;
}
for (i = 0; i < n; i++)
{
p = data_addr[i];
for (j = 0; j < data_size[i]; j += 4)
{
CRC_CalcCRC(*p);
ret = FLASH_ProgramWord(addr, *p);
if (ret != FLASH_COMPLETE)
goto end;
addr += 4;
p++;
}
}
ret = FLASH_ProgramWord(addr, CRC_GetCRC());
end:
FLASH_Lock();
if (ret != FLASH_COMPLETE)
printf("Failed!\n");
return ret;
}
void start(void)
{
uint8_t i, n;
uint32_t addr, size;
n = sizeof(data_size) / sizeof(uint32_t);
size = (n + 1) * 4;
for (i = 0; i < n; i++)
{
size += data_size[i];
if (data_size[i] % 4 != 0)
{
printf("Error: A size of %d bytes isn't a multiple of 4!\n", data_size[i]);
return;
}
}
printf("Count: %d\n", n);
printf("Total size: %d\n", size);
addr = ((DATAEND | (FLASH_PAGESIZE - 1)) - size + 1) & ~(FLASH_PAGESIZE - 1);
printf("Start address: 0x%08x\n", addr);
printf("End address: 0x%08x\n", addr + size - 1);
if (*(uint32_t *)addr != data_size[0])
{
printf("Data aren't programmed\n");
if (program(n, addr) != FLASH_COMPLETE)
return;
}
else
printf("Data have been programmed!\n");
while (1)
{
printf("Verify...\n");
CRC_ResetDR();
if (CRC_CalcBlockCRC((uint32_t *)addr, size / 4) == 0)
{
printf("OK!\n");
break;
}
else
{
printf("Not OK! ");
if (program(n, addr) == FLASH_COMPLETE)
break;
}
}
}
int main(void)
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);
USART_StructInit(&usart);
usart.USART_BaudRate = 115200; // 波特率
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
printf("Flash size: %dKB\n", FLASH_SIZE);
printf("Device ID: ");
dump_data(DEVICE_ID->U_ID, sizeof(DEVICE_ID->U_ID));
start();
while (1)
__WFI();
}
void HardFault_Handler(void)
{
printf("Hard Error!\n");
while (1);
}
程序的第一次运行结果如下:
Flash size: 512KB
Device ID: 35FFD9054753383333772443
Count: 2
Total size: 125444
Start address: 0x08061000
End address: 0x0807fa03
Data aren't programmed
Press Y to start programming: y
Erase page 0x08061000
Erase page 0x08061800
Erase page 0x08062000
Erase page 0x08062800
Erase page 0x08063000
Erase page 0x08063800
Erase page 0x08064000
Erase page 0x08064800
Erase page 0x08065000
Erase page 0x08065800
Erase page 0x08066000
Erase page 0x08066800
Erase page 0x08067000
Erase page 0x08067800
Erase page 0x08068000
Erase page 0x08068800
Erase page 0x08069000
Erase page 0x08069800
Erase page 0x0806a000
Erase page 0x0806a800
Erase page 0x0806b000
Erase page 0x0806b800
Erase page 0x0806c000
Erase page 0x0806c800
Erase page 0x0806d000
Erase page 0x0806d800
Erase page 0x0806e000
Erase page 0x0806e800
Erase page 0x0806f000
Erase page 0x0806f800
Erase page 0x08070000
Erase page 0x08070800
Erase page 0x08071000
Erase page 0x08071800
Erase page 0x08072000
Erase page 0x08072800
Erase page 0x08073000
Erase page 0x08073800
Erase page 0x08074000
Erase page 0x08074800
Erase page 0x08075000
Erase page 0x08075800
Erase page 0x08076000
Erase page 0x08076800
Erase page 0x08077000
Erase page 0x08077800
Erase page 0x08078000
Erase page 0x08078800
Erase page 0x08079000
Erase page 0x08079800
Erase page 0x0807a000
Erase page 0x0807a800
Erase page 0x0807b000
Erase page 0x0807b800
Erase page 0x0807c000
Erase page 0x0807c800
Erase page 0x0807d000
Erase page 0x0807d800
Erase page 0x0807e000
Erase page 0x0807e800
Erase page 0x0807f000
Erase page 0x0807f800
Program data...
Verify...
OK!
程序的第二次运行结果如下:
Flash size: 512KB
Device ID: 35FFD9054753383333772443
Count: 2
Total size: 125444
Start address: 0x08061000
End address: 0x0807fa03
Data have been programmed!
Verify...
OK!
以后就可以直接访问下面的地址来获取固件内容:
固件大小: 0x08061000-0x08061007
helper_sd: 0x08061008-0x080619db
sd8686: 0x080619dc-0x0807f9ff
CRC校验码: 0x0807fa00-0x0807fa03
通过CRC校验码可校验固件内容的完整性。
再新建一个没有固件数据的新工程,代码如下:
#include <stdio.h>
#include <stm32f10x.h>
typedef struct
{
uint32_t helper_size;
uint32_t sd8686_size;
uint8_t helper_sd[2516];
uint8_t sd8686[122916];
uint32_t crc;
} Firmware_TypeDef;
#define firmware_data ((Firmware_TypeDef *)0x08061000)
void dump_data(const void *data, uint32_t len)
{
const uint8_t *p = data;
while (len--)
printf("%02X", *p++);
printf("\n");
}
int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, '\r');
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
return ch;
}
void verify(void)
{
CRC_ResetDR();
if (CRC_CalcBlockCRC((uint32_t *)firmware_data, sizeof(*firmware_data) / sizeof(uint32_t)) == 0)
{
printf("Data is OK!\n");
printf("helper_size=%d\n", firmware_data->helper_size);
printf("sd8686_size=%d\n", firmware_data->sd8686_size);
printf("helper_sd: ");
dump_data(firmware_data->helper_sd, 16);
printf("sd8686: ");
dump_data(firmware_data->sd8686, 16);
printf("CRC=0x%08x\n", firmware_data->crc);
}
else
printf("Data is not OK!\n");
}
int main(void)
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);
USART_StructInit(&usart);
usart.USART_BaudRate = 115200; // 波特率
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
verify();
while (1)
__WFI();
}
void HardFault_Handler(void)
{
printf("Hard Error!\n");
while (1);
}
运行结果:
Data is OK!
helper_size=2516
sd8686_size=122916
helper_sd: 030000EA020000000800000000000000
sd8686: 01000000001000C000020000E96B4826
CRC=0x04465065
成功读取到固件数据!
现在我们可以修改原来的固件下载程序,删掉工程中的helper_sd.c和sd8686.c,从而大幅度降低每次烧写芯片的时间!
WiFi.c中要删除固件数组的引用:
//extern const unsigned char firmware_helper_sd[2516];
//extern const unsigned char firmware_sd8686[122916];
【寄存器版程序】
main.c:
RCC->AHBENR |= RCC_AHBENR_CRCEN | RCC_AHBENR_SDIOEN; // 打开CRC和SDIO外设的时钟, 注意AHBENR寄存器有初值
WiFi.c:
/* 下载固件 */
// 参考文档: marvell-88w8686-固件下载程序说明.doc
uint8_t WiFi_DownloadFirmware(void)
{
uint8_t helper_buf[64];
const uint8_t *data;
uint16_t size;
uint32_t len;
// 块大小设为32
SDIO->DCTRL = (SDIO->DCTRL & ~SDIO_DCTRL_DBLOCKSIZE) | SDIO_DCTRL_DBLOCKSIZE_2 | SDIO_DCTRL_DBLOCKSIZE_0;
WiFi_SetBlockSize(1); // 应用到Function 1
// 检验Flash中保存的固件的完整性
CRC->CR = CRC_CR_RESET;
data = (const uint8_t *)0x08061000;
len = 31361;
while (len--)
{
CRC->DR = *(uint32_t *)data;
data += 4;
}
if (CRC->DR)
{
printf("Error: The firmware stored in flash memory is corrupted!\n");
return 0;
}
// 下载helper
io_addr = WiFi_Read(1, WIFI_IOPORT0) | (WiFi_Read(1, WIFI_IOPORT1) << 8) | (WiFi_Read(1, WIFI_IOPORT2) << 16);
data = (const uint8_t *)0x08061008;
len = *(uint32_t *)0x08061000;
while (len)
{
// 每次下载64字节, 其中前4字节为本次下载的数据量
size = (len > 60) ? 60 : len;
*(uint32_t *)helper_buf = size;
memcpy(helper_buf + 4, data, size);
WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY);
WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf));
len -= size;
data += size;
}
*(uint32_t *)helper_buf = 0;
WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); // 以空数据包结束
// 下载固件
data = (const uint8_t *)0x080619dc;
len = *(uint32_t *)0x08061004;
while (len)
{
WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY);
while ((size = WiFi_Read(1, WIFI_SQREADBASEADDR0) | (WiFi_Read(1, WIFI_SQREADBASEADDR1) << 8)) == 0); // 获取本次下载的字节数
//printf("Required: %d bytes, Remaining: %d bytes\n", size, len);
if (size & 1)
{
// 若size为奇数(如17), 则说明接收端有CRC校验错误, 应重新传送上一次的内容(这部分代码省略)
printf("Error: an odd size is invalid!\n");
return 0;
}
if (size > len)
size = len;
// len为缓冲区剩余大小
#ifdef WIFI_HIGHSPEED // 高速模式下不能使用多字节传输模式, 只能使用块传输模式, 因此缓冲区要足够大
if (len < 32)
{
// 若缓冲区空间不足一个数据块, 则借用helper_buf
memcpy(helper_buf, data, size);
WiFi_WritePort(helper_buf, size, sizeof(helper_buf));
}
else
#endif
WiFi_WritePort(data, size, len);
if (!SDIO_SUCCEEDED())
{
printf("Data transfer error! SDIO->STA=0x%08x\n", SDIO->STA);
return 0;
}
len -= size;
data += size;
}
// 等待Firmware启动
while (WiFi_GetPacketLength() != 0xfedc);
printf("Firmware is successfully downloaded!\n");
return 1;
}
【库函数版程序】
main.c:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC | RCC_AHBPeriph_SDIO, ENABLE); // 打开CRC和SDIO外设的时钟
WiFi.c:
/* 下载固件 */
// 参考文档: marvell-88w8686-固件下载程序说明.doc
uint8_t WiFi_DownloadFirmware(void)
{
uint8_t helper_buf[64];
const uint8_t *data;
uint16_t size;
uint32_t len;
// 块大小设为32
sdio_data.SDIO_DataBlockSize = SDIO_DataBlockSize_32b;
WiFi_SetBlockSize(1); // 应用到Function 1
// 检验Flash中保存的固件的完整性
CRC_ResetDR();
if (CRC_CalcBlockCRC((uint32_t *)0x08061000, 31361))
{
printf("Error: The firmware stored in flash memory is corrupted!\n");
return 0;
}
// 下载helper
io_addr = WiFi_Read(1, WIFI_IOPORT0) | (WiFi_Read(1, WIFI_IOPORT1) << 8) | (WiFi_Read(1, WIFI_IOPORT2) << 16);
data = (const uint8_t *)0x08061008;
len = *(uint32_t *)0x08061000;
while (len)
{
// 每次下载64字节, 其中前4字节为本次下载的数据量
size = (len > 60) ? 60 : len;
*(uint32_t *)helper_buf = size;
memcpy(helper_buf + 4, data, size);
WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY);
WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf));
len -= size;
data += size;
}
*(uint32_t *)helper_buf = 0;
WiFi_WritePort(helper_buf, sizeof(helper_buf), sizeof(helper_buf)); // 以空数据包结束
// 下载固件
data = (const uint8_t *)0x080619dc;
len = *(uint32_t *)0x08061004;
while (len)
{
WiFi_Wait(WIFI_CARDSTATUS_DNLDCARDRDY);
while ((size = WiFi_Read(1, WIFI_SQREADBASEADDR0) | (WiFi_Read(1, WIFI_SQREADBASEADDR1) << 8)) == 0); // 获取本次下载的字节数
//printf("Required: %d bytes, Remaining: %d bytes\n", size, len);
if (size & 1)
{
// 若size为奇数(如17), 则说明接收端有CRC校验错误, 应重新传送上一次的内容(这部分代码省略)
printf("Error: an odd size is invalid!\n");
return 0;
}
if (size > len)
size = len;
// len为缓冲区剩余大小
#ifdef WIFI_HIGHSPEED // 高速模式下不能使用多字节传输模式, 只能使用块传输模式, 因此缓冲区要足够大
if (len < 32)
{
// 若缓冲区空间不足一个数据块, 则借用helper_buf
memcpy(helper_buf, data, size);
WiFi_WritePort(helper_buf, size, sizeof(helper_buf));
}
else
#endif
WiFi_WritePort(data, size, len);
if (!SDIO_SUCCEEDED())
{
printf("Data transfer error! SDIO->STA=0x%08x\n", SDIO->STA);
return 0;
}
len -= size;
data += size;
}
// 等待Firmware启动
while (WiFi_GetPacketLength() != 0xfedc);
printf("Firmware is successfully downloaded!\n");
return 1;
}