本帖最后由 Simon21ic 于 2014-7-24 16:45 编辑
MSCBoot是我以前就发布过的一个bootloader,通过在USB口上,实现MSC设备,模拟出一个FAT32的U盘,通过对U盘中firmware.bin文件的覆盖性复制,来更新固件应用程序。
VSF平台的简介,参考这里:https://bbs.21ic.com/icview-773982-1-1.html
这个做为VSF介绍的第一个实例吧,代码做过一些简化,去掉了不需要的部分。
不多说了,直接上代码:
// mal
// embedded flash
static struct embflash_param_t embflash_param =
{
0, // uint8_t index;
};
static struct mal_info_t embflash_mal_info =
{
{0, 0}, NULL, 0, 0, 0, &embflash_drv
};
static struct dal_info_t embflash_dal_info =
{
NULL,
&embflash_param,
NULL,
&embflash_mal_info,
};
// firmware, APP_CFG_FWSIZE bytes located at APP_CFG_BOOTSIZE
static struct malinmal_param_t firmware_param =
{
&embflash_dal_info, // struct dal_info_t *maldal;
APP_CFG_BOOTSIZE, // uint32_t addr;
APP_CFG_FWSIZE, // uint32_t size;
};
static struct mal_info_t firmware_mal_info =
{
{0, 0}, NULL, 0, 0, 0, &malinmal_drv
};
static struct dal_info_t firmware_dal_info =
{
NULL,
&firmware_param,
NULL,
&firmware_mal_info,
};
首先,定义mal实例(mal是存储器抽象层),embflash_dal_info是把芯片内部的flash,实现为通用的mal接口。
但是,实际我们的bootlaoder也同样会占用内存flash,那就需要用到malinmal的实例,把flash中,实际应用程序使用的部分,独立抽象出一个mal。
然后,定义fakefat32(同样也是一个mal的驱动,用代码的方式,实现一个假的FAT32文件系统)
static struct fakefat32_file_t root_dir[] =
{
{
"MSCBoot", NULL,
FKAEFAT32_FILEATTR_VOLUMEID,
},
{
"firmware", "bin",
FAKEFAT32_FILEATTR_ARCHIVE,
512,
{
ReadFirmwareArea,
ReadFirmwareArea_isready,
WriteFirmwareArea,
WriteFirmwareArea_isready
},
},
{
NULL,
}
};
为了简化,名字为MSCBoot的根目录里,只有一个firmware.bin文件。
ReadFirmwareArea等接口,是用户实现的代码,其实只是控制firmware_mal_info,实现对固件flash空间的非阻塞的读写访问。
定义好目录结构后,就需要定义fakefat32的mal的实例了:
static struct fakefat32_param_t fakefat32_param =
{
512, // uint16_t sector_size;
0x00760000, // uint32_t sector_number;
8, // uint8_t sectors_per_cluster;
0x0CA93E47, // uint32_t volume_id;
0x12345678, // uint32_t disk_id;
{ // struct fakefat32_file_t root;
{
"ROOT", NULL,
0,
0,
{fakefat32_dir_read, NULL, fakefat32_dir_write, NULL},
root_dir
}
}
};
static struct mal_info_t fakefat32_mal_info =
{
{0, 0}, NULL, 0, 0, 0, &fakefat32_drv
};
static struct dal_info_t fakefat32_dal_info =
{
NULL,
&fakefat32_param,
NULL,
&fakefat32_mal_info,
};
之后是USB设备端接口的MSC类驱动相关的类实例:
struct SCSI_LUN_info_t MSCBOT_LunInfo =
{
&fakefat32_dal_info,
{
true,
{'S', 'i', 'm', 'o', 'n', ' ', ' ', ' '},
{'M', 'S', 'C', 'B', 'o', 'o', 't', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
{'1', '.', '0', '0'},
SCSI_PDT_DIRECT_ACCESS_BLOCK
}
};
uint8_t MSCBOT_Buffer0[4096], MSCBOT_Buffer1[4096];
struct vsfusbd_MSCBOT_param_t MSCBOT_param =
{
1, // uint8_t ep_out;
1, // uint8_t ep_in;
0, // uint8_t max_lun;
&MSCBOT_LunInfo, // struct SCSI_LUN_info_t *lun_info;
NULL, // struct SCSI_handler_t *user_handlers;
{
{MSCBOT_Buffer0, sizeof(MSCBOT_Buffer0)},
{MSCBOT_Buffer1, sizeof(MSCBOT_Buffer1)}
}, // struct vsf_buffer_t page_buffer[2];
};
注意,这里使用了双缓冲的方式,USB在通信的同时,另一个缓存可以用于实际mal的读写操作。
MSCBOT_LunInfo里设置了实际mal为fakefat32_dal_info,也就是虚拟FAT32的mal驱动。
接下里就是USB设备端协议栈的实例,实际描述符就直接略过了:
描述符结构
static const struct vsfusbd_desc_filter_t descriptors[] =
{
VSFUSBD_DESC_DEVICE(0, MSCBOT_DeviceDescriptor, sizeof(MSCBOT_DeviceDescriptor), NULL),
VSFUSBD_DESC_CONFIG(0, 0, MSCBOT_ConfigDescriptor, sizeof(MSCBOT_ConfigDescriptor), NULL),
VSFUSBD_DESC_STRING(0, 0, MSCBOT_StringLangID, sizeof(MSCBOT_StringLangID), NULL),
VSFUSBD_DESC_STRING(0x0409, 1, MSCBOT_StringVendor, sizeof(MSCBOT_StringVendor), NULL),
VSFUSBD_DESC_STRING(0x0409, 2, MSCBOT_StringProduct, sizeof(MSCBOT_StringProduct), NULL),
VSFUSBD_DESC_STRING(0x0409, 3, MSCBOT_StringSerial, sizeof(MSCBOT_StringSerial), NULL),
VSFUSBD_DESC_NULL
};
USB接口的定义,每个接口需要设置对应的类驱动和参数。
static struct vsfusbd_iface_t ifaces[] =
{
{(struct vsfusbd_class_protocol_t *)&vsfusbd_MSCBOT_class, (void *)&MSCBOT_param},
{(struct vsfusbd_class_protocol_t *)NULL, (void *)NULL}
};
USB配置结构
static struct vsfusbd_config_t config0[] =
{
{
NULL, NULL, dimof(ifaces), (struct vsfusbd_iface_t *)ifaces,
}
};
USB设备端的定义
struct vsfusbd_device_t usb_device =
{
1, (struct vsfusbd_config_t *)config0,
(struct vsfusbd_desc_filter_t *)descriptors, 0,
(struct interface_usbd_t *)&core_interfaces.usbd,
{
NULL, // init
NULL, // fini
NULL, // poll
NULL, // on_set_interface
NULL, // on_ATTACH
NULL, // on_DETACH
NULL, // on_RESET
NULL, // on_ERROR
NULL, // on_WAKEUP
NULL, // on_SUSPEND
NULL, // on_RESUME
NULL, // on_SOF
NULL, // on_IN
NULL, // on_OUT
}, // callback
};
其中,core_interfaces.usbd定义了,这个协议栈使用的是芯片内部的USB硬件。
主函数就相当精简了:
int main(void)
{
主控芯片初始化
interfaces->core.init(NULL);
自保护
if (!interfaces->flash.isprotected(0))
{
interfaces->flash.unlock(0);
interfaces->flash.protect(0);
interfaces->flash.lock(0);
}
初始化应用固件的mal,并且读取第一个block
if (mal.init(&embflash_dal_info) ||
(0 == embflash_mal_info.capacity.block_size) ||
(sizeof(block_buffer) < embflash_mal_info.capacity.block_size) ||
mal.readblock(&embflash_dal_info, APP_CFG_BOOTSIZE, block_buffer, 1))
{
fatal_error();
}
pagesize = (uint32_t)embflash_mal_info.capacity.block_size;
pagenum = (uint32_t)embflash_mal_info.capacity.block_number;
size = pagesize * pagenum;
对于Cortex芯片,第一个block里有固件的堆栈指针和复位向量地址。
// read MSP and RST_VECT
MSP = GET_LE_U32(&block_buffer[0]);
RST_VECT = GET_LE_U32(&block_buffer[4]);
初始化按键IO,并且判断按键是否按下,如果按下的话(或者应用固件无效的话),进入Bootloader模式,否则跳转到应用:
interfaces->gpio.init(KEY_PORT);
interfaces->gpio.config_pin(KEY_PORT, KEY_PIN, KEY_VALID_LOW ? GPIO_INPU : GPIO_INPD);
key_val = interfaces->gpio.get(KEY_PORT, 1 << KEY_PIN);
if ((KEY_VALID_LOW ? key_val : !key_val) &&
((MSP & 0xFF000000) == 0x20000000) &&
((RST_VECT & 0xFF000000) == 0x08000000))
{
跳转到应用钱,释放对应的资源。
mal.fini(&embflash_dal_info);
interfaces->gpio.fini(KEY_PORT);
interfaces->core.set_stack(MSP);
((void (*)(void))RST_VECT)();
while (1);
}
fakefat32 mal的初始化
// fakefat32 parameter init
fakefat32_param.sector_size = pagesize;
fakefat32_param.sector_number = 128 * 1024 * 1024 / pagesize;
fakefat32_param.sectors_per_cluster = 1;
mal.init(&fakefat32_dal_info);
是能USB上拉,是的主机可以发现本设备
// Enable USB Pull-up
interfaces->gpio.set(USB_PULL_PORT, 1 << USB_PULL_PIN);
如果初始化USB设备协议栈成功的话,就进入轮训
if (!vsfusbd_device_init(&usb_device))
{
while (1)
{
if (vsfusbd_device_poll(&usb_device))
{
break;
}
}
}
return 0;
}
VSF的USB协议栈,确实就只需要vsfusbd_device_init和vsfusbd_device_poll 2个接口。
用户甚至不需要关心USB端口的设置,以及缓冲的分配。
用户只需关心应用部分就行。
|
|