简单实现stm32f103芯片usb模拟U盘进行IAP更新用户程序

Posted 听我吹牛逼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单实现stm32f103芯片usb模拟U盘进行IAP更新用户程序相关的知识,希望对你有一定的参考价值。

更新单片机内的用户程序,方式一般都是仿真器,串口,网络口,usb DFU,另类一点CAN也行,但是这些方式都有一个共同点,必须要有相应的上位机配合操作,还要教会别人使用,那么能不能有更简单傻瓜化的升级方式呢?
今天二逼程序猿就来说说另类一点的USB模拟U盘进行IAP更新升级用户程序的方法!USB模拟U盘,顾名思义就是用STM32的usb device接口,根据usb massstorage大容量存储协议连接电脑,由于现在的电脑都自带massstorage的驱动程序,因此可以在任意一台电脑使用而不必担心去了张三李四家但是电脑不能识别然后到处求驱动的问题,听起来似乎很牛逼的样子,那到底这个U盘 能用吗?好用吗?怎么用?

能用必须的,否则我还在这里废话作甚!
好用必须的,只要是个电脑且xp以上系统都可以!
怎么用?将usb捅到电脑菊花,啊不,是插到电脑usb接口,等待识别完成,即可在我的电脑里面发现多了一个U盘,然后将新的程序bin文件复制进去即可,文盲都会做!

实现这个U盘,首先需要准备一块带有stm32f103控制器的板子,然后到st官网下载相应的usb驱动库函数和外设库函数,官方usb库函数包含有多种类型的usb设备,我们只需要massstorage这个就好,然后建立IAR工程如下图一样,添加个main文件,和启动文件,自己想办法让工程能编译通过,出错一般都是找不到头文件,摸索一下即可解决,我就不废话呢么多了。
这里写图片描述

说说硬件,其实没什么好说的,就是两根usb引脚D+和D-,另外加一根引脚控制D+的上拉电阻,GPIO设置为1就可以让电脑枚举usb设备,设置为0即可断开连接,偷懒直接上拉到电源也行
这里写图片描述

假设你是个高手,已经将工程建立好并且编译通过了,那么我们现在来聊聊这个关键文件:mass_mal.c 这个文件是跟实际存储介质交互的入口,里面有4个函数和3个关键参数:
u16 MAL_Init(u8 lun)
u16 MAL_Write(u8 lun, u32 Memory_Offset, u32 *Writebuff, u16 Transfer_Length)
u16 MAL_Read(u8 lun, u32 Memory_Offset, u32 *Readbuff, u16 Transfer_Length)
u16 MAL_GetStatus (u8 lun)
u32 Mass_Memory_Size;//U盘容量大小
u32 Mass_Block_Size; //U盘扇区大小一般是512
u32 Mass_Block_Count;//U盘扇区总数=Memory_Size/Block_Size

u8 RAM_DISK[RAMDISK_SIZE];//定义一个缓冲,格式化和收发数据都在这里进行

只要实现了这几个接口就可以将pc发来的数据储存到任意地方,这几个函数即使是空函数,也可以被电脑识别,只不过是显示的U盘无法使用也无法打开而已。当然,我们只是模拟U盘,用RAM来当存储介质好了,先把数据缓存到ram,然后再写入flash,所以设置一个RAM_DISK缓存。
按照下面的样子写这几个函数即可让电脑识别出U盘了,

/*******************************************************************************
* File Name          : 
* Author             : 听我说瞎话
* Version            : V1.0
* Date               : 2015.09.9 
* QQ                 :0xF8C8DFA
* Description        : 初始化ramdis,建立FAT表让电脑识别U盘
*
*                      欺骗电脑,让电脑显示252k大小,否则会提示空间不足,boot占用24k,stm32f103rc总大小256k
*                      app可用256-24=232k,让电脑虚拟出252kU盘,格式化后正好是可用232k不多不少正好等于app最大长度   
*******************************************************************************/
 u16 MAL_Init(u8 lun)
 {   
   Mass_Block_Count = 252*1024/SECTOR_SIZE;
   Mass_Block_Size =  SECTOR_SIZE;     //512
   Mass_Memory_Size = 252*1024;  
   return lun == 0 ? MAL_OK : MAL_FAIL;
 }
