1. 按照移植rt-thread驱动的三部曲
- 第1步,用cubeMX创建spi/qspi的工程,并在Keil下测试成功spi/qspi读写spi-flash。分别用dma模式和polling模式测试。这一步前面已经ok。
- 第2步,在rt-thread环境下,创建test.c文件,将前面CubeMX工程的相应函数copy过来(main.c中的gpio/spi/dma初始化函数及测试函数及回调函数, stm32h7xx_hal_msp.c中的msp_init, msp_denit函数,以及stm32h7xx_it.c中的中断函数),并通过msh_cmd测试成功。
- 第3步,修改drv_spi.c,实现相应的函数,并测试它直到成功。
2. drv_spi.c
- spixfer中,定义#define DMA_MIN_SIZE 10 /set this=10000, means always use polling. set this=0, means always use dma. both works fine./
- 我们定义了noncache region,方便dma使用。
- 对于dma发送,我们无条件copy send_buf到tx_buffer,再发送。对于dma接收,我们总是使用rx_buffer执行dma,然后无条件copy rx_buffer到recv_buffer中。这样大大简化了操作。
- 注意GPIO速度要HIGH
- 注意分频比配置。先开始尽量让频率低,等成功了再逐步提高频率。
- 注意DMA配置,先关闭fifo。测试成功了,再可以考虑开启fifo。
- 注意检查drv_mpu.c配置nocache region
- 注意检查link.lds配置nocache region
- 注意rt_spi_configure函数存在一个提前return的bug,我已经修正了。
- 在spixfer函数中,我调用底层HAL库函数的时候,大量使用了RT_ASSSERT()宏,感觉非常不错。
- 注意,我们定义了DMA_SIZE=8192,这意味着支持的单个包的最大传输是8192 bytes。如果应用程序超过8192 bytes,需要做分包处理。
- 后续优化,可以考虑修改spixfer函数,让其实现分包功能。这样软件应用层无需做分包处理,没有传输size限制。
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
#include "trace_log.h"#ifdef BSP_USING_SPI#include "drv_spi.h"
#include "drv_config.h"
#define LOG_TAG "drv.spi"
#include <drv_log.h>#define DMA_SIZE 8192
#define USB_NOCACHE_RAM_SECTION __attribute__((section(".noncacheable")))
#define USB_MEM_ALIGNX __attribute__((aligned(32)))
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t tx_buffer[DMA_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t rx_buffer[DMA_SIZE];SPI_HandleTypeDef hspi1;
SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi1_tx;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi2_tx;
DMA_HandleTypeDef hdma_spi2_rx;static void MX_SPI1_Init(void);
static void MX_DMA_Init(void);struct rt_completion spi_cpt;
struct rt_spi_bus spi_bus;static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{#define DMA_MIN_SIZE 10 HAL_StatusTypeDef state = HAL_OK;rt_size_t message_length, already_send_length;rt_uint16_t send_length;rt_uint8_t *recv_buf;const rt_uint8_t *send_buf; if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE)){if (device->config.mode & RT_SPI_CS_HIGH){rt_pin_write(device->cs_pin, PIN_HIGH);}else{rt_pin_write(device->cs_pin, PIN_LOW);}}message_length = message->length;recv_buf = message->recv_buf;send_buf = message->send_buf;if(message_length < DMA_MIN_SIZE){ if (message->send_buf && message->recv_buf){RT_ASSERT(HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)send_buf, (uint8_t *)recv_buf, message_length, 1000) == HAL_OK);}else if (message->send_buf){RT_ASSERT(HAL_SPI_Transmit(&hspi1, (uint8_t *)send_buf, message_length, 1000) == HAL_OK);}else if (message->recv_buf){RT_ASSERT(HAL_SPI_Receive(&hspi1, (uint8_t *)recv_buf, message_length, 1000) == HAL_OK);}while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);}else { RT_ASSERT(message_length <= DMA_SIZE); if(message->send_buf){rt_memcpy(tx_buffer, send_buf, message_length);}if (message->send_buf && message->recv_buf){RT_ASSERT(HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t *)tx_buffer, (uint8_t *)rx_buffer, message_length) == HAL_OK);}else if (message->send_buf){RT_ASSERT(HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)tx_buffer, message_length) == HAL_OK);}else if (message->recv_buf){RT_ASSERT(HAL_SPI_Receive_DMA(&hspi1, (uint8_t *)rx_buffer, message_length) == HAL_OK);} RT_ASSERT(rt_completion_wait(&spi_cpt, 1000) == RT_EOK);if(message->recv_buf){rt_memcpy(recv_buf, rx_buffer, message_length);}}if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE)){if (device->config.mode & RT_SPI_CS_HIGH){rt_pin_write(device->cs_pin, PIN_LOW);}else{rt_pin_write(device->cs_pin, PIN_HIGH);}} return message->length;
}static rt_err_t spi_configure(struct rt_spi_device *device,struct rt_spi_configuration *configuration)
{MX_DMA_Init();MX_SPI1_Init();return RT_EOK;
}static const struct rt_spi_ops stm_spi_ops =
{.configure = spi_configure,.xfer = spixfer,
};rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin)
{RT_ASSERT(bus_name != RT_NULL);RT_ASSERT(device_name != RT_NULL);rt_err_t result;struct rt_spi_device *spi_device;spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));RT_ASSERT(spi_device != RT_NULL);result = rt_spi_bus_attach_device_cspin(spi_device, device_name, bus_name, cs_pin, RT_NULL);RT_ASSERT(result == RT_EOK);return result;
}void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{rt_completion_done(&spi_cpt);
}void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{rt_completion_done(&spi_cpt);
}void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{rt_completion_done(&spi_cpt);
}int rt_hw_spi_init(void)
{rt_err_t result;rt_completion_init(&spi_cpt);result = rt_spi_bus_register(&spi_bus, "spi1", &stm_spi_ops);RT_ASSERT(result == RT_EOK);return result;
}
INIT_BOARD_EXPORT(rt_hw_spi_init);static void MX_SPI1_Init(void){hspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_2LINES;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 0x0;hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;if (HAL_SPI_Init(&hspi1) != HAL_OK){Error_Handler();}}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(hspi->Instance==SPI1){__HAL_RCC_SPI1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOG_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_9;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);GPIO_InitStruct.Pin = GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);hdma_spi1_tx.Instance = DMA1_Stream0;hdma_spi1_tx.Init.Request = DMA_REQUEST_SPI1_TX;hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_spi1_tx.Init.Mode = DMA_NORMAL;hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;hdma_spi1_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;hdma_spi1_tx.Init.MemBurst = DMA_MBURST_SINGLE;hdma_spi1_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);hdma_spi1_rx.Instance = DMA1_Stream1;hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_spi1_rx.Init.Mode = DMA_NORMAL;hdma_spi1_rx.Init.Priority = DMA_PRIORITY_HIGH;hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;hdma_spi1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;hdma_spi1_rx.Init.MemBurst = DMA_MBURST_SINGLE;hdma_spi1_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(SPI1_IRQn);}else if(hspi->Instance==SPI2){__HAL_RCC_SPI2_CLK_ENABLE();__HAL_RCC_GPIOI_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);hdma_spi2_tx.Instance = DMA1_Stream2;hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_spi2_tx.Init.Mode = DMA_NORMAL;hdma_spi2_tx.Init.Priority = DMA_PRIORITY_LOW;hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE;hdma_spi2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx);hdma_spi2_rx.Instance = DMA1_Stream3;hdma_spi2_rx.Init.Request = DMA_REQUEST_SPI2_RX;hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_spi2_rx.Init.Mode = DMA_NORMAL;hdma_spi2_rx.Init.Priority = DMA_PRIORITY_HIGH;hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;hdma_spi2_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;hdma_spi2_rx.Init.MemBurst = DMA_MBURST_SINGLE;hdma_spi2_rx.Init.PeriphBurst = DMA_PBURST_SINGLE; if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(hspi,hdmarx,hdma_spi2_rx);HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);HAL_NVIC_EnableIRQ(SPI2_IRQn);}}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{if(hspi->Instance==SPI1){__HAL_RCC_SPI1_CLK_DISABLE();HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5);HAL_GPIO_DeInit(GPIOG, GPIO_PIN_9);HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5);HAL_DMA_DeInit(hspi->hdmatx);HAL_DMA_DeInit(hspi->hdmarx);HAL_NVIC_DisableIRQ(SPI1_IRQn);}else if(hspi->Instance==SPI2){__HAL_RCC_SPI2_CLK_DISABLE();HAL_GPIO_DeInit(GPIOI, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);HAL_DMA_DeInit(hspi->hdmatx);HAL_DMA_DeInit(hspi->hdmarx);HAL_NVIC_DisableIRQ(SPI2_IRQn);}}static void MX_DMA_Init(void){__HAL_RCC_DMA1_CLK_ENABLE();HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);}
void DMA1_Stream0_IRQHandler(void)
{rt_base_t level = rt_hw_interrupt_disable();HAL_DMA_IRQHandler(&hdma_spi1_tx);rt_hw_interrupt_enable(level);
}
void DMA1_Stream1_IRQHandler(void)
{rt_base_t level = rt_hw_interrupt_disable();HAL_DMA_IRQHandler(&hdma_spi1_rx);rt_hw_interrupt_enable(level);
}void SPI1_IRQHandler(void){rt_base_t level = rt_hw_interrupt_disable();HAL_SPI_IRQHandler(&hspi1);rt_hw_interrupt_enable(level);}#endif
3. port_spi_flash.c 挂载flash disk
#include <rtdevice.h>
#include <rtthread.h>
#include <board.h>
#include <dev_spi_flash.h>
#include <drv_spi.h>
#include "dev_spi_flash_sfud.h"
#include <dfs_fs.h>
#include <fal.h>
#include "trace_log.h"
#define SPI_BUS_NAME "spi1"
#define SPI_DEVICE_NAME "spi10"
#define SPI_FLASH_DEVICE_NAME "W25Q64S"
#define SPI_SECT_DEVICE_NAME "flashdb"#define SPI_FLASH_CS_PIN GET_PIN(A, 4)
int spi_flash_init(void)
{if (rt_hw_spi_device_attach(SPI_BUS_NAME, SPI_DEVICE_NAME, SPI_FLASH_CS_PIN) != RT_EOK){rt_kprintf("Failed to attach SPI Flash!\n");return -RT_ERROR;} if (RT_NULL == rt_sfud_flash_probe(SPI_FLASH_DEVICE_NAME, SPI_DEVICE_NAME)){return -RT_ERROR;};fal_init();fal_blk_device_create(SPI_SECT_DEVICE_NAME);#ifdef BSP_USING_FATFS_ROOTFSint try=10;while(dfs_mount(SPI_SECT_DEVICE_NAME, "/spi", "elm", 0, 0) != 0){LOG_W("mount to '/spi' failed! try again...");rt_thread_mdelay(100);try--;if(try<=0) break;}LOG_I("mount to '/spi' success!");#endif_exit:return 0;
}
INIT_APP_EXPORT(spi_flash_init);
4. 测试结果
- 将#define DMA_MIN_SIZE 10,混合使用polling+dma,测试成功
- 将#define DMA_MIN_SIZE 0,相当于只使用dma,测试成功
- 将#define DMA_MIN_SIZE 10000,相当于只使用polling,测试成功
