Fork me on GitHub

RT-Thread学习1 —— spiflash+SFUD理解记录

SPI + SFUD

1. SPI的初始化和使用

1. SPI1的配置

记录:

首先使用CubeMX配置spi1的相关信息,然后再board.h中配置#define BSP_USING_SPI1

image

在drv_spi.c中 可以看到SPI1的配置

image

image

在开机初始化时候会调用rt_hw_spi_init

image

我这里没有使用dma所以直接看rt_hw_spi_bus_init

函数中主要将SPI注册到spi_bus中

image

其中的stm_spi_ops中主要有两个函数,一个是spi_configure函数,主要是在这里调用SPI的配置,其中会调用HAL_SPI_Init函数,这个函数会调用Cubemx中spi.c中的HAL_SPI_MspInit函数。(这里留个坑1:还没有调用这个函数)。spixfer函数主要是发送数据的函数。

在drv_spi.c中还有rt_hw_spi_device_attach函数。这个函数需要传入总线名称设备名称SPI的CS脚信息

image

这里将我们要使用的SPI设备绑定到SPI总线上。

(补坑1)在如下函数有数据传输之前都会调用config函数

/* send data then receive data from SPI device */
rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
                               const void           *send_buf,
                               rt_size_t             send_length,
                               void                 *recv_buf,
                               rt_size_t             recv_length);

rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
                               const void           *send_buf1,
                               rt_size_t             send_length1,
                               const void           *send_buf2,
                               rt_size_t             send_length2);

/**
 * This function transmits data to SPI device.
 *
 * @param device the SPI device attached to SPI bus
 * @param send_buf the buffer to be transmitted to SPI device.
 * @param recv_buf the buffer to save received data from SPI device.
 * @param length the length of transmitted data.
 *
 * @return the actual length of transmitted.
 */
rt_size_t rt_spi_transfer(struct rt_spi_device *device,
                          const void           *send_buf,
                          void                 *recv_buf,
                          rt_size_t             length);

/**
 * This function transfers a message list to the SPI device.
 *
 * @param device the SPI device attached to SPI bus
 * @param message the message list to be transmitted to SPI device
 *
 * @return RT_NULL if transmits message list successfully,
 *         SPI message which be transmitted failed.
 */
struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,
                                               struct rt_spi_message *message);

rt_inline rt_size_t rt_spi_recv(struct rt_spi_device *device,
                                void                 *recv_buf,
                                rt_size_t             length)
{
    return rt_spi_transfer(device, RT_NULL, recv_buf, length);
}

rt_inline rt_size_t rt_spi_send(struct rt_spi_device *device,
                                const void           *send_buf,
                                rt_size_t             length)
{
    return rt_spi_transfer(device, send_buf, RT_NULL, length);
}

rt_inline rt_uint8_t rt_spi_sendrecv8(struct rt_spi_device *device,
                                      rt_uint8_t            data)
{
    rt_uint8_t value;

    rt_spi_send_then_recv(device, &data, 1, &value, 1);

    return value;
}

rt_inline rt_uint16_t rt_spi_sendrecv16(struct rt_spi_device *device,
                                        rt_uint16_t           data)
{
    rt_uint16_t value;

    rt_spi_send_then_recv(device, &data, 2, &value, 2);

    return value;
}

/**
 * This function appends a message to the SPI message list.
 *
 * @param list the SPI message list header.
 * @param message the message pointer to be appended to the message list.
 */
rt_inline void rt_spi_message_append(struct rt_spi_message *list,
                                     struct rt_spi_message *message)
{
    RT_ASSERT(list != RT_NULL);
    if (message == RT_NULL)
        return; /* not append */

    while (list->next != RT_NULL)
    {
        list = list->next;
    }

    list->next = message;
    message->next = RT_NULL;
}

总结:

使用SPI的步骤:

    1. 在board.h和cubemx中配置使用SPI的宏定义
    1. 使用rt_hw_spi_device_attach函数将spi设备注册到spi总线

2. SFUD的初始化和使用

初始化:

image

image

在这里直接使用默认配置。