/*******************************************************************************
* File Name          : 
* Author             : 听我说瞎话
* Version            : V1.0
* Date               : 2015.09.9 
* QQ                 :0xF8C8DFA
* Description        : 电脑发来的数据其实不必真的写入ramdisk,选取自己想要的数据即可
*******************************************************************************/
 u16 MAL_Write(u8 lun, u32 Memory_Offset, u32 *Writebuff, u16 Transfer_Length)
 {     
    //其实不需要真的写入ramdisk,注释掉写入ramdisk的话就可以阻止格式化操作,然并卵,让你格又怎样,反正不会丢数据
      if(Memory_Offset<RAMDISK_SIZE)
      {
         for( int i=0;i<Transfer_Length;i+=4)
         {
            *(u32*)(&RAM_DISK[Memory_Offset+i])=Writebuff[i>>2];
         }
      }                            
      return MAL_OK;          
 }
/*******************************************************************************
* File Name          : 
* Author             : 听我说瞎话
* Version            : V1.0
* Date               : 2015.09.9 
* QQ                 :0xF8C8DFA
* Description        : 读取出的数据只是RAM内容,不会有真正的app程序被读出
*******************************************************************************/
 u16 MAL_Read(u8 lun, u32 Memory_Offset, u32 *Readbuff, u16 Transfer_Length)
 {
        if(Memory_Offset<RAMDISK_SIZE)//防止溢出,这里读出的是ramdisk的内容,不会包含app程序
      {      
         for( i=0;i<Transfer_Length;i+=4)
         {
           Readbuff[i>>2] = *(u32*)(&RAM_DISK[Memory_Offset+i]);
         }
      }
      return MAL_OK;
 }
/*******************************************************************************
* File Name          : 
* Author             : 听我说瞎话
* Version            : V1.0
* Date               : 2015.09.9 
* QQ                 :0xF8C8DFA
* Description        : 
*******************************************************************************/
 u16 MAL_GetStatus (u8 lun)
 {

   if(lun == 0)
   {        
     return MAL_OK;
   }

   return MAL_FAIL;
 }

假设你有stm32使用经验,会处理编译产生的一些错误和警告会精简官方库代码中用不到的东西,那么现在你只要控制一下刚才的usb上拉电阻,然后初始化一下usb主循环就while(1);空函数即可,现在插电脑上应该可以识别成252k的一个U盘了。打开这个U盘,电脑会提示U盘未格式化,按照提示进行格式化然后就可以跟普通U盘一样用了,不过数据是写在RAM_DISK这个缓冲内,并没有写进flash

Mass_Memory_Size
Mass_Block_Size
Mass_Block_Count。

这三个参数可以随意修改,欺骗pc你是个1TB的硬盘都没问题,奸商的扩容盘就是这么干的!

捅了几次电脑菊花后你就会发现,尼玛每次插进去都要格式化一遍,真他妈坑爹呢,没错,我当时也是这么认为的,这样简直没天理了!二逼程序猿都有强迫症,怎么可能接受这种脑残设计?
下面就让我们来处理这个问题,首先看看格式化之前和格式化之后,RAM_DISK缓冲内的数据发生了什么变化,接上仿真器,IAR进入debug,将板子插入电脑,正常格式化,完毕后stop停下cpu,打开IAR memory观察窗

这里写图片描述
可以看到缓冲内已经多了不少数据,想想格式化前和格式化后就这点区别,如果我把格式数据事先保存内flash,初始化的时候直接还原到RAM_DISK那不就可以了吗?
其实就是这么简单,格式化后的RAM_DISK保存的是FATFS的MBR表格(详细格式请另外百度),这个表格告诉电脑我是FATFS文件系统,我有多大,每个扇区有多大,有几个文件,文件存在哪个位置等等,如果我们将整个RAM_DISK都保存在flash,那简直太浪费flash了,因为RAM_DISK只有部分位置有数据,其余位置大都是0,所以保存关键位置信息数据就好了,由于我本来的板子除了U盘,还有模拟串口功能,因此我存了一个inf串口驱动文件到虚拟的U盘里面,这样打开U盘后即可看到里面有一个inf驱动文件,你可以将这个文件替换为升级说明.txt。
格式化U盘,然后放入我的inf串口驱动文件,然后我就得到了下面的fat数据表:

#define RAMDISK_SIZE    32*1024  /* 放FAT表*/
#define SECTOR_SIZE     512     /* 一包数据大小,boot是以512位单位分包*/

