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 serial interrupt transport #2

Merged
merged 4 commits into from
Mar 25, 2021
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
Binary file added .images/Set_UART_DMA1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .images/Set_UART_DMA_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .images/Set_UART_IT.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .images/Set_freertos_stack.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 44 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ This tool aims to ease the micro-ROS integration in a STM32CubeMX project.

1. In the `root` folder, generate your STM32CubeMX project. A sample project can be generated with the provided `sample_project.ioc`.
2. Make sure that your STM32CubeMX project is using a `Makefile` toolchain under `Project Manager -> Project`
3. Make sure that if you are using FreeRTOS, the micro-ROS task has more than 10 kB of stack.
4. To use the default UART transport based on DMA:
- Enable USART in your STM32CubeMX
- For the selected USART, enable DMA for Tx and Rx under `DMA Settings`
- Set the DMA priotity to `Very High` for Tx and Rx
- Set the DMA mode to `Circular` for Rx
- For the selected, enable `global interrupt` under `NVIC Settings`
5. Modify the generated `Makefile` to include the following code before the `build the application` section:
3. Make sure that if you are using FreeRTOS, the micro-ROS task **has more than 10 kB of stack**: [Detail](.images/Set_freertos_stack.jpg)
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)) -->

```makefile
#######################################
Expand All @@ -22,21 +21,19 @@ This tool aims to ease the micro-ROS integration in a STM32CubeMX project.
LDFLAGS += microros_component/libmicroros.a
C_INCLUDES += -Imicroros_component/microros_include

# 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))

# Add micro-ROS utils
C_SOURCES += microros_component/custom_memory_manager.c
C_SOURCES += microros_component/microros_allocators.c
C_SOURCES += microros_component/microros_transports.c
C_SOURCES += microros_component/microros_time.c

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

print_cflags:
@echo $(CFLAGS)
```

3. Go to `microros_component` and 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.
6. Go to `microros_component` and 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.

