切换到宽版
  • 8347阅读
  • 14回复

[STM]U盘GPIO文件系统映射-STM32利用内置FLASH做U盘 [复制链接]

上一主题 下一主题
离线zengming00
 

发帖
157
M币
992
专家
5
粉丝
23
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2016-08-09

受到linux对一切设备的控制都当成文件对待的启发
于是便有了这个将GPIO映射到U盘中的想法,这样一来便可以在任何支持U盘的设备中扩展系统的硬件功能了
我的QQ是243786753,这属于原创作品,作为一个对单片机一窍不通的人从设想到实现成功付出了巨大的努力,
最初的设想是通过openwrt路由器远程控制一个开关,然而找遍整个网络都没有这样的东西,
所以只好花了两百元巨款买了整套的单片机实验套件,从51逐步调试成功,转载请注明出处,举手之劳,本文首发于CSDN和数码之家,20160809



在openwrt路由器中可以用这样的方法来控制路由器的led
echo 1 > /sys/devices/platform/leds-gpio/leds/tp-link:green:lan1/brightness   //亮
echo 0 > /sys/devices/platform/leds-gpio/leds/tp-link:green:lan1/brightness   //灭
遗憾的是,由于系统的原因,并不是所有led灯都能得到控制




实验环境:
win7 32位、Keil uVision4、Putty、FlyMcu
硬件:stm32f103c8t6 8M晶振、PL2303 USB转串口


代码修改:
参考资料: 5分钟用STM32的内置Flash做一个超小U盘  http://blog.sina.com.cn/s/blog_ae3f6c0f01017nyf.html
ST的官方网站的UM0424中有一个关于使用STM32做Mass Storage的例子,实现的第一步自然是要先实现利用内置Flash做一个超小U盘
之后的U盘GPIO正是在此基础上修改而来的



从ST官网下载UM0424 ,en.stsw-stm32121.zip解压后是STM32_USB-FS-Device_Lib_V4.0.0

这个库中没有任何关于文件系统的代码,(QQ:243786753原创作品,转载请注明出处)即文件系统的实现都是由操作系统完成的,这个库中只需要对操作系统发过来的读取与写入命令负责即可。
库中的mass_mal.c为文件系统与存储介质的访问接口(Medium Access Layer interface),负责将文件存储在实际的介质上,如NAND-Flash,SD卡等
我们需要把文件存储在STM32的内部Flash中,因此更改这里的接口即可
mass_mal提供了四个函数
u16 MAL_Init (u8 lun);  
//初始化介质,由于是内部Flash,为了能写入,只需Unlock FLASH
u16 MAL_GetStatus (u8 lun);  //读取介质的参数,页大小,总页数,以及总大小,这个视STM32的内Flash情况而定,为文件系统format介质提供依据
u16 MAL_Read(u8 lun, u32 Memory_Offset, u32 *Readbuff, u16 Transfer_Length);  //读取介质上的一块区域
u16 MAL_Write(u8 lun, u32 Memory_Offset, u32 *Writebuff, u16 Transfer_Length); // 写入介质上的一块区域
读写操作的Memory_Offset是从零偏移开始的,用作U盘的STM32内部Flash需要从代码之后开始
原作都说编译结束后代码在0x0800252B,取mini U盘开始地址为0x08003000
STM32F103C8T6,Flash为64K,还剩余52K即0xD000,每一页为1K
根据以上信息,(QQ:243786753原创作品,转载请注明出处)打开STM32_USB-FS-Device_Lib_V4.0.0\Projects\Mass_Storage\src\mass_mal.c

添加FLASH的定义:

#define     FLASH_START_ADDR        0x08008800    // Flash起始地址,为了保留更多的代码空间,这个地址比原文更靠后#define     FLASH_SIZE              30*1024       // 30K 大小,因为起始地址改了,所以这里也相应的改了#define     FLASH_PAGE_SIZE         1024          // 1K per page
#define     FLASH_WAIT_TIMEOUT      100000

将原有的四个函数清空,改成下面的样子:
uint16_t MAL_Init(uint8_t lun)
{
    FLASH_Unlock();
    return lun == 0 ? MAL_OK : MAL_FAIL;
}

uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
  if(lun == 0){
     uint16_t i;


         for( i=0; i<Transfer_Length;i+=FLASH_PAGE_SIZE){
           if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT){
             FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
           }
           FLASH_ErasePage(FLASH_START_ADDR + Memory_Offset + i);
         }
         for(i=0;i<Transfer_Length;i+=4){
           if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT){
             FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
           }
           FLASH_ProgramWord(FLASH_START_ADDR + Memory_Offset + i , Writebuff[i>>2]);
         }
    
     return MAL_OK;
   }
   return MAL_FAIL;
}

