FreeRTOS移植-教你修改portable(S3C2440、ARM9、gcc)

今天带大家从底层看一下移植FreeRTOS过程,刚好我手上只有S3C2440的开发板,刚好官方不支持ARM9架构(因为ARM9直接上Linux,用于FreeRTOS有点浪费),所以从看懂这篇文章,你将学会如何修改portable部分文件将FreeRTOS移植到官方不支持的芯片上。

FreeRTOS作为入门级实时操作系统,无论你是从事单片机还是嵌入式Linux,学习一下都大有好处。如果你手上是stm32之类的芯片,亦或者是官方portable文件夹中有的,那移植过程将会很简单,没有怎么办?

1. 简要介绍需要的文件

去官网下载源码,如果下不了,可以留言发给你。

压缩包里只有FreeRTOS这个文件夹是我们需要的

 FreeRTOS文件夹中也只有1个文件夹是我们需要的

我们只要include文件夹 ,加上5个文件。

还需要FreeRTOS/Source/portable/MemMang下的内存管理文件

源码中默认提供了5个文件,对应内存管理的5种方法,选一个就行,我们选heap_4.c

文件                            优点                                            缺点
heap_1.c    分配简单,时间确定                            只分配、不回收
heap_2.c    动态分配、最佳匹配                            碎片、时间不定
heap_3.c    调用标准库函数                                    速度慢、时间不定
heap_4.c    相邻空闲内存可合并                            可解决碎片问题、时间不定
heap_5.c    在heap_4基础上支持分隔的内存块    可解决碎片问题、时间不定

最后是一个用于全局配置的头文件FreeRTOSConfig.h

可以看这篇文章,了解如何配置:FreeRTOSConfig.h-FreeRTOS配置函数详解

2. portable硬件相关代码

可以参考freertos\Source\portable\GCC\ARM7_LPC2000的代码,但是我们是ARM9,和ARM7还是有区别的,需要一些修改。

2.1 port.c

static void prvSetupTimerInterrupt( void )
{
uint32_t ulCompareMatch;
extern void ( vTickISR )( void );

	// /* A 1ms tick does not require the use of the timer prescale.  This is
	// defaulted to zero but can be used if necessary. */
	// T0_PR = portPRESCALE_VALUE;

	// /* Calculate the match value required for our wanted tick rate. */
	// ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ;

	// /* Protect against divide by zero.  Using an if() statement still results
	// in a warning - hence the #if. */
	// #if portPRESCALE_VALUE != 0
	// {
		// ulCompareMatch /= ( portPRESCALE_VALUE + 1 );
	// }
	// #endif
	// T0_MR0 = ulCompareMatch;

	// /* Generate tick with timer 0 compare match. */
	// T0_MCR = portRESET_COUNT_ON_MATCH | portINTERRUPT_ON_MATCH;

	// /* Setup the VIC for the timer. */
	// VICIntSelect &= ~( portTIMER_VIC_CHANNEL_BIT );
	// VICIntEnable |= portTIMER_VIC_CHANNEL_BIT;
	
	// /* The ISR installed depends on whether the preemptive or cooperative
	// scheduler is being used. */

	// VICVectAddr0 = ( int32_t ) vTickISR;
	// VICVectCntl0 = portTIMER_VIC_CHANNEL | portTIMER_VIC_ENABLE;

	// /* Start the timer - interrupts are disabled when this function is called
	// so it is okay to do this here. */
	// T0_TCR = portENABLE_TIMER;
	
	INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
	INTMSK &= ~(1<<10);  /* enable timer0 int */
	TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */
    TCFG1 &= ~0xf;
    TCFG1 |= 3;  /* MUX0 : 1/16 */

    /* 设置TIMER0的初值 */
    TCNTB0 = 31;  /* 31250,1s中断一次 */

    /* 加载初值, 启动timer0 */
    TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

    /* 设置为自动加载并启动 */
     TCON &= ~(1<<1);
     TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */
}

port.c文件中只需要修改prvSetupTimerInterrupt( void )这一个函数,这个函数用于启动系统定时中断,相当于启动FreeRTOS的心跳。

涉及到的内容其实就是开启time0定时器中断,并设置为1ms中断一次。注释掉的部分为原来的代码。

2.2 portUSR.c

