天天看點

STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信

文章目錄

    • 前言
    • 一、基于STM32CubeMX建立STM32F407USB虛拟序列槽基本工程
    • 二、根據基本工程修改USB部分的代碼,提供rosserial會用到的序列槽收發接口。
    • 三、将ros_lib增加到工程中,并修改STM32Hardware.h檔案

前言

之前嘗試過移植好rosserial的stm32通過序列槽和ROS系統建立通信,但無奈在類似于釋出IMU類似的資料量很大的消息或消息的釋出頻率較高時經常會出現一些問題(釋出頻率達不到設定要求,啟動rosserial的終端會報一些警告和錯誤),在一般的學習中還可以,但在實際的應用中基本達不到使用要求。

寫道這想說明一點:我對整個rosserial内部的通信機制也不是很了解,類似于通信速度達到多少可以滿足通信速度的需求,如何測試通信速度等等問題(我也很納悶為什麼序列槽就會存在這些問題),如果大家有什麼見解歡迎交流。

一、基于STM32CubeMX建立STM32F407USB虛拟序列槽基本工程

  1. 配置STM32F407單片機的時鐘源,時鐘源選擇為晶振
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
  2. 配置調試方式和時基,此處選擇的為SW模式。時基選擇為系統滴答定時器。
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
    3. 配置USB為全速模式下的僅裝置模式
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
  3. 這裡使用ST官方提供的USB裝置庫,并使用CDC類
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
  4. 配置系統時鐘,這裡晶振頻率為8M并将系統時鐘配置為最高的168M,并注意USB的時鐘為48M
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
  5. 這裡要特别注意一點,下面紅框中标示的,堆空間一定要改大,否則會在裝置管理器中顯示黃色的感歎号。
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信
  6. 這些都配置完之後便可以點選右上角的生成代碼按鈕,來生成基本工程代碼。
    STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信

二、根據基本工程修改USB部分的代碼,提供rosserial會用到的序列槽收發接口。

這一部分推薦看一篇文章:http://bbs.21ic.com/icview-811704-1-1.html

這裡主要修改usbd_cdc_if.c檔案,首先我們要明确其中兩個函數的功能。

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  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*)hUsbDeviceFS.pClassData;
  if (hcdc->TxState != 0){
    return USBD_BUSY;
  }
  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
  result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
  /* 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 usb_rxBuffer[USB_RX_DATA_SIZE];
uint32_t usb_rxBufPtrIn = 0;
uint32_t usb_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 = (usb_rxBufPtrIn + 1) % USB_RX_DATA_SIZE;
    if(in != usb_rxBufPtrOut) //USB接收緩沖區未滿
    {
      usb_rxBuffer[usb_rxBufPtrIn] = Buf[i];
      usb_rxBufPtrIn = in;
    }
  }
  
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  return (USBD_OK);
  /* USER CODE END 6 */
}
           

這樣處理最終實作的效果是将接收到的資料存入了一環形緩沖區。再将資料存好之後便可以為rosserial提供接口函數了

//接收緩沖區中的資料大小
int vcp_available(void)
{
  return ((uint32_t)(USB_RX_DATA_SIZE + usb_rxBufPtrIn - usb_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(usb_rxBufPtrIn == usb_rxBufPtrOut)
  {
    return -1;
  }
  else
  {
    unsigned char ch = usb_rxBuffer[usb_rxBufPtrOut];
    usb_rxBufPtrOut = (uint16_t)(usb_rxBufPtrOut + 1) % USB_RX_DATA_SIZE;
    return ch;
  }
}
//通過usb_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函數的錯誤,和一系列警告消息。

STM32F407(CubeMX+HAL+USB(vcp))移植rosserial和ROS通信

針對找不到round()函數的錯誤可以在增加__USE_C99_MATH的宏,針對大量警告的問題可以在Misc Controls 的位置增加如:–diag_suppress=num,(num,為警告資訊#号後面的數字)。

接下來的重點便是修改STM32Hardware.h檔案了。

這裡試了一下不能直接粘貼太多代碼,就介紹一下如何修改好了。

在開頭的地方增加:

将類内部的讀寫函數修改為:

int read(){
     if(vcp_available()){
    return vcp_read();
     }else{
     return -1;
     } 
     void write(uint8_t* data, int length){
     vcp_write(data, length);
    }
           

将時間函數改為

到此整個移植工作便結束了,接下來可以按照rosserial wiki上面的教程開始測試學習了。

繼續閱讀