uint16_t MAL_Read(uint8_t lun, uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length)
{
   uint16_t i,Q243786753;
   if(lun == 0){
     for(i=0;i<Transfer_Length;i+=4){
       Readbuff[i>>2] = ((vu32*)(FLASH_START_ADDR + Memory_Offset))[i>>2];
     }
     return MAL_OK;
   }
   return MAL_FAIL;
}

uint16_t MAL_GetStatus (uint8_t lun)
{
  if(lun == 0){
     Mass_Block_Count[0] = FLASH_SIZE/FLASH_PAGE_SIZE;
     Mass_Block_Size[0] =  FLASH_PAGE_SIZE;
     Mass_Memory_Size[0] = FLASH_SIZE;
     return MAL_OK;
   }
   return MAL_FAIL;
}

改好了之后文件的存储目标就变成了STM32的内部Flash

实际还有一个问题,那就是在memory.c中Data_Buffer定义大小为512Bytes,这里我们的Flash是1K一页,要将Data_Buffer重新定义成1KBytes的
第49行uint32_t Data_Buffer[BULK_MAX_PACKET_SIZE * 2]; 改成下面


uint32_t Data_Buffer[BULK_MAX_PACKET_SIZE * 4];
编译好后烧到板子上,电脑会提示格式化磁盘,(QQ:243786753原创作品,转载请注明出处)格式化后就可以像一般的U盘那样使用这个mini U盘了


编译:

用Keil作开发环境,所以直接打开STM32_USB-FS-Device_Lib_V4.0.0\Projects\Mass_Storage\MDK-ARM\MassStorageSimpleBuffer.uvproj
因为是新下载的源码编译前需要配置(Doc/readme.txt有详细说明)
readme.txt中说要打开platform_config.h文件中的一个宏定义


果然有注释掉的宏定义,可是应该打开哪个呢?我用的是stm32f103c8t6芯片,直接搜stm32f103c8t6找不到

整个文件中只有一条stm32f10x.h



观察这里的结果,于是决定将#define USE_STM3210B_EVAL的注释去掉
另外还得:


找到STM32F103C8



修改项目配置生成hex文件,否则我们拿什么下载到芯片上去呢?


此时就可以顺利的编译了,编译结果在STM32_USB-FS-Device_Lib_V4.0.0\Projects\Mass_Storage\MDK-ARM\STM3210B-EVAL


下载到单片机:这里我用的是ISP下载方式,你也可以用其它方式下载,只要能下载到单片机上就行


选择相应的端口,打开程序文件,点击读器件信息然后按住板子上的ispk再按一下rstk读到信息后松开
最后点击开始编程,编程完毕后就可以连接电脑了,此时一个利用内置FLASH的超小U盘就做成了

这里用的是普中的ARM核心板(STM32F103C8T6)



接单片机的TXD1和RXD1就是PA9和PA10


连接电脑:
原理图(D+D-接在单片机的PA11和PA12上,D+也就是PA12需要接1.5K电阻到3.3V否则电脑无法识别,R1,R2可以取22欧电阻如果没有可以忽略)




STM32F103C8T6是3.3V的单片机,不能接5V的电压,好在核心板上自带了1117进行降压。所以直接这样接就行了


需要飞一个1.5K电阻!(很重要,否则无法工作):



连接电脑,提示格式化,格式化后就会出现一个35K的U盘


为什么单独拿出核心板来用?因为核心板放在普中的主板上接线电脑是无法识别的,主板上的电路是这样的:








到这里,U盘已经实现了,怎么通过文件来控制GPIO呢?
这里给出一个点亮led灯的例子(通过PA0)
还是在mass_mal.c文件中加入


#define RCC_LED   RCC_APB2Periph_GPIOA   #define GPIO_LED  GPIOA
#define PIN_LED   GPIO_Pin_0#define LED_ON()    GPIO_SetBits(GPIO_LED, PIN_LED);
#define LED_OFF()   GPIO_ResetBits(GPIO_LED, PIN_LED);
void LED_Init(void) {    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = PIN_LED;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    RCC_APB2PeriphClockCmd(RCC_LED, ENABLE);//打开LED使用的GPIO的时钟使能
    GPIO_Init(GPIO_LED, &GPIO_InitStructure);   //初始化相应的GPIO}

在MAL_Init()函数中调用LED_Init()初始化,然后修改MAL_Write()函数如下

/*
查看参数
            lun     不知道什么意思
  Memory_Offset     偏移地址(FLASH的地址)
      Writebuff     写入的内容
Transfer_Length     内容的长度
*/
uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
  if(lun == 0){
     uint16_t i;
     uint32_t data=Writebuff[0];
      
     if(data==0x30303030){ //前四个字符0
       LED_OFF();

     }else if(data==0x31313131){//前四个字符1
       LED_ON();

     }else{
         /*
         实际写入FLASH的代码,从中可行看出来首先会将内容清除,是以页的形式清除
         由此可以推断文件系统的最小单位是页(传说中的扇区?),读和写都是一次性操作一页的内容
         然后是写入操作,每次写4个字节,直到全部写完
         */
         for( i=0; i<Transfer_Length;i+=FLASH_PAGE_SIZE){
           if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT){
             FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
           }
           FLASH_ErasePage(FLASH_START_ADDR + Memory_Offset + i);
         }
         for(i=0;i<Transfer_Length;i+=4){
           if(FLASH_WaitForLastOperation(FLASH_WAIT_TIMEOUT)!=FLASH_TIMEOUT){
             FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);
           }
           FLASH_ProgramWord(FLASH_START_ADDR + Memory_Offset + i , Writebuff[i>>2]);
         }
     }
     return MAL_OK;
   }
   return MAL_FAIL;
}

