打印
[应用相关]

STM32外设驱动 - Storage驱动

[复制链接]
252|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、W25Qxx(FLASH)
1、w25qxx驱动
w25qxx.h

#pragma once

#include <stdbool.h>

#include <stdint.h>

#include <string.h>

#include <drv_common.h>

// 制造商ID

// Manufacturer ID: 0xEF (Winbond)

// Device ID: 0x16 (indicating W25Q64)

// Device ID: 0x17 (indicating W25Q128)

#define W25QX_ID                0x17EF

// 寄存器地址

#define W25QX_WriteEnable       0x06

#define W25QX_WriteDisable      0x04

#define W25QX_ReadStatusReg     0x05

#define W25QX_WriteStatusReg    0x01

#define W25QX_ReadData          0x03

#define W25QX_FastReadData      0x0B

#define W25QX_FastReadDual      0x3B

#define W25QX_PageProgram       0x02

#define W25QX_BlockErase        0xD8

#define W25QX_SectorErase       0x20

#define W25QX_ChipErase         0xC7

#define W25QX_PowerDown         0xB9

#define W25QX_ReleasePowerDown  0xAB

#define W25QX_DeviceID          0xAB

#define W25QX_ManufactDeviceID  0x90

#define W25QX_JedecDeviceID     0x9F

/*!

* W25QXX初始化

* @param busName 总线名称

* @param devName 设备名称

* @param cs_gpiox 片选GPIO

* @param cs_gpio_pin 片选PIN

* @return 初始化状态

*/

bool w25qxxInit(char *busName, char *devName, GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin);

/*!

* 读取制造商ID

* @return 制造商ID

*/

uint16_t w25qxxReadID(void);

/*!

* 读取状态寄存器

* @return 状态

*/

uint8_t w25qxxReadSR(void);

/*!

* 写使能

*/

void w25qxxWriteEnable(void);

/*!

* 写禁止

*/

void w25qxxWriteDisable(void);

/*!

* 忙碌等待

*/

void w25qxxWaitBusy(void);

/*!

* 读取数据

* @param addr 地址

* @param data 缓冲区

* @param bytes 字节数

*/

void w25qxxRead(uint32_t addr, void* data, uint16_t bytes);

/*!

* 页写-最大256字节

* @param addr 地址

* @param data 缓冲区

* @param bytes 字节数

*/

void w25qxxWritePage(uint32_t addr, void* data, uint16_t bytes);

/*!

* 跨页写

* @param addr 地址

* @param data 缓冲区

* @param bytes 字节数

*/

void w25qxxWrite(uint32_t addr, void* data, uint16_t bytes);

/*!

* 擦除扇区

* @param addr 地址

*/

void w25qxxEraseSector(uint32_t addr);

/*!

* 全片擦除

*/

void w25qxxEraseChip();

w25qxx.c

#include "w25qxx.h"

#include <rtthread.h>

#include <rtdevice.h>

#include "drv_spi.h"

static struct rt_spi_device *devW25q;

bool w25qxxInit(char *busName, char *devName, GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin)

{

    // ---------------------------注册SPI总线设备

    rt_hw_spi_device_attach(busName, devName, cs_gpiox, cs_gpio_pin);

    // 查找设备

    devW25q = (struct rt_spi_device *)rt_device_find(devName);

    if (devW25q == RT_NULL) {

        rt_kprintf("rt_device_find[%s] failed!\n", devName);

        return false;

    }

    // 配置SPI参数

    struct rt_spi_configuration cfg;

    cfg.data_width = 8;

    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_3 | RT_SPI_MSB;

    cfg.max_hz = 18 * 1000 *1000;

    rt_spi_configure(devW25q, &cfg);

    // ---------------------------读取制造商ID

    if(w25qxxReadID() != W25QX_ID) {

        rt_kprintf("w25qxx read id failed!\n");

        return false;

    }

    return true;

}

uint16_t w25qxxReadID(void)

{

    uint8_t cmd = W25QX_ManufactDeviceID;

    uint8_t id[5] = {0};

    rt_spi_send_then_recv(devW25q, &cmd, 1, id, 5);

    return id[3] << 8 | id[4];

}

uint8_t w25qxxReadSR(void)

{

    uint8_t cmd = W25QX_ReadStatusReg;

    uint8_t sr = 0;

    rt_spi_send_then_recv(devW25q, &cmd, 1, &sr, 1);

    return sr;

}

void w25qxxWriteEnable(void)

{

    uint8_t cmd = W25QX_WriteEnable;

    rt_spi_send(devW25q, &cmd, 1);

}

void w25qxxWriteDisable(void)

