How to add Console and FinSH on RT-Thread Nano?

Created at 2020-12-10 17:43:42

This document is divided into two parts: the first part is introducing the implementation of the UART console, which requires only two functions to complete the UART console printing function. The second part is the implementation of porting FinSH components, to implement the input command debugging system in the console, and its implementation is based on the UART console implementation of the first part, only need to add FinSH component source code and docking a system function.

Add UART Console on RT-Thread Nano

By adding the UART console printing feature to the RT-Thread Nano, you can print information in your code using the print function rt_kprintf() provided by RT-Thread to get custom print information, easy to locate code bugs and get to know the system operating status, and more. Implementing console printing (which requires to make sure that the RT_USING_CONSOLE macro definition has been enabled in rtconfig.h), the basic required hardware initialization and the functions for docking a system output character will be introduced in this article.

Implement Serial Port Initialization

Using a serial port to dock the printer of the console, you'll first need to initialize the serial ports, such as pins, baud rates, and so on. uart_init () needs to be call in the rt_hw_board_init() function in board.c.

/* Implementation 1: Initialize the serial port */
static int uart_init(void);

Sample Code: The following is the sample code of a HAL library-based STM32F103 serial driver that completes of adding the console:

static UART_HandleTypeDef UartHandle;
static int uart_init(void)
{
    /* Initialize serial parameters such as baud rate, stop bit, and so on */
    UartHandle.Instance = USART1;
    UartHandle.Init.BaudRate   = 115200;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;

    /* Initialize serial pins, etc */
    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while(1);
    }

    return 0;
}
INIT_BOARD_EXPORT(uart_init);
/* board.c */
void rt_hw_board_init(void)
{
    ....
    uart_init();             // Call serial port initialization function in rt_hw_board_init 
    ....
}

Implement rt_hw_console_output

Implementing the finsh component outputs a character, that is output the uart character in this function:

/* Implementation 2: Output a character, system function, function name can not be changed */
void rt_hw_console_output(const char *str);

Note

Prints already in the RT-Thread system all end with \n , not \r\n, so when the character is output, you need to output \r before output \n , complete the Enter and Newline, otherwise the information printed by the system will only be Newline.

Example code: The following is an rt_hw_console_output() function based on the STM32F103 HAL serial drive docking, implementing console character output.

void rt_hw_console_output(const char *str)
{
    rt_size_t i = 0, size = 0;
    char a = '\r';

    __HAL_UNLOCK(&UartHandle);

    size = rt_strlen(str);
    for (i = 0; i < size; i++)
    {
        if (*(str + i) == '\n')
        {
            HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
        }
        HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
    }
}

Result

Write code in your app code that includes rt_kprintf () prints, compile and download, and open the serial assistant for validation. The following image is an example effect of looping Hello RT-Thread every 1 second in the main() function:

示例

Add FinSH on RT-Thread Nano

RT-Thread FinSH is RT-Thread command-line component (shell) that provides a set of operational interfaces for users to invoke on the command line, primarily for debugging or viewing system information. It can communicate with a PC using serial ports/ Ethernet / USB, etc., and the effect of using the FinSH component basic commands is shown below:

效果图

In this article, we communicate with the PC by using serial UART as an input and output port for FinSH, describing how to implement FinSH shell functionality on RT-Thread Nano.

To add a FinSH component to the RT-Thread Nano, the steps to implement FinSH functionality are as follows:

  1. Add FinSH source code to the project.
  2. Implement function docking.

Add FinSH source code to project

KEIL adds FinSH source code

Click Manage Run-Environment:

Manage Run-Environment

Check in shell,which automatically add sources code of FinSH components to the project:

勾选 shell

Cube MX adds FinSH source code

Open a cube project, click Additional Software, check RealThread in Pack Vendor to quickly locate the RT-Thread package, and then tick the shell in the RT-Thread package to add the source code of the FinSH component to the project.

Cube MX 添加 FinSH 源码

Other IDEs add FinSH source code

Other IDEs add FinSH source code, which requires manual addition of FinSH source code and header file paths into the project, as described in the case of IAR IDE.

  1. Copy FinSH source code to the target bare metal project: directly copy the Nano source code to the finsh folder under the rtthread-nano/components folder , as shown in the figure:复制 finsh 源码
  2. Add FinSH source code to the target project:
  • Open the project, create a finsh group and add all the . c File under the finsh folders to the project, as shown below;
  • Add the header file path of the finsh folder (click Project -> Options... , enter the prompt box, as shown below);
  • Add a macro definition #define RT_USING_FINSH in rtconfig.h so that FinSH takes effect, as shown below.

