将RT-Thread Nano移植到STM32F401CCU6

将RT-Thread Nano移植到STM32F401CCU6
使用RT-Thread(后面简称RTT)是一次偶然的机会,之前并没有使用过嵌入式操作系统,一直使用前后台的方式实现单片机的程序处理,后来发现使用嵌入式操作系统真的很方便,尤其是在UI刷新和实时响应上有得天独厚的优势。当然,嵌入式操作系统有很多,秉承着支持国产的态度,我选择了RTT(其实RTT确实很厉害,在Keil中都可以直接下载其Nano版本),之所以这里讲关于Nano的移植,因为Nano非常小,虽然不支持一些Master版本的bsp外设,但是如果你手上的单片机根本就没有Master版本所提供的bsp,使用Master岂不是也没什么用。

STM32F401CCU6-RTT模板工程下载链接

硬件平台:

MCU:STM32F401CCU6(某宝20RMB以内)
OLED:中景园7针1.3寸OLED_12864(某宝12RMB)
晶振:25Mhz

移植过程:

第一步:下载RTT-Nano的Package

在这里插入图片描述

第二步:在工程中添加RTT-Nano的Package

在这里插入图片描述

第三步:修改一下"stm32f4xx_it.c"文件

因为RTT内核已经实现了
void HardFault_Handler(void)
void PendSV_Handler(void)
void SysTick_Handler(void)
这3个中断函数,因此需要在stm32f4xx_it.c里面注释掉这些函数,不然就会报错。

第四步:修改delay相关函数

因为工程模板里使用的是正点原子的延时函数,这里面是有用到SysTick的,但是RTT已经接管了SysTick,因此绝对不能再使用delay_init()这个函数,不然RTT就失去了SysTick的接管权限,然后就会运行失败。
将原来delay_ms();函数内部直接删完,换上rt_thread_mdelay(nms);即可,这样所有的延时都是线程延时了。

第五步:添加线程初始化代码

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "oled.h"
#include "bmp.h"
#include "timer32bit.h"
/*==========================================RT-Thread 头文件=========================================================*/
#include "rtthread.h"

/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
/*****************************************************程序改动说明*****************************************************/
//根据采用不同晶振 STM32F401 改动的地方有:
//OptionForTarget         xtal(改为和实际晶振大小一致)
//delay.c 								121、187、206行(降低计时误差 不改动定时误差为5% 改动后延时为偶数无误差,为奇数误差为1/n *100%)
//system_stm32f4xx.c			317行(根据system_stm32f4xx.c 137-181行的说明改动)
//stm32f4xx.h							123行(将HSE晶振数值更新为与实际一样)
/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/



/*=============================================LED闪烁的线程==========================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
static struct rt_thread led_thread;//定义线程
static char led_thread_stack[256];//线程栈大小
/*定义线程的入口函数*/
static void led_thread_entry(void *parameter)
{
	while(1)
	{
		LED0_ON;
		delay_ms(200);
		LED0_OFF;
		delay_ms(200);
	}
}
/*=====================================================END============================================================*/
/*====================================================================================================================*/




/*=============================================OLED刷屏的线程==========================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
static struct rt_thread oled_thread;//定义线程
static char oled_thread_stack[256];//线程栈大小
/*定义线程的入口函数*/
static void oled_thread_entry(void *parameter)
{
	while(1)
	{
		ShowFrequence();
		delay_ms(1000);
		OLED_ShowPicture(0,0,128,64,BMP4,1);//LOGO
		delay_ms(1000);
	}
}
/*=====================================================END============================================================*/
/*====================================================================================================================*/


/*====================================================主函数入口=======================================================*/
/*====================================================================================================================*/
int main(void)
{ 
	rt_err_t rst,rstt;
	LED_Init();		        //初始化LED端口
	KEY_Init();
	OLED_Init();
	LED_Shine(100,5);
	OLED_ShowPicture(0,0,128,64,BMP4,1);//LOGO
	delay_ms(2000);

	/**********************开启 led 线程*********************/
	rst = rt_thread_init(&led_thread,
						"led_rrt",
						led_thread_entry,
						RT_NULL,
						&led_thread_stack[0],
						sizeof(led_thread_stack),
						RT_THREAD_PRIORITY_MAX-2,
						20);
	if(rst==RT_EOK)
	{
		rt_thread_startup(&led_thread);
	}
	/**********************END*********************/
	
	/**********************开启 oled 线程*********************/
	rstt = rt_thread_init(&oled_thread,
							"led_rrt",
							oled_thread_entry,
							RT_NULL,
							&oled_thread_stack[0],
							sizeof(oled_thread_stack),
							RT_THREAD_PRIORITY_MAX-5,
							20);
	if(rstt==RT_EOK)
	{
		rt_thread_startup(&oled_thread);
	}
	/**********************END*********************/	
	return 1;
}
/*====================================================主函数结束=======================================================*/
/*====================================================================================================================*/

注意

但是需要注意的是,这个问题的原因我并不清楚,RTT Nano我没有找到us级别延时的解决方案,虽然RTT在rt_config.h中可以设置RT_TICK_PER_SECOND的值,但是经过我的实际测试在STM32F401CCU6平台上RT_TICK_PER_SECOND最高只能到420000,也就是单次线程tick延时为1/420000=2.38us左右。也就是使用rt_thread_delay(1);延时最短为最短的延时,为2.38us。【这实际是个愚蠢的做法,这样会导致OS大部分时间在Systick的中断中】
在网友的帮助下,找到了一个方法,就是按照正点原子的思路,数Systick就可以了,实际测试中使用下面这段代码可以做到us级别的延时,但是绝对不能超过1个OSTick,超出1个OSTick就会卡死。因为RTT初始化时钟时候对Systick->Load赋值为系统时钟/RT_TICK_PER_SECOND。而其tickperus的计算=系统时钟/1M。

//延时us不能超过一个OSTick
//OSTick = 1/RT_TICK_PER_SECOND 秒 = 10^6/RT_TICK_PER_SECOND 微秒 
void delay_us(u32 us)
{	
	rt_uint32_t start, now, delta, reload, us_tick;
    start = SysTick->VAL;
    reload = SysTick->LOAD;
    us_tick = SystemCoreClock / 1000000UL;
    do {
        now = SysTick->VAL; 
        delta = start > now ? start - now : reload + start - now;
    } while(delta < us_tick * us);
}

//延时nms 
void delay_ms(u16 nms)
{	 	 
	rt_thread_mdelay(nms);
} 

网上有的教程里说RTT会给初始化好时钟,其实是不正确的。因为他根本不知道你的外部晶振时钟是多少,因此PLL还是需要自己配置一下,配置的方法在Main函数开头,可以参见这篇文章。当然,你如果使用HAL库的话,用CubeMx配置好,然后复制到RTT的board.c里时钟配置的那里就可以了。我这里给的方法适用于标准库(标准库编译快)。

效果:

在这里插入图片描述

模板工程下载链接

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值