RT-Thread學習1 —— spiflash+SFUD理解記錄
阿新 • • 發佈:2022-04-01
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
在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的步驟:
-
- 在board.h和cubemx中配置使用SPI的巨集定義
-
- 使用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; }