欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

RT-Thread-STM32F103C8T6移植

程序员文章站 2022-03-04 16:00:21
RT-Thread-STM32F103C8T6移植准备材料合集1.RT-Thread原码下载并解压(官网下载)2.STM32F103裸机工程(我用的是正点原子的STM32F103的HAL库)3.温馨提示(看不清图片可以从Ctrl+鼠标滑轮放大)4.如果想要word版的笔记可以私聊我,因为这个编辑器还不太会用。。所以没有传图在里面,(没图你说个p呀)一、准备裸机工程1.我使用的是正点原子的HAL库的一个工程,提前编译好,调试裸机工程没有任何问题二、添加RT-Thread源码(src,inclu...

RT-Thread-STM32F103C8T6移植

准备材料合集

1.RT-Thread原码下载并解压(官网下载)

2.STM32F103裸机工程(我用的是正点原子的STM32F103的HAL库)

3.温馨提示(看不清图片可以从Ctrl+鼠标滑轮放大)

4.如果想要word版的笔记可以私聊我,因为这个编辑器还不太会用。。所以没有传图在里面,(没图你说个p呀)

一、准备裸机工程

1.我使用的是正点原子的HAL库的一个工程,提前编译好,调试裸机工程没有任何问题

二、添加RT-Thread源码(src,include,libcpu,rt_config.h)

1.在工程文件夹根目录里新建(RT-Thread)文件夹。

2.在子目录粘贴RT-Thread官网下载内核源码的src文件夹、include文件夹、libcpu下架构相关的代码(比如STM32F103使用的是arm-Cortex-M3内核)复制相应文件夹。

3.从源代码的BSP文件夹里找到相应MCU的工程配置文件rtconfig.h放在工程根目录下。例如我使用的是这个

4.将文件拷贝过来,需要再MDK中导入,创建①KERNEL(内核)②Cortex-M3(启动文件)③BOARD(板卡支持文件),分别将文件按如下导入到相应分组(board.c自行创建)

(1)Src全部加入kernel

(2)CPU这里要加两个,注意将文件类型更改为ALL.FEIL,不然找不到汇编文件

(3)新建一个board.c文件(有用)

(4)增加.头文件包含路径

(5)这三个是我增加的,因为根目录下有rt_config.h所以要将根目录包含进来。

5.编译工程,你会发现编译有错误,finsh_api.h,这是 因为rt_config.h里配置了很多api函数的调用接口,但是目前的裸机工程是没有的, 所以需要手动删除。打开刚才复制的rt_config.h;从底部删除至表示部分,不要把endif删除了。(记得保存,在打开MDK编译)

6.现在编译那会发现还是有错误,你会看到有两个中断服务函数重定义,在HAL库中提前定义好了,所以需要注释一下

7.如果是HAL库的话在 stm32f1xx.it.c ,在这中断服务函数库中

8.再编译会提示 rt_hw_board_init 这个函数未定义,这个函数是启动流程初始化板卡用的,所以用到刚才的board.c文件,在里面记得调用main函数的头文件,和

#include "rtthread.h" #include "rtconfig.h" 这两个头文件,
board.c里定义rt_hw_board_init 这个函数如下: void rt_hw_board_init(void) { HAL_Init(); //HAL库初始化  Stm32_Clock_Init(RCC_PLL_MUL9); //设置主频  LED_Init(); //外设  } 
因为我用的是HAL库,所以把正点原子main函数的初始化全都调用过来了,这个函数就是板卡初始化,以后所有的初始化都放到这里。

9.这一步完成之后需要更改配置静态内存,因为默认RT_Thread使用的是动态内存,但是咱们刚开始移植,没有初始化堆内存,所以这里无法创建main线程,以后如果调试的时候程序卡在创建main线程,很可能就是这里的原因。

这里打开rt_config.h,
注释掉:

#define RT_USING_HEAP 

就会使用静态内存空间(记得保存再编译)。

10.再编译就没有错误了, 可以下载进班子试一下,能不能进入main函数。正常是会创建main线程,进入main函数执行。

三、实现时钟管理(配置滴答定时器)

在操作系统中滴答定时器作为心跳包提供系统时间,所以任何操作系统都会使用到滴答定时器。Systick time

1.找到刚才中断服务函数中的滴答定时器中断,将其注释掉,粘贴到board.c中。

2.RT_Thread中有一个配置系统时钟的函数,需要先将其初始化。这个函数要在rt_hw_board_init里初始化

 SysTick_Config(SystemCoreClock); 

(1)SystemCoreClock,这个参数是每秒的tick数,数值是系统时钟主频,我这里是72M,所以每秒会加72次计数器;

3.然后配置滴答定时器中断服务函数,

void SysTick_Handler(void) { } 

4.这里可以尝试一下定时器是不是是不是配置好了,因为系统时钟计数器是24位,72M会溢出,所以这里把值设小一点,

SysTick_Config(SystemCoreClock/5); 

大概200ms进入一次中断

5.改变一下滴答定时器中断函数,尝试定时器是否配置成功, 我这里是对LED取反尝试。下载进去如果正常进入中断,而且定时差不多是200ms,那么就差最后一步啦。

6.真正的系统肯定不是200ms进入一次中断,对于操作系统这里配置为

SysTick_Config(SystemCoreClock/RT_TICK_PER_SECOND); 后面这个变量是个宏定义,可以跳转查看,我这里是1000 

7.加入时钟配置。把滴答定时器中断配置成这样就有完成了,函数都是在库中,可以直接调用。

void SysTick_Handler(void) { rt_interrupt_enter(); rt_tick_increase(); rt_interrupt_leave(); } 

8.这里可以测试一下时钟有没有移植好,在主函数中加入

#include "rtthread.h" #include "rtconfig.h" 

这两个头文件,然后在while1里可以写一个灯闪烁,延迟函数用RT库的,

rt_thread_delay(); 参数填RT_TICK_PER_SECOND,正好是1s的延迟,如下: LED=~LED; rt_thread_delay(RT_TICK_PER_SECOND); 

就是这样,可以实现1s的闪烁,如果正常闪烁,说明时钟配置完成。

四、实现控制台输出(实现rt_hw_console_output())

1. 定义字符串函数

 void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) { /* Check the parameters */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_DATA(Data)); /* Transmit Data */ USARTx->DR = (Data & (uint16_t)0x01FF); } void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) { USART_SendData(pUSARTx,ch); while ((USART1->SR&0X40)==0); } void Usart_SendString( USART_TypeDef * pUSARTx, char *str) { unsigned int k=0; do { Usart_SendByte( pUSARTx, *(str + k) ); k++; } while(*(str + k)!='\0'); while((USART1->SR&0X40)==0) } 

