本文由 RT-Thread 论坛用户 @jiao96 原创公布:https://club.rt-thread.org/ask/article/3020.html
摘要
因为我的项目须要,应用了复旦微 FM33LC026 单片机,移植了 RT_Thread 零碎。正好赶上 rt 官网的【国产 MCU 移植】流动,顺路参加一下。
芯片参数:
硬件 | 形容 |
---|---|
CPU | Cortex-M0 |
主频 | 64MHz |
SRAM | 24KB |
Flash | 128KB |
移植次要步骤:
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 移植奉献流动开启!