STM32 HAL库SPI驱动封装教程:阻塞、中断和DMA三种方式 implemetation
前言
- 配置功能参考rt-thread驱动代码
- 将中断配置和dma配置单独分开管理
代码
中断管理
头文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#ifndef TX_STM32_F4_DRV_NVIC_OS_H
#define TX_STM32_F4_DRV_NVIC_OS_H
#include "drv_common.h"
void stm32_nvic_common_enable(uint32_t instance,uint32_t preempt,uint32_t sub);
void stm32_nvic_common_disable(uint32_t instance);
uint8_t stm32_nvic_common_enabled_check(uint32_t instance);
#endif //TX_STM32_F4_DRV_NVIC_OS_H
源文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#include "drv_nvic_os.h"
enum stm32_irq_op_enum {
OPEN_IRQn,
CLOSE_IRQn,
READ_IRQn
};
/**
* @brief (适用于大部分配置)
* @param instance
* @param preempt
* @param sub
* @param open_flag
*/
static uint32_t stm32_nvic_common(uint32_t instance, uint32_t preempt, uint32_t sub, enum stm32_irq_op_enum mode) {
uint32_t irq;
switch (instance) {
#define irq_set(IRQn) {irq=IRQn;}break
case (uint32_t) SPI1: irq_set(SPI1_IRQn);
case (uint32_t) SPI2: irq_set(SPI2_IRQn);
case (uint32_t) SPI3: irq_set(SPI3_IRQn);
case (uint32_t) USART1: irq_set(USART1_IRQn);
case (uint32_t) USART2: irq_set(USART2_IRQn);
case (uint32_t) USART3: irq_set(USART3_IRQn);
case (uint32_t) UART4: irq_set(UART4_IRQn);
case (uint32_t) UART5: irq_set(UART5_IRQn);
case (uint32_t) DMA2_Stream0: irq_set(DMA2_Stream0_IRQn);
case (uint32_t) DMA2_Stream1: irq_set(DMA2_Stream1_IRQn);
case (uint32_t) DMA2_Stream2: irq_set(DMA2_Stream2_IRQn);
case (uint32_t) DMA2_Stream3: irq_set(DMA2_Stream3_IRQn);
case (uint32_t) DMA2_Stream4: irq_set(DMA2_Stream4_IRQn);
case (uint32_t) DMA2_Stream5: irq_set(DMA2_Stream5_IRQn);
case (uint32_t) DMA2_Stream6: irq_set(DMA2_Stream6_IRQn);
case (uint32_t) DMA2_Stream7: irq_set(DMA2_Stream7_IRQn);
case (uint32_t) DMA1_Stream0: irq_set(DMA1_Stream0_IRQn);
case (uint32_t) DMA1_Stream1: irq_set(DMA1_Stream1_IRQn);
case (uint32_t) DMA1_Stream2: irq_set(DMA1_Stream2_IRQn);
case (uint32_t) DMA1_Stream3: irq_set(DMA1_Stream3_IRQn);
case (uint32_t) DMA1_Stream4: irq_set(DMA1_Stream4_IRQn);
case (uint32_t) DMA1_Stream5: irq_set(DMA1_Stream5_IRQn);
case (uint32_t) DMA1_Stream6: irq_set(DMA1_Stream6_IRQn);
case (uint32_t) DMA1_Stream7: irq_set(DMA1_Stream7_IRQn);
default: {
return UINT32_MAX;
}
}
#undef irq_set
switch (mode) {
case OPEN_IRQn: {
HAL_NVIC_SetPriority(irq, preempt, sub);
HAL_NVIC_EnableIRQ(irq);
}
break;
case CLOSE_IRQn: {
HAL_NVIC_DisableIRQ(irq);
}
break;
default: {
break;
}
}
return irq;
}
/**
* @brief 中断使能
* @param instance
* @param preempt
* @param sub
*/
void stm32_nvic_common_enable(uint32_t instance, uint32_t preempt, uint32_t sub) {
stm32_nvic_common(instance, preempt, sub, OPEN_IRQn);
}
void stm32_nvic_common_disable(uint32_t instance) {
stm32_nvic_common(instance, 0, 0, CLOSE_IRQn);
}
/**
* @brief nvic 启用状态检测
* @param instance
* @return 0 未启用,1 启用
*/
uint8_t stm32_nvic_common_enabled_check(uint32_t instance) {
uint32_t irq = stm32_nvic_common(instance, 0, 0, READ_IRQn);
return NVIC_GetEnableIRQ(irq);
}
DMA管理
头文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#ifndef TX_STM32_F4_DRV_DMA_OS_H
#define TX_STM32_F4_DRV_DMA_OS_H
#include "drv_common.h"
struct stm32_dma_info {
uint32_t instance;
DMA_HandleTypeDef *dma_tx;
DMA_HandleTypeDef *dma_rx;
};
struct stm32_dma_info *dma_info_get(uint32_t instance);
void dma_clk_enable(DMA_HandleTypeDef *handle);
#endif //TX_STM32_F4_DRV_DMA_OS_H
源文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#include "drv_dma_os.h"
static DMA_HandleTypeDef spi1_dma_tx = {.Instance=DMA2_Stream3, .Init.Channel=DMA_CHANNEL_3};
static DMA_HandleTypeDef spi1_dma_rx = {.Instance=DMA2_Stream0, .Init.Channel=DMA_CHANNEL_3};
static struct stm32_dma_info dma_info_map[] = {
{(uint32_t) SPI1, &spi1_dma_tx, &spi1_dma_rx}
};
#define DMA_MAP_CNT ( sizeof(dma_info_map)/ sizeof(dma_info_map[0]))
struct stm32_dma_info *dma_info_get(uint32_t instance) {
for (int i = 0; i < DMA_MAP_CNT; ++i) {
if (dma_info_map[i].instance == instance) {
return dma_info_map + i;
}
}
return NULL;
}
void dma_clk_enable(DMA_HandleTypeDef *handle) {
switch ((uint32_t) handle->Instance) {
case (uint32_t) DMA2_Stream0:
case (uint32_t) DMA2_Stream1:
case (uint32_t) DMA2_Stream2:
case (uint32_t) DMA2_Stream3:
case (uint32_t) DMA2_Stream4:
case (uint32_t) DMA2_Stream5:
case (uint32_t) DMA2_Stream6:
case (uint32_t) DMA2_Stream7:
__HAL_RCC_DMA2_CLK_ENABLE();
break;
case (uint32_t) DMA1_Stream0:
case (uint32_t) DMA1_Stream1:
case (uint32_t) DMA1_Stream2:
case (uint32_t) DMA1_Stream3:
case (uint32_t) DMA1_Stream4:
case (uint32_t) DMA1_Stream5:
case (uint32_t) DMA1_Stream6:
case (uint32_t) DMA1_Stream7:
__HAL_RCC_DMA1_CLK_ENABLE();
break;
default:
return;
}
}
void DMA2_Stream0_IRQHandler(void) {
TX_INTERRUPT_SAVE_AREA
TX_DISABLE
HAL_DMA_IRQHandler(&spi1_dma_rx);
TX_RESTORE
}
void DMA2_Stream3_IRQHandler(void) {
TX_INTERRUPT_SAVE_AREA
TX_DISABLE
HAL_DMA_IRQHandler(&spi1_dma_tx);
TX_RESTORE
}
SPI驱动
头文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#ifndef TX_STM32_F4_DRV_SPI_OS_H
#define TX_STM32_F4_DRV_SPI_OS_H
#include "drv_common.h"
#define SPI_CONTROLLER_NUM 1
/**
* At CPOL=0 the base value of the clock is zero
* - For CPHA=0, data are captured on the clock's rising edge (low->high transition)
* and data are propagated on a falling edge (high->low clock transition).
* - For CPHA=1, data are captured on the clock's falling edge and data are
* propagated on a rising edge.
* At CPOL=1 the base value of the clock is one (inversion of CPOL=0)
* - For CPHA=0, data are captured on clock's falling edge and data are propagated
* on a rising edge.
* - For CPHA=1, data are captured on clock's rising edge and data are propagated
* on a falling edge.
*/
#define SPI_CPHA (1<<0) /* bit[0]:CPHA, clock phase */
#define SPI_CPOL (1<<1) /* bit[1]:CPOL, clock polarity */
#define SPI_LSB (0<<2) /* bit[2]: 0-LSB */
#define SPI_MSB (1<<2) /* bit[2]: 1-MSB */
#define SPI_MASTER (0<<3) /* SPI master device */
#define SPI_SLAVE (1<<3) /* SPI slave device */
#define SPI_CS_HIGH (1<<4) /* Chipselect active high */
#define SPI_NO_CS (1<<5) /* No chipselect */
#define SPI_3WIRE (1<<6) /* SI/SO pin shared */
#define SPI_READY (1<<7) /* Slave pulls low to pause */
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_MSB | SPI_SLAVE | SPI_CS_HIGH | SPI_NO_CS | SPI_3WIRE | SPI_READY)
#define SPI_MODE_0 (0 | 0) /* CPOL = 0, CPHA = 0 */
#define SPI_MODE_1 (0 | SPI_CPHA) /* CPOL = 0, CPHA = 1 */
#define SPI_MODE_2 (SPI_CPOL | 0) /* CPOL = 1, CPHA = 0 */
#define SPI_MODE_3 (SPI_CPOL | SPI_CPHA) /* CPOL = 1, CPHA = 1 */
// 预留 锁对象
#define spi_lock_init(controller) ((controller)->lock_obj)
#define spi_lock(controller)
#define spi_unlock(controller)
struct stm32_spi_configuration {
uint8_t mode;
uint8_t data_width;
uint16_t reserved;
uint32_t max_hz;
};
struct stm32_spi_controller {
SPI_HandleTypeDef handle;
uint8_t mode;
volatile uint32_t cpt_flag; /*完成标志位*/
struct {
GPIO_TypeDef *port;
uint32_t pin;
} cs;
void *lock_obj;
};
struct spi_message {
const void *send_buf;
void *recv_buf;
size_t length;
struct spi_message *next;
unsigned cs_take: 1;
unsigned cs_release: 1;
};
void bsp_SpiConfig(SPI_TypeDef *spi, struct stm32_spi_configuration *cfg);
void bsp_SpiDmaEnable(SPI_TypeDef *spi, uint8_t tx_dma_flag, uint8_t rx_dma_flag);
void bsp_SpiDmaParSet(SPI_TypeDef *spi, DMA_InitTypeDef *tx_cfg, DMA_InitTypeDef *rx_cfg);
void bsp_SpiCsParSet(SPI_TypeDef *spi, GPIO_TypeDef *port, uint32_t pin);
void bsp_InitSpi(SPI_TypeDef *spi, uint8_t it_flag);
HAL_StatusTypeDef stm32_spi_data_trans(SPI_TypeDef *spi, uint8_t *write_buf, uint8_t *read_buf, uint16_t len);
uint32_t stm32_spi_trans(SPI_TypeDef *spi, struct spi_message *msg);
// 扩展函数
struct spi_message *spi_transfer_message(SPI_TypeDef *spi, struct spi_message *message);
/* send data then receive data from SPI device */
uint8_t spi_send_then_recv(SPI_TypeDef *spi,
const void *send_buf, size_t send_length,
void *recv_buf, size_t recv_length);
uint8_t spi_send_then_send(SPI_TypeDef *spi,
const void *send_buf1, size_t send_length1,
const void *send_buf2, size_t send_length2);
uint32_t spi_transfer(SPI_TypeDef *spi, const void *send_buf, void *recv_buf, size_t length);
uint8_t spi_sendrecv8(SPI_TypeDef *spi, uint8_t senddata, uint8_t *recvdata);
inline rt_size_t spi_recv(SPI_TypeDef *spi, void *recv_buf, size_t length) {
return spi_transfer(spi, RT_NULL, recv_buf, length);
}
inline rt_size_t spi_send(SPI_TypeDef *spi, const void *send_buf, size_t length) {
return spi_transfer(spi, send_buf, RT_NULL, length);
}
#endif //TX_STM32_F4_DRV_SPI_OS_H
源文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-3 shchl first version
*/
#include "drv_spi_os.h"
#include "drv_dma_os.h"
#include "drv_gpio.h"
#include "drv_nvic_os.h"
enum {
TRANSFER_WAIT,
TRANSFER_COMPLETE,
TRANSFER_ERROR
};
enum {
SPI1_IDX = 0,
SPI2_IDX = 1,
SPI3_IDX = 2,
};
static struct stm32_spi_controller controller[SPI_CONTROLLER_NUM] = {0};
static inline int spi_idx_get(SPI_TypeDef *spi) {
#define spi_ret_idx(v) {return v;} break
switch ((uint32_t) spi) {
case (uint32_t) SPI1: spi_ret_idx(SPI1_IDX);
case (uint32_t) SPI2: spi_ret_idx(SPI2_IDX);
case (uint32_t) SPI3: spi_ret_idx(SPI3_IDX);
}
#undef spi_ret_idx
return -1;
}
/**
* @brief 初始化cs 引脚
* @param spi
*/
static void spi_cs_pin_init(SPI_TypeDef *spi) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
if (controller[idx].cs.port) {
GPIO_InitTypeDef gpio_init;
bsp_GpioClkEnable(controller[idx].cs.port); /* 打开GPIO时钟 */
gpio_init.Mode = GPIO_MODE_OUTPUT_PP; /* 设置推挽输出 */
gpio_init.Pull = GPIO_NOPULL; /* 上下拉电阻不使能 */
gpio_init.Speed = GPIO_SPEED_HIGH; /* GPIO速度等级 */
gpio_init.Pin = controller[idx].cs.pin;
HAL_GPIO_Init(controller[idx].cs.port, &gpio_init);
if (controller[idx].mode & SPI_CS_HIGH) {
HAL_GPIO_WritePin(controller[idx].cs.port, controller[idx].cs.pin, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(controller[idx].cs.port, controller[idx].cs.pin, GPIO_PIN_SET);
}
}
}
static inline void *stm32_spi_lock_init(struct stm32_spi_controller *control) {
return spi_lock_init(control);
}
static inline void stm32_spi_lock(struct stm32_spi_controller *control) {
spi_lock(control);
}
static inline void stm32_spi_unlock(struct stm32_spi_controller *control) {
spi_unlock(control);
}
/**
* @brief spi dma tx 默认配置
* @param dma_tx
*/
static inline void spi_dma_tx_default_set(DMA_HandleTypeDef *dma_tx) {
/* SPI DMA发送配置(INSTANCE 和 Channel 在外部配置,禁止内部设置) */
dma_tx->Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
dma_tx->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* 禁止FIFO此位不起作用,用于设置阀值 */
dma_tx->Init.MemBurst = DMA_MBURST_SINGLE; /* 禁止FIFO此位不起作用,用于存储器突发 */
dma_tx->Init.PeriphBurst = DMA_PBURST_SINGLE; /* 禁止FIFO此位不起作用,用于外设突发 */
dma_tx->Init.Direction = DMA_MEMORY_TO_PERIPH; /* 传输方向是从存储器到外设 */
dma_tx->Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址自增禁止 */
dma_tx->Init.MemInc = DMA_MINC_ENABLE; /* 存储器地址自增使能 */
dma_tx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据传输位宽选择字节,即8bit */
dma_tx->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; /* 存储器数据传输位宽选择字节,即8bit */
dma_tx->Init.Mode = DMA_NORMAL; /* 正常模式 */
dma_tx->Init.Priority = DMA_PRIORITY_MEDIUM; /* 优先级中 */
}
static inline void spi_dma_rx_default_set(DMA_HandleTypeDef *dma_rx) {
/* SPI DMA接收配置(INSTANCE 和 Channel 在外部配置,禁止内部设置) */
dma_rx->Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 禁止FIFO*/
dma_rx->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;/* 禁止FIFO此位不起作用,用于设置阀值 */
dma_rx->Init.MemBurst = DMA_MBURST_SINGLE; /* 禁止FIFO此位不起作用,用于存储器突发 */
dma_rx->Init.PeriphBurst = DMA_PBURST_SINGLE; /* 禁止FIFO此位不起作用,用于外设突发 */
dma_rx->Init.Direction = DMA_PERIPH_TO_MEMORY; /* 传输方向从外设到存储器 */
dma_rx->Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址自增禁止 */
dma_rx->Init.MemInc = DMA_MINC_ENABLE; /* 存储器地址自增使能 */
dma_rx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据传输位宽选择字节,即8bit */
dma_rx->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; /* 存储器数据传输位宽选择字节,即8bit */
dma_rx->Init.Mode = DMA_NORMAL; /* 正常模式 */
dma_rx->Init.Priority = DMA_PRIORITY_MEDIUM; /* 优先级低 */
}
/**
* @brief spi 参数设置
* @param spi
* @param cfg
*/
void bsp_SpiConfig(SPI_TypeDef *spi, struct stm32_spi_configuration *cfg) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
SPI_HandleTypeDef *spi_handle = &(controller[idx].handle);
if (cfg == NULL) {
struct stm32_spi_configuration spi_conf;
{
spi_conf.mode = SPI_MASTER | SPI_MSB | SPI_MODE_0;
spi_conf.data_width = 8;
spi_conf.max_hz = 20 * 1000 * 1000;
}
cfg = &spi_conf;
}
controller[idx].mode = cfg->mode;
spi_handle->Instance = spi;
spi_handle->Init.Mode = cfg->mode & SPI_SLAVE ? SPI_MODE_SLAVE : SPI_MODE_MASTER;
spi_handle->Init.Direction = cfg->mode & SPI_3WIRE ? SPI_DIRECTION_1LINE : SPI_DIRECTION_2LINES;
spi_handle->Init.DataSize = cfg->data_width == 8 ? SPI_DATASIZE_8BIT : SPI_DATASIZE_16BIT;
spi_handle->Init.CLKPhase = cfg->mode & SPI_CPHA ? SPI_PHASE_2EDGE : SPI_PHASE_1EDGE;
spi_handle->Init.CLKPolarity = cfg->mode & SPI_CPOL ? SPI_POLARITY_HIGH : SPI_POLARITY_LOW;
spi_handle->Init.FirstBit = cfg->mode & SPI_MSB ? SPI_FIRSTBIT_MSB : SPI_FIRSTBIT_LSB;
spi_handle->Init.NSS = cfg->mode & SPI_NO_CS ? SPI_NSS_HARD_OUTPUT : SPI_NSS_SOFT;
uint32_t SPI_APB_CLOCK = HAL_RCC_GetPCLK2Freq();
if (cfg->max_hz >= SPI_APB_CLOCK / 2) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 4) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 8) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 16) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 32) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 64) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
} else if (cfg->max_hz >= SPI_APB_CLOCK / 128) {
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
} else {
/* min prescaler 256 */
spi_handle->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
}
spi_handle->Init.TIMode = SPI_TIMODE_DISABLE;
spi_handle->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spi_handle->State = HAL_SPI_STATE_RESET;
}
/**
* @brief spi dma 启用设置
* @param spi
* @param tx_dma_flag 0: 不启用; 1:启用
* @param rx_dma_flag 0: 不启用; 1:启用
*/
void bsp_SpiDmaEnable(SPI_TypeDef *spi, uint8_t tx_dma_flag, uint8_t rx_dma_flag) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
struct stm32_dma_info *info = dma_info_get((uint32_t) spi);
if (info == NULL) {
Error_Handler();
return;
}
if (tx_dma_flag) {
controller[idx].handle.hdmatx = info->dma_tx;
info->dma_tx->Parent = &(controller[idx].handle);
}
if (rx_dma_flag) {
controller[idx].handle.hdmarx = info->dma_rx;
info->dma_rx->Parent = &(controller[idx].handle);
}
}
/**
* @brief spi dma 参数配置
* @param spi
* @param tx_cfg dma发送 null 使用默认配置
* @param rx_cfg dma接收 null 使用默认配置
*/
void bsp_SpiDmaParSet(SPI_TypeDef *spi, DMA_InitTypeDef *tx_cfg, DMA_InitTypeDef *rx_cfg) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
SPI_HandleTypeDef *spi_handle = &(controller[idx].handle);
if (spi_handle->hdmatx) {
if (tx_cfg == NULL) {
spi_dma_tx_default_set(spi_handle->hdmatx);
} else {
spi_handle->hdmatx->Init.FIFOMode = tx_cfg->FIFOMode; /* 禁止FIFO*/
spi_handle->hdmatx->Init.FIFOThreshold = tx_cfg->FIFOThreshold; /* 禁止FIFO此位不起作用,用于设置阀值 */
spi_handle->hdmatx->Init.MemBurst = tx_cfg->MemBurst; /* 禁止FIFO此位不起作用,用于存储器突发 */
spi_handle->hdmatx->Init.PeriphBurst = tx_cfg->PeriphBurst; /* 禁止FIFO此位不起作用,用于外设突发 */
spi_handle->hdmatx->Init.Direction = tx_cfg->Direction; /* 传输方向是从存储器到外设 */
spi_handle->hdmatx->Init.PeriphInc = tx_cfg->PeriphInc; /* 外设地址自增禁止 */
spi_handle->hdmatx->Init.MemInc = tx_cfg->MemInc; /* 存储器地址自增使能 */
spi_handle->hdmatx->Init.PeriphDataAlignment = tx_cfg->PeriphDataAlignment; /* 外设数据传输位宽选择字节,即8bit */
spi_handle->hdmatx->Init.MemDataAlignment = tx_cfg->MemDataAlignment; /* 存储器数据传输位宽选择字节,即8bit */
spi_handle->hdmatx->Init.Mode = tx_cfg->Mode; /* 正常模式 */
spi_handle->hdmatx->Init.Priority = tx_cfg->Priority; /* 优先级中 */
}
}
if (spi_handle->hdmarx) {
if (rx_cfg == NULL) {
spi_dma_rx_default_set(spi_handle->hdmarx);
} else {
spi_handle->hdmarx->Init.FIFOMode = rx_cfg->FIFOMode; /* 禁止FIFO*/
spi_handle->hdmarx->Init.FIFOThreshold = rx_cfg->FIFOThreshold; /* 禁止FIFO此位不起作用,用于设置阀值 */
spi_handle->hdmarx->Init.MemBurst = rx_cfg->MemBurst; /* 禁止FIFO此位不起作用,用于存储器突发 */
spi_handle->hdmarx->Init.PeriphBurst = rx_cfg->PeriphBurst; /* 禁止FIFO此位不起作用,用于外设突发 */
spi_handle->hdmarx->Init.Direction = rx_cfg->Direction; /* 传输方向是从存储器到外设 */
spi_handle->hdmarx->Init.PeriphInc = rx_cfg->PeriphInc; /* 外设地址自增禁止 */
spi_handle->hdmarx->Init.MemInc = rx_cfg->MemInc; /* 存储器地址自增使能 */
spi_handle->hdmarx->Init.PeriphDataAlignment = rx_cfg->PeriphDataAlignment; /* 外设数据传输位宽选择字节,即8bit */
spi_handle->hdmarx->Init.MemDataAlignment = rx_cfg->MemDataAlignment; /* 存储器数据传输位宽选择字节,即8bit */
spi_handle->hdmarx->Init.Mode = rx_cfg->Mode; /* 正常模式 */
spi_handle->hdmarx->Init.Priority = rx_cfg->Priority; /* 优先级中 */
}
}
}
/**
* @brief 使能引脚配置
* @param spi
* @param port
* @param pin
*/
void bsp_SpiCsParSet(SPI_TypeDef *spi, GPIO_TypeDef *port, uint32_t pin) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
controller[idx].cs.port = port;
controller[idx].cs.pin = pin;
}
/**
* @brief 初始化spi
* @param spi
*/
void bsp_InitSpi(SPI_TypeDef *spi, uint8_t it_flag) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
// lock 配置
controller[idx].lock_obj = stm32_spi_lock_init(&(controller[idx]));
// cs 配置
spi_cs_pin_init(spi);
HAL_SPI_Init(&(controller[idx].handle));
if (controller[idx].handle.hdmatx) {
// 启用
stm32_nvic_common_enable((uint32_t) (controller[idx].handle.hdmatx->Instance), 1, 0);
}
if (controller[idx].handle.hdmarx) {
stm32_nvic_common_enable((uint32_t) (controller[idx].handle.hdmarx->Instance), 1, 0);
}
if (it_flag) {
// 开启中断
stm32_nvic_common_enable((uint32_t) spi, 2, 0);
}
}
/**
* @brief stm32 spi 数据传输
* @param spi
* @param write_buf
* @param read_buf
* @param len
* @return
*/
HAL_StatusTypeDef stm32_spi_data_trans(SPI_TypeDef *spi, uint8_t *write_buf, uint8_t *read_buf, uint16_t len) {
HAL_StatusTypeDef state = HAL_ERROR;
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return HAL_ERROR;
SPI_HandleTypeDef *handle_ptr = &(controller[idx].handle);
ATOMIC_SET_BIT(controller[idx].cpt_flag, TRANSFER_WAIT);
// 判断是否为dma方式
if (write_buf && read_buf) {
if (handle_ptr->hdmatx && handle_ptr->hdmarx) {
state = HAL_SPI_TransmitReceive_DMA(handle_ptr, write_buf, read_buf, len);
} else if (stm32_nvic_common_enabled_check((uint32_t) spi)) {
state = HAL_SPI_TransmitReceive_IT(handle_ptr, write_buf, read_buf, len);
} else {
state = HAL_SPI_TransmitReceive(handle_ptr, write_buf, read_buf, len, 1000);
controller[idx].cpt_flag = state == HAL_OK ? TRANSFER_COMPLETE : TRANSFER_ERROR;
}
} else if (write_buf) {
if (handle_ptr->hdmatx) {
state = HAL_SPI_Transmit_DMA(handle_ptr, write_buf, len);
} else if (stm32_nvic_common_enabled_check((uint32_t) spi)) {
state = HAL_SPI_Transmit_IT(handle_ptr, write_buf, len);
} else {
state = HAL_SPI_Transmit(handle_ptr, write_buf, len, 1000);
controller[idx].cpt_flag = state == HAL_OK ? TRANSFER_COMPLETE : TRANSFER_ERROR;
}
} else if (read_buf) {
if (handle_ptr->hdmarx) {
state = HAL_SPI_Receive_DMA(handle_ptr, read_buf, len);
} else if (stm32_nvic_common_enabled_check((uint32_t) spi)) {
state = HAL_SPI_Receive_IT(handle_ptr, read_buf, len);
} else {
state = HAL_SPI_Receive(handle_ptr, read_buf, len, 1000);
controller[idx].cpt_flag = state == HAL_OK ? TRANSFER_COMPLETE : TRANSFER_ERROR;
}
}
return state;
}
/**
* @brief spi 消息传输(带cs)
* @param spi
* @param msg 消息
* @return
*/
uint32_t stm32_spi_trans(SPI_TypeDef *spi, struct spi_message *msg) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return 0;
rt_size_t message_length, already_send_length;
rt_uint16_t send_length;
rt_uint8_t *recv_buf;
const uint8_t *send_buf;
struct stm32_spi_controller *control = &controller[idx];
if (msg->cs_take && !(control->mode & SPI_NO_CS)) {
{
if (control->mode & SPI_CS_HIGH)
HAL_GPIO_WritePin(control->cs.port, control->cs.pin, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(control->cs.port, control->cs.pin, GPIO_PIN_RESET);
}
}
message_length = msg->length;
while (message_length) {
/* the HAL library use uint16 to save the data length */
if (message_length > 65535) {
send_length = 65535;
message_length = message_length - 65535;
} else {
send_length = message_length;
message_length = 0;
}
/* calculate the start address */
already_send_length = msg->length - send_length - message_length;
send_buf = (uint8_t *) msg->send_buf + already_send_length;
recv_buf = (uint8_t *) msg->recv_buf + already_send_length;
stm32_spi_data_trans(spi, (uint8_t *) send_buf, recv_buf, send_length);
while (control->cpt_flag == TRANSFER_WAIT);
if (control->cpt_flag == TRANSFER_ERROR) {
msg->length = 0;
}
}
if (msg->cs_release && !(control->mode & SPI_NO_CS)) {
{
if (control->mode & SPI_CS_HIGH)
HAL_GPIO_WritePin(control->cs.port, control->cs.pin, GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(control->cs.port, control->cs.pin, GPIO_PIN_SET);
}
}
return msg->length;
}
__attribute__((used)) void SPI1_IRQHandler(void) {
TX_INTERRUPT_SAVE_AREA
TX_DISABLE
HAL_SPI_IRQHandler(&controller[SPI1_IDX].handle);
TX_RESTORE
}
//__attribute__((used)) void SPI2_IRQHandler(void) {
// TX_INTERRUPT_SAVE_AREA
// TX_DISABLE
// HAL_SPI_IRQHandler(&controller[SPI2_IDX].handle);
// TX_RESTORE
//}
//__attribute__((used)) void SPI3_IRQHandler(void) {
// TX_INTERRUPT_SAVE_AREA
// TX_DISABLE
// HAL_SPI_IRQHandler(&controller[SPI3_IDX].handle);
// TX_RESTORE
//}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
struct stm32_spi_controller *control = rt_container_of(hspi, struct stm32_spi_controller, handle);
control->cpt_flag = TRANSFER_COMPLETE;
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
struct stm32_spi_controller *control = rt_container_of(hspi, struct stm32_spi_controller, handle);
control->cpt_flag = TRANSFER_COMPLETE;
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
struct stm32_spi_controller *control = rt_container_of(hspi, struct stm32_spi_controller, handle);
control->cpt_flag = TRANSFER_COMPLETE;
}
/**
* @brief SPI error callback.
* @param hspi pointer to a SPI_HandleTypeDef structure that contains
* the configuration information for SPI module.
* @retval None
*/
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
// todo 错误记录
struct stm32_spi_controller *control = rt_container_of(hspi, struct stm32_spi_controller, handle);
control->cpt_flag = TRANSFER_ERROR;
}
static void spi_lock_get(SPI_TypeDef *spi) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
stm32_spi_lock(&controller[idx]);
}
static void spi_lock_put(SPI_TypeDef *spi) {
int idx;
idx = spi_idx_get(spi);
if (idx < 0 || idx >= SPI_CONTROLLER_NUM)return;
stm32_spi_unlock(&controller[idx]);
}
struct spi_message *spi_transfer_message(SPI_TypeDef *spi, struct spi_message *message) {
uint32_t result;
struct spi_message *index;
/* get first message */
index = message;
spi_lock_get(spi);
/* transmit each SPI message */
while (index != RT_NULL) {
/* transmit SPI message */
result = stm32_spi_trans(spi, index);
if (result != index->length) {
LOG_E("transfer error");
break;
}
index = index->next;
}
spi_lock_put(spi);
return index;
}
/**
* @brief
* @param spi
* @param send_buf
* @param send_length
* @param recv_buf
* @param recv_length
* @return ok : 0 or fail
*/
uint8_t spi_send_then_recv(SPI_TypeDef *spi,
const void *send_buf, size_t send_length,
void *recv_buf, size_t recv_length) {
uint8_t result = 0;
struct spi_message message;
spi_lock_get(spi);
{
/* send data */
message.send_buf = send_buf;
message.recv_buf = RT_NULL;
message.length = send_length;
message.cs_take = 1;
message.cs_release = 0;
message.next = RT_NULL;
if (stm32_spi_trans(spi, &message) != message.length) {
LOG_E("SPI device transfer failed");
result = 1;
goto __exit;
}
/* recv data */
message.send_buf = RT_NULL;
message.recv_buf = recv_buf;
message.length = recv_length;
message.cs_take = 0;
message.cs_release = 1;
message.next = RT_NULL;
if (stm32_spi_trans(spi, &message) != message.length) {
LOG_E("SPI device transfer failed");
goto __exit;
}
result = RT_EOK;
}
__exit:
spi_lock_put(spi);
return result;
}
/**
* @brief
* @param spi
* @param send_buf
* @param send_length
* @param recv_buf
* @param recv_length
* @return ok : 0 or fail
*/
uint8_t spi_send_then_send(SPI_TypeDef *spi, const void *send_buf1, size_t send_length1, const void *send_buf2,
size_t send_length2) {
uint8_t result = 0;
struct spi_message message;
spi_lock_get(spi);
{
/* send data1 */
message.send_buf = send_buf1;
message.recv_buf = RT_NULL;
message.length = send_length1;
message.cs_take = 1;
message.cs_release = 0;
message.next = RT_NULL;
if (stm32_spi_trans(spi, &message) != message.length) {
LOG_E("SPI device transfer failed");
result = 1;
goto __exit;
}
/* send data2 */
message.send_buf = send_buf2;
message.recv_buf = RT_NULL;
message.length = send_length2;
message.cs_take = 0;
message.cs_release = 1;
message.next = RT_NULL;
if (stm32_spi_trans(spi, &message) != message.length) {
LOG_E("SPI device transfer failed");
result = 1;
goto __exit;
}
result = 0;
}
__exit:
spi_lock_put(spi);
return result;
}
uint32_t spi_transfer(SPI_TypeDef *spi, const void *send_buf, void *recv_buf, size_t length) {
uint32_t result;
struct spi_message message;
spi_lock_get(spi);
{
/* initial message */
message.send_buf = send_buf;
message.recv_buf = recv_buf;
message.length = length;
message.cs_take = 1;
message.cs_release = 1;
message.next = RT_NULL;
/* transfer message */
if (stm32_spi_trans(spi, &message) != message.length) {
LOG_E("SPI device transfer failed");
result = 0;
goto __exit;
}
result = message.length;
}
__exit:
spi_lock_put(spi);
return result;
}
uint8_t spi_sendrecv8(SPI_TypeDef *spi, uint8_t senddata, uint8_t *recvdata) {
return spi_transfer(spi, &senddata, recvdata, 1);
}
SPI的IO驱动
//
// Created by shchl on 2024/3/20.
//
#include "board.h"
#include "drv_nvic_os.h"
#include "drv_dma_os.h"
/**
* @brief SPI MSP Initialization
* This function configures the hardware resources used in this example
* @param hspi: SPI handle pointer
* @retval None
*/
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI1)
{
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|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);
// dma
if (hspi->hdmatx) {
dma_clk_enable(hspi->hdmatx);
HAL_DMA_Init(hspi->hdmatx);
}
if (hspi->hdmarx) {
dma_clk_enable(hspi->hdmarx);
HAL_DMA_Init(hspi->hdmarx);
}
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi){
if(hspi->Instance==SPI1)
{
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
// dma
if (hspi->hdmatx) {
HAL_DMA_DeInit(hspi->hdmatx);
}
if (hspi->hdmarx) {
HAL_DMA_DeInit(hspi->hdmarx);
}
}
}
测试
- 说明,如果dma和中断同时打开,底层逻辑优先采用dma的方式
使用DMA的方式,中断关闭(推荐,如果使用dma的话,中断就没有必要打开)
bsp_SpiConfig(SPI1, NULL);
bsp_SpiDmaEnable(SPI1, 1, 1);
bsp_SpiDmaParSet(SPI1,NULL,NULL);
bsp_SpiCsParSet(SPI1, GPIOB, GPIO_PIN_14);
bsp_InitSpi(SPI1, 0);
使用DMA的方式。中断打开
bsp_SpiConfig(SPI1, NULL);
bsp_SpiDmaEnable(SPI1, 1, 1);
bsp_SpiDmaParSet(SPI1,NULL,NULL);
bsp_SpiCsParSet(SPI1, GPIOB, GPIO_PIN_14);
bsp_InitSpi(SPI1, 1);
使用中断的方式.DMA关闭
bsp_SpiConfig(SPI1, NULL);
bsp_SpiDmaEnable(SPI1, 0, 0);
bsp_SpiDmaParSet(SPI1,NULL,NULL);
bsp_SpiCsParSet(SPI1, GPIOB, GPIO_PIN_14);
bsp_InitSpi(SPI1, 1);
使用阻塞的方式
bsp_SpiConfig(SPI1, NULL);
bsp_SpiDmaEnable(SPI1, 0, 0);
bsp_SpiDmaParSet(SPI1,NULL,NULL);
bsp_SpiCsParSet(SPI1, GPIOB, GPIO_PIN_14);
bsp_InitSpi(SPI1, 0);
测试例程
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-5-2 shchl first version
*/
#include "test_inc.h"
struct rt_spi_device *spi_device = RT_NULL;
static rt_err_t w25q128_tx_rx(uint8_t *send, uint16_t send_len, uint8_t *rec, uint16_t rec_len) {
if (send && rec) {
return spi_send_then_recv(SPI1, send, send_len, rec, rec_len);
} else if (send) {
return spi_send(SPI1, (const void *) send, send_len) == send_len ? RT_EOK : RT_EIO;
}
return spi_recv(SPI1, rec, rec_len) == rec_len ? RT_EOK : RT_EIO;
}
uint16_t w25q128_read_deviceID() {
uint8_t cmd[4] = {0x90, 00, 00, 00};
uint8_t rec[2];
if (w25q128_tx_rx(cmd, 4, rec, 2) != RT_EOK) return 0;
return (uint16_t) (rec[0] << 8 | rec[1]);
}
int spi_dev_tst() {
LOG_D("issd12:%#x\r\n", w25q128_read_deviceID());
LOG_D("...............");
return 0;
}
TX_TST_EXPORT(spi_dev_tst);
结果
总结
- 此版本的驱动,并没有做相关宏定义的显示,所以可以直接通过参数配置来进行切换,如果需要限定的话,可以根据以上代码进行调整。这里只添加了spi1,如果需要其他的spi,需要添加spi硬件部分io,dma配置部分,spi中断部分即可,整体的spi逻辑框架无需修改
作者:詩不诉卿