本帖最后由 Simon21ic 于 2016-6-28 15:49 编辑
之前的一个关于MCU组件化开发方式的帖子里提到了,这里就用VSF中的一个demo介绍一下组件化的开发方式。
代码在这里:
https://github.com/versaloon/vsf ... example/vsfusbd_eda
主要代码在main.c中
功能如下:
USB设备,实现3个设备,分别为CDC,MSC和RNDIS。
MSC实现的是模拟U盘,RNDIS实现网络连接,并且可以通过telnetd对外提供命令行接口
一些代码的说明:
.mal.fakefat32.sector_size = 512,
.mal.fakefat32.sector_number = 0x00001000,
.mal.fakefat32.sectors_per_cluster = 8,
.mal.fakefat32.volume_id = 0x0CA93E47,
.mal.fakefat32.disk_id = 0x12345678,
.mal.fakefat32.root[0].memfile.file.name= "ROOT",
.mal.fakefat32.root[0].memfile.d.child = (struct vsfile_memfile_t *)fakefat32_root_dir,
这个是虚拟的FAT32文件系统的属性设置,无非就是设置一些扇区大小、数量,每簇扇区数,一些ID,和更目录结构位置。
.usbd.rndis.param.CDCACM.CDC.ep_notify = 1,
.usbd.rndis.param.CDCACM.CDC.ep_out = 2,
.usbd.rndis.param.CDCACM.CDC.ep_in = 2,
.usbd.rndis.param.mac.size = 6,
.usbd.rndis.param.mac.addr.s_addr64 = 0x0605040302E0,
.usbd.rndis.param.cb.param = &app,
.usbd.rndis.param.cb.on_connect = app_rndis_on_connect,
.usbd.cdc.param.CDC.ep_notify = 3,
.usbd.cdc.param.CDC.ep_out = 4,
.usbd.cdc.param.CDC.ep_in = 4,
.usbd.cdc.param.CDC.stream_tx = (struct vsf_stream_t *)&app.usbd.cdc.stream_tx,
.usbd.cdc.param.CDC.stream_rx = (struct vsf_stream_t *)&app.usbd.cdc.stream_rx,
.usbd.cdc.param.line_coding.bitrate = 115200,
.usbd.cdc.param.line_coding.stopbittype = 0,
.usbd.cdc.param.line_coding.paritytype = 0,
.usbd.cdc.param.line_coding.datatype = 8,
.usbd.cdc.stream_tx.stream.op = &fifostream_op,
.usbd.cdc.stream_tx.mem.buffer.buffer = (uint8_t *)&app.usbd.cdc.txbuff,
.usbd.cdc.stream_tx.mem.buffer.size = sizeof(app.usbd.cdc.txbuff),
.usbd.cdc.stream_rx.stream.op = &fifostream_op,
.usbd.cdc.stream_rx.mem.buffer.buffer = (uint8_t *)&app.usbd.cdc.rxbuff,
.usbd.cdc.stream_rx.mem.buffer.size = sizeof(app.usbd.cdc.rxbuff),
.usbd.msc.param.ep_in = 5,
.usbd.msc.param.ep_out = 5,
.usbd.msc.param.scsi_dev = &app.mal.scsi_dev,
.usbd.ifaces[0].class_protocol = (struct vsfusbd_class_protocol_t *)&vsfusbd_RNDISControl_class,
.usbd.ifaces[0].protocol_param = &app.usbd.rndis.param,
.usbd.ifaces[1].class_protocol = (struct vsfusbd_class_protocol_t *)&vsfusbd_RNDISData_class,
.usbd.ifaces[1].protocol_param = &app.usbd.rndis.param,
.usbd.ifaces[2].class_protocol = (struct vsfusbd_class_protocol_t *)&vsfusbd_CDCACMControl_class,
.usbd.ifaces[2].protocol_param = &app.usbd.cdc.param,
.usbd.ifaces[3].class_protocol = (struct vsfusbd_class_protocol_t *)&vsfusbd_CDCACMData_class,
.usbd.ifaces[3].protocol_param = &app.usbd.cdc.param,
.usbd.ifaces[4].class_protocol = (struct vsfusbd_class_protocol_t *)&vsfusbd_MSCBOT_class,
.usbd.ifaces[4].protocol_param = &app.usbd.msc.param,
.usbd.config[0].num_of_ifaces = dimof(app.usbd.ifaces),
.usbd.config[0].iface = app.usbd.ifaces,
.usbd.device.num_of_configuration = dimof(app.usbd.config),
.usbd.device.config = app.usbd.config,
.usbd.device.desc_filter = (struct vsfusbd_desc_filter_t *)USB_descriptors,
.usbd.device.device_class_iface = 0,
.usbd.device.drv = (struct interface_usbd_t *)&core_interfaces.usbd,
.usbd.device.int_priority = 0,
USB设备端,3种设备的设置,3个设备中CDC和RNDIS对应2个USB的接口,所以一共是5个USB的接口。这里做了这些接口的数据结构的初始化。并且,初始化了USB的config、interface以及描述符、底层驱动等结构。
.usbd.rndis.param.netif.macaddr.size = 6,
.usbd.rndis.param.netif.macaddr.addr.s_addr64 = 0x0E0D0C0B0AE0,
.usbd.rndis.param.netif.ipaddr.size = 4,
.usbd.rndis.param.netif.ipaddr.addr.s_addr = 0x01202020,
.usbd.rndis.param.netif.netmask.size = 4,
.usbd.rndis.param.netif.netmask.addr.s_addr = 0x00FFFFFF,
.usbd.rndis.param.netif.gateway.size = 4,
.usbd.rndis.param.netif.gateway.addr.s_addr = 0x01202020,
RNDIS设备的属性设,这个在简单不过了
.vsfip.telnetd.telnetd.port = 23,
.vsfip.telnetd.telnetd.session_num = dimof(app.vsfip.telnetd.sessions),
.vsfip.telnetd.sessions[0].stream_tx = (struct vsf_stream_t *)&app.vsfip.telnetd.stream_tx,
.vsfip.telnetd.sessions[0].stream_rx = (struct vsf_stream_t *)&app.vsfip.telnetd.stream_rx,
.vsfip.telnetd.stream_tx.stream.op = &fifostream_op,
.vsfip.telnetd.stream_tx.mem.buffer.buffer = (uint8_t *)&app.vsfip.telnetd.txbuff,
.vsfip.telnetd.stream_tx.mem.buffer.size = sizeof(app.vsfip.telnetd.txbuff),
.vsfip.telnetd.stream_rx.stream.op = &fifostream_op,
.vsfip.telnetd.stream_rx.mem.buffer.buffer = (uint8_t *)&app.vsfip.telnetd.rxbuff,
.vsfip.telnetd.stream_rx.mem.buffer.size = sizeof(app.vsfip.telnetd.rxbuff),te
telnetd的属性设置,这里设置了数据收发的流,这个流的另一端就是接到命令行组件的,基于telnetd的命令行界面,几乎不用什么代码,通过这些简单的设置就能够实现
.shell.echo = false,
.shell.stream_tx = (struct vsf_stream_t *)&app.vsfip.telnetd.stream_tx,
.shell.stream_rx = (struct vsf_stream_t *)&app.vsfip.telnetd.stream_rx,
命令行界面的属性设置,telnet的客户端会自己回显,所以不需要shell的回显功能。另外就是链接shell的输入和输出流到telnetd的流。
int main(void)
{
vsf_enter_critical();
vsfsm_evtq_init(&app.pendsvq);
vsfsm_evtq_set(&app.pendsvq);
vsfhal_core_pendsv_config(app_on_pendsv, &app.pendsvq);
vsfsm_init(&app.sm);
vsf_leave_critical();
vsfsm_evtq_set(NULL);
while (1)
{
// no thread runs in mainq, just sleep in main loop
vsfhal_core_sleep(SLEEP_WFI);
}
}
main函数,这里的while(1)里放的是非实时代码。
static struct vsfsm_state_t *
app_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
switch (evt)
{
case VSFSM_EVT_INIT:
vsfhal_core_init(NULL);
vsfhal_tickclk_init();
vsfhal_tickclk_start();
VSFPOOL_INIT(&app.vsftimer_pool, struct vsftimer_t, APPCFG_VSFTIMER_NUM);
vsftimer_init((struct vsftimer_mem_op_t *)&vsftimer_memop);
vsfhal_tickclk_config_cb(app_tickclk_callback_int, NULL);
vsf_bufmgr_init(app.bufmgr_buffer, sizeof(app.bufmgr_buffer));
// fs init: currently supported fs are non-block, so ugly pt code below
{
struct vsfile_t *file;
VSFPOOL_INIT(&app.vfsfile_pool, struct vsfile_vfsfile_t, 2);
vsfile_init((struct vsfile_memop_t *)&app_vsfile_memop);
// create msc_root and httpd_root under root
app.caller_pt.state = 0;
vsfile_addfile(&app.caller_pt, 0, NULL, "msc_root", VSFILE_ATTR_DIRECTORY);
// mount fakefat32 under /msc_root
app.caller_pt.state = 0;
vsfile_getfile(&app.caller_pt, 0, NULL, "/msc_root", &file);
app.caller_pt.state = 0;
app.caller_pt.user_data = &app.mal.fakefat32;
vsfile_mount(&app.caller_pt, 0, (struct vsfile_fsop_t *)&fakefat32_fs_op, file);
}
// vsfip init
{
struct vsfip_buffer_t *buffer;
int i;
buffer = &app.vsfip.buffer_pool.buffer[0];
for (i = 0; i < APPCFG_VSFIP_BUFFER_NUM; i++)
{
buffer->buffer = app.vsfip.buffer_mem[i];
buffer++;
}
}
VSFPOOL_INIT(&app.vsfip.buffer_pool, struct vsfip_buffer_t, APPCFG_VSFIP_BUFFER_NUM);
VSFPOOL_INIT(&app.vsfip.socket_pool, struct vsfip_socket_t, APPCFG_VSFIP_SOCKET_NUM);
VSFPOOL_INIT(&app.vsfip.tcppcb_pool, struct vsfip_tcppcb_t, APPCFG_VSFIP_TCPPCB_NUM);
vsfip_init((struct vsfip_mem_op_t *)&app_vsfip_mem_op);
// telnet stream innit
STREAM_INIT(&app.vsfip.telnetd.stream_rx);
STREAM_INIT(&app.vsfip.telnetd.stream_tx);
vsfip_telnetd_start(&app.vsfip.telnetd.telnetd);
// usbd cdc init
STREAM_INIT(&app.usbd.cdc.stream_rx);
STREAM_INIT(&app.usbd.cdc.stream_tx);
vsfscsi_init(&app.mal.scsi_dev);
vsfusbd_device_init(&app.usbd.device);
#if defined(APPCFG_BUFMGR_SIZE) && (APPCFG_BUFMGR_SIZE > 0)
vsfshell_init(&app.shell);
#endif
if (app.usb_pullup.port != IFS_DUMMY_PORT)
{
vsfhal_gpio_init(app.usb_pullup.port);
vsfhal_gpio_clear(app.usb_pullup.port, 1 << app.usb_pullup.pin);
vsfhal_gpio_config_pin(app.usb_pullup.port,
app.usb_pullup.pin, GPIO_OUTPP);
}
app.usbd.device.drv->disconnect();
vsftimer_create(sm, 200, 1, APP_EVT_USBPU_TO);
break;
case APP_EVT_USBPU_TO:
if (app.usb_pullup.port != IFS_DUMMY_PORT)
{
vsfhal_gpio_set(app.usb_pullup.port,
1 << app.usb_pullup.pin);
}
app.usbd.device.drv->connect();
break;
}
return NULL;
}
APP初始化,其实也就是调用各个组件的初始化代码,所有组件初始化完成后,关闭USB的上拉,然后建立200ms定时器。
之后200ms到了时候,会发送定时器事件,然后重新使能USB上拉,系统就跑起来了。
void app_rndis_on_connect(void *param)
{
struct vsfapp_t *app = (struct vsfapp_t *)param;
vsfip_dhcpd_start(&app->usbd.rndis.param.netif, &app->usbd.rndis.dhcpd);
}
一些回调函数,这里在RNDIS的on_connect响应中,启动dhcpd组件,并应用于usb的rndis的网络接口
其它代码,也基本上都是类似的简单代码,甚至不需要写任何USB端点初始化的代码(USB组件具备自动初始化各个端点的功能,用户只需要输入正确的描述符即可),系统就可以运行起来了。
|