{

    uint8_t cmd = W25QX_WriteDisable;

    rt_spi_send(devW25q, &cmd, 1);

}

void w25qxxWaitBusy(void)

{

    while((w25qxxReadSR()&0x01) == 0x01);

}

void w25qxxRead(uint32_t addr, void* data, uint16_t bytes)

{

    uint32_t cmd = (W25QX_ReadData)|(addr<<8);

    rt_spi_send_then_recv(devW25q, &cmd, 4, data, bytes);

}

void w25qxxWritePage(uint32_t addr, void* data, uint16_t bytes)

{

    w25qxxWriteEnable();

    uint32_t cmd = (W25QX_PageProgram)|(addr<<8);

    rt_spi_send_then_send(devW25q, &cmd, 4, data, bytes);

    w25qxxWaitBusy();

}

void w25qxxWrite(uint32_t addr, void* data, uint16_t bytes)

{

    // 数据缓冲区操作指针

    uint8_t *ptr = data;

    // 页空间剩余字节

    uint16_t pageByte;

    while(bytes) {

        // 计算页空间剩余字节 - 每页256byte

        pageByte = 256-addr%256;

        if(bytes > pageByte) {

            // 待写入字节大于页空间剩余字节 - 分包写入

            w25qxxWritePage(addr, ptr, pageByte);

            ptr += pageByte;

            addr += pageByte;

            bytes -= pageByte;

        } else if(bytes <= pageByte) {

            // 待写入字节小于页空间剩余字节 - 一次性写入

            w25qxxWritePage(addr, ptr, bytes);

            ptr += bytes;

            addr += bytes;

            bytes -= bytes;

        }

    }

}

void w25qxxEraseSector(uint32_t addr)

{

    // 计算扇区索引

    addr /= 4096;

    // 根据索引计算扇区起始地址

    addr *= 4096;

    w25qxxWriteEnable();

    w25qxxWaitBusy();

    uint32_t cmd = (W25QX_SectorErase)|(addr<<8);

    rt_spi_send(devW25q, &cmd, 4);

    w25qxxWaitBusy();

}

void w25qxxEraseChip()

{

    w25qxxWriteEnable();

    uint8_t cmd = W25QX_ChipErase;

    rt_spi_send(devW25q, &cmd, 1);

    w25qxxWaitBusy();

}

// FLASH读写测试

#define spiFlashWRTestSize 260

int spiFlashWRTest(void)

{

    w25qxxEraseSector(0x2000);

    uint8_t testWrite[spiFlashWRTestSize] = {0};

    for(int i = 0;i < spiFlashWRTestSize;i++) {

        testWrite = i+1;

    }

    w25qxxWrite(0x2000, testWrite, spiFlashWRTestSize);

    uint8_t testRead[spiFlashWRTestSize] = {0};

    w25qxxRead(0x2000, testRead, spiFlashWRTestSize);

    for(int i = 0;i < spiFlashWRTestSize;i++) {

        rt_kprintf("read test[%d]: %d\n", i, testRead);

    }

    return 0;

}

MSH_CMD_EXPORT(spiFlashWRTest, spiFlashWRTest);

// FLASH全片擦除测试

MSH_CMD_EXPORT(w25qxxEraseChip, spiFlashEraseChip);

使用示例:

w25qxxInit("spi2", "w2qx", GPIOB, GPIO_PIN_12);



2、SFUD/FAL
SFUD(全称 Serial Flash Universal Driver)是一款开源的串行 SPI Flash 通用驱动库;

FAL:Flash 抽象层。      

使能组件:



修改board.h:

#define BSP_USING_ON_CHIP_FLASH

#define BSP_USING_SPI2

添加fal_config:



// 修改fal_cfg.h:

/*******************************************

* File: fal_cfg.h

* Describe: FAL_FLASH分区表

* Author: Xiangfu DING

* Time: 2025-02-10

*******************************************/

#ifndef _FAL_CFG_H_

#define _FAL_CFG_H_

#include <rtconfig.h>

#include <board.h>

#ifndef FAL_USING_NOR_FLASH_DEV_NAME

#define NOR_FLASH_DEV_NAME             "norflash0"

#else

#define NOR_FLASH_DEV_NAME              FAL_USING_NOR_FLASH_DEV_NAME

#endif

// STM32F407VGT6 - 内部 Flash 大小为 1024KB(分为 4 个 16KB 扇区、1 个 64KB 扇区、7 个 128KB 扇区)

#define FLASH_SIZE_GRANULARITY_16K        (4 * 16 * 1024)

#define FLASH_SIZE_GRANULARITY_64K        (64 * 1024)

#define FLASH_SIZE_GRANULARITY_128K        (7 * 128 * 1024)

