有了SPI總線設備對象,還需要實現(xiàn)總線的操作方法,操作方法的函數(shù)指針定義已經(jīng)在SPI總線設備框架中給出了:
/**
* SPI operators
*/
struct rt_spi_ops
{
rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
rt_uint32_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message);
};
configure:有兩個輸入?yún)?shù),其作用就是根據(jù)configuration配置參數(shù)配置SPI總線設備的傳輸數(shù)據(jù)寬度、時鐘極性、時鐘相位和總線速率等參數(shù),最后調(diào)用HAL庫初始化SPI總線。其stm32的實現(xiàn)代碼如下:
/**
* SPI configuration structure
*/
struct rt_spi_configuration
{
rt_uint8_t mode;
rt_uint8_t data_width;
rt_uint16_t reserved;
rt_uint32_t max_hz;
};
static rt_err_t spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration)
{
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
struct stm32_spi *spi_drv = rt_container_of(device->bus, struct stm32_spi, spi_bus);
spi_drv->cfg = configuration;
return stm32_spi_init(spi_drv, configuration);
}
static rt_err_t stm32_spi_init(struct stm32_spi *spi_drv, struct rt_spi_configuration *cfg)
{
RT_ASSERT(spi_drv != RT_NULL);
RT_ASSERT(cfg != RT_NULL);
SPI_HandleTypeDef *spi_handle = &spi_drv->handle;
if (cfg->mode & RT_SPI_SLAVE)
{
spi_handle->Init.Mode = SPI_MODE_SLAVE;
}
else
{
spi_handle->Init.Mode = SPI_MODE_MASTER;
}
if (cfg->mode & RT_SPI_3WIRE)
{
spi_handle->Init.Direction = SPI_DIRECTION_1LINE;
}
else
{
spi_handle->Init.Direction = SPI_DIRECTION_2LINES;
}
if (cfg->data_width == 8)
{
spi_handle->Init.DataSize = SPI_DATASIZE_8BIT;
spi_handle->TxXferSize = 8;
spi_handle->RxXferSize = 8;
}
else if (cfg->data_width == 16)
{
spi_handle->Init.DataSize = SPI_DATASIZE_16BIT;
}
else
{
return RT_EIO;
}
if (cfg->mode & RT_SPI_CPHA)
{
spi_handle->Init.CLKPhase = SPI_PHASE_2EDGE;
}
else
{
spi_handle->Init.CLKPhase = SPI_PHASE_1EDGE;
}
if (cfg->mode & RT_SPI_CPOL)
{
spi_handle->Init.CLKPolarity = SPI_POLARITY_HIGH;
}
else
{
spi_handle->Init.CLKPolarity = SPI_POLARITY_LOW;
}
if (cfg->mode & RT_SPI_NO_CS)
{
spi_handle->Init.NSS = SPI_NSS_HARD_OUTPUT;
}
else
{
spi_handle->Init.NSS = SPI_NSS_SOFT;
}
...
if (HAL_SPI_Init(spi_handle) != HAL_OK)
{
return RT_EIO;
}
...
}
當你需要更換MCU的時候,你就需要重寫上述的驅(qū)動部分代碼了。接下來看下xfer:用于傳輸數(shù)據(jù),通過xger方法對SPI總線的控制來完成一條message的傳輸,這里的傳輸肯能是雙向的 也可能是單向的,也就是所謂的單雙工,最終都是通過stm32的hal庫來實現(xiàn),直接看代碼:
static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
HAL_StatusTypeDef state;
rt_size_t message_length, already_send_length;
rt_uint16_t send_length;
rt_uint8_t *recv_buf;
const rt_uint8_t *send_buf;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
RT_ASSERT(message != RT_NULL);
struct stm32_spi *spi_drv = rt_container_of(device->bus, struct stm32_spi, spi_bus);
SPI_HandleTypeDef *spi_handle = &spi_drv->handle;
struct stm32_hw_spi_cs *cs = device->parent.user_data;
if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS))
{
HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_RESET);
}
...
while (message_length)
{
/* calculate the start address */
already_send_length = message->length - send_length - message_length;
send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;
/* start once data exchange in DMA mode */
if (message->send_buf && message->recv_buf)
{
if ((spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG) && (spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG))
{
state = HAL_SPI_TransmitReceive_DMA(spi_handle, (uint8_t *)send_buf, (uint8_t *)recv_buf, send_length);
}
else
{
state = HAL_SPI_TransmitReceive(spi_handle, (uint8_t *)send_buf, (uint8_t *)recv_buf, send_length, 1000);
}
}
else if (message->send_buf)
{
if (spi_drv->spi_dma_flag & SPI_USING_TX_DMA_FLAG)
{
state = HAL_SPI_Transmit_DMA(spi_handle, (uint8_t *)send_buf, send_length);
}
else
{
state = HAL_SPI_Transmit(spi_handle, (uint8_t *)send_buf, send_length, 1000);
}
if (message->cs_release && (device->config.mode & RT_SPI_3WIRE))
{
/* release the CS by disable SPI when using 3 wires SPI */
__HAL_SPI_DISABLE(spi_handle);
}
}
else
{
memset((uint8_t *)recv_buf, 0xff, send_length);
if (spi_drv->spi_dma_flag & SPI_USING_RX_DMA_FLAG)
{
state = HAL_SPI_Receive_DMA(spi_handle, (uint8_t *)recv_buf, send_length);
}
else
{
/* clear the old error flag */
__HAL_SPI_CLEAR_OVRFLAG(spi_handle);
state = HAL_SPI_Receive(spi_handle, (uint8_t *)recv_buf, send_length, 1000);
}
}
if (state != HAL_OK)
{
LOG_I("spi transfer error : %d", state);
message->length = 0;
spi_handle->State = HAL_SPI_STATE_READY;
}
else
{
LOG_D("%s transfer done", spi_drv->config->bus_name);
}
while (HAL_SPI_GetState(spi_handle) != HAL_SPI_STATE_READY);
}
if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS))
{
HAL_GPIO_WritePin(cs->GPIOx, cs->GPIO_Pin, GPIO_PIN_SET);
}
return message->length;
}
這里刪減了一些不影響函數(shù)主要功能的代碼,主要體現(xiàn)函數(shù)的功能,根據(jù)message中recv_buf和send_buf判斷是全雙工還是半雙工發(fā)送接收數(shù)據(jù),調(diào)用hal庫函數(shù)完成數(shù)據(jù)的傳輸,最后釋放cs引腳。 最后就是完成SPI總線設備注冊到操作系統(tǒng)中,需要定義rt_spi_ops來完成初始化時注冊借口中的ops參數(shù):
static const struct rt_spi_ops stm_spi_ops =
{
.configure = spi_configure,
.xfer = spixfer,
};
static int rt_hw_spi_bus_init(void)
{
rt_err_t result;
for (int i = 0; i < sizeof(spi_config) / sizeof(spi_config[0]); i++)
{
spi_bus_obj[i].config = &spi_config[i];
spi_bus_obj[i].spi_bus.parent.user_data = &spi_config[i];
spi_bus_obj[i].handle.Instance = spi_config[i].Instance;
result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, spi_config[i].bus_name, &stm_spi_ops);
RT_ASSERT(result == RT_EOK);
LOG_D("%s bus init done", spi_config[i].bus_name);
}
return result;
}
到這里就是關于SPI驅(qū)動部分的核心代碼講解完畢了,當然還有attach、DMA、SPI_IRQHandler部分源碼沒有詳細的羅列,這一部分就交給對驅(qū)動感興趣的小伙伴去源碼里探索吧。