Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add USB CDC transport #89

Merged
merged 2 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 38 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This tool aims to ease the micro-ROS integration in a STM32CubeMX/IDE project.
- [Transport configuration](#transport-configuration)
- [U(S)ART with DMA](#usart-with-dma)
- [U(S)ART with Interrupts](#usart-with-interrupts)
- [USB CDC](#usb-cdc)
- [Customizing the micro-ROS library](#customizing-the-micro-ros-library)
- [Adding custom packages](#adding-custom-packages)
- [Purpose of the Project](#purpose-of-the-project)
Expand All @@ -31,53 +32,54 @@ This package support the usage of micro-ROS on top of two different middlewares:
4. Configure the transport interface on the STM32CubeMX project, check the [Transport configuration](#Transport-configuration) section for instructions on the custom transports provided.
5. Modify the generated `Makefile` to include the following code **before the `build the application` section**:

<!-- # Removing heap4 manager while being polite with STM32CubeMX
TMPVAR := $(C_SOURCES)
C_SOURCES := $(filter-out Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c, $(TMPVAR)) -->
<!-- # Removing heap4 manager while being polite with STM32CubeMX
TMPVAR := $(C_SOURCES)
C_SOURCES := $(filter-out Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c, $(TMPVAR)) -->

```makefile
#######################################
# micro-ROS addons
#######################################
LDFLAGS += micro_ros_stm32cubemx_utils/microros_static_library/libmicroros/libmicroros.a
C_INCLUDES += -Imicro_ros_stm32cubemx_utils/microros_static_library/libmicroros/microros_include
```makefile
#######################################
# micro-ROS addons
#######################################
LDFLAGS += micro_ros_stm32cubemx_utils/microros_static_library/libmicroros/libmicroros.a
C_INCLUDES += -Imicro_ros_stm32cubemx_utils/microros_static_library/libmicroros/microros_include

# Add micro-ROS utils
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/custom_memory_manager.c
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_allocators.c
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_time.c
# Add micro-ROS utils
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/custom_memory_manager.c
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_allocators.c
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_time.c

# Set here the custom transport implementation
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_transports/dma_transport.c
# Set here the custom transport implementation
C_SOURCES += micro_ros_stm32cubemx_utils/extra_sources/microros_transports/dma_transport.c

print_cflags:
@echo $(CFLAGS)
```
print_cflags:
@echo $(CFLAGS)
```

6. Execute the static library generation tool. Compiler flags will retrieved automatically from your `Makefile` and user will be prompted to check if they are correct.


```bash
docker pull microros/micro_ros_static_library_builder:humble
docker run -it --rm -v $(pwd):/project --env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library microros/micro_ros_static_library_builder:humble
```
```bash
docker pull microros/micro_ros_static_library_builder:humble
docker run -it --rm -v $(pwd):/project --env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library microros/micro_ros_static_library_builder:humble
```

1. Modify your `main.c` to use micro-ROS. An example application can be found in `sample_main.c`.
2. Continue your usual workflow building your project and flashing the binary:

```bash
make -j$(nproc)
```
```bash
make -j$(nproc)
```

## Using this package with STM32CubeIDE

micro-ROS can be used with SMT32CubeIDE following these steps:

1. Clone this repository in your STM32CubeIDE project folder
2. Go to `Project -> Settings -> C/C++ Build -> Settings -> Build Steps Tab` and in `Pre-build steps` add:

```bash
docker pull microros/micro_ros_static_library_builder:humble && docker run --rm -v ${workspace_loc:/${ProjName}}:/project --env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library_ide microros/micro_ros_static_library_builder:humble
```
```bash
docker pull microros/micro_ros_static_library_builder:humble && docker run --rm -v ${workspace_loc:/${ProjName}}:/project --env MICROROS_LIBRARY_FOLDER=micro_ros_stm32cubemx_utils/microros_static_library_ide microros/micro_ros_static_library_builder:humble
```

3. Add micro-ROS include directory. In `Project -> Settings -> C/C++ Build -> Settings -> Tool Settings Tab -> MCU GCC Compiler -> Include paths` add `micro_ros_stm32cubemx_utils/microros_static_library_ide/libmicroros/include`
4. Add the micro-ROS precompiled library. In `Project -> Settings -> C/C++ Build -> Settings -> MCU GCC Linker -> Libraries`
Expand Down Expand Up @@ -109,6 +111,14 @@ Steps to configure:
- Enable U(S)ART in your STM32CubeMX
- For the selected USART, enable `global interrupt` under `NVIC Settings`: [Detail](.images/Set_UART_IT.jpg)

### USB CDC

Steps to configure:
- Enable the USB in your STM32CubeMX `Connectivity` tab.
- Select the `Communication Device Class (Virtual Port Com)` mode on the `Middleware -> USB_DEVICE` configuration.

**Note: The micro-ROS transport will override the autogenerated `USB_DEVICE/App/usbd_cdc_if.c` methods.**

## Customizing the micro-ROS library

All the micro-ROS configuration can be done in `colcon.meta` file before step 3. You can find detailed information about how to tune the static memory usage of the library in the [Middleware Configuration tutorial](https://micro.ros.org/docs/tutorials/advanced/microxrcedds_rmw_configuration/).
Expand Down
163 changes: 163 additions & 0 deletions extra_sources/microros_transports/usb_cdc_transport.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include <rmw_microros/rmw_microros.h>

#include "main.h"
#include "cmsis_os.h"
#include "usbd_cdc_if.h"
#include "usbd_cdc.h"

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#ifdef RMW_UXRCE_TRANSPORT_CUSTOM

// --- USB CDC Handles ---
extern USBD_CDC_ItfTypeDef USBD_Interface_fops_FS;
extern USBD_HandleTypeDef hUsbDeviceFS;

// --- Reimplemented USB CDC callbacks ---
static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum);
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length);
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len);

// Line coding: Rate: 115200bps; CharFormat: 1 Stop bit; Parity: None; Data: 8 bits
static uint8_t line_coding[7] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};

// --- micro-ROS Transports ---
#define USB_BUFFER_SIZE 2048
#define WRITE_TIMEOUT_MS 100U

volatile uint8_t storage_buffer[USB_BUFFER_SIZE] = {0};
volatile size_t it_head = 0;
volatile size_t it_tail = 0;
volatile bool g_write_complete = false;
bool initialized = false;

// Transmission completed callback
static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum)
{
(void) Buf;
(void) Len;
(void) epnum;

g_write_complete = true;
return USBD_OK;
}

// USB CDC requests callback
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
switch(cmd)
{
case CDC_SET_LINE_CODING:
memcpy(line_coding, pbuf, sizeof(line_coding));
break;

case CDC_GET_LINE_CODING:
memcpy(pbuf, line_coding, sizeof(line_coding));
break;

case CDC_SEND_ENCAPSULATED_COMMAND:
case CDC_GET_ENCAPSULATED_RESPONSE:
case CDC_SET_COMM_FEATURE:
case CDC_GET_COMM_FEATURE:
case CDC_CLEAR_COMM_FEATURE:
case CDC_SET_CONTROL_LINE_STATE:
case CDC_SEND_BREAK:
default:
break;
}

return USBD_OK;
}

// Data received callback
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);

// Circular buffer
if ((it_tail + *Len) > USB_BUFFER_SIZE)
{
size_t first_section = USB_BUFFER_SIZE - it_tail;
size_t second_section = *Len - first_section;

memcpy((void*) &storage_buffer[it_tail] , Buf, first_section);
memcpy((void*) &storage_buffer[0] , Buf, second_section);
it_tail = second_section;
}
else
{
memcpy((void*) &storage_buffer[it_tail] , Buf, *Len);
it_tail += *Len;
}

USBD_CDC_ReceivePacket(&hUsbDeviceFS);

return (USBD_OK);
}

bool cubemx_transport_open(struct uxrCustomTransport * transport){

if (!initialized)
{
// USB is initialized on generated main code: Replace default callbacks here
USBD_Interface_fops_FS.Control = CDC_Control_FS;
USBD_Interface_fops_FS.Receive = CDC_Receive_FS;
USBD_Interface_fops_FS.TransmitCplt = CDC_TransmitCplt_FS;
initialized = true;
}

return true;
}

bool cubemx_transport_close(struct uxrCustomTransport * transport){
return true;
}

size_t cubemx_transport_write(struct uxrCustomTransport* transport, uint8_t * buf, size_t len, uint8_t * err){
uint8_t ret = CDC_Transmit_FS(buf, len);

if (USBD_OK != ret)
{
return 0;
}

int64_t start = uxr_millis();
while(!g_write_complete && (uxr_millis() - start) < WRITE_TIMEOUT_MS)
{
vTaskDelay( 1 / portTICK_PERIOD_MS);
}

size_t writed = g_write_complete ? len : 0;
g_write_complete = false;

return writed;
}

size_t cubemx_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err){

int64_t start = uxr_millis();
size_t readed = 0;

do
{
if (it_head != it_tail)
{
while ((it_head != it_tail) && (readed < len)){
buf[readed] = storage_buffer[it_head];
it_head = (it_head + 1) % USB_BUFFER_SIZE;
readed++;
}

break;
}

vTaskDelay( 1 / portTICK_PERIOD_MS );
} while ((uxr_millis() - start) < timeout);

return readed;
}

#endif