<!--
pushd microros_component
Expand All @@ -51,21 +48,51 @@ docker run -it --rm -v $(pwd)/../:/microros_library microros/micro_ros_cubemx_bu
cd ..
```

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

```bash
make -j$(nproc)
```
## Transport configuration

Available transport for this platform are:
### U(S)ART with DMA

Steps to configure:
- Enable U(S)ART in your STM32CubeMX
- For the selected USART, enable DMA for Tx and Rx under `DMA Settings`
- Set the DMA priotity to `Very High` for Tx and Rx
- Set the DMA mode to `Circular` for Rx: [Detail](.images/Set_UART_DMA1.jpg)
- For the selected, enable `global interrupt` under `NVIC Settings`: [Detail](.images/Set_UART_DMA_2.jpg)

### U(S)ART with Interrupts

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)
## 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/core/microxrcedds_rmw_configuration/).

## Adding custom packages

Note that folders added to `microros_component/extra_packages` and entries added to `microros_component/extra_packages/extra_packages.repos` will be taken into account by this build system.

## Using this package with STM32CubeIDE

micro-ROS precompiled library can be used in an SMT32CubeIDE but SMT32CubeIMX should be used for generating it.
Once you have followed the steps in this first section:

1. Add micro-ROS include directory. In `Project -> Settings -> C/C++ Build -> Settings -> MCU GCC Compiler -> Include paths` add `microros_component/include`
2. Add the micro-ROS precompiled library. In `Project -> Settings -> C/C++ Build -> Settings -> MCU GCC Linker -> Libraries`
- add `microros_component` in `Library search path (-L)`
- add `microros` in `Libraries (-l)`
3. Add the following source code files to your project:
- `microros_component/microros_time.c`
- `microros_component/microros_allocators.c`
- `microros_component/microros_custom_memory_manager.c`
- `microros_component/microros_transports/dma_transport.c` or your transport selection.
4. Build and run your project

## Purpose of the Project

Expand Down
32 changes: 13 additions & 19 deletions microros_component/custom_memory_manager.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* A custom implementation of pvPortMalloc() and vPortFree() with realloc and
* A custom implementation of pvPortMallocMicroROS() and vPortFreeMicroROS() with realloc and
* calloc features based on FreeRTOS heap4.c.
*/

Expand All @@ -26,13 +26,7 @@ task.h is included from an application file. */
#define heapBITS_PER_BYTE ( ( size_t ) 8 )

/* Allocate the memory for the heap. */
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
/* The application writer has already defined the array used for the RTOS
heap - probably so it can be placed in a special segment or address. */
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

/* Define the linked list structure. This is used to link free blocks in order
of their memory address. */
Expand All @@ -54,7 +48,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );

/*
* Called automatically to setup the required heap structures the first time
* pvPortMalloc() is called.
* pvPortMallocMicroROS() is called.
*/
static void prvHeapInit( void );

Expand All @@ -80,7 +74,7 @@ static size_t xBlockAllocatedBit = 0;

/*-----------------------------------------------------------*/

void *pvPortMalloc( size_t xWantedSize )
void *pvPortMallocMicroROS( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
Expand Down Expand Up @@ -230,7 +224,7 @@ void *pvReturn = NULL;
}
/*-----------------------------------------------------------*/

void vPortFree( void *pv )
void vPortFreeMicroROS( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
Expand Down Expand Up @@ -294,11 +288,11 @@ size_t getBlockSize( void *pv )
}
/*-----------------------------------------------------------*/

void *pvPortRealloc( void *pv, size_t xWantedSize )
void *pvPortReallocMicroROS( void *pv, size_t xWantedSize )
{
vTaskSuspendAll();

void * newmem = pvPortMalloc(xWantedSize);
void * newmem = pvPortMallocMicroROS(xWantedSize);

uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
Expand All @@ -314,20 +308,20 @@ void *pvPortRealloc( void *pv, size_t xWantedSize )
while(count--)
*in_dest++ = *in_src++;

vPortFree(pv);
vPortFreeMicroROS(pv);

( void ) xTaskResumeAll();

return newmem;
}
/*-----------------------------------------------------------*/

void *pvPortCalloc( size_t num, size_t xWantedSize )
void *pvPortCallocMicroROS( size_t num, size_t xWantedSize )
{
vTaskSuspendAll();
size_t count = xWantedSize*num;

void * mem = pvPortMalloc(count);
void * mem = pvPortMallocMicroROS(count);
char *in_dest = (char*)mem;

while(count--)
Expand All @@ -338,19 +332,19 @@ void *pvPortCalloc( size_t num, size_t xWantedSize )
}
/*-----------------------------------------------------------*/

size_t xPortGetFreeHeapSize( void )
size_t xPortGetFreeHeapSizeMicroROS( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

size_t xPortGetMinimumEverFreeHeapSize( void )
size_t xPortGetMinimumEverFreeHeapSizeMicroROS( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/

void vPortInitialiseBlocks( void )
void vPortInitialiseBlocksMicroROS( void )
{
/* This just exists to keep the linker quiet. */
}
Expand Down
14 changes: 7 additions & 7 deletions microros_component/microros_allocators.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@
int absoluteUsedMemory = 0;
int usedMemory = 0;

void *pvPortRealloc( void *pv, size_t xWantedSize );
void *pvPortReallocMicroROS( void *pv, size_t xWantedSize );
size_t getBlockSize( void *pv );
void *pvPortCalloc( size_t num, size_t xWantedSize );
void *pvPortCallocMicroROS( size_t num, size_t xWantedSize );

void * microros_allocate(size_t size, void * state){
(void) state;
// printf("-- Alloc %d (prev: %d B)\n",size, xPortGetFreeHeapSize());
absoluteUsedMemory += size;
usedMemory += size;
return pvPortMalloc(size);
return pvPortMallocMicroROS(size);
}

void microros_deallocate(void * pointer, void * state){
(void) state;
// printf("-- Free %d (prev: %d B)\n",getBlockSize(pointer), xPortGetFreeHeapSize());
if (NULL != pointer){
usedMemory -= getBlockSize(pointer);
vPortFree(pointer);
vPortFreeMicroROS(pointer);
}
}

Expand All @@ -32,10 +32,10 @@ void * microros_reallocate(void * pointer, size_t size, void * state){
absoluteUsedMemory += size;
usedMemory += size;
if (NULL == pointer){
return pvPortMalloc(size);
return pvPortMallocMicroROS(size);
} else {
usedMemory -= getBlockSize(pointer);
return pvPortRealloc(pointer,size);
return pvPortReallocMicroROS(pointer,size);
}
}

Expand All @@ -44,5 +44,5 @@ void * microros_zero_allocate(size_t number_of_elements, size_t size_of_element,
// printf("-- Calloc %d x %d = %d -> (prev: %d B)\n",number_of_elements,size_of_element, number_of_elements*size_of_element, xPortGetFreeHeapSize());
absoluteUsedMemory += number_of_elements*size_of_element;
usedMemory += number_of_elements*size_of_element;
return pvPortCalloc(number_of_elements,size_of_element);
return pvPortCallocMicroROS(number_of_elements,size_of_element);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@
static uint8_t dma_buffer[UART_DMA_BUFFER_SIZE];
static size_t dma_head = 0, dma_tail = 0;

bool freertos_serial_open(struct uxrCustomTransport * transport){
bool cubemx_transport_open(struct uxrCustomTransport * transport){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;
HAL_UART_Receive_DMA(uart, dma_buffer, UART_DMA_BUFFER_SIZE);
return true;
}

bool freertos_serial_close(struct uxrCustomTransport * transport){
bool cubemx_transport_close(struct uxrCustomTransport * transport){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;
HAL_UART_DMAStop(uart);
return true;
}

size_t freertos_serial_write(struct uxrCustomTransport* transport, uint8_t * buf, size_t len, uint8_t * err){
size_t cubemx_transport_write(struct uxrCustomTransport* transport, uint8_t * buf, size_t len, uint8_t * err){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;

HAL_StatusTypeDef ret;
Expand All @@ -46,7 +46,7 @@ size_t freertos_serial_write(struct uxrCustomTransport* transport, uint8_t * buf
}
}

size_t freertos_serial_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err){
size_t cubemx_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;

int ms_used = 0;
Expand Down
73 changes: 73 additions & 0 deletions microros_component/microros_transports/it_transport.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <uxr/client/transport.h>

#include <rmw_microxrcedds_c/config.h>

#include "main.h"
#include "cmsis_os.h"

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

#ifdef RMW_UXRCE_TRANSPORT_CUSTOM

// --- micro-ROS Transports ---
#define UART_IT_BUFFER_SIZE 2048

static uint8_t it_buffer[UART_IT_BUFFER_SIZE];
static uint8_t it_data;
static size_t it_head = 0, it_tail = 0;


bool cubemx_transport_open(struct uxrCustomTransport * transport){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;
HAL_UART_Receive_IT(uart, &it_data, 1);
return true;
}

bool cubemx_transport_close(struct uxrCustomTransport * transport){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;
HAL_UART_Abort_IT(uart);
return true;
}

size_t cubemx_transport_write(struct uxrCustomTransport* transport, uint8_t * buf, size_t len, uint8_t * err){
UART_HandleTypeDef * uart = (UART_HandleTypeDef*) transport->args;

HAL_StatusTypeDef ret;
if (uart->gState == HAL_UART_STATE_READY){
ret = HAL_UART_Transmit_IT(uart, buf, len);
while (ret == HAL_OK && uart->gState != HAL_UART_STATE_READY){
osDelay(1);
}

return (ret == HAL_OK) ? len : 0;
}else{
return 0;
}
}

size_t cubemx_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err){
size_t wrote = 0;
while ((it_head != it_tail) && (wrote < len)){
buf[wrote] = it_buffer[it_head];
it_head = (it_head + 1) % UART_IT_BUFFER_SIZE;
wrote++;
}

return wrote;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(it_tail == UART_IT_BUFFER_SIZE)
it_tail = 0;

it_buffer[it_tail] = it_data;
it_tail++;

HAL_UART_Receive_IT(huart, &it_data, 1);
}

#endif //RMW_UXRCE_TRANSPORT_CUSTOM
16 changes: 8 additions & 8 deletions sample_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,10 @@ static void MX_GPIO_Init(void)
}

/* USER CODE BEGIN 4 */
bool freertos_serial_open(struct uxrCustomTransport * transport);
bool freertos_serial_close(struct uxrCustomTransport * transport);
size_t freertos_serial_write(struct uxrCustomTransport* transport, const uint8_t * buf, size_t len, uint8_t * err);
size_t freertos_serial_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err);
bool cubemx_transport_open(struct uxrCustomTransport * transport);
bool cubemx_transport_close(struct uxrCustomTransport * transport);
size_t cubemx_transport_write(struct uxrCustomTransport* transport, const uint8_t * buf, size_t len, uint8_t * err);
size_t cubemx_transport_read(struct uxrCustomTransport* transport, uint8_t* buf, size_t len, int timeout, uint8_t* err);

void * microros_allocate(size_t size, void * state);
void microros_deallocate(void * pointer, void * state);
Expand All @@ -381,10 +381,10 @@ void StartDefaultTask(void *argument)
rmw_uros_set_custom_transport(
true,
(void *) &huart3,
freertos_serial_open,
freertos_serial_close,
freertos_serial_write,
freertos_serial_read);
cubemx_transport_open,
cubemx_transport_close,
cubemx_transport_write,
cubemx_transport_read);

rcl_allocator_t freeRTOS_allocator = rcutils_get_zero_initialized_allocator();
freeRTOS_allocator.allocate = microros_allocate;
Expand Down