freertos_port

如何移植freertos到一块riscv 板子上。

移植FreeRTOS到RISC-V板子的指南

在这篇博客中,我们将深入探讨如何将FreeRTOS移植到RISC-V开发板上,帮助你了解整个过程的每个步骤及其挑战。

1. 前期准备

首先,需要准备开发环境,包括GCC交叉编译工具链(RISC-V GNU工具链)、RISC-V架构的开发板或者仿真器(如QEMU)以及FreeRTOS源码。你还需要准备一份开发板的手册,以便了解硬件的具体细节,包括内存映射、中断控制器和引导设置等。博主使用的是玄铁cdk,cklink,debugserver。

2. 理解FreeRTOS移植层

FreeRTOS的移植主要集中在其硬件抽象层(HAL)和启动代码部分。你需要理解port.cportASM.S文件的内容。port.c负责实现与硬件相关的功能,例如上下文切换、任务调度等,而portASM.S则用于处理汇编级别的上下文切换代码。如果你的板子没有适配freertos(很少出现这种情况),很遗憾,你需要自己实现port.c与portASM.c。

  1. port.c文件
  • 这个文件中包含与任务调度和上下文切换相关的代码,尤其是FreeRTOS任务切换所需的C语言逻辑。

  • 需要在

    port.c
    

    中实现的功能包括:

    • 上下文切换函数:例如vPortYield(),用于手动切换任务。(这个vPortYield,尤其重要,它是在比如任务切换中进行cpu使用权的转移)
    • 节拍计时器设置:确保定时器中断能够用于系统节拍。(保证你的一秒是一秒)

通常可以找到FreeRTOS的port.c模板代码,你可以基于此进行修改,以适配你所使用的特定RISC-V芯片。

2. portASM.S文件

  • portASM.S中主要是汇编代码,用于上下文保存和恢复。这对于实现任务切换至关重要,因为RISC-V有自己独特的寄存器集合和指令集。

  • 主要需要实现的功能包括:

    • 上下文保存:保存当前任务的所有寄存器(包括x0x31,以及状态寄存器等)。

    • 上下文恢复:从新任务的栈中恢复这些寄存器,以切换到该任务执行。

    • 中断入口:确保硬件中断能跳转到正确的处理例程,并正确保存/恢复状态。

      通常情况下,port.c与portASM.S的内容功能都差不多,只需要依据你板子实现相应的相似的功能即可。

3. 配置startup代码

startup.S文件是系统启动的关键,涉及到堆栈的初始化和中断向量表的设置。确保正确地配置了MTVEC(机器模式中断向量寄存器),将其指向你编写的中断服务例程的入口地址。所有的中断源,包括软中断和时钟中断,都需要在这里配置以便正确触发。一定要设置好MTVEC,否则比如在Task切换的时候,会因为找不到中断处理地址找不到你的软中断函数,(除了你自己自定义的中断处理函数,这可能会让你十分疑惑)。

4. 中断和任务切换

FreeRTOS通过定时器中断(通常是SysTick)来实现任务调度。在RISC-V中,你需要使用机器定时器(MTIME)或平台特有的定时器。确保在mie寄存器中使能了对应的中断源,并且在mstatus寄存器中使能全局中断。使用csrs mstatus, (1 << 3)来设置全局中断使能位。

5. 修改freertosConfig.c

在将FreeRTOS移植到RISC-V的过程中,FreeRTOSConfig.h文件是非常重要的配置文件,用于定制化RTOS的行为。根据你的开发板和目标应用场景,你可能需要对FreeRTOSConfig.h进行一些定制化修改,以下是一些关键的配置选项:

1. configCPU_CLOCK_HZ

  • 配置CPU频率:设置CPU的频率,这样RTOS可以根据系统时钟进行准确的计时和任务调度。
  • 例如:
    #define configCPU_CLOCK_HZ (80000000UL)  // 设置为你的RISC-V板子的频率
    

2. configTICK_RATE_HZ

  • 配置系统节拍频率:设置RTOS节拍频率,通常为1000Hz(1ms的时间片),但也可以根据应用需要进行调整。
    #define configTICK_RATE_HZ (1000)  // 每秒钟1000次节拍中断
    

3. configMAX_PRIORITIES

  • 最大任务优先级:设定系统中可用的最大任务优先级,以适应你的应用程序中的任务调度复杂性。
    #define configMAX_PRIORITIES (5)  // 任务优先级的数量
    

4. configMINIMAL_STACK_SIZEconfigTOTAL_HEAP_SIZE

  • 最小栈大小:设定任务使用的最小栈大小,通常与任务复杂性和函数调用深度相关。
  • 堆大小:设置FreeRTOS分配给任务、队列等动态资源使用的堆大小。
    #define configMINIMAL_STACK_SIZE (128)  // 单位:字
    #define configTOTAL_HEAP_SIZE (10240)   // 单位:字节
    

5. configUSE_PREEMPTION

  • 抢占式调度:启用或禁用抢占式调度。如果你需要FreeRTOS基于优先级抢占任务,请设置为1
    #define configUSE_PREEMPTION 1  // 启用抢占式调度
    

6. configUSE_TIME_SLICING

  • 时间片调度:配置是否对同优先级任务进行时间片轮转。如果多个任务具有相同优先级,启用时间片可以确保公平性。
    #define configUSE_TIME_SLICING 1  // 启用时间片调度
    

7. configKERNEL_INTERRUPT_PRIORITYconfigMAX_SYSCALL_INTERRUPT_PRIORITY

  • 中断优先级:设定内核和系统调用的中断优先级。对于RISC-V,需要确保这些优先级与硬件要求匹配,并且符合FreeRTOS对中断优先级的要求。
    #define configKERNEL_INTERRUPT_PRIORITY 255
    #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191  // 中断优先级,较低值表示较高优先级
    