#define STM32_FLASH_START_ADRESS_16K    STM32_FLASH_START_ADRESS

#define STM32_FLASH_START_ADRESS_64K    (STM32_FLASH_START_ADRESS_16K + FLASH_SIZE_GRANULARITY_16K)

#define STM32_FLASH_START_ADRESS_128K    (STM32_FLASH_START_ADRESS_64K + FLASH_SIZE_GRANULARITY_64K)

/* ===================== Flash device Configuration ========================= */

extern const struct fal_flash_dev stm32_onchip_flash_16k;

extern const struct fal_flash_dev stm32_onchip_flash_64k;

extern const struct fal_flash_dev stm32_onchip_flash_128k;

/* ===================== Flash device Configuration ========================= */

extern struct fal_flash_dev nor_flash0;

/* flash device table */

#define FAL_FLASH_DEV_TABLE                                          \

{                                                                    \

    &stm32_onchip_flash_16k,                                         \

    &stm32_onchip_flash_64k,                                         \

    &stm32_onchip_flash_128k,                                        \

    &nor_flash0,                                                     \

}

/* ====================== Partition Configuration ========================== */

#ifdef FAL_PART_HAS_TABLE_CFG

/* partition table */

#define FAL_PART_TABLE \

{ \

    {FAL_PART_MAGIC_WROD,        "app",     "onchip_flash",      0 * 1024,       512 * 1024, 0}, \

    {FAL_PART_MAGIC_WROD,   "download", NOR_FLASH_DEV_NAME,      0 * 1024,       512 * 1024, 0}, \

}

#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */

// 初始化

#include "fal.h"

int main(void)

{

// 初始化W25Q64

    rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12);

    //flash名称要正确

    if (RT_NULL == rt_sfud_flash_probe("W25Q64", "spi20")) {

     rt_kprintf("rt_sfud_flash_probe failed!\n");

    }

// 初始化FAL

fal_init();

    ... ...



二、FM24CLxx(FRAM)
IIC通信,FM24CLxx(铁电随机存取存储器(FRAM))系列存储容量范围从4 Kbit到1 Mbit不等,型号/存储容量:

        FM24CL04B:4 Kbit (512字节)

        FM24CL16B:16 Kbit(2 KB)

        FM24CL64B:64 Kbit(8 KB)

        FM24CL128B:128 Kbit(16 KB)

        FM24CL256B:256 Kbit(32 KB)

        FM24CL512B:512 Kbit(64 KB)

        FM24CL512L:512 Kbit(64 KB)

        FM24CL01B:1 Mbit(128 KB)

FM24CL04是一种4Kb的F-RAM(Ferroelectric Random Access Memory),在FM24CL04中,每个字节的地址被表示为一个8位字节地址;

与EEPROM或Flash不同,F-RAM的地址不需要跨页,这意味着可以直接在单个页面(或单个字节)内进行读写操作,而无需担心页边界问题,因此,在FM24CL04中,可以按任意顺序访问和修改存储的数据,而不需要进行复杂的加载和存储算法;

FM24CL04的地址不需要跨页,可以直接访问和操作存储器中的任何地址。

fm24clxx.h

#pragma once

#include <stdbool.h>

#include <stdint.h>

#include <string.h>

#include <drv_common.h>

void fm24clxxInit(rt_base_t pinScl, rt_base_t pinSda);

void fm24clxxWrite(uint16_t addr, void *data, uint16_t length);

void fm24clxxRead(uint16_t addr, void *data, uint16_t length);

fm24clxx.c

#include "fm24clxx.h"

#include "drv_bus_i2c.h"

#define FM24CLXX_ADDR 0xA0

static iicPort_t iicPort;

void fm24clxxInit(rt_base_t pinScl, rt_base_t pinSda)

{

    iicPort.pinScl = pinScl;

    iicPort.pinSda = pinSda;

    iicInit(&iicPort);

}

static void fm24clxxWriteBytes(uint16_t addr, void *data, uint16_t length)

{

    uint8_t *buffer = data;

    iicStart(&iicPort);

    iicSendByte(&iicPort, FM24CLXX_ADDR|(addr&0x0100)>>7);

    iicSendByte(&iicPort, (uint8_t)addr&0xFF);

    while(length--) {

        iicSendByte(&iicPort, *buffer++);

    }

    iicStop(&iicPort);

}

void fm24clxxWrite(uint16_t addr, void *data, uint16_t length)

{

    uint8_t *w_buff = data;

    while(length) {

        uint16_t free_byte = (256-addr%256);

        if(free_byte < length) {

            fm24clxxWriteBytes(addr, w_buff, free_byte);

            length -= free_byte;

            addr += free_byte;

            w_buff += free_byte;

        } else {

            fm24clxxWriteBytes(addr, w_buff, length);

            length -= length;

        }

    }

}

