首先阄描述下遇到的问题
Spi读取读取读取读取读取读取读取,读取1000Hz,使用STM32F407主控,发现cpu占70%,且占达到了达到了且且了了了时序的的恢复正常,看这里要着重优化,便萌生了使用DMA的想法。(后记:最后发现是SPI时钟频率配置错误了)
使用SPI+DMA要进行的配置
RTT部分
1.启动RTT设备驱动。点击自己的程序 -》RT-Thread Setting,启动SPI设备驱动
2.在board.h中添加开启宏
启动后设备驱动会自动调试HAL库进入底层硬件的初始化默认配置,并将spi注册到设备容器
int rt_hw_spi_init(void)
{
stm32_get_dma_info();
return rt_hw_spi_bus_init();
}
INIT_BOARD_EXPORT(rt_hw_spi_init);
HAL库部分
3.在board.c文件里加入以下数,此数受设备框架调整使用以进入底层硬件初始化
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi-》Instance == SPI2)
{
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration,这里配置自己的
PB13 ------》 SPI3_MISO
PB14 ------》 SPI3_SCK
PB15 ------》 SPI3_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_13| GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
4.使能HAL库-SPI,即可与RTT设备驱动接口
至此基本配置已经完成,接下来是用户配置,配置片选,并挂载到具体的SPI设备
选择想要的io作为片选引脚,即一个设备对应一个片选
int spi_device_attach(void)
{
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*******传感器片选引脚*********/
rt_pin_mode(56, PIN_MODE_OUTPUT); //PD8,配置引脚为输出模式
rt_pin_mode(57, PIN_MODE_OUTPUT); //PD9
rt_pin_mode(58, PIN_MODE_OUTPUT); //PD10
rt_pin_mode(33, PIN_MODE_OUTPUT); //PC1
rt_pin_write(56, PIN_HIGH);//配置初始化引脚高电平(解除片选)
rt_pin_write(57, PIN_HIGH);
rt_pin_write(58, PIN_HIGH);
rt_pin_write(33, PIN_HIGH);
/*这里挂载了四个设备spi2-0,spi2-1,spi2-2,spi2-3*/
rt_hw_spi_device_attach(“spi2”,ICM20602_SPI_DEVICE_NAME, GPIOD, GPIO_PIN_8); //片选引脚PD8
rt_hw_spi_device_attach(“spi2”,SPL06_SPI_DEVICE_NAME, SPL06_CS_GPIO,SPL06_CS_PIN);//片选引脚PD10
rt_hw_spi_device_attach(“spi2”,“spi22”, GPIOD,GPIO_PIN_9);//片选引脚PD9
rt_hw_spi_device_attach(“spi2”,“spi23”, GPIOC,GPIO_PIN_1);//片选引脚PC1
return RT_EOK;
}
INIT_DEVICE_EXPORT(spi_device_attach);//导出到自动初始化
初化spi
struct rt_spi_device *spi_dev_icm20602 = RT_NULL; /*spi设备句柄*/
int icm20602_spi_device_init(void)
{
struct rt_spi_configuration spi_cfg;
spi_dev_icm20602 = (struct rt_spi_device *)rt_device_find(ICM20602_SPI_DEVICE_NAME);/* 查找 spi2 设备获取设备句柄 */
spi_cfg.data_width = 8;
spi_cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
spi_cfg.max_hz = 10000000; /*10M*/
rt_spi_configure(spi_dev_icm20602,&spi_cfg);
return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(icm20602_spi_device_init);
配置完成,开始使用
spi底层读写函数现实
/****************************************************************************************
*@Brief 读取数据
*@param[in]
*****************************************************************************************/
void icm20602_reg_read(rt_uint8_t addr,rt_uint8_t *rev_buf,rt_uint32_t len)
{
struct rt_spi_message msg1,msg2;
rt_uint8_t reg = addr|0x80;
msg1.send_buf = ®
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 = rev_buf;
msg2.length = len;
msg2.cs_take = 0;
msg2.cs_release = 1;
msg2.next = RT_NULL;
/*给icm20602设备读取和发送消息*/
rt_spi_transfer_message(spi_dev_icm20602, &msg1);//发送消息
}
/****************************************************************************************
*@brief 写数据
*@param[in]
*****************************************************************************************/
void icm20602_reg_write(rt_uint8_t addr,rt_uint8_t value)
{
struct rt_spi_message msg1;
rt_uint8_t send_buf[2];
send_buf[0] = addr;
send_buf[1] = value;
msg1.send_buf = send_buf;
msg1.recv_buf = RT_NULL;
msg1.length = 2;
msg1.cs_take = 1;
msg1.cs_release = 1;
msg1.next = RT_NULL;
rt_spi_transfer_message(spi_dev_icm20602, &msg1);//发送消息
}
解决RTT的SPI设备驱动(drv_spi.c)缺陷(使用DMA)
rt_spi_transfer_message(spi_dev_icm20602, &msg1);//发送消息
(这个函数在spi_core.c后面会提到)
函数会调用这个函数进行数据传输
就是这个
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)
{
HAL_GPIO_WritePin(cs-》GPIOx, cs-》GPIO_Pin, GPIO_PIN_RESET);
}
LOG_D(“%s transfer prepare and start”, spi_drv-》config-》bus_name);
LOG_D(“%s sendbuf: %X, recvbuf: %X, length: %d”,
spi_drv-》config-》bus_name,
(uint32_t)message-》send_buf,
(uint32_t)message-》recv_buf, message-》length);
message_length = message-》length;
recv_buf = message-》recv_buf;
send_buf = message-》send_buf;
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 = 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);
}
}
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
{
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);
}
/* For simplicity reasons, this example is just waiting till the end of the
transfer, but application may peRForm other tasks while transfer operation
is ongoing. */
while (HAL_SPI_GetState(spi_handle) != HAL_SPI_STATE_READY);
}
if (message-》cs_release)
{
HAL_GPIO_WritePin(cs-》GPIOx, cs-》GPIO_Pin, GPIO_PIN_SET);
}
return message-》length;
}
使用DMA时也是在这里死等,这就失去了我们使用DMA的初阶,现在优化这个函数,在DMA传输数据时释放CPU去干其他事情
/* For simplicity reasons, this example is just waiting till the end of the
transfer, but application may perform other tasks while transfer operation
is ongoing. */
while (HAL_SPI_GetState(spi_handle) != HAL_SPI_STATE_READY);
添加如下函数,这里的方法是用信号释放CPU
static rt_uint32_t spixfer_my(struct rt_spi_device *device, struct rt_spi_message *message, rt_sem_t sem)
{
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)
{
HAL_GPIO_WritePin(cs-》GPIOx, cs-》GPIO_Pin, GPIO_PIN_RESET);
}
LOG_D(“%s transfer prepare and start”, spi_drv-》config-》bus_name);
LOG_D(“%s sendbuf: %X, recvbuf: %X, length: %d”,
spi_drv-》config-》bus_name,
(uint32_t)message-》send_buf,
(uint32_t)message-》recv_buf, message-》length);
message_length = message-》length;
recv_buf = message-》recv_buf;
send_buf = message-》send_buf;
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 = 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);
}
}
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
{
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);
}
/* For simplicity reasons, this example is just waiting till the end of the
transfer, but application may perform other tasks while transfer operation
is ongoing. */
while (HAL_SPI_GetState(spi_handle) != HAL_SPI_STATE_READY)
{
rt_sem_take(sem, RT_WAITING_FOREVER);
}
}
if (message-》cs_release)
{
HAL_GPIO_WritePin(cs-》GPIOx, cs-》GPIO_Pin, GPIO_PIN_SET);
}
return message-》length;
}
替换本文件里的方法结构体
static const struct rt_spi_ops stm_spi_ops =
{
.configure = spi_configure,
.xfer = spixfer,
.xfer_my = spixfer_my,
};
OK,drv_spi.c修改完成
下面更改spi.h文件
同理,替换这个结构体
/**
* 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);
rt_uint32_t (*xfer_my)(struct rt_spi_device *device, struct rt_spi_message *message, rt_sem_t sem);
};
OK,spi.h文件修改完成
下面更改spi_core.c文件
使用这个函数支持我们自定义的函数
struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device,
struct rt_spi_message *message)
添加如下载数量
struct rt_spi_message *rt_spi_transfer_message_my(struct rt_spi_device *device,
struct rt_spi_message *message, rt_sem_t sem)
{
rt_err_t result;
struct rt_spi_message *index;
RT_ASSERT(device != RT_NULL);
/* get first message */
index = message;
if (index == RT_NULL)
return index;
result = rt_mutex_take(&(device-》bus-》lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(-RT_EBUSY);
return index;
}
/* reset errno */
rt_set_errno(RT_EOK);
/* configure SPI bus */
if (device-》bus-》owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device-》bus-》ops-》configure(device, &device-》config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device-》bus-》owner = device;
}
else
{
/* configure SPI bus faiLED */
rt_set_errno(-RT_EIO);
goto __exit;
}
}
/* transmit each SPI message */
while (index != RT_NULL)
{
/* transmit SPI message */
result = device-》bus-》ops-》xfer_my(device, index, sem);
if (result == 0)
{
rt_set_errno(-RT_EIO);
break;
}
index = index-》next;
}
__exit:
/* release bus lock */
rt_mutex_release(&(device-》bus-》lock));
return index;
}
在spi.h里新加入此函数的声音,提供外部调用
struct rt_spi_message *rt_spi_transfer_message_my(struct rt_spi_device *device,
struct rt_spi_message *message, rt_sem_t sem);
OK,全部搞定
回过头来
spi底层读写函数实现小改一下
static rt_sem_t spidma_sem = RT_NULL;//定义信号量,供我们的自定义函数使用
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
rt_sem_release(spidma_sem);//读完成回调,释放信号量
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
rt_sem_release(spidma_sem);//写完成回调,释放信号量
}
/****************************************************************************************
*@brief 读取数据
*@param[in]
*****************************************************************************************/
void icm20602_reg_read(rt_uint8_t addr,rt_uint8_t *rev_buf,rt_uint32_t len)
{
struct rt_spi_message msg1,msg2;
rt_uint8_t reg = addr|0x80;
msg1.send_buf = ®
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 = rev_buf;
msg2.length = len;
msg2.cs_take = 0;
msg2.cs_release = 1;
msg2.next = RT_NULL;
/*给icm20602设备读取和发送消息*/
rt_spi_transfer_message_my(spi_dev_icm20602, &msg1, spidma_sem);//发送消息
}
/****************************************************************************************
*@brief 写数据
*@param[in]
*****************************************************************************************/
void icm20602_reg_write(rt_uint8_t addr,rt_uint8_t value)
{
struct rt_spi_message msg1;
rt_uint8_t send_buf[2];
send_buf[0] = addr;
send_buf[1] = value;
msg1.send_buf = send_buf;
msg1.recv_buf = RT_NULL;
msg1.length = 2;
msg1.cs_take = 1;
msg1.cs_release = 1;
msg1.next = RT_NULL;
rt_spi_transfer_message_my(spi_dev_icm20602, &msg1, spidma_sem);//发送消息
}
全部完成,可以愉快的使用spi+dma干活了
CPU,DMA搭配干活不累
最后附上一张导图
|