改好之后重新编译,下载,插电脑,格式化。
此时新建一个文件,往文件开头写1111,led就会亮起,写0000,led就会熄灭


代码里是以直接判断内容来进行控制的,这样做的好处是不用考虑哪个文件控制哪个io,
对任何一个文件的读写都可以捕捉到,只需要对写入的内容进行处理就可以了,比如控制PA是文件的前几个字节,控制PB是文件的后几个字节。。。
这里给出的只是往单片机发送数据,获取数据的方法是相同的原理


需要注意的地方:

1.因为单片机内部的FLASH是有擦写次数限制的,超过一定次数芯片就会损坏,安卓手机、固态硬盘、U盘等这类使用FLASH的都有一定的擦写次数限制,所以需要以特别的方式在建立好文件系统后彻底关闭对FLASH的写入
2.这种控制IO的方式速度并不高,但是用来控制开关,采集少量数据是完全可以的,这种方式极大的简化了各系统之间的差异。使得在任何支持U盘的系统上能够以非常简单的方式增加硬件功能。如果需要更快的控制速度可以使用串口通信的形式
3.由于操作系统为了提高效率,文件系统是有缓存的,也就是说对文件的读写并不会立即写到物理磁盘上

root@OpenWrt:/mnt# ls
GPIO.txt
root@OpenWrt:/mnt# echo "1111" > ./GPIO.txt            #并不会立即点亮LED
root@OpenWrt:/mnt# sync         #强制写入磁盘,此时对IO的操作就成功了
root@OpenWrt:/mnt# echo "0000" > ./GPIO.txt
root@OpenWrt:/mnt# sync
root@OpenWrt:/mnt# cat GPIO.txt           #被缓存了,按照我们的U盘实现,是不可能写到磁盘里的
0000
root@OpenWrt:/mnt# echo 1 > /proc/sys/vm/drop_caches    #释放页缓存
root@OpenWrt:/mnt# cat GPIO.txt
.....root@OpenWrt:/mnt#       #此时文件已经重新从磁盘里读取了,因为往U盘写入0000和1111会被我们的程序拦截,
                              #内容不会写到FLASH(但是文件的长度、时间这些会写入),所以重新读磁盘不可能读到0000或1111,读到的是FLASH上的默认数据(0xff)

本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖提到的人: @openwrt
本帖最近打赏记录:共12条打赏 ,M币+129 ,专家+1
12
离线tulips

发帖
1766
M币
2200
专家
2
粉丝
24
只看该作者 1楼 发表于: 2016-08-09
请登录后查看
离线straw

发帖
13554
M币
6062
专家
3
粉丝
49
只看该作者 2楼 发表于: 2016-08-09
请登录后查看
离线zengming00

发帖
157
M币
992
专家
5
粉丝
23
只看该作者 3楼 发表于: 2016-08-09
回 straw 的帖子
请登录后查看

发帖
1078
M币
12
专家
2
粉丝
15
只看该作者 4楼 发表于: 2016-08-09
请登录后查看
离线zengming00

发帖
157
M币
992
专家
5
粉丝
23
只看该作者 5楼 发表于: 2016-08-09
请登录后查看
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
离线2545889167

发帖
13268
M币
20754
专家
302
粉丝
4860
只看该作者 6楼 发表于: 2016-08-09
请登录后查看
离线lxa0

发帖
1139
M币
322
专家
10
粉丝
47
只看该作者 7楼 发表于: 2016-08-09
请登录后查看
离线duxingkei

发帖
431
M币
37
专家
3
粉丝
18
只看该作者 8楼 发表于: 2016-08-09
请登录后查看
离线cao57508

发帖
2265
M币
3657
专家
9
粉丝
85
只看该作者 9楼 发表于: 2016-08-10
请登录后查看
快速回复
限80 字节
温馨提示:所有技术区严禁灌水,“沙发”“顶”字样;禁止广告贴;以免被删除
 
上一个 下一个