添加 finsh 源码

添加 finsh 头文件路径

添加 finsh 所需宏定义

Implement rt_hw_console_getchar

To implement FinSH component functionality: you can print as well as input commands for debugging, the console has already implemented printing, and now you need to dock the console input function in board.c for character input:

/* Implementation 3:FinSH gets a character, a system function, and the function name cannot be changed */
char rt_hw_console_getchar(void);
  • rt_hw_console_getchar(): The console gets a character, that is, uart gets characters in the function, which can be obtained using a query (note that you'll need to give up the CPU if the character is not acquired), or you can get it by interrupt.

Example code: The following is an function of rt_hw_console_getchar() based on the STM32F103 HAL serial-driven docking, complete docking the FinSH component, where the characters are queried.

char rt_hw_console_getchar(void)
{
    int ch = -1;

    if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE) != RESET)
    {
        ch = UartHandle.Instance->DR & 0xff;
    }
    else
    {
        if(__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_ORE) != RESET)
        {
            __HAL_UART_CLEAR_OREFLAG(&UartHandle);
        }
        rt_thread_mdelay(10);
    }
    return ch;
}

Result Validation

Compile and download the code, open the serial assistant, you can print and input help command in the serial assistant, enter to see the commands supported by the system:

下载验证 finsh

If it does not run successfully, check that if the docking function is implemented correctly.

Porting Sample Code

Interrupt Sample

The following is based on the STM32F103 HAL serial driver, which implements console output with FinSH Shell, where the acquired characters are interrupted. The principle is that there is an interrupt when uart receives the data, deposits the data in the ringbuffer buffer during the interrupt, then releases the semader, the tshell thread receives the semader, and then reads the data that exists in the ringbuffer.

/* Part 1: ringbuffer implementation part */
#include <rtthread.h>
#include <string.h>

#define rt_ringbuffer_space_len(rb) ((rb)->buffer_size - rt_ringbuffer_data_len(rb))

struct rt_ringbuffer
{
    rt_uint8_t *buffer_ptr;

    rt_uint16_t read_mirror : 1;
    rt_uint16_t read_index : 15;
    rt_uint16_t write_mirror : 1;
    rt_uint16_t write_index : 15;

    rt_int16_t buffer_size;
};

enum rt_ringbuffer_state
{
    RT_RINGBUFFER_EMPTY,
    RT_RINGBUFFER_FULL,
    /* half full is neither full nor empty */
    RT_RINGBUFFER_HALFFULL,
};

rt_inline enum rt_ringbuffer_state rt_ringbuffer_status(struct rt_ringbuffer *rb)
{
    if (rb->read_index == rb->write_index)
    {
        if (rb->read_mirror == rb->write_mirror)
            return RT_RINGBUFFER_EMPTY;
        else
            return RT_RINGBUFFER_FULL;
    }
    return RT_RINGBUFFER_HALFFULL;
}

/** 
 * get the size of data in rb 
 */
rt_size_t rt_ringbuffer_data_len(struct rt_ringbuffer *rb)
{
    switch (rt_ringbuffer_status(rb))
    {
    case RT_RINGBUFFER_EMPTY:
        return 0;
    case RT_RINGBUFFER_FULL:
        return rb->buffer_size;
    case RT_RINGBUFFER_HALFFULL:
    default:
        if (rb->write_index > rb->read_index)
            return rb->write_index - rb->read_index;
        else
            return rb->buffer_size - (rb->read_index - rb->write_index);
    };
}

void rt_ringbuffer_init(struct rt_ringbuffer *rb,
                        rt_uint8_t           *pool,
                        rt_int16_t            size)
{
    RT_ASSERT(rb != RT_NULL);
    RT_ASSERT(size > 0);

    /* initialize read and write index */
    rb->read_mirror = rb->read_index = 0;
    rb->write_mirror = rb->write_index = 0;

    /* set buffer pool and size */
    rb->buffer_ptr = pool;
    rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
}

/**
 * put a character into ring buffer
 */
