25446|30

1372

帖子

2

TA的资源

五彩晶圆(初级)

楼主
 

[11月DIY]打造最简STM32F0 USB开发板 [复制链接]

 
本帖最后由 cruelfox 于 2015-11-23 16:45 编辑

想学STM32,不知道从哪开始的有木有? 想学ARM单片机,嫌买开发板、调试器费钱的有木有? 买了STM32开发板没有资料不会玩,放在那里吃灰的有木有? 买了开发板,照着例子跑通了几个程序,依然一头雾水的有木有?
我cruelfox是个非常抠门的人,搞DIY也省得很——一切从简。(太复杂了的搞不定,软件硬件都是如此) 所以正在玩的STM32也简化到底了,有兴趣的看看吧。

这是刚完成的STM32F072 USB开发板,使用48脚LQFP的STM32F072C8T6,也可以使用其它封装兼容的带USB型号,甚至是M3系的STM32F103C8T6这种。上半年从论坛买了块STM32F091 Nucleo, 但是不带USB,所以为了学习USB自己做一块咯。下面是电路图,除了一片1117 3.3V LDO,外围器件少到极致了吧,晶振不用的话是可以不装的。板子可以直接通过 USB mini口供电。外围引出的插针有一路 SPI, 一路 I2S, 一路 UART, 一路 I2C, 一路 8-bit GPIO, 一路 UART/I2C共用,以及几个零星的GPIO。这些已方便开发简单的USB设备了。

PCB layout 示意图





好,STM32F072 10块钱以内就可以搞定,整个开发板成本很低了吧。 如果你有ST-Link, 或者是带有ST-Link的STM32 Discovery/Nucleo开发板,用SWD调试线连上就可以下载程序了。如果没有ST-Link, 还可以从串口下载程序,只需要把BOOT0跳线接上即可,因为STM32内带了Bootloader.  如果连串口线都没有?呵呵,要是像F072这样带USB的,还可以从USB直接下载,别的硬件也省了,怎么样,够简吧?

OK,来写第一个测试程序:定时控制LED闪烁。
  1. #include "stm32f0xx.h"

  2. int main(void)
  3. {

  4.     RCC->AHBENR |= RCC_AHBENR_GPIOAEN;  // enable GPIO port A & B clock
  5.     GPIOA->MODER = GPIO_MODER_MODER8_0; // PA8 as general output (LED)

  6.     RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;     // enable basic timer 6
  7.     TIM6->PSC = 9999;       // prescaler
  8.     TIM6->ARR = 399;        // auto reload value
  9.     TIM6->CR1 = TIM_CR1_URS|TIM_CR1_CEN;    // start counter

  10.     while(1)
  11.     {
  12.         static char a=0;
  13.         if(TIM6->SR & TIM_SR_UIF)   // check if overflow
  14.         {
  15.             TIM6->SR &= ~TIM_SR_UIF;    // clear flag
  16.             if(a==0)
  17.             {
  18.                 GPIOA->BSRR = (1<<8);
  19.                 a=1;
  20.             }
  21.             else
  22.             {
  23.                 GPIOA->BRR = (1<<8);
  24.                 a=0;
  25.             }
  26.         }
  27.     }
  28. }
复制代码

上面这个程序所做的事情,先是初始化GPIO, 设置PA8为输出口(板子上连了一个LED),然后是设置定时器Timer 6, 这是一个自动重装的计数器,我把它调到0.5秒中溢出一次。在下面的循环里面,就是检测溢出标志,然后切换LED的亮和灭状态。学过C语言的,都应该看得懂;至于RCC, GPIOA, TIM6 这几个结构指针的定义,都在#include的头文件里面,这是和硬件相关的,具体请查阅"RM0091 STM32F0x1/STM32F0x2/STM32F0x8 Reference Manual"编程手册。

如何编译上面这个 C 程序,且听下回分解。这里暂且假定编译成功了,得到一个 HEX 文件,也就是要烧写的二进制代码。
如果你是使用KEIL, IAR等集成开发环境,那么用自带的烧写工具就可以进行写入了。如果是像我cruelfox这样追求精简,仅使用GCC命令行工具的,就需要再找下载程序用的软件了。