void vTickISR( void )
{
	// /* Save the context of the interrupted task. */
	portSAVE_CONTEXT();

	// /* Increment the RTOS tick count, then look for the highest priority
	// task that is ready to run. */
	 __asm volatile
	 (
		 "	bl xTaskIncrementTick	\t\n" \
		 "	cmp r0, #0				\t\n" \
		 "	beq SkipContextSwitch	\t\n" \
		 "	bl vTaskSwitchContext	\t\n" \
		 "SkipContextSwitch:			\t\n"
	 );

	 /* Ready for the next interrupt. */
	// T0_IR = portTIMER_MATCH_ISR_BIT;
	// VICVectAddr = portCLEAR_VIC_INTERRUPT;
	SRCPND = (1<<10);
	INTPND = (1<<10);	
	// /* Restore the context of the new task. */
	portRESTORE_CONTEXT();
	
}

这个文件也只需要改一个函数:vTickISR( void )为定时器中断处理函数

主要功能为检测是否需要切换任务,如果需要切换则切换任务,在这里我们要清除中断,以便继续心跳。

也就是这两行代码:

	SRCPND = (1<<10);
	INTPND = (1<<10);	

3. 启动代码

启动代码的关键在于进入irq中断

发生定时器中断时,pc会自动跳到中断向量表中irq中断所指示位置执行

我们需要让pc跳到vTickISR( void ),也就是上文所说的定时器中断处理函数:

b vTickISR

 这一句就够了,为什么直接跳进处理函数?不应该保存现场吗?

回到上文看定时器中断处理函数vTickISR( void )中:

portSAVE_CONTEXT();

 这一句的宏定义在portmacro.h中,他可以保存所有寄存器(如果移植到别的芯片,需要修改,无非就是异常保存现场那一套,特别简单,自己可以写出来)

而恢复现场也有:

portRESTORE_CONTEXT();

这一句帮我们做了。注意是b,而不是bl,因为加上l将改变lr寄存器。

但是这样做有个问题,会导致,无论什么irq中断都跳到定时器中断处理函数中,所以还要简单修改一下,分辨一下中断源是什么

do_irq:	
    stmdb sp!, {r0-r12}
	ldr r0,=0X4A000014/*INTOFFSET*/
	ldr r1,[r0]
	cmp r1,#10
	beq tick
	sub lr, lr, #4
	stmdb sp!, {lr}
	/* 处理irq异常 */
    bl handle_irq_c
    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr_irq的值恢复到cpsr里 */
	
tick:
    ldmia sp!, {r0-r12}
	b vTickISR

INTOFFSET如果为10则代表了timer0中断。

4. Makefile

objs = boot.o main.o tasks.o timers.o list.o queue.o event_groups.o  sdram.o uart.o interrupt.o
objs += ./portable/MemMang/heap_4.o
objs += ./portable/ARM920T/port.o
objs += ./portable/ARM920T/portISR.o
CC = arm-linux-gcc
CFLAGS = -I /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include/
CFLAGS += -I ./portable/ARM920T
CFLAGS += -I ./include
CFLAGS += -I .


all: $(objs)
	arm-linux-ld -T s3c2440.lds $^ /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/lib/libc.a  /usr/local/arm/4.3.2/lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a -o s3c2440.elf
	arm-linux-objcopy -O binary -S s3c2440.elf s3c2440.bin
	

clean:
	rm *.bin *.o *.elf *.dis

distclean:
	rm $(dep_files)
	
%.o : %.c
	$(CC) -march=armv4t -c  $(CFLAGS) -o $@ $<

%.o : %.S
	$(CC) -c -o $@ $<

Makefile也很简单,无非就是把所有文件连起来。

5. 创建任务与测试

创建两个简单的任务用于测试,一个打印T2 run,一个打印T1 run和一个变量,成功则交替执行。

void vTask1( void *pvParameters )
{
	const char *pcTaskName = "T1 run\r\n";
	char i='A';
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
	    i++;
		/* 打印任务1的信息 */
		puts( pcTaskName );
		//GPFDAT=~GPFDAT;
		put_char(i);
		puts("\r\n");
		delay(500000);
	}
}
void vTask2( void *pvParameters )
{
	const char *pcTaskName = "T2 run\r\n";
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 打印任务1的信息 */
		puts( pcTaskName );
		//GPFDAT=~GPFDAT;
		delay(500000);
	}
}

测试结果: 

 完整代码已上传,发文时还未过审,有需要至我主页查看自取。

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闪耀大叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值