单片机学习小组
直播中

吴藩

10年用户 912经验值
擅长:可编程逻辑
私信 关注

怎样在STM32F407上移植rosserial并与ROS进行通信呢

怎样去建立一种基于STM32CubeMX的STM32F407USB虚拟串口基本工程呢?有哪些步骤?

回帖(1)

安瑞娟

2022-2-24 14:21:54
一、基于STM32CubeMX建立STM32F407USB虚拟串口基本工程




  • 配置STM32F407单片机的时钟源,时钟源选择为晶振

  • 配置调试方式和时基,此处选择的为SW模式。时基选择为系统滴答定时器。
    3. 配置USB为全速模式下的仅设备模式

  • 这里使用ST官方提供的USB设备库,并使用CDC类

  • 配置系统时钟,这里晶振频率为8M并将系统时钟配置为最高的168M,并注意USB的时钟为48M

  • 这里要特别注意一点,下面红框中标示的,堆空间一定要改大,否则会在设备管理器中显示黄色的感叹号。

  • 这些都配置完之后便可以点击右上角的生成代码按钮,来生成基本工程代码。



二、根据基本工程修改USB部分的代码,提供rosserial会用到的串口收发接口。

这里主要修改u***d_cdc_if.c文件,首先我们要明确其中两个函数的功能。

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hU***DeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hU***DeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 7 */
  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hU***DeviceFS.pClassData;
  if (hcdc->TxState != 0){
    return USBD_BUSY;
  }
  USBD_CDC_SetTxBuffer(&hU***DeviceFS, Buf, Len);
  result = USBD_CDC_TransmitPacket(&hU***DeviceFS);
  /* USER CODE END 7 */
  return result;
}
单从这两个函数的名字上看也比较好理解,上面的时数据接收函数,下面的时数据发送函数。CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)是数据接收的回调函数,USB每次收到数据后都会调用该函数将接收到的数据存入Buf指向的缓冲区当中,Len是接收到数据的长度。USBD_CDC_ReceivePacket()函数的作用是复位OUT端点接收缓冲区,CDC_Itf_Receive()函数在接收完数据之后要调用该函数复位缓冲区。因此如果接收到的数据没有及时处理,就会被下次接收到的数据覆盖掉了。所以在此处要增加数据转存环节。

#define USB_RX_DATA_SIZE 2048

uint8_t u***_rxBuffer[USB_RX_DATA_SIZE];
uint32_t u***_rxBufPtrIn = 0;
uint32_t u***_rxBufPtrOut = 0;

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  uint32_t i;
  uint16_t in;
  for(i = 0; i < *Len; ++i)
  {
    in = (u***_rxBufPtrIn + 1) % USB_RX_DATA_SIZE;
    if(in != u***_rxBufPtrOut) //USB接收缓冲区未满
    {
      u***_rxBuffer[u***_rxBufPtrIn] = Buf;
      u***_rxBufPtrIn = in;
    }
  }
  
  USBD_CDC_SetRxBuffer(&hU***DeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hU***DeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}
这样处理最终实现的效果是将接收到的数据存入了一环形缓冲区。再将数据存好之后便可以为rosserial提供接口函数了

//接收缓冲区中的数据大小
int vcp_available(void)
{
  return ((uint32_t)(USB_RX_DATA_SIZE + u***_rxBufPtrIn - u***_rxBufPtrOut)) % USB_RX_DATA_SIZE;
}
//从接收缓冲区中读取
int vcp_read(void)
{
  // if the head isn't ahead of the tail, we don't have any characters
  if(u***_rxBufPtrIn == u***_rxBufPtrOut)
  {
    return -1;
  }
  else
  {
    unsigned char ch = u***_rxBuffer[u***_rxBufPtrOut];
    u***_rxBufPtrOut = (uint16_t)(u***_rxBufPtrOut + 1) % USB_RX_DATA_SIZE;
    return ch;
  }
}
//通过u***_vcp向外发送
void vcp_write(uint8_t* Buf, uint16_t Len)
{
  while(CDC_Transmit_FS(Buf, Len) != HAL_OK);
}
三、将ros_lib增加到工程中,并修改STM32Hardware.h文件

ros_lib 可以根据rosserial wiki 的教程中执行产生。
ros_lib增加到工程中之后会报找不到round函数的错误,和一系列警告消息。

针对找不到round()函数的错误可以在增加__USE_C99_MATH的宏,针对大量警告的问题可以在Misc Controls 的位置增加如:–diag_suppress=num,(num,为警告信息#号后面的数字)。
接下来的重点便是修改STM32Hardware.h文件了。
这里试了一下不能直接粘贴太多代码,就介绍一下如何修改好了。
在开头的地方增加:

extern __IO uint32_t uwTick;
将类内部的读写函数修改为:

int read(){
     if(vcp_available()){
    return vcp_read();
     }else{
     return -1;
     }
     void write(uint8_t* data, int length){
     vcp_write(data, length);
    }
将时间函数改为

unsigned long time(){return uwTick;}
到此整个移植工作便结束了,接下来可以按照rosserial wiki上面的教程开始测试学习了。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分