本文由RT-Thread论坛用户@jiao96 原创公布:https://club.rt-thread.org/ask/article/3020.html

摘要

因为我的项目须要,应用了复旦微FM33LC026单片机,移植了RT_Thread零碎。正好赶上rt官网的【国产MCU移植】流动,顺路参加一下。

芯片参数:

硬件形容
CPUCortex-M0
主频64MHz
SRAM24KB
Flash128KB

移植次要步骤:
1.工程搭建
2.增加复旦微官网库
3.板级初始化
4.对接串口驱动

开发工具:
Keil5、Env工具、Scons

工程搭建

芯片为ARM Cortex M0内核,能够用M0内核的其它BSP批改一个进去。
因为平时应用STM32比拟多,同时rt官网适配的较好,所以应用了stm32f072-st-nucleo进行批改。

将stm32f072-st-nucleo复制到bsp目录下,改名为fm33lc026。

Stm32的libraries一起挪到新工程目录下。

只搞了keil5的工程,其它没用的删掉。

批改keil5工程。
我的项目工程是工具通过template模板生成的,所以,批改型号只须要批改template.uvprojx就能够了。

关上template.uvprojx

批改芯片型号为FM33LC02X,其余大部分会依据抉择的芯片主动批改。

再进入linker,扩散加载文件批改一下。

template模板批改实现,接下来须要用模板生成工程,因为工程是STM32拷贝进去的,一些相对路径等会有变动,脚本会有问题,先进行一下批改。

批改fm33lc026文件夹下SConstruct文件,文本文档关上,这里门路改为如图的门路。

批改fm33lc026文件夹下Kconfig文件,批改如下。

批改好了,通过env工具,menuconfig,啊哈,报错了。

依据报错信息,还须要批改board/Kconfig下libraries的门路。

批改实现,menuconfig关上,当初还是原来工程的配置,接下来还须要持续批改,退出,生成一下keil5工程,关上工程,各配置失常,文件门路都能够找到,工程搭建结束。

### 增加复旦微官网库

接下来,增加复旦微FM33LC0XX的官网库函数到门路下。
须要替换掉本来STM32的官网HAL库。
STM32的库函数和驱动文件都在\libraries文件夹下,也就是方才咱们复制到工程内的文件夹。
咱们只须要进入\libraries文件夹下,把官网库函数文件夹增加进来,通过批改脚本,让它本人增加就OK了。