如果是使用ST-Link,可以使用ST自己的STVP (Visual Programmer),这个东东在ST网站上可以下载到,不过是包含在九十兆左右的一个大包"ST Toolset"里面。(下载URL http://www.st.com/web/catalog/tools/FM147/CL1794/SC961/SS1533/PF210568 )这个软件的界面是这个样子的:

主菜单上面 Erase, Program, Verify, Read 功能很明了了,File-->Open可以加载HEX文件。第一次运行STVP的时候,要选择ST-LINK调试器,和 SWD接口。

如果没有ST-Link, 使用串口下载的话,需要"Flash Loader Demostrator"软件,这个也可以从ST网站直接下载(URL http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/demo_and_example/stsw-mcu005.zip)。下载前要把BOOT0跳线接上,使STM32进入Bootloader模式,USART1连接到PC的串口(我用的是FT232RL USB转串口),把MCU加电。运行软件,界面是这样的:

选择串口,然后点"Next",如果成功连上了,则界面变成下面这样

点"Next"继续

这时已显示出识别出的STM32型号,点"Next"到下一步进行具体的操作。

OK, 下载HEX,擦除,上载(读Flash内容) 功能都一看就明白了吧。

第三种下载方式,从USB,需要ST的"DFUSe Demo"软件,也是从ST网站下载的(URL http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/demo_and_example/stsw-stm32080.zip)。也需要把BOOT0跳线接上,还必须连接USB口,然后PC提示找到了新硬件。安装好驱动以后,再启动软件,界面如下:

不过现在不能把HEX文件直接写入,而需要先生成dfu文件,使用一起安装得到的"DFU File Manager"程序,从HEX生成dfu.

至于 VID, PID 我还是保留和原来的一致,不然得重新安装驱动(为什么要使用DFU文件我还没理解清楚)。得到dfu文件就可以用上面的软件烧写了。

怎么样,我cruelfox的开发板够精简吧? 二楼帖子会我会跟贴讲解怎么编译程序,上面的程序是怎么被STM32运行的。
六楼帖子里面,我分享我的第一个USB工程,mass storage 虚拟存储盘.

stor_test.zip

175.31 KB, 阅读权限: 5, 下载次数: 117

工程文件:虚拟U盘

最新回复

太好了,圈粉了   详情 回复 发表于 2020-11-8 20:58

赞赏

3

查看全部赞赏

点赞 关注(16)
 

回复
举报

1372

帖子

2

TA的资源

五彩晶圆(初级)

沙发
 

C程序是如何运行的

本帖最后由 cruelfox 于 2015-11-21 16:18 编辑

STM32F0xx 系列是ARM Cortex-M0架构,地址空间32位,也就是4G Bytes的访问范围。数据和代码使用同一编址,下图是地址空间的布局:


实际上单片机用到的资源很少,地址空间大部分都没有内容。我使用的STM32F072C8T6带有64kB的Flash ROM, 16kB的SRAM,起始分别是 0x08000000 和 0x20000000. (由于有硬件映射功能,在0x00000000也就是最低地址,还可以访问ROM或者RAM的内容). 单片机片上外设的寄存器,则分布在更高的地址空间。读写这些寄存器,在CPU看来和读写内存(RAM)操作是一样的。

所以,C语言访问设备寄存器,和访问内存中的一个变量一样。只要知道寄存器的地址,通过一个指针访问就可以实现读写。上一贴子我的程序中引用了 RCC, GPIOA, TIM6 这三个(结构)指针,它们的值(也就是地址)以及类型(代表访问的内容)定义在 stm32f0xx.h 这个头文件中。因为设备寄存器太多了哇,如果每一个都定义一个指针就太烦琐了,所以把按功能划分定义成组,每组用一个C语言的结构类型表示,写起来也更清晰。而寄存器里面的位描述也可以定义成一些宏,在读程序的时候就知道是什么意思了。如果有兴趣,可以把 stm32f0xx.h 文件和STM32F0的手册对照着阅读。

好,假设已经熟悉寄存器操作了,知道怎么配置寄存器实现想要的功能,那么就可以写C程序让STM32工作了。现在需要一个工具来将C程序翻译成机器代码——编译器,或者是叫做工具链(Tool chain)。Keil MDK-ARM 或者 IAR-EWARM 开发环境都带有各自的编译器,不过我更偏向于用开源的GCC-ARM. 在launchpad.net上可以下载到编译好的arm-gcc工具链zip包,将它解压缩,加到PATH里面就可以直接用了,很方便(很精简吧)。


OK,现在来编译上面那个mini.c文件,命令行:
arm-none-eabi-gcc -c -Os -mcpu=cortex-m0 -mthumb mini.c
gcc的参数 -c 是表示仅编译,-Os 是优化代码大小,-mcpu=cortex-m0 -mthumb 是指定指令集的,因为ARM有不同的版本。对了,include的头文件还没弄到呢。要编译通过需要把 stm32f0xx.h 这个文件找来。我的建议是下载ST提供的 "STM32F0x2 USB FS Device Library" 程序库(URL http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stsw-stm32092.zip),把里面需要的头文件等等扒出来。在 stm32f0xx.h 中还包含了另外几个头文件,一并弄出来放到工程目录下。

如果编译成功,将得到 mini.o 目标文件。可以用 arm-none-eabi-objdump -S mini.o 反汇编看看翻译成什么代码了。
  1. E:\arm\test072\mini>arm-none-eabi-objdump -S mini.o

  2. mini.o:     file format elf32-littlearm


  3. Disassembly of section .text.startup:

  4. 00000000 <main>:
  5.    0:   4b16            ldr     r3, [pc, #88]   ; (5c <main+0x5c>)
  6.    2:   2280            movs    r2, #128        ; 0x80
  7.    4:   6959            ldr     r1, [r3, #20]
  8.    6:   0292            lsls    r2, r2, #10
  9.    8:   430a            orrs    r2, r1
  10.    a:   b510            push    {r4, lr}
  11.    c:   2180            movs    r1, #128        ; 0x80
  12.    e:   615a            str     r2, [r3, #20]
  13.   10:   2290            movs    r2, #144        ; 0x90
  14.   12:   0249            lsls    r1, r1, #9
  15.   14:   05d2            lsls    r2, r2, #23
  16.   16:   6011            str     r1, [r2, #0]
  17.   18:   69da            ldr     r2, [r3, #28]
  18.   1a:   2110            movs    r1, #16
  19.   1c:   430a            orrs    r2, r1
  20.   1e:   61da            str     r2, [r3, #28]
  21.   20:   4b0f            ldr     r3, [pc, #60]   ; (60 <main+0x60>)
  22.   22:   4a10            ldr     r2, [pc, #64]   ; (64 <main+0x64>)
  23.   24:   851a            strh    r2, [r3, #40]   ; 0x28
  24.   26:   2290            movs    r2, #144        ; 0x90
  25.   28:   32ff            adds    r2, #255        ; 0xff
  26.   2a:   62da            str     r2, [r3, #44]   ; 0x2c
  27.   2c:   2205            movs    r2, #5
  28.   2e:   801a            strh    r2, [r3, #0]
  29.   30:   4a0d            ldr     r2, [pc, #52]   ; (68 <main+0x68>)
  30.   32:   7812            ldrb    r2, [r2, #0]
  31.   34:   8a1c            ldrh    r4, [r3, #16]
  32.   36:   2101            movs    r1, #1
  33.   38:   4809            ldr     r0, [pc, #36]   ; (60 <main+0x60>)
  34.   3a:   420c            tst     r4, r1
  35.   3c:   d0fa            beq.n   34 <main+0x34>
  36.   3e:   8a04            ldrh    r4, [r0, #16]
  37.   40:   438c            bics    r4, r1
  38.   42:   8204            strh    r4, [r0, #16]
  39.   44:   2090            movs    r0, #144        ; 0x90
  40.   46:   2480            movs    r4, #128        ; 0x80
  41.   48:   05c0            lsls    r0, r0, #23
  42.   4a:   408c            lsls    r4, r1
  43.   4c:   2a00            cmp     r2, #0
  44.   4e:   d102            bne.n   56 <main+0x56>
  45.   50:   6184            str     r4, [r0, #24]
  46.   52:   1c0a            adds    r2, r1, #0
  47.   54:   e7ee            b.n     34 <main+0x34>
  48.   56:   8504            strh    r4, [r0, #40]   ; 0x28
  49.   58:   2200            movs    r2, #0
  50.   5a:   e7eb            b.n     34 <main+0x34>
  51.   5c:   40021000        .word   0x40021000
  52.   60:   40001000        .word   0x40001000
  53.   64:   0000270f        .word   0x0000270f
  54.   68:   00000000        .word   0x00000000
复制代码

如上,其实里面就一个main函数。但是 main 的入口地址还没有确定,而且它还使用了一个static型的内存变量,地址也还没有确定。可以用 arm-none-eabi-nm mini.o 来查看模块里面的全局符号表:
  1. E:\arm\test072\mini>arm-none-eabi-nm mini.o
  2. 00000000 b a.4686
  3. 00000000 T main
复制代码

那么,怎么让程序放到ROM中合适的地址,并运行呢?如果熟悉C语言编程就知道还有一步——链接,才能确定符号的地址。但是,到目前为止我们还没有告诉GCC地址的布局,也就是RAM从哪里开始,代码放在哪里。因为ARM的器件很多,这并不是统一的,所以需要提供一些信息给链接程序。具体地,需要一个Linker Script, 可以从软件包中找到 STM32F072C8_FLASH.ld (或者用近似的来修改得到)
  1. /*
  2. *****************************************************************************
  3. **  File        : stm32_flash.ld
  4. *****************************************************************************
  5. */

  6. /* Entry Point */
  7. ENTRY(Reset_Handler)

  8. /* Highest address of the user mode stack */
  9. _estack = 0x20003FFF;    /* end of RAM */

  10. /* Generate a link error if heap and stack don't fit into RAM */
  11. _Min_Heap_Size = 0x200;      /* required amount of heap  */
  12. _Min_Stack_Size = 0x400; /* required amount of stack */

  13. /* Specify the memory areas */
  14. MEMORY
  15. {
  16. FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 64K
  17. RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 16K
  18. }

  19. /* Define output sections */
  20. SECTIONS
  21. {
  22.   /* The startup code goes first into FLASH */
  23.   .isr_vector :
  24.   {
  25.     . = ALIGN(4);
  26.     KEEP(*(.isr_vector)) /* Startup code */
  27.     . = ALIGN(4);
  28.   } >FLASH

  29.   /* The program code and other data goes into FLASH */
  30.   .text :
  31.   {
  32.     . = ALIGN(4);
  33.     *(.text)           /* .text sections (code) */
  34.     *(.text*)          /* .text* sections (code) */
  35.     *(.glue_7)         /* glue arm to thumb code */
  36.     *(.glue_7t)        /* glue thumb to arm code */
  37.     *(.eh_frame)

  38.     KEEP (*(.init))
  39.     KEEP (*(.fini))

  40.     . = ALIGN(4);
  41.     _etext = .;        /* define a global symbols at end of code */
  42.   } >FLASH

  43.   /* Constant data goes into FLASH */
  44.   .rodata :
  45.   {
  46.     . = ALIGN(4);
  47.     *(.rodata)         /* .rodata sections (constants, strings, etc.) */
  48.     *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
  49.     . = ALIGN(4);
  50.   } >FLASH

  51.   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  52.   .ARM : {
  53.     __exidx_start = .;
  54.     *(.ARM.exidx*)
  55.     __exidx_end = .;
  56.   } >FLASH

  57.   .preinit_array     :
  58.   {
  59.     PROVIDE_HIDDEN (__preinit_array_start = .);
  60.     KEEP (*(.preinit_array*))
  61.     PROVIDE_HIDDEN (__preinit_array_end = .);
  62.   } >FLASH
  63.   .init_array :
  64.   {
  65.     PROVIDE_HIDDEN (__init_array_start = .);
  66.     KEEP (*(SORT(.init_array.*)))
  67.     KEEP (*(.init_array*))
  68.     PROVIDE_HIDDEN (__init_array_end = .);
  69.   } >FLASH
  70.   .fini_array :
  71.   {
  72.     PROVIDE_HIDDEN (__fini_array_start = .);
  73.     KEEP (*(SORT(.fini_array.*)))
  74.     KEEP (*(.fini_array*))
  75.     PROVIDE_HIDDEN (__fini_array_end = .);
  76.   } >FLASH

  77.   /* used by the startup to initialize data */
  78.   _sidata = LOADADDR(.data);

  79.   /* Initialized data sections goes into RAM, load LMA copy after code */
  80.   .data :
  81.   {
  82.     . = ALIGN(4);
  83.     _sdata = .;        /* create a global symbol at data start */
  84.     *(.data)           /* .data sections */
  85.     *(.data*)          /* .data* sections */

  86.     . = ALIGN(4);
  87.     _edata = .;        /* define a global symbol at data end */
  88.   } >RAM AT> FLASH

  89.   
  90.   /* Uninitialized data section */
  91.   . = ALIGN(4);
  92.   .bss :
  93.   {
  94.     /* This is used by the startup in order to initialize the .bss secion */
  95.     _sbss = .;         /* define a global symbol at bss start */
  96.     __bss_start__ = _sbss;
  97.     *(.bss)
  98.     *(.bss*)
  99.     *(COMMON)

  100.     . = ALIGN(4);
  101.     _ebss = .;         /* define a global symbol at bss end */
  102.     __bss_end__ = _ebss;
  103.   } >RAM

  104.   /* User_heap_stack section, used to check that there is enough RAM left */
  105.   ._user_heap_stack :
  106.   {
  107.     . = ALIGN(4);
  108.     PROVIDE ( end = . );
  109.     PROVIDE ( _end = . );
  110.     . = . + _Min_Heap_Size;
  111.     . = . + _Min_Stack_Size;
  112.     . = ALIGN(4);
  113.   } >RAM

  114.   

  115.   /* Remove information from the standard libraries */
  116.   /DISCARD/ :
  117.   {
  118.     libc.a ( * )
  119.     libm.a ( * )
  120.     libgcc.a ( * )
  121.   }

  122.   .ARM.attributes 0 : { *(.ARM.attributes) }
  123. }
复制代码

OK,下面就是链接了,使用命令 arm-none-eabi-ld mini.o -Le:\arm-2014q3\arm-none-eabi\lib\armv6-m -Le:\arm-2014q3\lib\gcc\arm-none-eabi\4.8.4\armv6-m -T STM32F072C8_FLASH.ld -o mini.elf
这里面 -L 参数是添加标准库文件的搜索路径,虽然暂时并没有用到C标准库里面的东西,但是Linker Script里面引用了标准库文件。-o 指定输出的目标文件。这么就快要得到最终的机器码了,不过好象还缺少了什么……
arm-none-eabi-ld: warning: cannot find entry symbol Reset_Handler; defaulting to  08000000

linker给了一个警告:找不到入口地址 Reset_Handler 的值,设成了默认 0x08000000. 下面再用objdump -S反汇编看一下
  1. E:\arm\test072\mini>arm-none-eabi-objdump -S mini.elf

  2. mini.elf:     file format elf32-littlearm


  3. Disassembly of section .text:

  4. 08000000 <main>:
  5. 8000000:       4b16            ldr     r3, [pc, #88]   ; (800005c <main+0x5c>)
  6. 8000002:       2280            movs    r2, #128        ; 0x80
  7. 8000004:       6959            ldr     r1, [r3, #20]
  8. 8000006:       0292            lsls    r2, r2, #10
  9. 8000008:       430a            orrs    r2, r1
  10. 800000a:       b510            push    {r4, lr}
  11. 800000c:       2180            movs    r1, #128        ; 0x80
  12. 800000e:       615a            str     r2, [r3, #20]
  13. 8000010:       2290            movs    r2, #144        ; 0x90
  14. 8000012:       0249            lsls    r1, r1, #9
  15. 8000014:       05d2            lsls    r2, r2, #23
  16. 8000016:       6011            str     r1, [r2, #0]
  17. 8000018:       69da            ldr     r2, [r3, #28]
  18. 800001a:       2110            movs    r1, #16
  19. 800001c:       430a            orrs    r2, r1
  20. 800001e:       61da            str     r2, [r3, #28]
  21. 8000020:       4b0f            ldr     r3, [pc, #60]   ; (8000060 <main+0x60>)
  22. 8000022:       4a10            ldr     r2, [pc, #64]   ; (8000064 <main+0x64>)
  23. 8000024:       851a            strh    r2, [r3, #40]   ; 0x28
  24. 8000026:       2290            movs    r2, #144        ; 0x90
  25. 8000028:       32ff            adds    r2, #255        ; 0xff
  26. 800002a:       62da            str     r2, [r3, #44]   ; 0x2c
  27. 800002c:       2205            movs    r2, #5
  28. 800002e:       801a            strh    r2, [r3, #0]
  29. 8000030:       4a0d            ldr     r2, [pc, #52]   ; (8000068 <main+0x68>)
  30. 8000032:       7812            ldrb    r2, [r2, #0]
  31. 8000034:       8a1c            ldrh    r4, [r3, #16]
  32. 8000036:       2101            movs    r1, #1
  33. 8000038:       4809            ldr     r0, [pc, #36]   ; (8000060 <main+0x60>)
  34. 800003a:       420c            tst     r4, r1
  35. 800003c:       d0fa            beq.n   8000034 <main+0x34>
  36. 800003e:       8a04            ldrh    r4, [r0, #16]
  37. 8000040:       438c            bics    r4, r1
  38. 8000042:       8204            strh    r4, [r0, #16]
  39. 8000044:       2090            movs    r0, #144        ; 0x90
  40. 8000046:       2480            movs    r4, #128        ; 0x80
  41. 8000048:       05c0            lsls    r0, r0, #23
  42. 800004a:       408c            lsls    r4, r1
  43. 800004c:       2a00            cmp     r2, #0
  44. 800004e:       d102            bne.n   8000056 <main+0x56>
  45. 8000050:       6184            str     r4, [r0, #24]
  46. 8000052:       1c0a            adds    r2, r1, #0
  47. 8000054:       e7ee            b.n     8000034 <main+0x34>
  48. 8000056:       8504            strh    r4, [r0, #40]   ; 0x28
  49. 8000058:       2200            movs    r2, #0
  50. 800005a:       e7eb            b.n     8000034 <main+0x34>
  51. 800005c:       40021000        .word   0x40021000
  52. 8000060:       40001000        .word   0x40001000
  53. 8000064:       0000270f        .word   0x0000270f
  54. 8000068:       20000000        .word   0x20000000
复制代码
现在 main() 被放到ROM最开始去了,这好象是对的?如果了解ARM Cortex-M0下就知道这样错了,因为最开始应该是中断向量表。我们还没有编写Linker Script中的 .isr_vectors 段的内容。而且,一上来初始化堆栈指针等工作都没有做就直接运行 main() 了也不合适吧?还缺少了初始化代码。

在软件包中搜刮一个 startup_stm32f072.s 汇编文件
  1. /**
  2.   ******************************************************************************
  3.   * @file      startup_stm32f072.s
  4.   * @author    MCD Application Team
  5.   ******************************************************************************
  6.   */

  7.   .syntax unified
  8.   .cpu cortex-m0
  9.   .fpu softvfp
  10.   .thumb

  11. .global g_pfnVectors
  12. .global Default_Handler

  13. /* start address for the initialization values of the .data section.
  14. defined in linker script */
  15. .word _sidata
  16. /* start address for the .data section. defined in linker script */
  17. .word _sdata
  18. /* end address for the .data section. defined in linker script */
  19. .word _edata
  20. /* start address for the .bss section. defined in linker script */
  21. .word _sbss
  22. /* end address for the .bss section. defined in linker script */
  23. .word _ebss

  24.   .section .text.Reset_Handler
  25.   .weak Reset_Handler
  26.   .type Reset_Handler, %function
  27. Reset_Handler:
  28.   ldr   r0, =_estack
  29.   mov   sp, r0          /* set stack pointer */

  30. /*Check if boot space corresponds to test memory*/

  31.     LDR R0,=0x00000004
  32.     LDR R1, [R0]
  33.     LSRS R1, R1, #24
  34.     LDR R2,=0x1F
  35.     CMP R1, R2
  36.     BNE ApplicationStart

  37. /*SYSCFG clock enable*/

  38.     LDR R0,=0x40021018
  39.     LDR R1,=0x00000001
  40.     STR R1, [R0]

  41. /*Set CFGR1 register with flash memory remap at address 0*/
  42.     LDR R0,=0x40010000
  43.     LDR R1,=0x00000000
  44.     STR R1, [R0]

  45. ApplicationStart:
  46. /* Copy the data segment initializers from flash to SRAM */
  47.   movs r1, #0
  48.   b LoopCopyDataInit

  49. CopyDataInit:
  50.   ldr r3, =_sidata
  51.   ldr r3, [r3, r1]
  52.   str r3, [r0, r1]
  53.   adds r1, r1, #4

  54. LoopCopyDataInit:
  55.   ldr r0, =_sdata
  56.   ldr r3, =_edata
  57.   adds r2, r0, r1
  58.   cmp r2, r3
  59.   bcc CopyDataInit
  60.   ldr r2, =_sbss
  61.   b LoopFillZerobss
  62. /* Zero fill the bss segment. */
  63. FillZerobss:
  64.   movs r3, #0
  65.   str  r3, [r2]
  66.   adds r2, r2, #4


  67. LoopFillZerobss:
  68.   ldr r3, = _ebss
  69.   cmp r2, r3
  70.   bcc FillZerobss

  71. /* Call the application's entry point.*/
  72.   bl main
  73.   
  74. LoopForever:
  75.     b LoopForever


  76. .size Reset_Handler, .-Reset_Handler

  77. /**
  78. * @brief  This is the code that gets called when the processor receives an
  79. *         unexpected interrupt.  This simply enters an infinite loop, preserving
  80. *         the system state for examination by a debugger.
  81. *
  82. * @param  None
  83. * @retval : None
  84. */
  85.     .section .text.Default_Handler,"ax",%progbits
  86. Default_Handler:
  87. Infinite_Loop:
  88.   b Infinite_Loop
  89.   .size Default_Handler, .-Default_Handler
  90. /******************************************************************************
  91. *
  92. * The minimal vector table for a Cortex M0.  Note that the proper constructs
  93. * must be placed on this to ensure that it ends up at physical address
  94. * 0x0000.0000.
  95. *
  96. ******************************************************************************/
  97.    .section .isr_vector,"a",%progbits
  98.   .type g_pfnVectors, %object
  99.   .size g_pfnVectors, .-g_pfnVectors

  100. g_pfnVectors:
  101.   .word _estack
  102.   .word Reset_Handler

  103.   .word NMI_Handler
  104.   .word HardFault_Handler
  105.   .word 0
  106.   .word 0
  107.   .word 0
  108.   .word 0
  109.   .word 0
  110.   .word 0
  111.   .word 0
  112.   .word SVC_Handler
  113.   .word 0
  114.   .word 0
  115.   .word PendSV_Handler
  116.   .word SysTick_Handler


  117.   .word WWDG_IRQHandler
  118.   .word PVD_VDDIO2_IRQHandler
  119.   .word RTC_IRQHandler
  120.   .word FLASH_IRQHandler
  121.   .word RCC_CRS_IRQHandler
  122.   .word EXTI0_1_IRQHandler
  123.   .word EXTI2_3_IRQHandler
  124.   .word EXTI4_15_IRQHandler
  125.   .word TSC_IRQHandler
  126.   .word DMA1_Channel1_IRQHandler
  127.   .word DMA1_Channel2_3_IRQHandler
  128.   .word DMA1_Channel4_5_6_7_IRQHandler
  129.   .word ADC1_COMP_IRQHandler
  130.   .word TIM1_BRK_UP_TRG_COM_IRQHandler
  131.   .word TIM1_CC_IRQHandler
  132.   .word TIM2_IRQHandler
  133.   .word TIM3_IRQHandler
  134.   .word TIM6_DAC_IRQHandler
  135.   .word TIM7_IRQHandler   
  136.   .word TIM14_IRQHandler
  137.   .word TIM15_IRQHandler
  138.   .word TIM16_IRQHandler
  139.   .word TIM17_IRQHandler
  140.   .word I2C1_IRQHandler
  141.   .word I2C2_IRQHandler
  142.   .word SPI1_IRQHandler
  143.   .word SPI2_IRQHandler
  144.   .word USART1_IRQHandler
  145.   .word USART2_IRQHandler
  146.   .word USART3_4_IRQHandler
  147.   .word CEC_CAN_IRQHandler
  148.   .word USB_IRQHandler

  149. /*******************************************************************************
  150. *
  151. * Provide weak aliases for each Exception handler to the Default_Handler.
  152. * As they are weak aliases, any function with the same name will override
  153. * this definition.
  154. *
  155. *******************************************************************************/

  156.   .weak NMI_Handler
  157.   .thumb_set NMI_Handler,Default_Handler

  158.   .weak HardFault_Handler
  159.   .thumb_set HardFault_Handler,Default_Handler

  160.   .weak SVC_Handler
  161.   .thumb_set SVC_Handler,Default_Handler

  162.   .weak PendSV_Handler
  163.   .thumb_set PendSV_Handler,Default_Handler

  164.   .weak SysTick_Handler
  165.   .thumb_set SysTick_Handler,Default_Handler

  166.   .weak WWDG_IRQHandler
  167.   .thumb_set WWDG_IRQHandler,Default_Handler

  168.   .weak PVD_VDDIO2_IRQHandler
  169.   .thumb_set PVD_VDDIO2_IRQHandler,Default_Handler
  170.   
  171.   .weak RTC_IRQHandler
  172.   .thumb_set RTC_IRQHandler,Default_Handler
  173.   
  174.   .weak FLASH_IRQHandler
  175.   .thumb_set FLASH_IRQHandler,Default_Handler
  176.   
  177.   .weak RCC_CRS_IRQHandler
  178.   .thumb_set RCC_CRS_IRQHandler,Default_Handler
  179.   
  180.   .weak EXTI0_1_IRQHandler
  181.   .thumb_set EXTI0_1_IRQHandler,Default_Handler
  182.   
  183.   .weak EXTI2_3_IRQHandler
  184.   .thumb_set EXTI2_3_IRQHandler,Default_Handler
  185.   
  186.   .weak EXTI4_15_IRQHandler
  187.   .thumb_set EXTI4_15_IRQHandler,Default_Handler
  188.   
  189.   .weak TSC_IRQHandler
  190.   .thumb_set TSC_IRQHandler,Default_Handler
  191.   
  192.   .weak DMA1_Channel1_IRQHandler
  193.   .thumb_set DMA1_Channel1_IRQHandler,Default_Handler
  194.   
  195.   .weak DMA1_Channel2_3_IRQHandler
  196.   .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler
  197.   
  198.   .weak DMA1_Channel4_5_6_7_IRQHandler
  199.   .thumb_set DMA1_Channel4_5_6_7_IRQHandler,Default_Handler
  200.   
  201.   .weak ADC1_COMP_IRQHandler
  202.   .thumb_set ADC1_COMP_IRQHandler,Default_Handler
  203.    
  204.   .weak TIM1_BRK_UP_TRG_COM_IRQHandler
  205.   .thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler
  206.   
  207.   .weak TIM1_CC_IRQHandler
  208.   .thumb_set TIM1_CC_IRQHandler,Default_Handler
  209.   
  210.   .weak TIM2_IRQHandler
  211.   .thumb_set TIM2_IRQHandler,Default_Handler
  212.   
  213.   .weak TIM3_IRQHandler
  214.   .thumb_set TIM3_IRQHandler,Default_Handler
  215.   
  216.   .weak TIM6_DAC_IRQHandler
  217.   .thumb_set TIM6_DAC_IRQHandler,Default_Handler
  218.   
  219.   .weak TIM7_IRQHandler
  220.   .thumb_set TIM7_IRQHandler,Default_Handler

  221.   .weak TIM14_IRQHandler
  222.   .thumb_set TIM14_IRQHandler,Default_Handler
  223.   
  224.   .weak TIM15_IRQHandler
  225.   .thumb_set TIM15_IRQHandler,Default_Handler
  226.   
  227.   .weak TIM16_IRQHandler
  228.   .thumb_set TIM16_IRQHandler,Default_Handler
  229.   
  230.   .weak TIM17_IRQHandler
  231.   .thumb_set TIM17_IRQHandler,Default_Handler
  232.   
  233.   .weak I2C1_IRQHandler
  234.   .thumb_set I2C1_IRQHandler,Default_Handler
  235.   
  236.   .weak I2C2_IRQHandler
  237.   .thumb_set I2C2_IRQHandler,Default_Handler
  238.   
  239.   .weak SPI1_IRQHandler
  240.   .thumb_set SPI1_IRQHandler,Default_Handler
  241.   
  242.   .weak SPI2_IRQHandler
  243.   .thumb_set SPI2_IRQHandler,Default_Handler
  244.   
  245.   .weak USART1_IRQHandler
  246.   .thumb_set USART1_IRQHandler,Default_Handler
  247.   
  248.   .weak USART2_IRQHandler
  249.   .thumb_set USART2_IRQHandler,Default_Handler

  250.   .weak USART3_4_IRQHandler
  251.   .thumb_set USART3_4_IRQHandler,Default_Handler
  252.   
  253.   .weak CEC_CAN_IRQHandler
  254.   .thumb_set CEC_CAN_IRQHandler,Default_Handler

  255.   .weak USB_IRQHandler
  256.   .thumb_set USB_IRQHandler,Default_Handler

  257. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
原来是这样,中断向量表在这里进行了描述,还有设置堆栈,初始化全局变量的代码,然后跳转到 main 执行。好了,这样就该差不多了。这个汇编程序是GNU AS的语法,可以用 arm-none-eabi-gcc 来直接汇编
arm-none-eabi-gcc -c startup_stm32f072.s
链接两个目标模块
arm-none-eabi-ld mini.o startup_stm32f072.o -Le:\arm-2014q3\arm-none-eabi\lib\armv6-m -Le:\arm-2014q3\lib\gcc\arm-none-eabi\4.8.4\armv6-m -T STM32F072C8_FLASH.ld -o mini.elf
最后转换出一个 HEX 文件
arm-none-eabi-objcopy -Oihex mini.elf mini.hex
可以进行烧写了。

我这个是最简化的例子,使用最简化的软件工具,不过已经包含了基本的C语言框架。后面随着我本人的学习,我会继续分享怎么开发一个简单的 USB 设备。

mini_project.zip

68.2 KB, 阅读权限: 5, 下载次数: 37

 
 

回复

2453

帖子

19

TA的资源

五彩晶圆(中级)

板凳
个人签名    懒得很
 
 
 

回复

3414

帖子

0

TA的资源

纯净的硅(高级)

4
 
好详细啊,顶一个
 
 
 

回复

1万

帖子

24

TA的资源

版主

5
 
很不错。
 
 
 

回复

1372

帖子

2

TA的资源

五彩晶圆(初级)

6
 
本帖最后由 cruelfox 于 2015-11-23 17:25 编辑

第一个USB工程例子:USB存储盘。因为手边没有SPI或I2C接口的Flash, 就权且用单片机的RAM虚拟一下吧。这个"U盘"容量标成160kB, 实际上有效的存储只有10kB,用一点扇区映射的技巧骗过操作系统,当然存放文件只能几kB了。

ST官方提供了一个 STM32F0 的USB固件库,(URL http://www.st.com/st-web-ui/static/active/en/st_prod_software_internet/resource/technical/software/firmware/stsw-stm32092.zip),包括了硬件库、核心库和类库的C头文件和源文件。我cruelfox琢磨了一整天,还是没搞清楚怎么调用这些函数,只好照着个Example改吧。

于是就用MassStorage类的例子来下手了,这个目录里面的主文件 app.c 非常简单
  1. /**
  2.   * @file    app.c
  3.   */

  4. /* Includes ------------------------------------------------------------------*/
  5. #include  "usbd_msc_core.h"
  6. #include  "usbd_usr.h"

  7. USB_CORE_HANDLE  USB_Device_dev ;

  8. int main(void)
  9. {

  10.   USBD_Init(&USB_Device_dev,
  11.             &USR_desc,
  12.             &USBD_MSC_cb,
  13.             &USR_cb);
  14.   
  15.   while (1)
  16.   {
  17.   }
  18. }

  19. #ifdef USE_FULL_ASSERT
  20. void assert_failed(uint8_t* file, uint32_t line)
  21. {
  22.   while (1)
  23.   {}
  24. }
  25. #endif
复制代码
一切USB相关的东西都在 USBD_init() 这个函数里面了。追查源代码,可以发现这个函数调用其实是向库函数提供了一堆回调函数的接口,也就是说写一些子程序,让驱动程序在需要的时候调用。这样开发USB的我们就不用去管中断什么的了,要做的就是实现各种USB的请求——其实程序库里面已经把大部分的请求应答都实现了,而具体的类库(比如我这里用的MassStorage类)又处理了剩余的大部分,所以只剩下功能性的需要自己写。

在 usbd_desc.c 这个程序里面可以看到描述符是怎么创建和返回的
  1. /**
  2.   ******************************************************************************
  3.   * @file    usbd_desc.c
  4.   * @author  MCD Application Team
  5.   * @version V1.0.0
  6.   * @date    31-January-2014
  7.   * @brief   This file provides the USBD descriptors and string formating method.
  8.   ******************************************************************************
  9.   * @attention
  10.   *
  11.   * <h2><center>© COPYRIGHT 2014 STMicroelectronics</center></h2>
  12.   *
  13.   * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  14.   * You may not use this file except in compliance with the License.
  15.   * You may obtain a copy of the License at:
  16.   *
  17.   *        http://www.st.com/software_license_agreement_liberty_v2
  18.   *
  19.   * Unless required by applicable law or agreed to in writing, software
  20.   * distributed under the License is distributed on an "AS IS" BASIS,
  21.   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22.   * See the License for the specific language governing permissions and
  23.   * limitations under the License.
  24.   *
  25.   ******************************************************************************
  26.   */

  27. /* Includes ------------------------------------------------------------------*/
  28. #include "usbd_desc.h"

  29. /* Private typedef -----------------------------------------------------------*/
  30. /* Private define ------------------------------------------------------------*/
  31. #define USBD_VID                   0x0483
  32. #define USBD_PID                   0x5720

  33. #define USBD_LANGID_STRING         0x409
  34. #define USBD_MANUFACTURER_STRING   "STMicroelectronics"

  35. #define USBD_PRODUCT_FS_STRING     "Mass Storage in FS Mode"

  36. #define USBD_CONFIGURATION_FS_STRING  "MSC Config"
  37. #define USBD_INTERFACE_FS_STRING      "MSC Interface"
  38. /* Private macro -------------------------------------------------------------*/
  39. /* Private variables ---------------------------------------------------------*/
  40. char USBD_SERIALNUMBER_FS_STRING[26];

  41. USBD_DEVICE USR_desc =
  42. {
  43.   USBD_USR_DeviceDescriptor,
  44.   USBD_USR_LangIDStrDescriptor,
  45.   USBD_USR_ManufacturerStrDescriptor,
  46.   USBD_USR_ProductStrDescriptor,
  47.   USBD_USR_SerialStrDescriptor,
  48.   USBD_USR_ConfigStrDescriptor,
  49.   USBD_USR_InterfaceStrDescriptor,
  50.   
  51. };

  52. /* USB Standard Device Descriptor */
  53. const uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] =
  54. {
  55.   0x12,                       /*bLength */
  56.   USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
  57.   0x00,                       /*bcdUSB */
  58.   0x02,
  59.   0x00,                       /*bDeviceClass*/
  60.   0x00,                       /*bDeviceSubClass*/
  61.   0x00,                       /*bDeviceProtocol*/
  62.   USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/
  63.   LOBYTE(USBD_VID),           /*idVendor*/
  64.   HIBYTE(USBD_VID),           /*idVendor*/
  65.   LOBYTE(USBD_PID),           /*idVendor*/
  66.   HIBYTE(USBD_PID),           /*idVendor*/
  67.   0x00,                       /*bcdDevice rel. 2.00*/
  68.   0x02,
  69.   USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
  70.   USBD_IDX_PRODUCT_STR,       /*Index of product string*/
  71.   USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
  72.   USBD_CFG_MAX_NUM            /*bNumConfigurations*/
  73. } ; /* USB_DeviceDescriptor */

  74. /* USB Standard Device Descriptor */
  75. const uint8_t USBD_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] =
  76. {
  77.   USB_LEN_DEV_QUALIFIER_DESC,
  78.   USB_DESC_TYPE_DEVICE_QUALIFIER,
  79.   0x00,
  80.   0x02,
  81.   0x00,
  82.   0x00,
  83.   0x00,
  84.   0x40,
  85.   0x01,
  86.   0x00,
  87. };

  88. /* USB Standard Device Descriptor */
  89. const uint8_t USBD_LangIDDesc[USB_SIZ_STRING_LANGID] =
  90. {
  91.   USB_SIZ_STRING_LANGID,         
  92.   USB_DESC_TYPE_STRING,      
  93.   LOBYTE(USBD_LANGID_STRING),
  94.   HIBYTE(USBD_LANGID_STRING),
  95. };

  96. uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] =
  97. {
  98.   USB_SIZ_STRING_SERIAL,       /* bLength */
  99.   USB_STRING_DESCRIPTOR_TYPE,  /* bDescriptorType */
  100. };

  101. /* Private function prototypes -----------------------------------------------*/
  102. /* Private functions ---------------------------------------------------------*/
  103. static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len);

  104. /**
  105.   * @brief return the device descriptor
  106.   * @param  speed : current device speed
  107.   * @param  length : pointer to data length variable
  108.   * @retval pointer to descriptor buffer
  109.   */
  110. uint8_t *  USBD_USR_DeviceDescriptor( uint8_t speed , uint16_t *length)
  111. {
  112.   *length = sizeof(USBD_DeviceDesc);
  113.   return (uint8_t*)USBD_DeviceDesc;
  114. }

  115. /**
  116.   * @brief  return the LangID string descriptor
  117.   * @param  speed : current device speed
  118.   * @param  length : pointer to data length variable
  119.   * @retval pointer to descriptor buffer
  120.   */
  121. uint8_t *  USBD_USR_LangIDStrDescriptor( uint8_t speed , uint16_t *length)
  122. {
  123.   *length =  sizeof(USBD_LangIDDesc);  
  124.   return (uint8_t*)USBD_LangIDDesc;
  125. }


  126. /**
  127.   * @brief  return the product string descriptor
  128.   * @param  speed : current device speed
  129.   * @param  length : pointer to data length variable
  130.   * @retval pointer to descriptor buffer
  131.   */
  132. uint8_t *  USBD_USR_ProductStrDescriptor( uint8_t speed , uint16_t *length)
  133. {
  134.   USBD_GetString ( (uint8_t*)USBD_PRODUCT_FS_STRING, USBD_StrDesc, length);   
  135.   return USBD_StrDesc;
  136. }

  137. /**
  138.   * @brief  return the manufacturer string descriptor
  139.   * @param  speed : current device speed
  140.   * @param  length : pointer to data length variable
  141.   * @retval pointer to descriptor buffer
  142.   */
  143. uint8_t *  USBD_USR_ManufacturerStrDescriptor( uint8_t speed , uint16_t *length)
  144. {
  145.   USBD_GetString ( (uint8_t*)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
  146.   return USBD_StrDesc;
  147. }

  148. /**
  149.   * @brief  return the serial number string descriptor
  150.   * @param  speed : current device speed
  151.   * @param  length : pointer to data length variable
  152.   * @retval pointer to descriptor buffer
  153.   */
  154. uint8_t *  USBD_USR_SerialStrDescriptor( uint8_t speed , uint16_t *length)
  155. {
  156.   *length = USB_SIZ_STRING_SERIAL;
  157.   return USBD_StringSerial;
  158. }

  159. /**
  160.   * @brief return the configuration string descriptor
  161.   * @param  speed : current device speed
  162.   * @param  length : pointer to data length variable
  163.   * @retval pointer to descriptor buffer
  164.   */
  165. uint8_t *  USBD_USR_ConfigStrDescriptor( uint8_t speed , uint16_t *length)
  166. {
  167.   USBD_GetString ( (uint8_t*)USBD_CONFIGURATION_FS_STRING, USBD_StrDesc, length);
  168.   return USBD_StrDesc;  
  169. }


  170. /**
  171.   * @brief  return the interface string descriptor
  172.   * @param  speed : current device speed
  173.   * @param  length : pointer to data length variable
  174.   * @retval pointer to descriptor buffer
  175.   */
  176. uint8_t *  USBD_USR_InterfaceStrDescriptor( uint8_t speed , uint16_t *length)
  177. {
  178.   USBD_GetString ( (uint8_t*)USBD_INTERFACE_FS_STRING, USBD_StrDesc, length);
  179.   return USBD_StrDesc;  
  180. }

  181. /**
  182.   * @brief  Create the serial number string descriptor
  183.   * @param  None
  184.   * @retval None
  185.   */
  186. void Get_SerialNum(void)
  187. {
  188.   uint32_t Device_Serial0, Device_Serial1, Device_Serial2;
  189.   
  190.   Device_Serial0 = *(uint32_t*)Device1_Identifier;
  191.   Device_Serial1 = *(uint32_t*)Device2_Identifier;
  192.   Device_Serial2 = *(uint32_t*)Device3_Identifier;
  193.   
  194.   Device_Serial0 += Device_Serial2;
  195.   
  196.   if (Device_Serial0 != 0)
  197.   {
  198.     IntToUnicode (Device_Serial0, &USBD_StringSerial[2] ,8);
  199.     IntToUnicode (Device_Serial1, &USBD_StringSerial[18] ,4);
  200.   }
  201. }

  202. /**
  203.   * @brief  Convert Hex 32Bits value into char
  204.   * @param  value: value to convert
  205.   * @param  pbuf: pointer to the buffer
  206.   * @param  len: buffer length
  207.   * @retval None
  208.   */
  209. static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len)
  210. {
  211.   uint8_t idx = 0;
  212.   
  213.   for( idx = 0 ; idx < len ; idx ++)
  214.   {
  215.     if( ((value >> 28)) < 0xA )
  216.     {
  217.       pbuf[ 2* idx] = (value >> 28) + '0';
  218.     }
  219.     else
  220.     {
  221.       pbuf[2* idx] = (value >> 28) + 'A' - 10;
  222.     }
  223.    
  224.     value = value << 4;
  225.    
  226.     pbuf[ 2* idx + 1] = 0;
  227.   }
  228. }
  229. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
插一句: 开发USB设备,有很重要的一部分是编写描述符(当然也要借用已有的模板),设备叫什么,功能是什么等等都从描述符体现。只要设备向主机成功返回了描述符,主机就会提示找到硬件。

设计到U盘读写操作的部分在 usbd_storage_msd.c 里面,我就是在例子程序上改的
  1. /**
  2.   ******************************************************************************
  3.   * @file    usbd_storage_msd.c
  4.   * @author  MCD application Team
  5.   * @version V1.0.0
  6.   * @date    31-January-2014
  7.   * @brief   This file provides the disk operations functions.
  8.   ******************************************************************************
  9.   * @attention
  10.   *
  11.   * <h2><center>© COPYRIGHT 2014 STMicroelectronics</center></h2>
  12.   *
  13.   * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  14.   * You may not use this file except in compliance with the License.
  15.   * You may obtain a copy of the License at:
  16.   *
  17.   *        http://www.st.com/software_license_agreement_liberty_v2
  18.   *
  19.   * Unless required by applicable law or agreed to in writing, software
  20.   * distributed under the License is distributed on an "AS IS" BASIS,
  21.   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22.   * See the License for the specific language governing permissions and
  23.   * limitations under the License.
  24.   *
  25.   ******************************************************************************
  26.   */
  27. #include <string.h>

  28. /* Includes ------------------------------------------------------------------*/
  29. #include "usbd_msc_mem.h"

  30. /* Private typedef -----------------------------------------------------------*/
  31. /* Private define ------------------------------------------------------------*/
  32. #define STORAGE_LUN_NBR                  1

  33. /* Private macro -------------------------------------------------------------*/
  34. /* Private variables ---------------------------------------------------------*/
  35. /* USB Mass storage Standard Inquiry Data */
  36. const int8_t  STORAGE_Inquirydata[] = {
  37.   
  38.   /* LUN 0 */
  39.   0x00,     
  40.   0x80,     
  41.   0x02,     
  42.   0x02,
  43.   (USBD_STD_INQUIRY_LENGTH - 5),
  44.   0x00,
  45.   0x00,
  46.   0x00,
  47.   'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
  48.   'm', 'i', 'c', 'r', 'o', 'S', 'D', ' ', /* Product      : 16 Bytes */
  49.   'F', 'l', 'a', 's', 'h', ' ', ' ', ' ',
  50.   '1', '.', '0' ,'0',                     /* Version      : 4 Bytes */
  51. };

  52. __IO uint32_t count = 0;
  53. /* Private function prototypes -----------------------------------------------*/
  54. int8_t STORAGE_Init (uint8_t lun);

  55. int8_t STORAGE_GetCapacity (uint8_t lun,
  56.                            uint32_t *block_num,
  57.                            uint32_t *block_size);

  58. int8_t  STORAGE_IsReady (uint8_t lun);

  59. int8_t  STORAGE_IsWriteProtected (uint8_t lun);

  60. int8_t STORAGE_Read (uint8_t lun,
  61.                         uint8_t *buf,
  62.                         uint32_t blk_addr,
  63.                         uint16_t blk_len);

  64. int8_t STORAGE_Write (uint8_t lun,
  65.                         uint8_t *buf,
  66.                         uint32_t blk_addr,
  67.                         uint16_t blk_len);

  68. int8_t STORAGE_GetMaxLun (void);


  69. USBD_STORAGE_cb_TypeDef USBD_MICRO_SDIO_fops =
  70. {
  71.   STORAGE_Init,
  72.   STORAGE_GetCapacity,
  73.   STORAGE_IsReady,
  74.   STORAGE_IsWriteProtected,
  75.   STORAGE_Read,
  76.   STORAGE_Write,
  77.   STORAGE_GetMaxLun,
  78.   (int8_t *)STORAGE_Inquirydata,
  79. };

  80. USBD_STORAGE_cb_TypeDef  *USBD_STORAGE_fops = &USBD_MICRO_SDIO_fops;

  81. /* Private functions ---------------------------------------------------------*/

  82. /**
  83.   * @brief  Initialize the storage medium
  84.   * @param  lun : logical unit number
  85.   * @retval Status
  86.   */

  87. uint8_t dummy_disk[20][512];
  88. uint16_t sec_index[20];     // sector usage

  89. int8_t STORAGE_Init (uint8_t lun)
  90. {
  91.     static char init=0;
  92.     if(!init)
  93.     {
  94.         short int i;
  95.         for(i=0;i<20;i++)
  96.             sec_index[i]=0xffff;
  97.         init=1;
  98.     }
  99.   return (0);
  100. }

  101. /**
  102.   * @brief  return medium capacity and block size
  103.   * @param  lun : logical unit number
  104.   * @param  block_num :  number of physical block
  105.   * @param  block_size : size of a physical block
  106.   * @retval Status
  107.   */
  108. int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_size)
  109. {
  110.   
  111.   *block_size =  512;  
  112.   *block_num = 40*9;
  113.   
  114.   return (0);
  115. }

  116. /**
  117.   * @brief  check whether the medium is ready
  118.   * @param  lun : logical unit number
  119.   * @retval Status
  120.   */
  121. int8_t  STORAGE_IsReady (uint8_t lun)
  122. {
  123.   static uint8_t status = 0;
  124.   
  125.   return (0);
  126. }

  127. /**
  128.   * @brief  check whether the medium is write-protected
  129.   * @param  lun : logical unit number
  130.   * @retval Status
  131.   */
  132. int8_t  STORAGE_IsWriteProtected (uint8_t lun)
  133. {
  134.   return  0;
  135. }

  136. /**
  137.   * @brief  Read data from the medium
  138.   * @param  lun : logical unit number
  139.   * @param  buf : Pointer to the buffer to save data
  140.   * @param  blk_addr :  address of 1st block to be read
  141.   * @param  blk_len : number of blocks to be read
  142.   * @retval Status
  143.   */
  144. int8_t STORAGE_Read (uint8_t lun,
  145.                  uint8_t *buf,
  146.                  uint32_t blk_addr,                       
  147.                  uint16_t blk_len)
  148. {
  149.     short int i;
  150.     for(i=0;i<blk_len;i++)
  151.     {
  152.         short int j;
  153.         for(j=0;j<20;j++)
  154.         {
  155.             if(sec_index[j]==blk_addr+i)
  156.             {
  157.                 memcpy(buf+i*512,dummy_disk[j],512);
  158.                 break;
  159.             }
  160.         }
  161.         if(j>=20)
  162.         {
  163.             for(j=0;j<512;j++)
  164.                 buf[i*512+j]=0x00;
  165.         }
  166.     }
  167. /*  if( SD_ReadMultiBlocks (buf,
  168.                           blk_addr * 512,
  169.                           512,
  170.                           blk_len) != 0)
  171.   {
  172.     return 5;
  173.   }
  174. */   
  175.   return 0;
  176. }

  177. /**
  178.   * @brief  Write data to the medium
  179.   * @param  lun : logical unit number
  180.   * @param  buf : Pointer to the buffer to write from
  181.   * @param  blk_addr :  address of 1st block to be written
  182.   * @param  blk_len : number of blocks to be read
  183.   * @retval Status
  184.   */
  185. int8_t STORAGE_Write (uint8_t lun,
  186.                   uint8_t *buf,
  187.                   uint32_t blk_addr,
  188.                   uint16_t blk_len)
  189. {
  190.     short int i;
  191.     for(i=0;i<blk_len;i++)
  192.     {
  193.         short int j;
  194.         char zero=1;
  195.         unsigned char avail=0xff;
  196.         for(j=0;j<512;j++)
  197.             if(buf[j+i*512])
  198.                 zero=0;
  199.         for(j=0;j<20;j++)
  200.         {
  201.             if(sec_index[j]==blk_addr+i)
  202.                 break;
  203.             if(avail==0xff && sec_index[j]==0xffff)
  204.                 avail=j;
  205.         }
  206.         if(zero)
  207.         {
  208.             if(j<20)
  209.                 sec_index[j]=0xffff;    // free
  210.             return 0;
  211.         }
  212.         if(j>=20)
  213.         {
  214.             if(avail<20)
  215.                 j=avail;
  216.             else    // No free sectors
  217.             {
  218.                 j=1+rand()%19;
  219.             }
  220.         }
  221.         memcpy(dummy_disk[j],buf+i*512,512);
  222.         sec_index[j]=blk_addr+i;
  223.     }
  224.   return (0);
  225. }

  226. /**
  227.   * @brief  Return number of supported logical unit
  228.   * @param  None
  229.   * @retval number of logical unit
  230.   */

  231. int8_t STORAGE_GetMaxLun (void)
  232. {
  233.   return (STORAGE_LUN_NBR - 1);
  234. }
  235. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码
主要是需要完成 报告容量、读、写 三个函数。如果有可实现存储的硬件,接口连起来即可。我上面是用了10kB的RAM来存放,动态分配20个扇区来存储。如果检测到要写的整个扇区都是0,那么就忽略。于是这样就做成了虚拟的U盘。

附件包含了所有需要的程序源代码,USB库的部分在lib子目录下面。build.bat 中是编译的命令。依靠这个USB函数库开发起来还算不难吧,只要看懂了例子,mass storage class还比较简单,因为硬件涉及就是读扇区、写扇区。 USB HID设备五花八门,就不是这么简单了,继续研究去。

stor_test.zip

175.31 KB, 下载次数: 67

 
 
 

回复

155

帖子

1

TA的资源

一粒金砂(中级)

7
 
膜拜大神!
 
 
 

回复

6069

帖子

4

TA的资源

版主

8
 
楼主辛苦了。
 
 
 

回复

24

帖子

0

TA的资源

一粒金砂(中级)

9
 
其实淘宝上STM32F072比STM32F103C8卖得贵而少吧。。。
 
 
 

回复

1万

帖子

16

TA的资源

版主

10
 
写得挺好顶
个人签名http://shop34182318.taobao.com/
https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

11
 
楼主有做USBHID功能的开发么??

点评

还没。这个用库里的例子应该不难,我还没时间弄。  详情 回复 发表于 2016-1-19 08:09
 
 
 

回复

1372

帖子

2

TA的资源

五彩晶圆(初级)

12
 
maken 发表于 2016-1-18 15:15
楼主有做USBHID功能的开发么??

还没。这个用库里的例子应该不难,我还没时间弄。
 
 
 

回复

1027

帖子

0

TA的资源

五彩晶圆(中级)

13
 
可以用它来做JLink OB啦
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

14
 
看起来很牛逼的样子,学习ing
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

15
 
cruelfox 发表于 2016-1-19 08:09
还没。这个用库里的例子应该不难,我还没时间弄。

楼主,我现在想把STM321系列的HID程序移植到072上来,找你探讨一下,望见信回复下

点评

有何见教? STM321我倒是没有用过。  详情 回复 发表于 2016-1-22 16:27
 
 
 

回复

1372

帖子

2

TA的资源

五彩晶圆(初级)

16
 
bush4023 发表于 2016-1-22 13:29
楼主,我现在想把STM321系列的HID程序移植到072上来,找你探讨一下,望见信回复下

有何见教?
STM321我倒是没有用过。
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

17
 
cruelfox 发表于 2016-1-22 16:27
有何见教?
STM321我倒是没有用过。

方便留个企鹅号不?或者你加我233030352
 
 
 

回复

6

帖子

0

TA的资源

一粒金砂(初级)

18
 
bush4023 发表于 2016-1-22 16:39
方便留个企鹅号不?或者你加我233030352

层主,请问你在做USB HID  stm32f070开发么?

点评

没有哈,没有时间暂时顾不上了。  详情 回复 发表于 2016-2-2 16:59
 
 
 

回复

1372

帖子

2

TA的资源

五彩晶圆(初级)

19
 
maken 发表于 2016-1-27 13:41
层主,请问你在做USB HID  stm32f070开发么?

没有哈,没有时间暂时顾不上了。
 
 
 

回复

20

帖子

0

TA的资源

一粒金砂(中级)

20
 
哇,这个想法好,初入门用这个看起来棒棒的
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表