调用过程:
  • 添加初始化函数:

    static int rt_hw_spi_flash_init(void)
    {
        rt_hw_spi_device_attach("spi1", "spi1.0", GPIOA, GPIO_PIN_4);
    
        if (RT_NULL == rt_sfud_flash_probe(FAL_USING_NOR_FLASH_DEV_NAME, "spi1.0"))
        {
            return -RT_ERROR;
        }
    
        return RT_EOK;
    }
    INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
    

    其中rt_hw_spi_device_attach函数

    /*
    *	主要功能:
    *		1. 配置SPI的CSS脚的GPIO相关配置
    *		2. 注册一个spi_device设备
    *		3. 注册一个PIN设备
    *		4. 将spi_device设备绑定到SPI Bus上
    *		5. 返回结果
    */
    
    
    rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint16_t cs_gpio_pin)
    {
        RT_ASSERT(bus_name != RT_NULL);
        RT_ASSERT(device_name != RT_NULL);
    
        rt_err_t result;
        struct rt_spi_device *spi_device;
        struct stm32_hw_spi_cs *cs_pin;
    
        /* initialize the cs pin && select the slave*/
        GPIO_InitTypeDef GPIO_Initure;
        GPIO_Initure.Pin = cs_gpio_pin;
        GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_Initure.Pull = GPIO_PULLUP;
        GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(cs_gpiox, &GPIO_Initure);
        HAL_GPIO_WritePin(cs_gpiox, cs_gpio_pin, GPIO_PIN_SET);
    
        /* attach the device to spi bus*/
        spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
        RT_ASSERT(spi_device != RT_NULL);
        cs_pin = (struct stm32_hw_spi_cs *)rt_malloc(sizeof(struct stm32_hw_spi_cs));
        RT_ASSERT(cs_pin != RT_NULL);
        cs_pin->GPIOx = cs_gpiox;
        cs_pin->GPIO_Pin = cs_gpio_pin;
        result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
    
        if (result != RT_EOK)
        {
            LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
        }
    
        RT_ASSERT(result == RT_EOK);
    
        LOG_D("%s attach to %s done", device_name, bus_name);
    
        return result;
    }
    

    rt_sfud_flash_probe函数主要调用rt_sfud_flash_probe_ex函数

    /*
    *	主要调用了以下几个函数
    *		1. rt_spi_configure  配置spi相关参数
    *		2. sfud_device_init(sfud_dev)
    *		3. rt_device_register 注册spi_flash设备函数
    */
    
    
    /**
     * Probe SPI flash by SFUD (Serial Flash Universal Driver) driver library and though SPI device by specified configuration.
     *
     * @param spi_flash_dev_name the name which will create SPI flash device
     * @param spi_dev_name using SPI device name
     * @param spi_cfg SPI device configuration
     * @param qspi_cfg QSPI device configuration
     *
     * @return probed SPI flash device, probe failed will return RT_NULL
     */
    rt_spi_flash_device_t rt_sfud_flash_probe_ex(const char *spi_flash_dev_name, const char *spi_dev_name,
            struct rt_spi_configuration *spi_cfg, struct rt_qspi_configuration *qspi_cfg)
    {
        rt_spi_flash_device_t rtt_dev = RT_NULL;
        sfud_flash *sfud_dev = RT_NULL;
        char *spi_flash_dev_name_bak = RT_NULL, *spi_dev_name_bak = RT_NULL;
        extern sfud_err sfud_device_init(sfud_flash *flash);
    #ifdef SFUD_USING_QSPI
        struct rt_qspi_device *qspi_dev = RT_NULL;
    #endif
    
        RT_ASSERT(spi_flash_dev_name);
        RT_ASSERT(spi_dev_name);
    
        rtt_dev = (rt_spi_flash_device_t) rt_malloc(sizeof(struct spi_flash_device));
        sfud_dev = (sfud_flash_t) rt_malloc(sizeof(sfud_flash));
        spi_flash_dev_name_bak = (char *) rt_malloc(rt_strlen(spi_flash_dev_name) + 1);
        spi_dev_name_bak = (char *) rt_malloc(rt_strlen(spi_dev_name) + 1);
    
        if (rtt_dev) {
            rt_memset(rtt_dev, 0, sizeof(struct spi_flash_device));
            /* initialize lock */
            rt_mutex_init(&(rtt_dev->lock), spi_flash_dev_name, RT_IPC_FLAG_FIFO);
        }
    
        if (rtt_dev && sfud_dev && spi_flash_dev_name_bak && spi_dev_name_bak) {
            rt_memset(sfud_dev, 0, sizeof(sfud_flash));
            rt_strncpy(spi_flash_dev_name_bak, spi_flash_dev_name, rt_strlen(spi_flash_dev_name));
            rt_strncpy(spi_dev_name_bak, spi_dev_name, rt_strlen(spi_dev_name));
            /* make string end sign */
            spi_flash_dev_name_bak[rt_strlen(spi_flash_dev_name)] = '\0';
            spi_dev_name_bak[rt_strlen(spi_dev_name)] = '\0';
            /* SPI configure */
            {
                /* RT-Thread SPI device initialize */
                rtt_dev->rt_spi_device = (struct rt_spi_device *) rt_device_find(spi_dev_name);
                if (rtt_dev->rt_spi_device == RT_NULL || rtt_dev->rt_spi_device->parent.type != RT_Device_Class_SPIDevice) {
                    LOG_E("ERROR: SPI device %s not found!", spi_dev_name);
                    goto error;
                }
                sfud_dev->spi.name = spi_dev_name_bak;
    
    #ifdef SFUD_USING_QSPI
                /* set the qspi line number and configure the QSPI bus */
                if(rtt_dev->rt_spi_device->bus->mode &RT_SPI_BUS_MODE_QSPI) {
                    qspi_dev = (struct rt_qspi_device *)rtt_dev->rt_spi_device;
                    qspi_cfg->qspi_dl_width = qspi_dev->config.qspi_dl_width;
                    rt_qspi_configure(qspi_dev, qspi_cfg);
                }
                else
    #endif
                    rt_spi_configure(rtt_dev->rt_spi_device, spi_cfg);
            }
            /* SFUD flash device initialize */
            {
                sfud_dev->name = spi_flash_dev_name_bak;
                /* accessed each other */
                rtt_dev->user_data = sfud_dev;
                rtt_dev->rt_spi_device->user_data = rtt_dev;
                rtt_dev->flash_device.user_data = rtt_dev;
                sfud_dev->user_data = rtt_dev;
                /* initialize SFUD device */
                if (sfud_device_init(sfud_dev) != SFUD_SUCCESS) {
                    LOG_E("ERROR: SPI flash probe failed by SPI device %s.", spi_dev_name);
                    goto error;
                }
                /* when initialize success, then copy SFUD flash device's geometry to RT-Thread SPI flash device */
                rtt_dev->geometry.sector_count = sfud_dev->chip.capacity / sfud_dev->chip.erase_gran;
                rtt_dev->geometry.bytes_per_sector = sfud_dev->chip.erase_gran;
                rtt_dev->geometry.block_size = sfud_dev->chip.erase_gran;
    #ifdef SFUD_USING_QSPI
                /* reconfigure the QSPI bus for medium size */
                if(rtt_dev->rt_spi_device->bus->mode &RT_SPI_BUS_MODE_QSPI) {
                    qspi_cfg->medium_size = sfud_dev->chip.capacity;
                    rt_qspi_configure(qspi_dev, qspi_cfg);
                    if(qspi_dev->enter_qspi_mode != RT_NULL)
                        qspi_dev->enter_qspi_mode(qspi_dev);
    
                    /* set data lines width */
                    sfud_qspi_fast_read_enable(sfud_dev, qspi_dev->config.qspi_dl_width);
                }
    #endif /* SFUD_USING_QSPI */
            }
    
            /* register device */
            rtt_dev->flash_device.type = RT_Device_Class_Block;
    #ifdef RT_USING_DEVICE_OPS
            rtt_dev->flash_device.ops  = &flash_device_ops;
    #else
            rtt_dev->flash_device.init = RT_NULL;
            rtt_dev->flash_device.open = RT_NULL;
            rtt_dev->flash_device.close = RT_NULL;
            rtt_dev->flash_device.read = rt_sfud_read;
            rtt_dev->flash_device.write = rt_sfud_write;
            rtt_dev->flash_device.control = rt_sfud_control;
    #endif
    
            rt_device_register(&(rtt_dev->flash_device), spi_flash_dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
    
            LOG_I("Probe SPI flash %s by SPI device %s success.",spi_flash_dev_name, spi_dev_name);
            return rtt_dev;
        } else {
            LOG_E("ERROR: Low memory.");
            goto error;
        }
    
    error:
    
        if (rtt_dev) {
            rt_mutex_detach(&(rtt_dev->lock));
        }
        /* may be one of objects memory was malloc success, so need free all */
        rt_free(rtt_dev);
        rt_free(sfud_dev);
        rt_free(spi_flash_dev_name_bak);
        rt_free(spi_dev_name_bak);
    
        return RT_NULL;
    }
    

    sfud_device_init函数

    sfud_err sfud_device_init(sfud_flash *flash) {
        sfud_err result = SFUD_SUCCESS;
    
        /* hardware initialize */
        result = hardware_init(flash);
        if (result == SFUD_SUCCESS) {
            result = software_init(flash);
        }
        if (result == SFUD_SUCCESS) {
            flash->init_ok = true;
            SFUD_INFO("%s flash device is initialize success.", flash->name);
        } else {
            flash->init_ok = false;
            SFUD_INFO("Error: %s flash device is initialize fail.", flash->name);
        }
    
        return result;
    }
    
    

    hardware_init函数

    /**
     * hardware initialize
     * 设置flash_chip的相关信息
     *		1. 从flash中读出jedec相关信息
     *		2. 尝试使用SFDP读取flash芯片具体信息
     *		3. 如果不适用SFDP那就使用flash_chip_table中定义的芯片类型遍历尝试
     *      4. 根据信息进行一些基础操作
     */
    static sfud_err hardware_init(sfud_flash *flash) {
        extern sfud_err sfud_spi_port_init(sfud_flash * flash);
    
        sfud_err result = SFUD_SUCCESS;
        size_t i;
    
        SFUD_ASSERT(flash);
    
        result = sfud_spi_port_init(flash);
        if (result != SFUD_SUCCESS) {
            return result;
        }
    
    #ifdef SFUD_USING_QSPI
        /* set default read instruction */
        flash->read_cmd_format.instruction = SFUD_CMD_READ_DATA;
    #endif /* SFUD_USING_QSPI */
    
        /* SPI write read function must be initialize */
        SFUD_ASSERT(flash->spi.wr);
        /* if the user don't configure flash chip information then using SFDP parameter or static flash parameter table */
        if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
                || flash->chip.erase_gran_cmd == 0) {
            /* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */
            result = read_jedec_id(flash);
            if (result != SFUD_SUCCESS) {
                return result;
            }
    
    #ifdef SFUD_USING_SFDP
            extern bool sfud_read_sfdp(sfud_flash *flash);
            /* read SFDP parameters */
            if (sfud_read_sfdp(flash)) {
                flash->chip.name = NULL;
                flash->chip.capacity = flash->sfdp.capacity;
                /* only 1 byte or 256 bytes write mode for SFDP */
                if (flash->sfdp.write_gran == 1) {
                    flash->chip.write_mode = SFUD_WM_BYTE;
                } else {
                    flash->chip.write_mode = SFUD_WM_PAGE_256B;
                }
                /* find the the smallest erase sector size for eraser. then will use this size for erase granularity */
                flash->chip.erase_gran = flash->sfdp.eraser[0].size;
                flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd;
                for (i = 1; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
                    if (flash->sfdp.eraser[i].size != 0 && flash->chip.erase_gran > flash->sfdp.eraser[i].size) {
                        flash->chip.erase_gran = flash->sfdp.eraser[i].size;
                        flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd;
                    }
                }
            } else {
    #endif
    
    #ifdef SFUD_USING_FLASH_INFO_TABLE
                /* read SFDP parameters failed then using SFUD library provided static parameter */
                for (i = 0; i < sizeof(flash_chip_table) / sizeof(sfud_flash_chip); i++) {
                    if ((flash_chip_table[i].mf_id == flash->chip.mf_id)
                            && (flash_chip_table[i].type_id == flash->chip.type_id)
                            && (flash_chip_table[i].capacity_id == flash->chip.capacity_id)) {
                        flash->chip.name = flash_chip_table[i].name;
                        flash->chip.capacity = flash_chip_table[i].capacity;
                        flash->chip.write_mode = flash_chip_table[i].write_mode;
                        flash->chip.erase_gran = flash_chip_table[i].erase_gran;
                        flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd;
                        break;
                    }
                }
    #endif
    
    #ifdef SFUD_USING_SFDP
            }
    #endif
    
        }
    
        if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0
                || flash->chip.erase_gran_cmd == 0) {
            SFUD_INFO("Warning: This flash device is not found or not support.");
            return SFUD_ERR_NOT_FOUND;
        } else {
            const char *flash_mf_name = NULL;
            /* find the manufacturer information */
            for (i = 0; i < sizeof(mf_table) / sizeof(sfud_mf); i++) {
                if (mf_table[i].id == flash->chip.mf_id) {
                    flash_mf_name = mf_table[i].name;
                    break;
                }
            }
            /* print manufacturer and flash chip name */
            if (flash_mf_name && flash->chip.name) {
                SFUD_INFO("Find a %s %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.name,
                        flash->chip.capacity);
            } else if (flash_mf_name) {
                SFUD_INFO("Find a %s flash chip. Size is %ld bytes.", flash_mf_name, flash->chip.capacity);
            } else {
                SFUD_INFO("Find a flash chip. Size is %ld bytes.", flash->chip.capacity);
            }
        }
    
        /* reset flash device */
        result = reset(flash);
        if (result != SFUD_SUCCESS) {
            return result;
        }
    
        /* I found when the flash write mode is supported AAI mode. The flash all blocks is protected,
         * so need change the flash status to unprotected before write and erase operate. */
        if (flash->chip.write_mode & SFUD_WM_AAI) {
            result = sfud_write_status(flash, true, 0x00);
            if (result != SFUD_SUCCESS) {
                return result;
            }
        }
    
        /* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */
        if (flash->chip.capacity > (1L << 24)) {
            result = set_4_byte_address_mode(flash, true);
        } else {
            flash->addr_in_4_byte = false;
        }
    
        return result;
    }
    
    

image

posted @ 2022-04-01 14:22  一地鸡毛-  阅读(1192)  评论(0编辑  收藏  举报