zoukankan      html  css  js  c++  java
  • RTT下spi flash+elm fat文件系统移植小记

    背景:

    • MCU:STM32F207
    • SPI flash: Winbond W25Q16BV
    • OS: RTT V1.1.1
    • bsp: STM32F20x

    1 将spi_core.c,spi_dev.c及spi.h三个文件加入工程


    spi_core.c,spi_dev.c这两个文件位于RTTcomponentsdriversspi目录下,而spi.h头文件位于RTT\componentsdriversincludedrivers目录下.

    可在MKD工程的Drivers组下将上面两个源文件加进行,并将spi.h头文件所在目录添加到工程的include path下.

    spi_core.c文件实现了spi的抽象操作,如注册spi总线(spi_bus),向SPI总线添加设备函数等.

    注: 这里将MCU的一路spi外设虚拟成spi总线,然后总线上可以挂很多spi设备(spi_device),很个spi_device有一个片选cs.

    spi总线和spi设备要在RTT中可以生效就必须先向RTT注册,因此就需要使用上面的注册SPI总线函数和向SPI总线中添加SPI设备.

    spi_core.c还包含了配置SPI函数,发送和接收等通信函数,占用和释放SPI总线函数及选择SPI设备函数.这些函数都是抽象出来的,反映出SPI总线上的一些常规操作.真正执行这些操作的过程并不在spi_core.c源文件中,实际上,这些操作信息都是通过注册SPI总线和向总线添加SPI设备时这些操作集就已经"注册"下来了,真正操作时是通过注册信息内的操作函数去实现,也可以说是一种回调操作.

    而spi_dev.c实现了SPI设备的一些抽象操作,比如读,写,打开,关闭,初始化等,当然当MCU操作SPI设备的时候,是需要通过SPI总线与SPI设备进行通信的,既然通信就必然会有SPI通信协议,但是通信协议并不在这里具体,spi_dev.c这里还只是SPI设备的抽象操作而已,它只是简单地调用spi_core.c源文件中的抽象通信而已,具体实现还是要靠上层通过SPI总线或SPI设备注册下来的信息而实现的.

    在确保了spi_core.c,spi_dev.c和spi.h这三个源文件在MDK工程内之后,接着往下走.

    2 添加stm32f20x_40x_spi.c及其对应头文件

    将stm32f20x_40x_spi.c添加到Drivers组内.这个stm32f20x_40x_spi.c要在realtouch源码工程里找来,在文件系统示例代码中有.

    在源文件在spi.h的基础上根据STM32F20x这款MCU的特点进行了进一步封装.这里修改的地方只有一处理:

    即在config函数内配置SPI最大时钟时可根据MCU的具体特性配置为:30000000;

    //#ifdef STM32F4XX
    //        stm32_spi_max_clock = 37500000;
    //#elif STM32F2XX
            stm32_spi_max_clock = 30000000;
    //#endif

    3 添加spi_flash_w25qxx.c进工程

    由于这里使用的FLASH是Winbond的W25Q16BV,所以以此文件命名,此源文件及其头文件可以在realtouch的源文件中找到.realtouch工程正好也是使用的此flash.

    顾名思义,此源文件正是针对w25q16这款芯片的特点来实现的,当然包含

    在这里,具体实现了SPI通信的参数,这些参数传递给stm32f20x_4-x_spi.c文件中定义的函数,然后再进一步传递给spi_core来进行通信,spi_core的通信过程又会回调回来.

    spi_flash_w25qxx.c还实现了read.write,open,close这些标准操作,这些函数就封装到一个结构体中通信注册函数注册到spi_core内部以供其回调所用.同时spi_flash_w25qxx.c还传递操作所必要的参数下去.

    4 初始化 spi flash

    接下来就是在RTT系统初始化时对SPI FLASH做些必要的初始化.

    如下:

    #ifdef RT_USING_SPI
        rt_hw_spi2_init();
    
    #ifdef RT_USING_DFS
        w25qxx_init("flash0", "spi20");
    #endif /* RT_USING_DFS */


    rt_hw_spi2_int()函数的作用是向RTT设备管理系统注册SPI2总线和向SPI2总线添加spi设备及其必要的IO管脚初始化. 这里仅仅只是注册设备而已.

    w25qxx_init的作用是将SPI2总线上的SPI设备spi20注册成FLASH设备,也就是说将之前的设备告诉RTT其实是FLASH存储设备,然后配上相关的SPI FLASH存储设备参数.

    在w25qxx_init函数内会读取spi flash的device id, 这里得根据自己所使用的具体FLASH进行修改.

    rt_hw_spi_init函数如下:

    这里PB12用作SPI FLASH的片选管脚.

    static void rt_hw_spi2_init(void)
    {
        /* register spi bus */
        {
            static struct stm32_spi_bus stm32_spi;
            GPIO_InitTypeDef GPIO_InitStructure;
    
            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
            GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
            GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
            GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
            /*!< SPI SCK pin configuration */
            GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
            GPIO_Init(GPIOB, &GPIO_InitStructure);
    
            /* Connect alternate function */
            GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
            GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
            GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
    
            stm32_spi_register(SPI2, &stm32_spi, "spi2");
        }
    
        /* attach cs */
        {
            static struct rt_spi_device spi_device;
            static struct stm32_spi_cs  spi_cs;
    
            GPIO_InitTypeDef GPIO_InitStructure;
    
            GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
            GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
            GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
            /* spi21: PB12 */
            spi_cs.GPIOx = GPIOB;
            spi_cs.GPIO_Pin = GPIO_Pin_12;
            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
            GPIO_InitStructure.GPIO_Pin = spi_cs.GPIO_Pin;
            GPIO_SetBits(spi_cs.GPIOx, spi_cs.GPIO_Pin);
            GPIO_Init(spi_cs.GPIOx, &GPIO_InitStructure);
    
            rt_spi_bus_attach_device(&spi_device, "spi20", "spi2", (void*)&spi_cs);
        }
    }

    注:在rt_config.h头文件中得将#define RT_USING_SPI宏打开.

    5 移植ELM FAT文件系统

    ELM FAT一般用不着移植,MDK工程中一般默认就有,如果没有就得自己添加了,一般包含6个C文件:

    ff.c,dfs_elm.c,dfs.c,dfs_file.c,dfs_fs.c,dfs_posix.c,这些源文件在RTTcomponentsdfs目录下可以找到,一般不需要修改.

    但是在rtconfig.h头文件中针对ELM需要做些修改,如下:

    /* SECTION: device filesystem */
    #define RT_USING_DFS 
    #define RT_USING_DFS_ELMFAT
    #define RT_DFS_ELM_REENTRANT
    #define RT_DFS_ELM_WORD_ACCESS
    #define RT_DFS_ELM_DRIVES			1
    #define RT_DFS_ELM_USE_LFN			0 //这里一般设置为0,不使用长文件名,否则需要加入另外的源文件才能编译通过
    #define RT_DFS_ELM_MAX_LFN			255
    #define RT_DFS_ELM_MAX_SECTOR_SIZE  4096     //这里一定要与实际的spi flash一个扇区所包含的字节数相符,太小了会出现内存非法覆盖的情况
    
    /* the max number of mounted filesystem */
    #define DFS_FILESYSTEMS_MAX			2
    /* the max number of opened files 		*/
    #define DFS_FD_MAX					4

    此时附上MDK工程示图如下:

    6  初始化文件系统及挂载文件系统

    /* Filesystem Initialization */
    #ifdef RT_USING_DFS
    	{
    		/* init the device filesystem */
    		dfs_init();
    
    #ifdef RT_USING_DFS_ELMFAT
    		/* init the elm chan FatFs filesystam*/
    		elm_init();
    
    		/* mount sd card fat partition 1 as root directory */
    		if (dfs_mount("flash0", "/", "elm", 0, 0) == 0)
    		{
    			rt_kprintf("flash0 mount to /.
    ");
    		}
    		else
    			rt_kprintf("flash0 mount to / failed.
    ");
    #endif
    	}
    #endif


    7 格式化spi flash

    烧录进MCU,首次时SPI FLASH是未格式化的,因此会挂载出错,此时可以在finish下使用mkfs来格式化FLASH,然后使用mkdir来创建一个目录,再使用ls指令来查看创建的目录是否存在,如果存在,则说明正常了.如下图:

    通过这一步就说明SPI FLASH能正常工作了.

    下面通过代码来测试.

    8 通过代码来测试打开读写文件系统

    在工程中加入如下测试代码:

    {
            //文件系统测试代码
            int fd=0;
    
            fd =open("/myfile.txt",DFS_O_CREAT|DFS_O_RDWR,0);
            if(fd <0)
            {
                rt_kprintf("open file failed!
    ");
            }
            else
            {
                int count =write(fd,"123456",7);
                char buf[10];
    
                close(fd);
                fd =0;
    
    
                rt_thread_delay(50);
                rt_memset(buf,0,10);
                fd =open("/myfile.txt",DFS_O_RDONLY,0);
                if(read(fd,buf,7))
                {
                    rt_kprintf("read=%s
    ",buf);
                }
                else
                {
                    rt_kprintf("read file err!
    ");
                }
    
            }
        }


    结果如下:

    写进myfile.txt文件中的内容又可以原样读出来.

    也可以通过ls指令再次查看/目录下的内容:

    这样就验证了文件系统完全可以正常工作了.

    完!

  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3281378.html
Copyright © 2011-2022 走看看