static const u8 FAT_DBR_TABLE[]=
{ //FAT表的第0号扇区,有这个表就不用每次连接电脑都要格式化一遍,这个产生的方法是在电脑格式化成功后dump出整个ramdisk取前面512字节即可
0xEB,0x3C,0x90,0x4D,0x53,0x44,0x4F,0x53,0x35,0x2E,0x30,0x00,0x02,0x01,0x04,0x00,
0x02,0x00,0x02,0xF8,0x01,0xF8,0x02,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x80,0x00,0x29,0x9C,0xF8,0x5B,0x50,0x4E,0x4F,0x20,0x4E,0x41,
0x4D,0x45,0x20,0x20,0x20,0x20,0x46,0x41,0x54,0x31,0x32,0x20,0x20,0x20,0x33,0xC9,
0x8E,0xD1,0xBC,0xF0,0x7B,0x8E,0xD9,0xB8,0x00,0x20,0x8E,0xC0,0xFC,0xBD,0x00,0x7C,
0x38,0x4E,0x24,0x7D,0x24,0x8B,0xC1,0x99,0xE8,0x3C,0x01,0x72,0x1C,0x83,0xEB,0x3A,
0x66,0xA1,0x1C,0x7C,0x26,0x66,0x3B,0x07,0x26,0x8A,0x57,0xFC,0x75,0x06,0x80,0xCA,
0x02,0x88,0x56,0x02,0x80,0xC3,0x10,0x73,0xEB,0x33,0xC9,0x8A,0x46,0x10,0x98,0xF7,
0x66,0x16,0x03,0x46,0x1C,0x13,0x56,0x1E,0x03,0x46,0x0E,0x13,0xD1,0x8B,0x76,0x11,
0x60,0x89,0x46,0xFC,0x89,0x56,0xFE,0xB8,0x20,0x00,0xF7,0xE6,0x8B,0x5E,0x0B,0x03,
0xC3,0x48,0xF7,0xF3,0x01,0x46,0xFC,0x11,0x4E,0xFE,0x61,0xBF,0x00,0x00,0xE8,0xE6,
0x00,0x72,0x39,0x26,0x38,0x2D,0x74,0x17,0x60,0xB1,0x0B,0xBE,0xA1,0x7D,0xF3,0xA6,
0x61,0x74,0x32,0x4E,0x74,0x09,0x83,0xC7,0x20,0x3B,0xFB,0x72,0xE6,0xEB,0xDC,0xA0,
0xFB,0x7D,0xB4,0x7D,0x8B,0xF0,0xAC,0x98,0x40,0x74,0x0C,0x48,0x74,0x13,0xB4,0x0E,
0xBB,0x07,0x00,0xCD,0x10,0xEB,0xEF,0xA0,0xFD,0x7D,0xEB,0xE6,0xA0,0xFC,0x7D,0xEB,
0xE1,0xCD,0x16,0xCD,0x19,0x26,0x8B,0x55,0x1A,0x52,0xB0,0x01,0xBB,0x00,0x00,0xE8,
0x3B,0x00,0x72,0xE8,0x5B,0x8A,0x56,0x24,0xBE,0x0B,0x7C,0x8B,0xFC,0xC7,0x46,0xF0,
0x3D,0x7D,0xC7,0x46,0xF4,0x29,0x7D,0x8C,0xD9,0x89,0x4E,0xF2,0x89,0x4E,0xF6,0xC6,
0x06,0x96,0x7D,0xCB,0xEA,0x03,0x00,0x00,0x20,0x0F,0xB6,0xC8,0x66,0x8B,0x46,0xF8,
0x66,0x03,0x46,0x1C,0x66,0x8B,0xD0,0x66,0xC1,0xEA,0x10,0xEB,0x5E,0x0F,0xB6,0xC8,
0x4A,0x4A,0x8A,0x46,0x0D,0x32,0xE4,0xF7,0xE2,0x03,0x46,0xFC,0x13,0x56,0xFE,0xEB,
0x4A,0x52,0x50,0x06,0x53,0x6A,0x01,0x6A,0x10,0x91,0x8B,0x46,0x18,0x96,0x92,0x33,
0xD2,0xF7,0xF6,0x91,0xF7,0xF6,0x42,0x87,0xCA,0xF7,0x76,0x1A,0x8A,0xF2,0x8A,0xE8,
0xC0,0xCC,0x02,0x0A,0xCC,0xB8,0x01,0x02,0x80,0x7E,0x02,0x0E,0x75,0x04,0xB4,0x42,
0x8B,0xF4,0x8A,0x56,0x24,0xCD,0x13,0x61,0x61,0x72,0x0B,0x40,0x75,0x01,0x42,0x03,
0x5E,0x0B,0x49,0x75,0x06,0xF8,0xC3,0x41,0xBB,0x00,0x00,0x60,0x66,0x6A,0x00,0xEB,
0xB0,0x42,0x4F,0x4F,0x54,0x4D,0x47,0x52,0x20,0x20,0x20,0x20,0x0D,0x0A,0x52,0x65,
0x6D,0x6F,0x76,0x65,0x20,0x64,0x69,0x73,0x6B,0x73,0x20,0x6F,0x72,0x20,0x6F,0x74,
0x68,0x65,0x72,0x20,0x6D,0x65,0x64,0x69,0x61,0x2E,0xFF,0x0D,0x0A,0x44,0x69,0x73,
0x6B,0x20,0x65,0x72,0x72,0x6F,0x72,0xFF,0x0D,0x0A,0x50,0x72,0x65,0x73,0x73,0x20,
0x61,0x6E,0x79,0x20,0x6B,0x65,0x79,0x20,0x74,0x6F,0x20,0x72,0x65,0x73,0x74,0x61,
0x72,0x74,0x0D,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAC,0xCB,0xD8,0x55,0xAA
//下面是两个FAT偏移,手动输入数据
//0x800 = F8 FF FF 03 40 00 05 F0 FF
//0xC00 = F8 FF FF 03 40 00 05 F0 FF
};
//这个是usb转串口的inf驱动的文件名和盘符信息
static const u8 FAT_FILE_NAME[]=
{//offect 0x1000
0x42,0x69,0x00,0x76,0x00,0x65,0x00,0x72,0x00,0x2E,0x00,0x0F,0x00,0x61,0x69,0x00,
0x6E,0x00,0x66,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,
0x01,0x59,0x00,0x61,0x00,0x6E,0x00,0x48,0x00,0x75,0x00,0x0F,0x00,0x61,0x61,0x00,
0x2D,0x00,0x43,0x00,0x4F,0x00,0x4D,0x00,0x2D,0x00,0x00,0x00,0x44,0x00,0x72,0x00,
0x59,0x41,0x4E,0x48,0x55,0x41,0x7E,0x31,0x49,0x4E,0x46,0x20,0x00,0xC6,0x63,0x8C,
0x29,0x47,0x29,0x47,0x00,0x00,0x2A,0x8C,0x29,0x47,0x02,0x00,0x49,0x07,0x00,0x00,
0x59,0x41,0x4E,0x48,0x55,0x41,0x20,0x4F,0x42,0x44,0x20,0x08,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x6F,0x8C,0x29,0x47  
};
//这个是usb转串口的inf驱动文件内容,连接电脑识别U盘后可以直接复制出来,可以说自带驱动
static const u8 FAT_FILE_DATA[]=
{//offect 0x5000
0x3B,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x0D,
0x0A,0x3B,0x20,0x59,0x61,0x6E,0x68,0x75,0x61,0x20,0x43,0x6F,0x6D,0x75,0x6E,0x69,
0x63,0x61,0x74,0x69,0x6F,0x6E,0x20,0x44,0x65,0x76,0x69,0x63,0x65,0x20,0x43,0x6C,
0x61,0x73,0x73,0x20,0x64,0x72,0x69,0x76,0x65,0x72,0x20,0x28,0x43,0x44,0x43,0x29,
0x20,0x49,0x4E,0x46,0x20,0x46,0x49,0x4C,0x45,0x0D,0x0A,0x3B,0x20,0x28,0x43,0x29,
0x32,0x30,0x31,0x30,0x20,0x43,0x6F,0x70,0x79,0x72,0x69,0x67,0x68,0x74,0x20,0x59,
0x61,0x6E,0x48,0x75,0x61,0x2D,0x54,0x45,0x43,0x48,0x20,0x43,0x4F,0x2E,0x4C,0x74,
0x64,0x0D,0x0A,0x3B,0x20,0x4C,0x52,0x4D,0x20,0x32,0x30,0x31,0x35,0x2E,0x30,0x39,
0x2E,0x30,0x39,0x0D,0x0A,0x3B,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D,0x0D,0x0A,0x0D,0x0A,0x5B,0x56,0x65,0x72,0x73,0x69,0x6F,0x6E,
0x5D,0x0D,0x0A,0x53,0x69,0x67,0x6E,0x61,0x74,0x75,0x72,0x65,0x3D,0x22,0x24,0x57,
0x69,0x6E,0x64,0x6F,0x77,0x73,0x20,0x4E,0x54,0x24,0x22,0x0D,0x0A,0x43,0x6C,0x61,
0x73,0x73,0x3D,0x50,0x6F,0x72,0x74,0x73,0x0D,0x0A,0x43,0x6C,0x61,0x73,0x73,0x47,
0x75,0x69,0x64,0x3D,0x7B,0x34,0x44,0x33,0x36,0x45,0x39,0x37,0x38,0x2D,0x45,0x33,
0x32,0x35,0x2D,0x31,0x31,0x43,0x45,0x2D,0x42,0x46,0x43,0x31,0x2D,0x30,0x38,0x30,
0x30,0x32,0x42,0x45,0x31,0x30,0x33,0x31,0x38,0x7D,0x0D,0x0A,0x50,0x72,0x6F,0x76,
0x69,0x64,0x65,0x72,0x3D,0x25,0x50,0x52,0x56,0x44,0x52,0x25,0x0D,0x0A,0x43,0x61,
0x74,0x61,0x6C,0x6F,0x67,0x46,0x69,0x6C,0x65,0x3D,0x73,0x74,0x6D,0x63,0x64,0x63,
0x2E,0x63,0x61,0x74,0x0D,0x0A,0x44,0x72,0x69,0x76,0x65,0x72,0x56,0x65,0x72,0x3D,
0x30,0x34,0x2F,0x32,0x35,0x2F,0x32,0x30,0x31,0x30,0x2C,0x31,0x2E,0x33,0x2E,0x31,
0x0D,0x0A,0x0D,0x0A,0x5B,0x53,0x6F,0x75,0x72,0x63,0x65,0x44,0x69,0x73,0x6B,0x73,
0x4E,0x61,0x6D,0x65,0x73,0x5D,0x0D,0x0A,0x31,0x3D,0x25,0x44,0x72,0x69,0x76,0x65,
0x72,0x73,0x44,0x69,0x73,0x6B,0x25,0x2C,0x2C,0x2C,0x0D,0x0A,0x0D,0x0A,0x5B,0x53,
0x6F,0x75,0x72,0x63,0x65,0x44,0x69,0x73,0x6B,0x73,0x46,0x69,0x6C,0x65,0x73,0x5D,
0x0D,0x0A,0x0D,0x0A,0x5B,0x4D,0x61,0x6E,0x75,0x66,0x61,0x63,0x74,0x75,0x72,0x65,
0x72,0x5D,0x0D,0x0A,0x25,0x4D,0x46,0x47,0x4E,0x41,0x4D,0x45,0x25,0x3D,0x44,0x65,
0x76,0x69,0x63,0x65,0x4C,0x69,0x73,0x74,0x2C,0x4E,0x54,0x2C,0x4E,0x54,0x61,0x6D,
0x64,0x36,0x34,0x0D,0x0A,0x0D,0x0A,0x5B,0x44,0x65,0x73,0x74,0x69,0x6E,0x61,0x74,
0x69,0x6F,0x6E,0x44,0x69,0x72,0x73,0x5D,0x0D,0x0A,0x44,0x65,0x66,0x61,0x75,0x6C,
0x74,0x44,0x65,0x73,0x74,0x44,0x69,0x72,0x20,0x3D,0x20,0x31,0x32,0x0D,0x0A,0x0D,
0x0A,0x3B,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,
0x2D,0x2D,0x2D,0x2D以上是关于简单实现stm32f103芯片usb模拟U盘进行IAP更新用户程序的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 HAL库开发 -- USB U盘

STM32F4 HAL库开发 -- USB U盘

STM32F4 HAL库开发 -- USB U盘

STM32F4 HAL库开发 -- USB U盘

stm32f103vct6外扩sram芯片

stm32串口通讯,就是我们现在的stm32f103RBT6的最小系统版,有引出usb线,现在想做串口通信