8. configUSE_MUTEXESconfigUSE_COUNTING_SEMAPHORES

  • 启用互斥锁和信号量:根据应用需求,决定是否使用这些同步原语。
    #define configUSE_MUTEXES 1
    #define configUSE_COUNTING_SEMAPHORES 1
    

9. configASSERT

  • 调试辅助:使用configASSERT来在开发阶段帮助发现错误。
    #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
    

10. configUSE_TICK_HOOKconfigUSE_IDLE_HOOK

  • 钩子函数:可以选择启用空闲任务钩子或节拍钩子函数,用于执行一些后台或周期性任务。
    #define configUSE_TICK_HOOK 0
    #define configUSE_IDLE_HOOK 1  // 启用空闲钩子函数
    

11. configUSE_PORT_OPTIMISED_TASK_SELECTION

  • 任务选择优化:对于RISC-V这样有较多寄存器的处理器,可以启用优化的任务选择算法。
    #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
    

记得特别关注栈与堆的设置,因为一些板子,他的栈与堆的容量特别小,导致虽然你可以创建task,但是在真正启动的时候,会因为一些堆栈的溢出,甚至说freertos在ram中的写入时出先问题,但是这些问题的报错都是memalloc error,会让你十分疑惑。

6. 调试与验证

在编译之前,你需要依据你板子的内存情况调整link文件,因为如果你的板子是特殊型号的,你的内存可能非常小或者说特殊,你需要参考别人的link文件来调整存储的排布。移植完成后,首先是编译,可能是freertos的本身的bug,我在编译的时候,需要加上一个-fno-strict-aliasing来放宽别名的限制,否则又会爆出一些奇怪的错误。最后,你需要使用调试工具(如OpenOCD)对整个移植过程进行调试。首先,检查系统是否可以进入main()函数并启动第一个任务。其次,验证定时器中断和任务切换是否正常工作。如果系统无法正确启动,可能需要查看中断是否被使能,以及上下文切换代码是否存在错误。

7. 结束语

将FreeRTOS移植到RISC-V板子上需要对处理器架构、启动代码以及RTOS的内部机制有深入的理解。虽然这个过程充满挑战,但完成后,你将对系统的底层硬件有更加深刻的认识,并且能够为你的应用程序构建一个高度可定制的实时操作系统环境。

希望这篇指南对你有所帮助,祝你移植顺利!

### FreeRTOS Socket 使用方法及示例 #### 一、FreeRTOS中的Socket API概述 在嵌入式系统开发中,网络通信是不可或缺的一部分。FreeRTOS提供了丰富的API用于TCP/IP协议栈操作,其中包括Socket接口。这些接口允许开发者创建基于TCP或UDP的应用程序[^1]。 对于FreeRTOS环境下的socket编程来说,主要涉及如下几个核心函数: - `FreeRTOS_socket()`:创建一个新的套接字实例。 - `FreeRTOS_bind()` :绑定本地地址到已创建的套接字上。 - `FreeRTOS_listen()`: 对于服务器端而言,在给定监听队列长度的情况下使套接字进入监听状态等待连接请求到来。 - `FreeRTOS_accept()` : 接受来自客户端的新连接并返回新的连接描述符。 - `FreeRTOS_connect()` : 客户端调用此函数发起与远程主机之间的连接建立过程。 - `FreeRTOS_sendto()/FreeRTOS_write()` 和 `FreeRTOS_recvfrom()/FreeRTOS_read()` 分别用来发送/接收数据报文或者流形式的数据包。 - `FreeRTOS_closesocket()` : 关闭不再使用的套接字资源释放相关联的所有内存空间。 #### 二、简单TCP Server实现案例 下面给出一段简单的TCP服务端代码片段作为例子展示如何利用上述提到的一些基本API完成一个可以接受外部设备连接的服务端应用逻辑: ```c #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "lwip/api_shell.h" #define SERVER_PORT (80) void prvTCPServerTask(void *pvParameters) { struct freertos_sockaddr server_addr; int listen_fd, new_conn_fd; /* 创建监听套接字 */ if ((listen_fd = FreeRTOS_socket(FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP)) != FREERTOS_INVALID_SOCKET) { memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = FREERTOS_AF_INET; server_addr.sin_port = FreeRTOS_htons(SERVER_PORT); /* 绑定地址信息至套接字 */ if (FreeRTOS_bind(listen_fd, &server_addr, sizeof(server_addr)) == 0) { /* 开始监听传入连接 */ if (FreeRTOS_listen(listen_fd, 5) == 0) { while (1) { /* 阻塞直到有新连接到达 */ new_conn_fd = FreeRTOS_accept(listen_fd, NULL, 0); if (new_conn_fd >= 0) { char buffer[64]; /* 处理接收到的消息 */ size_t received_bytes = FreeRTOS_recv(new_conn_fd, buffer, sizeof(buffer), 0); if(received_bytes>0){ //处理消息... /* 发送响应 */ const char* response="HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=UTF-8\r\nConnection:close\r\n\r\n<html><body>Hello from FreeRTOS TCP Server!</body></html>"; FreeRTOS_send(new_conn_fd,response,strlen(response),0); } /* 关闭当前会话对应的套接字 */ FreeRTOS_closesocket(new_conn_fd); } } } /* 清理工作 */ FreeRTOS_closesocket(listen_fd); } } } ``` 这段代码展示了怎样通过FreeRTOS提供的API构建一个能够回应HTTP GET请求的基础版Web服务器。当浏览器访问运行着该任务的目标板卡IP地址加上指定端口号时(例如http://192.168.x.y:80),将会看到一条欢迎语句显示出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值