2.因为正点原子的库里面没有字符串输出的函数,所以我移植了野火的字符串函数,也不晓得你们用不用得了。

这个函数移植到串口文件里,然后在串口头文件里声明
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
这个函数,后面要调用。

3. 在board.c里

定义这个函数,

void rt_hw_console_output(const char *str) { Usart_SendString( USART1,(char *)str); } 

然后打开串口重启,你就能看到RT_Thread的LOGO和控制台了,它可以像命令行一样使用哦。

五、实现内存动态管理

1.这里用最简单的方法,在数组里定义一个数组;

 static uint8_t heap_buf[10*1024]; 

2.动态内存就使用这个数组的空间吧,10K也是挺大的了。毕竟C8T6只有64K的flash。

3.在rt_hw_board_init函数中,初始化

 rt_system_heap_init(heap_buf,heap_buf+sizeof(heap_buf)-1); 初始化动态内存。 

4.然后在rt_config.h里把刚才屏蔽的动态内存取消掉,取消掉注释然后重新编译。就OK啦。

六、体验一下进程创建吧

1.开辟线程

在主函数开始的位置,

rt_thread_t tid; //开辟一个新线程 

2.创建线程

 /*函数参数,1.线程名字,2.线程入口函数名,3.线程入口参数 4.线程栈大小 5.线程优先级 6.线程tick数*/ tid=rt_thread_create("task1" ,rt_task1 ,RT_NULL ,200 ,7 ,1); 

3.定义线程入口函数

 void rt_task1(void *parameter) { while(1) { LED0=0; rt_thread_delay(RT_TICK_PER_SECOND); } } 

4. 条件判断如果线程创建成功, 开始运行,否则显示出来

 if(tid !=RT_NULL) { rt_thread_startup(tid); } else { rt_kprintf("thread1 create fail"); } 

5.这就完成了线程的创建,使用灯来看不明显,使用串口打印才明显, 能看出明显的线程切换。

本文地址:https://blog.****.net/qq_21479819/article/details/107922835