static void fm24clxxReadBytes(uint16_t addr,void *data, uint8_t length)

{

    uint8_t *buffer = data;

    iicStart(&iicPort);

    iicSendByte(&iicPort, FM24CLXX_ADDR|(addr&0x0100)>>7);

    iicSendByte(&iicPort, addr&0xFF);

    iicStop(&iicPort);

    iicStart(&iicPort);

    iicSendByte(&iicPort, FM24CLXX_ADDR|(addr&0x0100)>>7|0x01);

    length--;

    while(length--) {

        *buffer++ = iicReadByte(&iicPort, true);

    }

    *buffer = iicReadByte(&iicPort, false);

    iicStop(&iicPort);

}

void fm24clxxRead(uint16_t addr, void *data, uint16_t length)

{  

    uint8_t *r_buff = data;

    while(length) {

        uint16_t free_byte = (256-addr%256);

        if(free_byte < length) {

            fm24clxxReadBytes(addr, r_buff, free_byte);

            length -= free_byte;

            addr += free_byte;

            r_buff += free_byte;

        } else {

            fm24clxxReadBytes(addr, r_buff, length);

            length -= length;

        }

    }

}

// FRAM读写测试

int iicFramWRTest(void)

{

    uint8_t data[20] = {0};

    for(int i = 0;i < sizeof(data);i++) {

        data = i;

    }

    fm24clxxWrite(0x10, &data, sizeof(data));

    memset(data, 0x00 , sizeof(data));

    fm24clxxRead(0x10, &data, sizeof(data));

    for(int i = 0;i < sizeof(data);i++) {

        rt_kprintf("read test[%d]: %d\n", i, data);

    }

    return 0;

}

MSH_CMD_EXPORT(iicFramWRTest, iicFramWRTest);

三、SDCARD
1、SPI SD
使用前提:配置好对应的SPI总线。

打开SPI SD/TF卡驱动程序:



使用设备虚拟文件系统:



注册SPI SD设备:

#include "spisd.h"

#include <rtdevice.h>

#include <dfs_fs.h>

#include "drv_spi.h"

#include "spi_msd.h"

static rt_base_t sdinsertPin;

static char *spisdDevName = NULL;

int spisdPreInit(char *busName, char *devName,

        GPIO_TypeDef* cs_gpiox, uint16_t cs_gpio_pin, rt_base_t insert_pin)

{

    spisdDevName = devName;

    rt_hw_spi_device_attach(busName, devName, cs_gpiox, cs_gpio_pin);

    sdinsertPin = insert_pin;

    return 0;

}

int spisdInit()

{

    return msd_init(SD_DEVICE_NAME, spisdDevName);

}

int spisdDeInit()

{

    return msd_deinit();

}

bool spisdInsertStatus()

{

    uint8_t status = rt_pin_read(sdinsertPin);

    if(status == PIN_LOW) return true;

    else return false;

}

修改rt-thread\components\drivers\spi\spi_msd.c/h(增加函数):

rt_err_t msd_deinit()

{

    return rt_device_unregister(&_msd_device.parent);

}

extern rt_err_t msd_deinit();

热插拔SD卡及文件系统挂载/卸载:

static rt_thread_t thread_Fatfs = RT_NULL;

static void threadEntry_Fatfs(void *parameter)

{

    bool sdInsert = false;

    bool dfsStatus = false;

    while (1) {

        sdInsert = spisdInsertStatus();

        if(sdInsert) {

            // 文件系统挂载

            if (!dfsStatus) {

                spisdInit();

                if(dfs_mount("sd0", "/", "elm", 0, 0) == 0) {

                    rt_kprintf("Fat filesystem mount ok.\n");

                    dfsStatus = true;

                    beeper(BEEPER_SHORT);

                } else {

                    rt_kprintf("Fat filesystem mount failed!\n");

                    dfsStatus = false;

                }

            }

        } else {

            // 文件系统卸载

            if (dfsStatus) {

                if (dfs_unmount("/")) {

                    rt_kprintf("Fat filesystem umount failed!\n");

                } else {

                    rt_kprintf("Fat filesystem umount ok.\n");

                    beeper(BEEPER_SHORT);

                }

                spisdDeInit();

                dfsStatus = false;

            }

        }

        rt_thread_mdelay(100);

    }

}

// 其他文件系统操作参考SDIO

2、SDIO SD
参考STM32片上驱动 - SDIO驱动。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/dxf1971738522/article/details/146060793

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

90

帖子

0

粉丝