一、步驟
1.打開RT-Thread Setting中軟件模擬I2C
2.開啓board.h/stm32F1xx_hal_config.h中關於SPI的宏定義
3.用stm32cube初始化spi相關內容(或直接複製RT文件夾下對應的bsp中代碼)
複製cube中stm32f1xx_hal_msp.c中的void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)到board.c中,HAL庫會自動初始化該函數,因爲本來存在虛函數。
此部分代碼也可從rtthread文件夾中對應的bsp目錄中,直接打開stm32f1xx_hal_msp.c,進行復制對應部分的代碼進行修改。
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
/**SPI3 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
像rt_hw_spi_init(void)這種初始化SPI的函數,在drv_spi.c的最後面已經爲我們自動寫好了,會根據初始化的配置進行編譯,所以不需要進行改動。
編譯下載,到此處已經將SPI總線設備註冊到系統中
4.SPI從設備驅動編寫
SPI的總線設備已經註冊完畢,接下來需要進行SPI從設備驅動編寫,這裏直接使用板載的SPI Flash W25Q64進行測試,新建app_spi.c。
#include <rtthread.h>
#include <board.h>
#include <rtdevice.h>
#include "drv_spi.h"
static int rt_hw_spi_flash_init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
rt_hw_spi_device_attach("spi1", "spi10", GPIOC, GPIO_PIN_0);// spi10 表示掛載在 spi3 總線上的 0 號設備,PC0是片選,這一步就可以將從設備掛在到總線中。
if (RT_NULL == rt_sfud_flash_probe("W25Q64", "spi10")) //註冊塊設備,這一步可以將外部flash抽象爲系統的塊設備
{
return -RT_ERROR;
};
return RT_EOK;
}
/* 導出到自動初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
編譯下載
5.在main.c中main()函數中初始化SPI例程
總線設備、從設備都已經註冊完畢,接下來就是在從設備上測試spi通訊配置、讀寫,實現最終的使用。更多的使用接口函數參照官方SPI部分。RT官方SPI參考資料
//SPI1配置例程
void SPI1_INIT(void)
{
struct rt_spi_device *spi_dev_w25q;
char name[RT_NAME_MAX]="spi10";
rt_uint8_t w25x_read_id = 0x90;//命令碼
rt_uint8_t id[5] = {0};
/* 查找 spi 設備獲取設備句柄 */
spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
if (!spi_dev_w25q)
{
rt_kprintf("spi sample run failed! can't find %s device!\n", name);
}
else
{
/*配置SPI通訊相關參數 */
struct rt_spi_configuration cfg;
cfg.data_width = 8; /* 數據寬度 */
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB; /* 主從模式、時鐘極性和時鐘相位、數據傳輸順序是MSB位在前還是LSB位在前 */
cfg.max_hz = 20 * 1000 *1000; /* 20M */
rt_spi_configure(spi_dev_w25q, &cfg);
/* 方式1:使用 rt_spi_send_then_recv()發送命令讀取ID */
rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);
rt_kprintf("use rt_spi_send_then_recv() read w25q ID is:%x%x\n", id[3], id[4]);
/* 方式2:使用 rt_spi_transfer_message()發送命令讀取ID */
struct rt_spi_message msg1, msg2;
msg1.send_buf = &w25x_read_id;
msg1.recv_buf = RT_NULL;
msg1.length = 1;
msg1.cs_take = 1;
msg1.cs_release = 0;
msg1.next = &msg2;
msg2.send_buf = RT_NULL;
msg2.recv_buf = id;
msg2.length = 5;
msg2.cs_take = 0;
msg2.cs_release = 1;
msg2.next = RT_NULL;
rt_spi_transfer_message(spi_dev_w25q, &msg1);
rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]);
}
}
另外配置SPI通訊相關參數的部分可以替換成下面的部分,就是直接使用HAL庫初始化SPI通訊。
SPI_HandleTypeDef hspi1;
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_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}