44 RT-Thread Nano移植到stm32F103CBT6 (STD标准库)

一、前言

最近在学习rt-thread,然后网上有介绍用RT-Thread Studio和stm32cubex结合进行开发,还有用rt-thread nano提供的pack直接开发,但是这种方式真的不太行,对于新手而言,不能很好的了解整个代码的结构。

为此,就怀着跟前面网友一样的心思,准备要自己拷贝代码,一步一步来对rt-thread进行移植,然后发现还是挺简单的,如果rt-thread已经移植过相应的cpu的话,那简直移植的方便就超级简单了。

本文章把rt-thread nano移植到stm32f103c8t6,然后也是基于STD标准库进行移植的,网上比较多的是基于HAL库进行移植的,写该文章一是为了下次自己回来看的时候能记忆起来;二是也为新手提供一种思路,如果他们要学习的话。

然后再说说,为什么选用rt-thread,第一是因为国产;第二是因为它能有像Linux的控制台;第三我只想要用一个线程调用的核,所以就选用了它!!挺好的加入支持国产的道路。

二、rt-thread移植记录

  1. stm32-led工程准备。

本次移植RT-Thread Nano内核到一个stm32f103cBt6标准库的一个led工程中,所以需要准备一个点灯程序,其目录如下图示。

 

 

移植前的led点灯工程主程序,以及编译后的大小如下图示。移植前程序所占ROM大小为:2216+268=2484字节;所用RAM:3840+48=3888字节;

2.下载RT-Thread Nano内核

首先,到官网上下载RT-Thread Nano源码,如下图示,本次下载的是rt-thread-3.1.3。下载链接:https://www.rt-thread.org/page/download.html

3.拷贝文件到目标led工程

首先,在原有的led工程的目录下,建立一个RT-Thread目录,如下图示。

然后打开rt-thread-3.1.3,拷贝下面三个目录的内容到刚建好的RT-Thread目录中。

拷贝完成后,删除掉libcpu里面没用到的文件,留下有用的。

最后只剩下面这个cortex-m3目录。

然后打开rt-thread-3.1.3目录下的dsp木,把拷贝board.c和rtconfig.h到工程的RT-Thread目录下。

4.添加目录及文件到led的keil工程。

把board.c和rtconfig.h两个文件,添加这两个文件到User目录下。

5.编译工程解决错误

编译的时候肯定会出现这三个问题,打开代码,定位到这三个函数,把led工程原有的这三个函数注释掉,因为RT-Thread已经实现了这三个函数,所以导致重定义。

注释:HardFault_Handler,如下图示:

注释:PendSV_Handler和SysTick_Handler

然后编译,发现编译通过,然后继续移植。

6.更改board.c文件

RT-Thread系统框架下,设备的时钟及硬件初始化都是放在board.c文件中的,我们只需要在rt_hw_board_init函数配置好,系统时钟和滴答定时器。

然后我把自己的main函数的时钟初始化SystemInit函数拷贝到rt_hw_board_init函数中。

主函数改成这样。

int main(void)
{

	LED_Init(); //LED 端口初始化 
	

	while(1)
	{
		
		LED_SetRGBLight(RGBLED_MODE_Y);
        rt_thread_mdelay(1000);
		LED_SetRGBLight(RGBLED_MODE_G);
        rt_thread_mdelay(1000);

	}

	return 0;
}

然后对工程重新编译,给予RT-Thread 内核的led工程编译通过,下载到开发板,led正常一闪一闪。

编译前我把邮箱和信号量的宏注释掉,关闭了线程通信的方式,所以可以看到移植后,代码量ROM:4004+576=4580,RAM:4720+112=4832

7.添加Finish控制

第一步:在rtconfig.h中确定RT_USING_CONSOLE和RT_USING_FINSH已经打开,如图所示。

第二步,串口初始化。

首先,在board.c实现rt_hw_console_getchar函数,然后再kservice实现rt_hw_console_output函数,下列使用的是标准库,如下图所示。

char rt_hw_console_getchar(void)
{
	int ch = -1;
	if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)
	{
		ch = (int)USART_ReceiveData(USART1);
	}
	else
	{
		if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
		{
			USART_ClearFlag(USART1,USART_FLAG_TC);
		}
		rt_thread_mdelay(10);
	}
	return ch;	
}
RT_WEAK void rt_hw_console_output(const char *str)
{
    /* empty console output */
	rt_size_t i = 0, size = 0;
	char a = '\r';

	USART_ClearFlag(USART1, USART_FLAG_TC);
	
	size = rt_strlen(str);
	for (i = 0; i < size; i++)
	{
		if (*(str + i) == '\n') 
		{
			USART_SendByte(USART1, a);
		}
		USART_SendByte(USART1, *(str + i));
	}
}

 

然后实现串口初始化,如下图示。但这里有一个坑,因为默认的shell使用的是轮询的方式实现,所以串口初始化这里,不能启动中断接收,否则shell将不正常。



void uart1_init(u32 bound)
{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//使能USART1,GPIOA时钟

	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置
	USART_DeInit(USART1);
	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

	USART_Init(USART1, &USART_InitStructure); //初始化串口1
	//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_Cmd(USART1, ENABLE);                    //使能串口1 
	USART_ClearFlag(USART1, USART_FLAG_TC);
}

然后,将初始化放在board.c的rt_hw_board_init函数里面,如下图示。

void rt_hw_board_init()
{
		SystemInit();				//配置系统时钟为72M 

	  /* System Tick Configuration */
    _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);

		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
		uart1_init(115200); 		//调试串口

    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

到此,算是通过拷贝代码的方式将rt-thread搞到自己的开发板上了吧!

over!

猜你喜欢

转载自blog.csdn.net/Chasing_Chasing/article/details/114643898