打印
[新唐博主]

[经验] 在RTThread上使用SPI+DMA要进行的配置部分

[复制链接]
628|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
首先阄描述下遇到的问题
  Spi读取读取读取读取读取读取读取,读取1000Hz,使用STM32F407主控,发现cpu占70%,且占达到了达到了且且了了了时序的的恢复正常,看这里要着重优化,便萌生了使用DMA的想法。(后记:最后发现是SPI时钟频率配置错误了)
  使用SPI+DMA要进行的配置
  RTT部分
  1.启动RTT设备驱动。点击自己的程序 -》RT-Thread Setting,启动SPI设备驱动

2.在board.h中添加开启宏

使用特权

评论回复
沙发
豌豆爹|  楼主 | 2023-4-19 14:54 | 只看该作者
启动后设备驱动会自动调试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);
  }
  }

使用特权

评论回复
板凳
豌豆爹|  楼主 | 2023-4-19 14:54 | 只看该作者
4.使能HAL库-SPI,即可与RTT设备驱动接口

使用特权

评论回复
地板
豌豆爹|  楼主 | 2023-4-19 14:55 | 只看该作者
至此基本配置已经完成,接下来是用户配置,配置片选,并挂载到具体的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);//发送消息
  }

使用特权

评论回复
5
豌豆爹|  楼主 | 2023-4-19 14:56 | 只看该作者
解决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;
  }

使用特权

评论回复
6
豌豆爹|  楼主 | 2023-4-19 14:56 | 只看该作者
使用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;
  }

使用特权

评论回复
7
豌豆爹|  楼主 | 2023-4-19 14:56 | 只看该作者
替换本文件里的方法结构体
  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;
  }

使用特权

评论回复
8
豌豆爹|  楼主 | 2023-4-19 14:56 | 只看该作者
在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搭配干活不累

使用特权

评论回复
9
豌豆爹|  楼主 | 2023-4-19 14:57 | 只看该作者
最后附上一张导图

使用特权

评论回复
10
xiaoyaodz| | 2023-5-10 20:17 | 只看该作者
rtthread适配spi用中断应该怎么实现

使用特权

评论回复
11
eefas| | 2023-5-11 19:00 | 只看该作者
SPI 使用DMA方式如何来做              

使用特权

评论回复
12
两只袜子| | 2023-5-12 10:54 | 只看该作者
以上两个问题我也想同问一下

使用特权

评论回复
13
小辣鸡0930| | 2023-11-30 14:06 | 只看该作者
eefas 发表于 2023-5-11 19:00
SPI 使用DMA方式如何来做

大佬,找的方法了吗?我是带Linux操作系统的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

500

主题

1896

帖子

4

粉丝