从STM32F103到ACM32F403的U盘程序移植工程

前言
本项目是以spi flash(如w25q128等)存储元件作为存储单元,mcu主控完成usb接口通信并根据scsi协议实现u盘功能。其结构如下图所示:
spi flash部分移植
spi功能部分相对简单,acm32f403的接口引脚和stm32f103的相同,可直接对接,按照acm32f403的说明对spi接口进行初始化,并对底层读写函数进行更改即可。
usb部分移植
1. stm32f103代码结构
在st的芯片上,usb的数据是由两个中断,usb_lp_can1_rx0_irqhandler和usb_hp_can1_tx_irqhandler来进行,其中高优先级中断(usb_hp_can1_tx_irqhandler)用于处理同步(isochronous)模式传输或双缓冲块(bulk)传输模式下的正确传输事件,而低优先级中断(usb_lp_can1_rx0_irqhandler)用于处理其他传输时间。st的usb数据处理如下图所示:
由于usbfs协议的限制,一包数据中最多可携带64字节数据,因此,当存在大量数据需要进行传输(in或out包)时,需要分批次进行传输。在st的代码中,通过变量“bot_state”来进行控制,以read10指令为例,其读数据流程可如下图所示:
需要注意的是,read10指令解析完成之后(即上图左侧流程图)则进入数据传输阶段,此阶段是通过多次进入usb高优先级中断中,调用read_memory();来实现的。read_memory();函数内每次传输64字节数据。
2. acm32f403代码移植要点
本文基于上海航芯官方usb例程进行移植,移植后的程序结构如下图所示:
acm32f403的usb是采用一个中断来进行数据处理。在官方例程中,usb的中断函数内判定接收数据类型,包括suspend,resume,reset,ep0_pack以及其他端点的接收数据。判定结束后,会调用usb_monitor();函数来处理suspend,resume,reset以及ep0_pack数据。而其他端点数据会在usb_transfer_monitor();函数中进行解析,该函数由客户调用,一般在主函数的死循环中进行处理。在本文的移植中,主要需对usb的端点数据进行处理。
a. ep0_pack
ep0接收的setup数据会被存放在setip_0_3_data和setip_4_7_data寄存器中 ,数据结构如下所示:
dev_req.bmrequesttype=usbctrl->setip_0_3_data &0xff;    
dev_req.brequest=(usbctrl->setip_0_3_data>>8)&0xff;
dev_req.wvalue=(usbctrl->setip_0_3_data>>16)&0xffff;
dev_req.windex   = usbctrl->setip_4_7_data&0xffff;
dev_req.wlength=(usbctrl->setip_4_7_data>>16)&0xffff;  
该部分解析,可由用户在函数void usb_control_transfer(void)中添加需要的处理函数。该函数由航芯官方例程里提供。在做u disk程序移植时,需添加getmaxlun和storage_reset处理函数,如下图所示:
b. ep1_pack
在本文所述的代码中,acm32f403采用ep1完成数据的收发工作。主要是完成对scsi协议的解析工作。移植过程中,需要文件mass_mal.c、memory.c、scsi_data.c、usb_scsi.c、usb_bot.c及其头文件。本段主要就上述文件中代码需要改动的地方进行说明,部分参数需要重新定义,读者可自行解决。下表列出了st和aisino的usb收发功能函数,该部分移植时需要修改的主要部分:
a. void mass_storage_in (void)
在st的工程代码中该部分主要用于处理scsi的读指令。由于全速usb一包数据最大支持64字节,因此,当需要传输的数据个数大于该数值时,则需要分包传输。在使用acm32f403时,可直接传送需要的数据长度,内部会进行分包处理,因此,该函数可省略。
b. void mass_storage_out (void)
该函数用于处理scsi指令解析以及发送指令,需在usb_transfer_monitor()中调用,并将函数内部的接收数据部分更改为:
“data_len = hal_fsusb_receive_data(bulk_data_buff, 64, out_ep_index, 1);”
c.void transfer_data_request(uint8_t* data_pointer, uint16_t data_len)
将usb发送函数更改为acm32f403对应的发送函数。在st的工程中,该函数用于传输完数据后,进入bot_data_in_last状态,并在下一次的mass_storage_in()函数调用时,回复csw指令。而本文的移植代码中,省略了mass_storage_in()函数,因此,可在该函数的尾部增加csw发送指令:
set_csw (csw_cmd_passed, send_csw_enable);
d.void set_csw (uint8_t csw_status, uint8_t send_permission)
将usb发送函数更改为acm32f403对应的发送函数。
e.void bot_abort(uint8_t direction)
该函数主要对收发端点的stall状态进行处理,在acm32f403的收发库函数中,对端点的stall已做出相应控制,因此,该函数可省略。
f.void read_memory(uint8_t lun, uint32_t memory_offset, uint32_t transfer_length)
read_memory函数用于收到pc端的in包请求后将存储器中的数据读取并发送至pc端。而acm32f403的usb发送库函数中,自行进行分包操作(一包最大数据为64字节),因此在数据缓冲区容量允许条件下,可直接发送完毕,该函数修改如下:
{
uint32_t offset, length;
offset = memory_offset * mass_block_size[lun];
length = transfer_length * mass_block_size[lun];
csw.ddataresidue = cbw.ddatalength;
while(transfer_length --)
{
mal_read(lun ,
offset ,
data_buffer,
mass_block_size[lun]);
length = min(mass_block_size[lun], csw.ddataresidue);
offset += mass_block_size[lun];
hal_fsusb_send_data((uint8_t *)(data_buffer), length, in_ep_index);
csw.ddataresidue -= length;
}
set_csw (csw_cmd_passed, send_csw_enable);
}
g.void write_memory (uint8_t lun, uint32_t memory_offset, uint32_t transfer_length)
写数据指令完成后,将bot_state 值更改为 bot_idle。st的工程代码中,变量“bot_state”收发状态机的状态值,其值如下表所示:
而基于acm32f403的u disk工程,in包可由函数hal_fsusb_send_data()在其内部进行分包处理,不需要额外逻辑,因此,移植后bot_state仅需要在bot_idle、bot_data_out、bot_error之间转换,其他对bot_state的控制可省略。


日本国内的白色家电销售表现强劲
AI会不会哪天就取代了人类?
华为荣耀Magic新机将发布:售价9999元?是概念机?还是未来手机?
空调制热原理简单说明 空调制热开辅热和不开辅热的区别
购买示波器时的技巧之这些探头接口你得懂
从STM32F103到ACM32F403的U盘程序移植工程
RFID的主要特性包括哪4个方面
马斯克表示在为特斯拉电动皮卡Cybertruck超级工厂选址
专家解答新能源汽车驱动电机新技术
新型冠状病毒肺炎影像云,AI助力疫情防控
元宇宙数字货币龙头
技术专长:两种激光雷达车用方案
2023北京智源大会亮点回顾 | 高性能计算、深度学习和大模型:打造通用人工智能AGI的金三角
明天见!华为亚太合作伙伴大会2023来了
撬动数据中心蓝海,加速AI应用——FPGA繁荣启示
油烟在线监测-智慧化餐饮油烟浓度监测系统
忆阻器简介及测试方案
微美全息(NASDAQ:WIMI)开发基于增强现实控制的闭环混合信号脑机接口机械臂控制系统
美光科技第六次年度可持续发展报告
第二届中国电子信息博览会在深圳胜利闭幕