关上\libraries文件夹,先看一下文件构造。
[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-2OHYyQCx-1631071758745)(https://oss-club.rt-thread.or...)]

把STM32的HAL库全副删掉,找到官网的FM33LC0xx_FL_Driver库,复制一份进来。同时FM33LC0XX内核相干文件也复制进来。新的目录构造如下,驱动文件留着,低层驱动须要套接到内核驱动层,在本来STM32的批改就能够了。

批改Kconfig

进入接下来的文件夹,持续批改Kconfig。

FM内的文件是board里Kconfig配置的,而且只用到了.s启动文件,下一节进行配置。

先进入FM33LC0xx_FL_Driver文件夹。
FM33LC0xx_FL_Driver对应STM32_HAL库,复制一份STM32_HAL的SConscript到FM33LC0xx_FL_Driver目录下,进行批改。
复制bsp\stm32\libraries\STM32F0xx_HAL内的SConscript到FM33LC0xx_FL_Driver目录下。关上。

改为如下:

进入HAL_Drivers文件夹下。

因为我只适配了串口驱动,删掉没用的文件。删完如下图。

Config文件夹下

批改SConscript,都是依据宏定义增加drv_XXX.c到工程。
Emmmm,算了,不批改了,当前增加驱动还要加回来,顶多生成的工程找不到相应的drv_.c,根本没啥影响。
应用env生成一下工程,报错了,依据报错信息,找到报错的地位。
[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-xVZaPI6j-1631071758753)(https://oss-club.rt-thread.or...)]

关上报错的文件,官网库门路在这一步批改过,须要从新批改。

从新生成工程,胜利,关上工程,库文件也增加胜利。

板级初始化

相干库函数曾经增加胜利,接下来,要让单片机运行起来,并且进入零碎。
启动流程能够看RT_Thread官网文档。这里借用一张图。

要批改的有,.s启动文件,rt_hw_board_init()函数内相干的内容。

增加完官网库文件后,关上keil5工程,会发现.s启动文件还是STM32F072的,这里,咱们要批改一下。

关上board文件夹,关上SConscript,批改启动文件门路,批改如下。

还有一个Kconfig文件,和启动无关,顺路也批改一下。外面次要是menuconfig里单片机设施等的配置菜单,批改成咱们须要的。

进入工程board文件夹,删掉STM32的CubeMX文件夹。
删掉linker_scripts扩散加载文件(扩散加载相干在最后面曾经配置了,如果须要扩散加载文件,举荐不删,把文件内容改成FM33LC0XX的即可)。
OK..
进入env,关上menuconfig,发现串口图形化配置曾经OK了。

退出menuconfig,生成工程,关上工程,发现所有文件都增加结束,接下来要批改代码了。

进行rt_hw_board_init()内相干的函数的批改。
间接上改好的吧,具体相干的自行查看源码,代码现曾经合并到RT_Thread主分支。

关上drv_common.c,批改相干。

关上board.c

板级初始化这部分就实现了。工程这部分编译还是会报错,因为串口驱动相干的代码还是STM32的。对于驱动局部的对接,在接下来的局部。

对接串口驱动

对于设施,能够参考官网设施文档
持续援用一张图。

对应到工程:

简略来讲,就是写一个套接层,把官网的库函数套接到RT_Thread的设施管理层内。只须要批改设施驱动框架层,对应到工程就是keil5里Drivers里相干的代码。
关上Drivers分组,找到咱们要批改的串口局部程序,别离为drv_usart.c,drv_usart.h,uart_config.h。同时,对于串口引脚的初始化,模拟STM32的bsp包,独自拿了进去,放到了board.c里。

首先是drv_usart.h文件,定义了串口配置构造体和串口设施构造体,删掉原来STM32的,批改成如下:
串口配置构造体,一些须要独自进行配置的局部单拉进去。

/* config class */struct _uart_config{    const char *name;    void *InitTypeDef;    IRQn_Type irq_type;    uint32_t clockSrc;};

串口构造体,通用的,不须要批改的局部。

/* uart dirver class */struct _uart{    FL_UART_InitTypeDef  handle;    struct _uart_config *config;    rt_uint16_t uart_dma_flag;    struct rt_serial_device serial;};

drv_usart.c文件里,定义了串口实例,串口的操作方法函数,串口的注册函数。批改如下:
串口实例,依据rtconfig.h里的宏定义进行配置。

enum{#ifdef BSP_USING_UART0    UART0_INDEX,#endif#ifdef BSP_USING_UART1    UART1_INDEX,#endif#ifdef BSP_USING_UART4    UART4_INDEX,#endif#ifdef BSP_USING_UART5    UART5_INDEX,#endif#ifdef BSP_USING_LPUART0    LPUART0_INDEX,#endif#ifdef BSP_USING_LPUART1    LPUART1_INDEX,#endif};static struct _uart_config uart_config[] ={#ifdef BSP_USING_UART0    UART0_CONFIG,#endif#ifdef BSP_USING_UART1    UART1_CONFIG,#endif#ifdef BSP_USING_UART4    UART4_CONFIG,#endif#ifdef BSP_USING_UART5    UART5_CONFIG,#endif#ifdef BSP_USING_LPUART0    LPUART0_CONFIG,#endif#ifdef BSP_USING_LPUART1    LPUART1_CONFIG,#endif};

串口的操作方法函数,这里不具体列出,须要把这几个构造体里函数批改实现。

static const struct rt_uart_ops _uart_ops ={    .configure = uart_configure,    .control = uart_control,    .putc = uart_putc,    .getc = uart_getc,    .dma_transmit = 0};

串口的注册函数,将串口设施注册到设施管理层。

int rt_hw_usart_init(void){    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct _uart);    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;    rt_err_t result = 0;    for (int i = 0; i < obj_num; i++)    {        /* init UART object */        uart_obj[i].config = &uart_config[i];        uart_obj[i].serial.ops    = &_uart_ops;        uart_obj[i].serial.config = config;        /* register UART device */        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,                                       RT_DEVICE_FLAG_RDWR                                       | RT_DEVICE_FLAG_INT_RX                                       | RT_DEVICE_FLAG_INT_TX                                       | uart_obj[i].uart_dma_flag                                       , NULL);        RT_ASSERT(result == RT_EOK);    }    return result;}

uart_config.h文件,在drv_config.h里蕴含,给串口配置构造体赋值,须要填入对应的串口中断向量地址等:

#if defined(BSP_USING_UART0)#ifndef UART0_CONFIG#define UART0_CONFIG                                                \    {                                                               \        .name = "uart0",                                            \        .InitTypeDef = UART0,                                       \        .irq_type = UART0_IRQn,                                    \        .clockSrc = FL_RCC_UART0_CLK_SOURCE_APB1CLK,                \    }#endif /* UART0_CONFIG */#endif /* BSP_USING_UART0 */#if defined(BSP_USING_UART1)#ifndef UART1_CONFIG#define UART1_CONFIG                                                \    {                                                               \        .name = "uart1",                                            \        .InitTypeDef = UART1,                                       \        .irq_type = UART1_IRQn,                                    \        .clockSrc = FL_RCC_UART1_CLK_SOURCE_APB1CLK,               \    }#endif /* UART1_CONFIG */#endif /* BSP_USING_UART1 */

board.c里放入了串口引脚初始化,和其余一些对于硬件底层的配置。

FL_ErrorStatus FL_UART_GPIO_Init(UART_Type *UARTx){    FL_ErrorStatus status = FL_FAIL;    FL_GPIO_InitTypeDef    GPIO_InitStruct;    if (UARTx ==  UART0)    {        GPIO_InitStruct.pin = FL_GPIO_PIN_13;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOA, &GPIO_InitStruct);        GPIO_InitStruct.pin = FL_GPIO_PIN_14;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOA, &GPIO_InitStruct);    }    else if (UARTx ==  UART1)    {        GPIO_InitStruct.pin = FL_GPIO_PIN_13;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOB, &GPIO_InitStruct);        GPIO_InitStruct.pin = FL_GPIO_PIN_14;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOB, &GPIO_InitStruct);    }    else if (UARTx ==  UART4)    {        GPIO_InitStruct.pin = FL_GPIO_PIN_0;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOA, &GPIO_InitStruct);        GPIO_InitStruct.pin = FL_GPIO_PIN_1;        GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;        GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;        GPIO_InitStruct.pull = FL_DISABLE;        GPIO_InitStruct.remapPin = FL_DISABLE;        status = FL_GPIO_Init(GPIOA, &GPIO_InitStruct);    }    return status;}

批改结束,配置好串口到控制台,编译下载,失常应用。

点灯

最初在main函数里点灯

int main(void){    FL_GPIO_InitTypeDef GPIO_InitStruct = {0};    FL_GPIO_SetOutputPin(GPIOD, FL_GPIO_PIN_4);    GPIO_InitStruct.pin = FL_GPIO_PIN_4;    GPIO_InitStruct.mode = FL_GPIO_MODE_OUTPUT;    GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;    GPIO_InitStruct.pull = FL_DISABLE;    FL_GPIO_Init(GPIOD, &GPIO_InitStruct);    while (1)    {        FL_GPIO_SetOutputPin(GPIOD, FL_GPIO_PIN_4);        rt_thread_mdelay(500);        FL_GPIO_ResetOutputPin(GPIOD, FL_GPIO_PIN_4);        rt_thread_mdelay(500);    }}

结束。

总结

没有总结,第一次写技术文档,欢送大家学习交换。

近来芯片缺货大幕拉开,掀起新一轮国产代替浪潮。RT-Thread发动一场国产MCU移植奉献流动,邀请开发者们加入!
流动详情:国潮崛起!RT-Thread国产MCU移植奉献流动开启!