rt_size_t rt_ringbuffer_putchar(struct rt_ringbuffer *rb, const rt_uint8_t ch)
{
    RT_ASSERT(rb != RT_NULL);

    /* whether has enough space */
    if (!rt_ringbuffer_space_len(rb))
        return 0;

    rb->buffer_ptr[rb->write_index] = ch;

    /* flip mirror */
    if (rb->write_index == rb->buffer_size-1)
    {
        rb->write_mirror = ~rb->write_mirror;
        rb->write_index = 0;
    }
    else
    {
        rb->write_index++;
    }

    return 1;
}
/**
 * get a character from a ringbuffer
 */
rt_size_t rt_ringbuffer_getchar(struct rt_ringbuffer *rb, rt_uint8_t *ch)
{
    RT_ASSERT(rb != RT_NULL);

    /* ringbuffer is empty */
    if (!rt_ringbuffer_data_len(rb))
        return 0;

    /* put character */
    *ch = rb->buffer_ptr[rb->read_index];

    if (rb->read_index == rb->buffer_size-1)
    {
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = 0;
    }
    else
    {
        rb->read_index++;
    }

    return 1;
}


/* Part 2:finsh porting*/
#define UART_RX_BUF_LEN 16
rt_uint8_t uart_rx_buf[UART_RX_BUF_LEN] = {0};
struct rt_ringbuffer  uart_rxcb;         /* Define ringbuffer cb */
static UART_HandleTypeDef UartHandle;
static struct rt_semaphore shell_rx_sem; /* Define a static senum */

/* Initialize the serial port, interrupt mode */
static int uart_init(void)
{
    /* Initialize the serial port to receive ringbuffer  */
    rt_ringbuffer_init(&uart_rxcb, uart_rx_buf, UART_RX_BUF_LEN);

    /* Initialize the semaphore of the serial port receives data */
    rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0);

    /* Initialize serial parameters such as baud rate, stop bit, and so on */
    UartHandle.Instance = USART2;
    UartHandle.Init.BaudRate   = 115200;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;

    /* Initialize serial pins, etc */
    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while (1);
    }

    /* Interrupt Configuration */
    __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_RXNE);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
    HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);

    return 0;
}
INIT_BOARD_EXPORT(uart_init);

/* Port the console, implement the console output, docking rt_hw_console_output */
void rt_hw_console_output(const char *str)
{
    rt_size_t i = 0, size = 0;
    char a = '\r';

    __HAL_UNLOCK(&UartHandle);

    size = rt_strlen(str);
    for (i = 0; i < size; i++)
    {
        if (*(str + i) == '\n')
        {
            HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
        }
        HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
    }
}

/* Port FinSH for command-line interaction, add FinSH source code, then dock rt_hw_console_getchar */
/* Interrupt mode */
char rt_hw_console_getchar(void)
{
    char ch = 0;

    /* Take the data out of ringbuffer */
    while (rt_ringbuffer_getchar(&uart_rxcb, (rt_uint8_t *)&ch) != 1)
    {
        rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER);
    } 
    return ch;   
}

/* uart interrupt */
void USART2_IRQHandler(void)
{
    int ch = -1;
    rt_base_t level;
    /* enter interrupt */
    rt_interrupt_enter();          //Be sure to call this pair of functions in the interrupt and enter the interrupt

    if ((__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET) &&
        (__HAL_UART_GET_IT_SOURCE(&(UartHandle), UART_IT_RXNE) != RESET))
    {
        while (1)
        {
            ch = -1;
            if (__HAL_UART_GET_FLAG(&(UartHandle), UART_FLAG_RXNE) != RESET)
            {
                ch =  UartHandle.Instance->DR & 0xff;
            }
            if (ch == -1)
            {
                break;
            }  
            /* Read the data and store it in ringbuffer */
            rt_ringbuffer_putchar(&uart_rxcb, ch);
        }        
        rt_sem_release(&shell_rx_sem);
    }

    /* leave interrupt */
    rt_interrupt_leave();    //Be sure to call this pair of functions in the interrupt and leave the interrupt
}

#define USART_TX_Pin GPIO_PIN_2
#define USART_RX_Pin GPIO_PIN_3

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (huart->Instance == USART2)
    {
        __HAL_RCC_USART2_CLK_ENABLE();

        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**USART2 GPIO Configuration
        PA2     ------> USART2_TX
        PA3     ------> USART2_RX
        */
        GPIO_InitStruct.Pin = USART_TX_Pin | USART_RX_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
}

更多

Follower
0
Views
946
0 Answer
There is no answer, come and add the answer

Write Your Answer

Log in to publish your answer,Click here to log in.

Create
Post

Share