这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » [转]基于stm32cubeMX配置生成RT-thread-nano的工程、实现

共2条 1/1 1 跳转至

[转]基于stm32cubeMX配置生成RT-thread-nano的工程、实现shell指令串口控制台

菜鸟
2021-01-01 19:08:09     打赏

    RT-thread近年来频繁出现在嵌入式开发者的视野中,他们的大力推广以及技术支持受到了很多人的广泛支持与关注。本人在去年(还是一个职业小白)也参加了rt的教学实验,体验过env工具的方便以及如何结合stm32cubemx实现快速建立rt工程。不过,rt官方主要打造针对一些较高资源的ic的开发生态环境,比如stm32f407或者stm32f103zet6等高ram和高rom的ic(品牌开发板标配)。但是实际工作中,对于我们公司开发的ic并不需要很大资源的ic去开发,比如物联网项目我们往往是只用stm32f030或者很高资源用的stm32f103RB等,其最大rom保持在128k以下,甚至更低,而ram更加是只需要20k。可是基于rt的开发生态通过env工具建立工程时,当移植完部分组件之后还未开发应用时便几乎超过了20k的ram了。因此往往我们还是拿rtthread的nano精简版本进行开发。原因是其配合stm32cubemx开发太方便了。

硬件:nucleo-g070RB开发板

    该开发板板载了stlink烧录调试器,因此我们只需要准备一个miniusb数据线即可实现电脑pc和开发板的调试对接。又因为其stlinkv2-1版本又支持扩展串口功能,这个开发板实现了stlink拓展串口连接到stm32g070rb主控芯片的uart2串口的引脚。因此我们不仅不需要自备烧录器,也不需要自备ch340等串口硬件工具,只需要直接准备一根micro数据线完成烧录、调试及串口通讯。其他具体资源介绍可参考以下链接:nucleo-g070rb开发板数据手册

    

只需要一根数据线就可以开发了。插到电脑上如下图:

                                        

打开cube新建工程,本人选择stm32f070rb型号

 

工程预览

 

时钟源选择,外部时钟源本人全部失能,准备启用内部时钟源配置。

SW调试使能,勾选Serial Wire选项,基础时钟选择系统滴答systick

串口异步配置,默认配置。

添加RT软件包,找到软件软件包选项点击,然后在选择组件

本人已经下载好了rt组件,勾选其组件,点击ok退出。

rt组件就出现在cube配置下,勾选它然后如下配置(根据需求参考)

再到NVIC配置取消掉硬件错误中断,因为rt组件会帮你配置,不消掉iar工程编译会报错,也可以iar工程注释掉其中断。

时钟树配置,内部高速时钟源,选择最快的时钟频率64Mhz,不考虑低功耗。

工程管理,添加工程名

代码生成配置,勾选如下图所示的箭头,这样驱动文件更加模块化分配到各个c文件中。

预设置,把两个初始化去掉,这样main里就不会生成这两个初始化调用,因为到时rt配置的时候会重复,不勾的话最终在工程还是要删除掉,否则程序功能不正常。

点击生成工程GENEEATE CODE,生成IAR工程。

打开open project,即可开始配置IAR工程。


    直接编译运行,是没有如官方所示会打印RT logo的。原因是还没实现finsh接口,实现finsh接口的函数有:

void rt_hw_console_output(const char *str)  //输出接口,finsh里面一些debug信息依赖这个接口打印。

char rt_hw_console_getchar(void) //输入接口,串口接收实现,看他返回的是一个char说明只需要实现读取串口一个数据的功能

官方推荐rt_hw_console_output驱动如下:

void rt_hw_console_output(const char *str) {

    rt_size_t i = 0, size = 0;

    char a = '\r';

    __HAL_UNLOCK(&huartx);

    size = rt_strlen(str);

    for (i = 0; i < size; i++)

    {

        if (*(str + i) == '\n') {

            HAL_UART_Transmit(&huartx, (uint8_t *)&a, 1, 1);

        }

        HAL_UART_Transmit(&huartx, (uint8_t *)(str + i), 1, 1);

    } 

}

接收部分rt_hw_console_getchar官方两个例程一个是用查询方法来接收、一个是中断结合信号量接收。

中断结合信号量接收参考官方链接如下:

finsh控制台实现

但是,本人觉得两者都存在缺陷:查询方法的缺陷,实时系统多线程的环境下,可能会有查漏的情况,所以在越复杂的功能环境下越可能漏接数据。而中断信号量结合的方法,需要用到回环缓冲区,类似fifo缓冲区。代码量较大,实际上信号量加缓冲区的功能不就是消息队列功能吗,所以本人再次基础上做了一个翻新的改动。这样代码则更加简洁清晰。代码如下:

char rt_hw_console_getchar(void) {
    char ch ;
    //rt_sem_take(&shell_rx_sem, RT_WAITING_FOREVER); //接 收 信 号 量
    //ch = huartx.Instance->DR & 0xff; //读 取 数 据
    if(rt_mq_recv(&shell_rx_mq, &ch,1, RT_WAITING_FOREVER) == RT_EOK) {
        
    }
    return ch;
}
 
