发现rt-thread在某个版本更新中,STM32 BSP下的库函数从标准库切换到了HAL库,HAL库应该是stm32日后发展的主流,但是个人感觉标准库更简洁,易于理解,因此在旧版的RTT上改写了一版SPI的驱动,便于加深对SPI的理解。
关于SPI的协议有大量的文章描述,因此不再赘述。
一、内核中的SPI device
SPI包含以下几个结构体:
![]()
因此,我们在驱动中需要实现的部分,就是将stm32的SPI总线注册到内核中,并且实现底层的ops,以及SPI设备的挂载。
二、驱动中的结构体
stm32_hw_spi_cs是片选引脚的结构体,用于设备挂载到总线。
通过查询手册,SPI1引脚使用如下:
NSS----PA4
SCK----PA5
MISO----PA6
MOSI----PA7
三、驱动中的函数
首先是总线的挂载,此处仅以SPI1为例。
![]()
逐个来看以下这一函数做了哪些事情。首先是RCC_Configuration。
![]()
RCC_Configuration中使能了SPI1和GPIOA的时钟。
然后是GPIO_Configuration。
![]()
GPIO_Configuration中配置了SPI各个引脚的功能。
之后开始配置spi1,spi1由之前的stm32_spi结构体定义,然后调用rt_spi_bus_register,将spi1注册到内核。
注册时需要传入ops参数,ops是内核中SPI device的操作,因此在驱动中定义ops如下:
![]()
xfer函数如下:
![]()
![]()
函数根据内核调用时的不同传输情况,需要实现三个传输函数,如下:
int spi_transmit(SPI_InitTypeDef spi_init, SPI_TypeDef *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart = 0U;
int errorcode = 0;
__IO uint16_t TxXferCount;
__IO uint32_t SPITimeout;
/* Init tickstart for timeout management*/
tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;
if((pData == NULL ) || (Size == 0))
{
errorcode = 1;
goto error;
}
TxXferCount = Size;
/* Configure communication direction : 1Line */
if(spi_init.SPI_Direction == SPI_Direction_1Line_Rx)
{
spi_instance->CR1 |= SPI_CR1_BIDIOE;
}
/* Check if the SPI is already enabled */
if((spi_instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
spi_instance->CR1 |= SPI_CR1_SPE;
}
/* Transmit data in 16 Bit mode */
if(spi_init.SPI_DataSize == SPI_DataSize_16b)
{
if((spi_init.SPI_Mode == SPI_Mode_Slave) || (TxXferCount == 0x01))
{
spi_instance->DR = *((uint16_t *)pData);
pData += sizeof(uint16_t);
TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (TxXferCount > 0U)
{
SPITimeout = 0x1000;
/* Wait until TXE flag is set to send data */
while (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0)
{
rt_kprintf("spi超时n");
errorcode = 3;
goto error;
}
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
spi_instance->DR = *((uint16_t *)pData);
pData += sizeof(uint16_t);
TxXferCount--;
}
}
/* Transmit data in 8 Bit mode */
else
{
if((spi_init.SPI_Mode == SPI_Mode_Slave) || (TxXferCount == 0x01U))
{
*((__IO uint8_t*)&spi_instance->DR) = (*pData);
pData += sizeof(uint8_t);
TxXferCount--;
}
while(TxXferCount > 0)
{
SPITimeout = 0x1000;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0)
{
rt_kprintf("spi超时n");
errorcode = 3;
goto error;
}
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
*((__IO uint8_t*)&spi_instance->DR) = (*pData);
pData += sizeof(uint8_t);
TxXferCount--;
}
}
/* Wait until TXE flag */
if(spi_wait_until_timeout(spi_init, spi_instance, SPI_FLAG_TXE, SET, Timeout, tickstart) != 0)
{
errorcode = 3;
goto error;
}
/* Check Busy flag */
if(spi_wait_until_timeout(spi_init, spi_instance, SPI_FLAG_TXE, SET, Timeout, tickstart) != 0)
{
errorcode = 1;
goto error;
}
error:
return errorcode;
}
int spi_transmitreceive(SPI_InitTypeDef spi_init, SPI_TypeDef *spi_instance, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
uint32_t tmp = 0U;
uint32_t tickstart = 0U;
/* Variable used to alternate Rx and Tx during transfer */
uint32_t txallowed = 1U;
int errorcode = 0;
__IO uint16_t TxXferCount;
__IO uint16_t RxXferCount;
/* Init tickstart for timeout management*/
tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;
tmp = spi_init.SPI_Mode;
if(!((tmp == SPI_Mode_Master) && (spi_init.SPI_Direction == SPI_Direction_2Lines_FullDuplex)))
{
errorcode = 2;
goto error;
}
if((pTxData == NULL) || (pRxData == NULL) || (Size == 0))
{
errorcode = 1;
goto error;
}
TxXferCount = Size;
RxXferCount = Size;
/* Check if the SPI is already enabled */
if((spi_instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
spi_instance->CR1 |= SPI_CR1_SPE;
}
/* Transmit and Receive data in 16 Bit mode */
if(spi_init.SPI_DataSize == SPI_DataSize_16b)
{
if((spi_init.SPI_Mode == SPI_Mode_Slave) || (TxXferCount == 0x01U))
{
SPI_I2S_ReceiveData(spi_instance);
spi_instance->DR = *((uint16_t *)pTxData);
pTxData += sizeof(uint16_t);
TxXferCount--;
}
while ((TxXferCount > 0U) || (RxXferCount > 0U))
{
/* Check TXE flag */
if(txallowed && (TxXferCount > 0U) && (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_TXE) == SET))
{
SPI_I2S_ReceiveData(spi_instance);
spi_instance->DR = *((uint16_t *)pTxData);
pTxData += sizeof(uint16_t);
TxXferCount--;
/* Next Data is a reception (Rx). Tx not allowed */
txallowed = 0U;
}
/* Check RXNE flag */
if((RxXferCount > 0U) && (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_RXNE) == SET))
{
*((uint16_t *)pRxData) = spi_instance->DR;
pRxData += sizeof(uint16_t);
RxXferCount--;
/* Next Data is a Transmission (Tx). Tx is allowed */
txallowed = 1U;
}
if((Timeout != 0xFFFFFFFFU) && ((rt_tick_get() * 1000 / RT_TICK_PER_SECOND-tickstart) >= Timeout))
{
errorcode = 3;
goto error;
}
}
}
/* Transmit and Receive data in 8 Bit mode */
else
{
if((spi_init.SPI_Mode == SPI_Mode_Slave) || (TxXferCount == 0x01U))
{
SPI_I2S_ReceiveData(spi_instance);
*((__IO uint8_t*)&spi_instance->DR) = (*pTxData);
pTxData += sizeof(uint8_t);
TxXferCount--;
}
while((TxXferCount > 0U) || (RxXferCount > 0U))
{
/* 等待发送缓冲区为空,TXE事件 */
if (txallowed && (TxXferCount > 0U) && (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_TXE) == SET))
{
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_ReceiveData(spi_instance);
*((__IO uint8_t*)&spi_instance->DR) = (*pTxData);
pTxData += sizeof(uint8_t);
TxXferCount--;
txallowed = 0U;
}
if ((RxXferCount > 0U) && SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_RXNE) == SET)
{
*(uint8_t *)pRxData = spi_instance->DR;
pRxData += sizeof(uint8_t);
RxXferCount--;
txallowed = 1U;
}
if((Timeout != 0xFFFFFFFFU) && ((rt_tick_get() * 1000 / RT_TICK_PER_SECOND-tickstart) >= Timeout))
{
errorcode = 3;
goto error;
}
}
}
/* Wait until TXE flag */
if(spi_wait_until_timeout(spi_init, spi_instance, SPI_FLAG_TXE, SET, Timeout, tickstart) != 0)
{
errorcode = 3;
goto error;
}
/* Check Busy flag */
if(spi_wait_until_timeout(spi_init, spi_instance, SPI_FLAG_BSY, RESET, Timeout, tickstart) != 0)
{
errorcode = 1;
goto error;
}
error :
return errorcode;
}
int spi_receive(SPI_InitTypeDef spi_init, SPI_TypeDef *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart = 0U;
int errorcode = 0;
__IO uint16_t RxXferCount;
__IO uint32_t SPITimeout;
if((spi_init.SPI_Mode == SPI_Mode_Master) && (spi_init.SPI_Direction == SPI_Direction_2Lines_FullDuplex))
{
/* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
return spi_transmitreceive(spi_init, spi_instance,pData,pData,Size,Timeout);
}
/* Init tickstart for timeout management*/
tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;
if((pData == NULL ) || (Size == 0))
{
errorcode = 1;
goto error;
}
RxXferCount = Size;
/* Configure communication direction: 1Line */
if(spi_init.SPI_Direction == SPI_Direction_1Line_Rx)
{
spi_instance->CR1 &= (~SPI_CR1_BIDIOE);
}
/* Check if the SPI is already enabled */
if((spi_instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
spi_instance->CR1 |= SPI_CR1_SPE;
}
/* Receive data in 8 Bit mode */
if(spi_init.SPI_DataSize == SPI_DataSize_8b)
{
/* Transfer loop */
while(RxXferCount > 0U)
{
SPITimeout = 0x1000;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0)
{
rt_kprintf("spi超时n");
errorcode = 3;
goto error;
}
}
*(uint8_t *)pData = spi_instance->DR;
pData += sizeof(uint8_t);
RxXferCount--;
}
}
else
{
/* Transfer loop */
while(RxXferCount > 0U)
{
SPITimeout = 0x1000;
/* Check the RXNE flag */
while (SPI_I2S_GetFlagStatus(spi_instance, SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0)
{
rt_kprintf("spi超时n");
errorcode = 3;
goto error;
}
}
*((uint16_t*)pData) = spi_instance->DR;
pData += sizeof(uint16_t);
RxXferCount--;
}
}
/* Check the end of the transaction */
if((spi_init.SPI_Mode == SPI_Mode_Master)&&((spi_init.SPI_Direction == SPI_Direction_1Line_Rx)||(spi_init.SPI_Direction == SPI_Direction_2Lines_RxOnly)))
{
/* Disable SPI peripheral */
spi_instance->CR1 &= (~SPI_CR1_SPE);
}
error :
return errorcode;
}
至此SPI驱动需要实现的功能基本已经完成,SPI总线在内核启动过程中会注册到内核。 当然还需要挂载SPI设备,如下: 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.GPIO_Pin = cs_gpio_pin;
GPIO_Initure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Initure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(cs_gpiox, &GPIO_Initure);
GPIO_WriteBit(cs_gpiox, cs_gpio_pin, Bit_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, %dn", device_name, bus_name, result);
}
RT_ASSERT(result == RT_EOK);
LOG_D("%s attach to %s done", device_name, bus_name);
return result;
}
现在SPI设备就可以使用了,其中改写了很多HAL库封装的内容,以便让自己更好的理解SPI。
原作者:存在即合理
|