打印
[其他ST产品]

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

[复制链接]
522|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
elephant00|  楼主 | 2023-4-4 10:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
首先阄描述下遇到的问题
  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搭配干活不累
  最后附上一张导图









使用特权

评论回复
沙发
Uriah| | 2024-4-15 07:31 | 只看该作者

环氧树脂由于硬度的原因不能用于应力敏感和含有贴片元件的模块灌封,在模块电源中基本被淘汰

使用特权

评论回复
板凳
帛灿灿| | 2024-4-15 09:27 | 只看该作者

产生一个充电放电的过程

使用特权

评论回复
地板
Bblythe| | 2024-4-15 10:30 | 只看该作者

齐纳二极管作为ESD保护与单向TVS原理相同

使用特权

评论回复
5
周半梅| | 2024-4-15 12:26 | 只看该作者

之所以称为ESD静电保护器

使用特权

评论回复
6
Pulitzer| | 2024-4-15 13:29 | 只看该作者

钳位二极管最常用于集成电路中

使用特权

评论回复
7
童雨竹| | 2024-4-15 15:25 | 只看该作者

它迅速由高阻态变为低阻态

使用特权

评论回复
8
Wordsworth| | 2024-4-15 16:28 | 只看该作者

减少反射,避免振荡,方便调试

使用特权

评论回复
9
Clyde011| | 2024-4-15 17:31 | 只看该作者

大于1的定义为极高导热的性能,而对于模块电源此水平的导热系数是无法达到其散热功能的需求

使用特权

评论回复
10
公羊子丹| | 2024-4-15 18:24 | 只看该作者

得到正常工作状态下的输出电压和电流。

使用特权

评论回复
11
万图| | 2024-4-15 19:27 | 只看该作者

在电路中有部分电子元器件比较“脆弱”

使用特权

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

本版积分规则

988

主题

3089

帖子

7

粉丝