1. 程式人生 > 其它 >RT-Thread學習1 —— spiflash+SFUD理解記錄

RT-Thread學習1 —— spiflash+SFUD理解記錄

SPI + SFUD

1. SPI的初始化和使用

1. SPI1的配置

記錄:

首先使用CubeMX配置spi1的相關資訊,然後再board.h中配置#define BSP_USING_SPI1

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

在開機初始化時候會呼叫rt_hw_spi_init

我這裡沒有使用dma所以直接看rt_hw_spi_bus_init

函式中主要將SPI註冊到spi_bus中

其中的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腳資訊

這裡將我們要使用的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的初始化和使用

初始化:

在這裡直接使用預設配置。

呼叫過程:
  • 新增初始化函式:

    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;
    }