当前位置: 首页 > news >正文

FreeRTOS学习day1:Keil 工程配置与 FreeRTOS 链表机制理解

今天主要复盘了两个部分:一是Keil 工程配置中的 Linker、Scatter File、时钟配置问题;二是FreeRTOS 内核中链表结构、类型转换和常用宏定义的理解


一、Keil 中 Linker 和 Scatter File 的理解

一开始遇到的报错是:

Error: L6218E: Undefined symbol Image$$ARM_LIB_STACK$$ZI$$Limit

这个错误不是main.c的语法问题,而是链接阶段的问题

Keil 构建工程大致分为几个阶段:

.c 文件 ↓ 编译 .o 目标文件 ↓ 链接 .axf 可执行文件 ↓ 下载/调试

其中Compiler 编译器负责把每个.c文件单独编译成.o文件,而Linker 链接器负责把这些.o文件组合起来,生成最终的.axf文件。

Linker 主要做三件事:

  1. 把多个.o文件合并成完整程序;
  2. 解决函数、变量之间的引用关系;
  3. 根据内存布局文件,把代码和数据放到正确的 Flash/RAM 地址中。

Keil 里面的.sct文件就是Scatter File,也就是内存布局说明文件。它告诉链接器:

代码放在哪里 只读数据放在哪里 全局变量放在哪里 堆和栈放在哪里

比如 Cortex-M3 常见地址是:

Flash 起始地址:0x00000000 RAM 起始地址:0x20000000

之前报错的原因是启动文件需要一个栈顶地址符号:

Image$$ARM_LIB_STACK$$ZI$$Limit

但是 Linker 没有从当前的 Scatter File 中找到对应的栈区定义,所以无法生成这个符号。后来把 Scatter File 修改成:

.\RTE\Device\ARMCM3\ARMCM3_ac6.sct

参考博客:Keil5报错:Error: L6218E: Undefined symbol Image$$ARM_LIB_STACK$$ZI$$Limit_keil5 l6218e-CSDN博客


二、ULU这类整数后缀的含义

system_ARMCM3.c里面看到:

#define XTAL (50000000UL) #define SYSTEM_CLOCK (XTAL / 2U)

这里的UL是 C 语言整数常量后缀。

U = unsigned,无符号 L = long,长整型 UL = unsigned long,无符号长整型

所以:

50000000UL

表示这个数字是一个unsigned long类型的常量。

而:

2U

表示数字2是一个unsigned int类型。

这样写的好处是类型更明确,尤其在嵌入式里,时钟频率、寄存器值、地址值通常都希望使用无符号类型,避免符号扩展或者编译警告。

这段代码的含义是:

XTAL = 50 MHz SYSTEM_CLOCK = XTAL / 2 = 25 MHz

也就是系统时钟为 25 MHz。


三、Keil 中 Xtal 灰色无法修改的原因

在 Keil 的 Target 页面里看到:

Xtal (MHz): <undefined>

而且是灰色的,无法修改。

原因是当前工程选择的是:

ARM ARMCM3

这是一个通用 Cortex-M3 内核模板,不是某一颗真实芯片。Keil 不知道这颗“芯片”的外部晶振是多少 MHz,所以这个地方显示<undefined>,并且不能修改。

如果选择真实芯片,例如 STM32F103、LPC1768 等,Keil 可能会根据芯片包提供更多可配置项。


四、FreeRTOS 链表的基本结构

今天重点学习了 FreeRTOS 的链表机制。

FreeRTOS 链表不是普通的NULL结尾链表,而是一个带哨兵节点的双向循环链表

核心结构有三个:

typedef struct xLIST_ITEM { TickType_t xItemValue; struct xLIST_ITEM * pxNext; struct xLIST_ITEM * pxPrevious; void * pvOwner; struct xLIST * pxContainer; } ListItem_t;

普通链表节点ListItem_t中:

xItemValue :排序辅助值 pxNext :下一个节点 pxPrevious :上一个节点 pvOwner :节点拥有者,通常是 TCB pxContainer :该节点当前属于哪个链表

链表本体大致是:

typedef struct xLIST { UBaseType_t uxNumberOfItems; ListItem_t * pxIndex; MiniListItem_t xListEnd; } List_t;

其中:

uxNumberOfItems :链表中真实节点数量 pxIndex :链表遍历游标 xListEnd :链表结束标记节点,也叫哨兵节点

空链表不是NULL,而是:

xListEnd.pxNext 指向 xListEnd 自己 xListEnd.pxPrevious 指向 xListEnd 自己 uxNumberOfItems 等于 0

有节点时结构类似:

xListEnd <-> A <-> B <-> C <-> xListEnd

这种设计的好处是:插入、删除、遍历时不需要频繁判断头节点、尾节点或者空指针,代码更统一。


五、FreeRTOS 中的类型转换

之前看到这句代码:

pxList->xListEnd.pxNext = ( ListItem_t * ) &pxList->xListEnd;

这句代码的意思是:

让 xListEnd 的 next 指针指向它自己

也就是初始化空链表。

其中:

&pxList->xListEnd

表示取xListEnd的地址。

而:

( ListItem_t * )

是强制类型转换,把xListEnd的地址转换成ListItem_t *类型。

这里之所以需要强制转换,是因为xListEnd的真实类型是MiniListItem_t,而pxNext的类型是ListItem_t *

MiniListItem_tListItem_t少了后两个成员:

pvOwner pxContainer

但它们前三个成员布局一致:

xItemValue pxNext pxPrevious

所以 FreeRTOS 可以把MiniListItem_t当成一个简化版的ListItem_t使用。这样既能作为链表结束节点,又能节省内存。


六、删除链表节点时为什么要处理pxIndex

uxListRemove()中看到这段代码:

if( pxList->pxIndex == pxItemToRemove ) { pxList->pxIndex = pxItemToRemove->pxPrevious; }

这段代码的作用是:如果链表的遍历游标正好指向要删除的节点,就必须把游标移到一个仍然有效的节点上。

比如链表是:

xListEnd <-> A <-> B <-> C <-> xListEnd

假设:

pxIndex = B pxItemToRemove = B

如果删除 B 之后不处理pxIndex,那么pxIndex仍然指向 B,但 B 已经不在链表中,这样后续遍历可能出错。

所以删除前要把它改成:

pxList->pxIndex = pxItemToRemove->pxPrevious;

也就是让pxIndex退回到 B 的前一个节点 A。

为什么不是指向 B 的后一个节点 C?

因为 FreeRTOS 在获取下一个节点时,通常会先执行:

pxIndex = pxIndex->pxNext;

如果删除 B 后让pxIndex = A,下一次遍历时就会走到 C,刚好不会跳过节点。

如果直接让pxIndex = C,下一次再执行pxIndex = pxIndex->pxNext,就会跳过 C。

所以这里设置为pxPrevious是为了配合 FreeRTOS 的遍历逻辑。

http://www.jsqmd.com/news/1138513/

相关文章:

  • 终极指南:用10000+行Java代码构建完整的三国杀游戏框架
  • Wayback Machine浏览器扩展:你的免费网页时光机,拯救消失的互联网记忆
  • 构建下一代认知智能体的技术架构与实践
  • MoE架构解析:如何在普通电脑上运行753B参数的GLM5.2模型
  • 太炸了!开源 AI 语音工作室 Voicebox:克隆声音、听写输入、AI Agent 开口说话,全本地运行
  • Claude Code藏了三个月的“卧底代码“被扒了——阿里连夜全面禁用,这5个AI编程安全陷阱开发者必须知道
  • 工训赛智能小车 PCB 自制实战:从核心板到 4 路 BTN7971B 驱动,成本降低 60%
  • 双主轴同步加工VERICUT仿真:从原理到实践的全流程指南
  • 【Java毕业设计】基于 SpringBoot 的母婴健康月子服务管理系统的设计与实现 基于前后端分离的月子会所预约运维系统(源码+文档+远程调试,全bao定制等)
  • 长沙封窗避坑指南:这 5 个低价套路,装修业主基本都踩过
  • 重装系统须知
  • VERICUT双主轴同步加工仿真:车架高效加工与碰撞检测实战
  • 3 种激光雷达相机标定方案对比:cam_lidar_calibration vs 特征点法 vs 深度学习法
  • [实战]Python爬取全国考研数据与可视化(附源码)
  • 国产CPU指令集授权深度解析:ARM v8永久授权、X86 IP授权与LoongArch自研的3条技术路线
  • NitroGen:CVPR 2026最佳论文提名,英伟达让AI“打遍天下游戏”
  • 当 AI Agent 开始“团队作战”,Skill 也需要一个共享仓库:Nacos Skill Registry 入门(以及平替方案介绍)
  • 人工智能学习笔记-Transformer
  • 3000米驻空飞艇视频流融合,立体全域视频孪生野外态势侦监技术解读
  • 工业大模型算法与架构设计
  • wtdbg v2.5安装与使用--生信工具105
  • Spring Boot 3 + Vue 3 高校宿舍水电费管理系统源码前后端分离实战
  • WTAPI框架:解锁微信个人号开发的无限可能
  • 【Java课程设计/毕业设计】基于 SpringBoot+Vue 的物流快件流转管理系统的设计与实现 基于 SpringBoot 的快递物流信息化管理系统【附源码、数据库、万字文档】
  • 烫金与UV工艺的技术对比:基于350g铜版纸的实测数据与选型逻辑
  • 【Bug已解决】Codex Desktop 更新后无法启动/闪退解决方案
  • 锡膏存储方法有哪些?
  • Spring Boot 3 + Vue 3 + MySQL MES生产制造执行系统源码实战前后端分离
  • 收藏!3个落地点教你从零基础进阶AI应用层,数据说话!
  • 企业级开源AI 智能代码评审系统,一键部署,真正解决代码质量问题