RTThread与CubeMX – (4)整合RTThread与CubeMX

2017-10-09

CubeMX, rtthread

RTThread与CubeMX – (4)整合RTThread与CubeMX已关闭评论


CubeMX生成的代码结构如下

CubeMX在生成代码的时候可以选择是否要把库复制到工程所在目录,为了减少外部依赖,我选择把库复制到工程中,这样在生成的工程目录中就包含了库相关的文件。

CubeMX的源代码分为了inc,src和drivers。为了让rtthread的编译工具编译这些文件,需要把src和drivers中的文件需要加入到scons脚本中,而inc只需要把路径加入到scons脚本中。根据前面了解到的sconscript规则,写出src和drivers的规则,并把相关的路径添加在sconscript中。drivers下面有cmsis的system文件和启动文件。在前面的rtt工程中,scons是把cmsis目录下的这两个文件加入了工程中。而CubeMX会根据工程配置情况,把这个system文件生成在src目录,我选择的工具是mdk,启动文件则生成在MDK-ARM目录中。在sconscript中加入CubeMX生成的system文件和启动文件,对于cmsis目录中的文件就不添加了,但是cmsis的路径需要添加到工程中。不同的源代码添加到不同的group中。这样在后面生成ide工程文件的时候也会将他们放在不同的group中。

# RT-Thread building script for component

Import('rtconfig')
from building import *

cwd = GetCurrentDir()
src = Split('''
Device/ST/STM32F7xx/Source/Templates/system_stm32f7xx.c
''')
CPPPATH = [cwd + '/Device/ST/STM32F7xx/Include', cwd + '/Include']
CPPDEFINES = [rtconfig.STM32_TYPE]

# add for startup script 
if rtconfig.CROSS_TOOL == 'gcc':
  src += ['Device/ST/STM32F7xx/Source/Templates/gcc/startup_stm32f746xx.s']
elif rtconfig.CROSS_TOOL == 'keil':
  src += ['Device/ST/STM32F7xx/Source/Templates/arm/startup_stm32f746xx.s']
elif rtconfig.CROSS_TOOL == 'iar':
  src += ['Device/ST/STM32F7xx/Source/Templates/iar/startup_stm32f746xx.s']

group = DefineGroup('CMSIS', [], depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES)

Return('group')

上面是drivers/cmsis目录的scons规则,这里只添加了头文件路径信息,没有添加源代码,源代码会在MDK-ARM目录的规则中添加。

import rtconfig
Import('RTT_ROOT')
from building import *

# get current directory
cwd = GetCurrentDir()

# The set of source files associated with this SConscript file.

srcx = Glob('Src/*.c')
src = []
for t in srcx:
    if t.name.find('template') == -1:
        src.append(t)
path = [cwd + '/Inc']

#CPPDEFINES = ['USE_HAL_DRIVER', rtconfig.STM32_TYPE]
CPPDEFINES = ['USE_HAL_DRIVER']
group = DefineGroup('STM32F7xx_HAL_Driver', src, depend = [''], CPPPATH = path, CPPDEFINES = CPPDEFINES)

Return('group')

上面是drivers/stm32f7xx_hal_driver目录下的scons规则。添加了c文件和头文件路径,hal库中有一些外设有template文件,这里把template文件去掉了。这里还可以根据stm32 config文件中的宏定义来确定哪些文件需要添加到编译环境中,减少工程中的文件数目。

CubeMX的初始化工作是在main中完成的,而rtt的初始化工作是在hw_init中做的,main只是一个普通的线程。简单点的做法就是把main里面的初始化代码复制到board中去,或者是在main文件中实现init函数。

添加CubeMX版的串口设备,CubeMX的串口初始化由自动生成的代码完成,之前的发送和接收都用的是中断方式。CubeMX的中断方式接收比较奇葩,需要接收指定长度的数据才算完成。而rtthread的中断接收方式是当到来一个字节后,把它放入serial模块管理的buffer中。如果一定要用CubeMX的代码来接收,那就得每次都接收一个字节,这个代价太大,我这里选择接管HAL库的UART中断处理代码,用rtt的方式来接收和发送。UART基本的配置还是沿用CubeMX生成的代码。至此一个基本的rtt程序就算完成了,通过串口可以操作finsh,进行一些基本的命令操作。

void USART3_IRQHandler(void)
{
    struct stm32_uart *uart;

    uart = &uart1;

    /* enter interrupt */
    rt_interrupt_enter();

    /* UART in mode Receiver ---------------------------------------------------*/
    if ((__HAL_UART_GET_IT(&uart->UartHandle, UART_IT_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(&uart->UartHandle, UART_IT_RXNE) != RESET))
    {
        rt_hw_serial_isr(&serial1, RT_SERIAL_EVENT_RX_IND);
        /* Clear RXNE interrupt flag */
        __HAL_UART_SEND_REQ(&uart->UartHandle, UART_RXDATA_FLUSH_REQUEST);
    }
    /* leave interrupt */
    rt_interrupt_leave();
}

static int stm32_putc(struct rt_serial_device *serial, char c)
{
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;

    while (!(uart->UartHandle.Instance->ISR & UART_FLAG_TXE));
    uart->UartHandle.Instance->TDR = c;

    return 1;
}

static int stm32_getc(struct rt_serial_device *serial)
{
    int ch;
    struct stm32_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;

    ch = -1;
    if (uart->UartHandle.Instance->ISR & UART_FLAG_RXNE)
    {
        ch = uart->UartHandle.Instance->RDR & 0xff;
    }

    return ch;
}

 

上面的代码用rtthread的串口中断处理机制接管了hal库的中断处理机制,采用中断方式进行串口接收。发送采用的是阻塞方式。

有了串口之后finsh功能就可以用起来了,方便后续的开发调试工作。在这个框架的基础之上,可以添加更多硬件功能和rtt组件。