void USARTx_IRQHandler(void) {
    int ch = -1;
    if((__HAL_UART_GET_FLAG(&huartx, UART_FLAG_RXNE) != RESET) &&
    (__HAL_UART_GET_IT_SOURCE(&(huartx), UART_IT_RXNE) != RESET)) //接 收 中 断 
    {
        __HAL_UART_CLEAR_FLAG(&(huartx), UART_FLAG_RXNE); //清 除 中 断
        __HAL_UART_CLEAR_PEFLAG(&(huartx)) ;
#if defined(STM32G0xx_HAL_H)
        ch = huartx.Instance->RDR & 0xff; //读 取 数 据
#elif defined(__STM32F1xx_HAL_H)
        ch = huartx.Instance->DR & 0xff; //读 取 数 据
#else
#error no sure ic type
#endif
        rt_mq_send(&shell_rx_mq, &ch,1);
 
    } 
    __HAL_UART_CLEAR_PEFLAG(&huartx) ;
}

对比官方驱动,我们不需要实现ringbuf环缓冲区等功能。

上面代码中的shell_rx_mq消息队列需要初始化,实现finsh驱动函数之后,需要对串口进行初始化实现如下(直接通过cube的生成usart.c代码中复制出来):

void lc_shell_uart_init(void) {

 

    //rt_sem_init(&(shell_rx_sem), "shell_rx", 0, 0);

    rt_mq_init(&shell_rx_mq,

                        "shell_rx",

                        shell_rx_buf,                                            /* 内存池指向 shell_rx_buf */

                        1,                                                       /* 每个消息的大小是 1 字节 */

                        sizeof(shell_rx_buf),                                   /* 内存池的大小是 shell_rx_buf 的大小 */

                        RT_IPC_FLAG_FIFO);                                       /* 如果有多个线程等待,按照先来先得到的方法分配消息 */

    MX_USART2_UART_Init();

}

 

void MX_USART2_UART_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_USART2_CLK_ENABLE();

 

    __HAL_RCC_GPIOA_CLK_ENABLE();

    /**USART2 GPIO Configuration

    PA2     ------> USART2_TX

    PA3     ------> USART2_RX

    */

    GPIO_InitStruct.Pin = DEBUG_TX_Pin;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(DEBUG_TX_GPIO_Port, &GPIO_InitStruct);

 

    GPIO_InitStruct.Pin = DEBUG_RX_Pin;

    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

    GPIO_InitStruct.Pull = GPIO_PULLUP;

    HAL_GPIO_Init(DEBUG_RX_GPIO_Port, &GPIO_InitStruct);

 

    huartx.Instance = USART2;

    huartx.Init.BaudRate = 115200;

    huartx.Init.WordLength = UART_WORDLENGTH_8B;

    huartx.Init.StopBits = UART_STOPBITS_1;

    huartx.Init.Parity = UART_PARITY_NONE;

    huartx.Init.Mode = UART_MODE_TX_RX;

    huartx.Init.HwFlowCtl = UART_HWCONTROL_NONE;

    huartx.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&huartx) != HAL_OK)

    {

//        Error_Handler();

    }

 

    __HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);//中断使能

    HAL_NVIC_EnableIRQ(USART2_IRQn);

    HAL_NVIC_SetPriority(USART2_IRQn, 3, 3);

}

然后在board.c中的初始化rt_hw_board_init函数添加lc_shell_uart_init()函数即可。

cube生成RT组件配置的潜规则:前面提到时钟源及时钟配置初始化SystemClock_Config()就是需要替换掉rt_hw_board_init()里的systemcoreclockupdate()函数的。同时需要把主函数里的HAL_Init()移动到rt_hw_board_init()里来。因为实际上main函数并不是启动入口,IAR启动入口会定位到__low_level_init(void)这里。

rtthread_startup其调用结构大致如下:

rtthread_startup()

{

    rt_hw_interrupt_disable();

    rt_hw_board_init();//因此时钟初始化需要在里面重新配置(用cube生成配置代替其rt的默认配置),同时HAL_init硬件初始化从main中也搬运过来。

    。。。

    rt_application_init();----》main线程

    。。。

    rt_system_scheduler_start();//启用系统调度器

}

最后还需要在rtconfig.h头文件添加一个RT_USING_FINSH宏

最后在main主函数里需要添加delay延时,保证让出运行线程给其他任务执行(finsh任务)

执行程序

整个移植工作总结

1.完成cube配置生成工程,包含串口、时钟、调试使能、RT启用、硬件中断弃用。

2.IAR工程:添加RT_USING_FINSH宏、实现finsh的输出输入函数两个函数且还需实现串口接收中断函数、实现串口初始化函数。

3.时钟配置、硬件初始化搬运到board.c中,添加串口初始化函数。

当然模块化配置还是需要有的,因此本人已经将finsh实现的内容模块化成一个组件中

同时配有移植使用说明:

 

需要用时,将这5个文件添加到项目中,现已实现了stm32f103xx或者stm32g0xx的支持(实际添加支持特别简单,改动特别少,一般只改头文件包含和部分寄存器,比如g070的串口接收寄存器和f103的接收寄存器变量名不同,代码中已经体现了),只需要修改lc_shell_cfg.h文件配置之后,在应用中添加lc_shell_uart_init()即可快速移植成功。链接如下:

https://download.csdn.net/download/fangjiaze444/12858497

本贴属于转载,如有侵权请联系删除。

本贴属于转载,如有侵权请联系删除。

本贴属于转载,如有侵权请联系删除。



工程师
2021-01-12 23:22:16     打赏
2楼

讲解的还是蛮到位的


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]