Tuesday 18 April 2017

First steps with stm32l152. Uart handling.

The purpose of this introductory post is to show how to receive data via Uart from a host machine. Also, I will see how to create a simple FreeRTOS task.


Configuring the board.


First of all, we should generate the code from CubeMx. This is my scheme:
In this tutorial we want to utilize uart2. After code generation we should add required files and compile our executable. In our example we are going to do the following:
1. Send data from our host machine
2. Receive the data from computer on our board and response with: "Received: *received message*".

Problems.


There are several issues in our example. First of all, we can't connect our UART to computer's COM port, because they have different voltage levels.To solve this issue, I will use FT232-FTDI USB-UART converter. Secondly, we should decide how we want to handle interrupts. In this tutorial, I want to use standard HAL routine functions and a freeRTOS task for message sending.

HAL part.



First of all, we should install an interrupt handler for UART reception and nable it in NVIC. NVIC is an interrupt controller in ARM cores. In order to do this, let's paste:

HAL_NVIC_EnableIRQ(38);

into the last HAL_UART_MspInit User code section, where 38 is the number ob USART global interrupt. All the interrupt numbers can be found in file stm32l152xc.h, which is located in Drivers/CMSIS/Device/ST/STM32L1xx/Include. Let's now set a priority for the handler – add the line

HAL_NVIC_SetPriority(USART2_IRQn,15, 0);

to USER CODE MspInit 1 section in function HAL_MspInit(void). If we don't do this, we have no ability to handle the interrupt, because it has a zero priority by default (and it won't be called by scheduler). Now we should install a handler itself, let's put the lines:

void USART2_IRQHandler()
{
    HAL_UART_IRQHandler(&huart2);
}

Into user code section in usart.c file. Doing this, we implement a handler for the UART global interrupt, HAL_UART_IRQHandler(&huart2) – is the default routine for interrupt handling.
At this stage, in order to receive some message we should use the HAL_UART_Receive_IT(...) function, which is intended to use for reception of UART messages with interrupts enabled.

FreeRTOS part.


Now, it is the time to create out first task. Its function is to receive messages from UART and send them back. Our function will do it in the following way:
1. it creates an event group
2. it waits for receiption of two bytes, and send them back if receive them
3. if it doesn't receive anything, it goes to sleep again
The code of such task look like this:

#include "my_task.h"
/* Declare a variable to hold the created event group. */
EventGroupHandle_t xEventGroup;
void myTask( void * pvParameters )
{
    /* The parameter value is expected to be 1 as 1 is passed in the
    pvParameters value in the call to xTaskCreate() below. */
    configASSERT( ( ( uint32_t ) pvParameters ) == 1 );
    /* Attempt to create the event group. */
    xEventGroup = xEventGroupCreate();
    char ch[] = "Before the task\n";
    HAL_UART_Transmit(&huart2,  (uint8_t*)ch, 16, 50);
    /* Was the event group created successfully? */
    if( xEventGroup == NULL )
    {
    /* The event group was not created because there was insufficient
            FreeRTOS heap available. */
    }
    else
    {
            /* The event group was created. */
    }
    char buf[3] = "aa\n";
    EventBits_t uxBits;
    const TickType_t xTicksToWait = 1*3200;//000;
    for( ;; )
    {
        HAL_UART_Receive_IT(&huart2,  (uint8_t*)buf, 2);
        uxBits = xEventGroupWaitBits(
                    xEventGroup,   /* The event group being \
                                      tested. */
                    BIT_0,          /* The bits within the \
                                       event group to wait for. */
                    pdTRUE,        /* BIT_0 should be cleared\
                                      before returning. */
                    pdFALSE,        /* Any bit aise the event */
                    xTicksToWait );/* Wait a maximum of 100ms \
                                      for either bit to be set. */
        if ( (uxBits & BIT_0) == BIT_0)
        {
            char warn[9] = "Received:";
            HAL_UART_Transmit(&huart2, (uint8_t*)warn, 9, 50);
            HAL_UART_Transmit(&huart2, (uint8_t*)buf, 3, 50);
        } else
        {
            char warn[9] = "No income";
            HAL_UART_Transmit(&huart2, (uint8_t*)warn, 9, 50);
        }
    }
}

Of course, the header file should be provided:

#ifndef MY_TASK_H
#define MY_TASK_H
#include "FreeRTOS.h"
#include "task.h"
#include "event_groups.h"
#include "usart.h"

#define BIT_0   ( 1 << 0 )

extern EventGroupHandle_t xEventGroup;
void myTask( void * pvParameters );

#endif // MY_TASK_H
Let's now install the task. For this purpose, in the main file we should add it of a task queue, place this code in a USER CODE section before the scheduler starting:
  
BaseType_t xReturned;
TaskHandle_t xHandle = NULL;
xReturned = xTaskCreate (
            myTask,
            "UartHandler",
            configMINIMAL_STACK_SIZE,
            ( void * ) 1,
            3,
            &xHandle );

Also, we should define the following header files:
#include "FreeRTOS.h"
#include "event_groups.h"
#include "task.h"
#include "../Usr/my_task.h"

Now, we should implement a callback function which is called from UART handler (uart_handler.c):

#include "uart_handle.h"
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    EventBits_t uxBits;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    uxBits = xEventGroupSetBitsFromISR(xEventGroup, BIT_0, \
                                       &xHigherPriorityTaskWoken);
    char ch[] = "In callback\n";
    HAL_UART_Transmit(huart,  (uint8_t*)ch, 12, 50);
}

and this is its header file:

#ifndef UART_HANDLE_H
#define UART_HANDLE_H
#include "../Inc/usart.h"
#include "stm32l1xx_hal_uart.h"
#include "my_task.h"
//User implementation of uart callback on complete receiving
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

#endif // UART_HANDLE_H


In this function, we install the event bits, when this function is called. Thus, we wake up our task and it in its turn read the received bytes and send them to the computer.
Let's see, how it works. In the next example I will try to send paars of bytes.


From the picture we can see that I send "hi", "ho", "ha", "la", "it". But the task responds "hi", "h", "oh", "al", "ai". That happens, because I send not exactly two bytes, I send two bytes and "\n" symbol. Thus, every message in response consists exactly of two bytes.
There are also "No income" messages, these mean that our task woken up several times, and there were no messages to read, so it went to sleep again.

Let's now try to send something bigger, e.g. phrase "Hello, cute board!" and see what will happen.


As you can see, out message was received partly. In the next posts I will try to show what is the reason of this behavior.



No comments:

Post a Comment