在STM32上模拟Linux自动初始化

发布者:EtherealHeart最新更新时间:2024-04-16 来源: elecfans关键字:STM32 手机看文章 扫描二维码
随时随地手机看文章

Linux中有很多编程思想可以学习,很多大佬把这些思想、机制运用到单片机的编程上。

下文,在STM32上模拟Linux kernel自动初始化流程。


通常我们写程序都是按照这个套路,一个函数一个函数按照顺序逻辑一个一个的执行下去。

wKgaomRZoXqAX6rbAAEIxGySr_E226.jpg?imageView2/2/w/1000

如果逻辑非常复杂,涉及的模块比较多,那么这种顺序执行的代码就会比较臃肿,各模块耦合非常紧密。Linux kernel 中,有各种外设驱动,想按照一个顺序逻辑执行下去,几乎是不可能的。

而kenrel 代码能有这么大的代码量,大而不乱,把各层次,各模块有效的分离,而大量的代码又有逻辑的组织在一起,和这个initcall 有至关重要的作用。

通过模仿这种方式,最后把图片中main函数代码清空,分离这种逻辑,又实现同样的功能。

如何能实现这样的功能了,需要一些背景知识:

1,程序代码的组织

2,链接脚本相关的知识。

3,函数指针的应用。

wKgZomRZoXqAGB9mAAB5N68kDh0447.jpg?imageView2/2/w/1000

代码的组织,如图片需要知道变量a,b及函数指针 f,f2是存放在程序的哪些段中,可以去看一下这篇stm32 启动代码 实现|C语言,上述的a,f都是存放在bss 段中,b,f2是存放在data段中,因为已经给定了初始值,而实现这个intcall会把需要自动初始化的数据放到一个自定义的段中去,如.initcall。

如何放到特定的段中了,就需要用到了attribute((section)) 关键字来改变的数据存放段了。

目前的程序编译出来用到了这些个段,除了.isr_vector也是添加的,其他都是编译器默认的。

wKgaomRZoXqAYgguAALDZ7tSpOo994.png?imageView2/2/w/1000

先加段代码:

wKgZomRZoXqAYaVoAABqFGapjxQ792.png?imageView2/2/w/1000

当然这还不够,还需要告诉连接器(LD) 要把 .initcall 段也链接到程序中,所以也需要这段修改。

wKgaomRZoXqAKTtAAAEV4ZDxaz4342.jpg?imageView2/2/w/1000

这段按8字节对齐,定义两个全局变量,及按0-5顺序的链接这些数据,这样的两处修改,再来看一下程序各段的情况。

如图片:

wKgaomRZoXqAAOXZAAIqQD5KTys550.png?imageView2/2/w/1000

已经多出红色框框为.initcalls段,这段总共是8个字节,从0x80005a8除开始。

在来看一下具体的这一段的情况,用readelf 工具。

wKgaomRZoXqAO4SyAACwT_haxtc325.png?imageView2/2/w/1000

和上面的size工具是匹配的,而绿色框框的地址就是SystemInit(0x08000231,小端模式。)

wKgZomRZoXqABp0rAAEJjXL8JG8510.png?imageView2/2/w/1000

所以通过attribute及修改链接脚本,就把函数指针变量放到了.initcall 段中。

那么如何来调用这个函数了,和之前的初始化data段数据类似,遍历这个段,然后取出这个函数地址,然后强制把段中的地址,转成函数指针,再直接调用即可。

wKgaomRZoXqAJnrlAACJK4aTfXI240.png?imageView2/2/w/1000

wKgZomRZoXqAMgNBAAFWSNbCAg4585.png?imageView2/2/w/1000

实现的这张图片,就是从.initcall段中取出函数地址,然后直接调用,非常容易把函数的地址及这个函数指针变量的地址搞混。

代码这么修改,需要自动初始化函数的确是可以调到了,但是每次都写这么长长的一段static initcall_t __ attribute__(( __ used__,__ section__('.initcall.0.init'))),就是不舒服. linux kernel中通过宏来修改。

这个也一样。

wKgZomRZoXqAAwZjAAHF0FeQdh0722.png?imageView2/2/w/1000

添加 按照程序逻辑顺序执行的一些宏

0,low_level_init 比如放始化系统基本时钟

1,arch_init 比如放CPU架构d如初始化NVIC的一些初始化。

2,dev_init 外设模块初始化,比 i2c, flash, spi等。

3,board_init 做具体硬件板及的一些设置。

4,os_init 操作系统的一些设置如,文件系统,网络协议栈等。

5,app_init 最后跑用户程序。

把自己的程序也做一下修改,用宏代替。这样子掉调用do_initcalls 就会按照0,1-到5的顺序执行了。

wKgaomRZoXqAR4AIAADA5rrXPBo591.png?imageView2/2/w/1000

wKgaomRZoXqANQbXAADaxBlZWoc941.png?imageView2/2/w/1000

最后在来看一下initcall 段:

wKgaomRZoXqAee7BAAF4g331D14969.png?imageView2/2/w/1000

wKgZomRZoXqAF2OkAAAPYn2Y9cE421.png?imageView2/2/w/1000

wKgZomRZoXqAIQ7QAAJ4F9IxZcE643.jpg?imageView2/2/w/1000

wKgZomRZoXqAa-9rAAEBcgMElwo640.jpg?imageView2/2/w/1000

这样只要在需要自动初始化函数加上类似于dev_init(),app_init() 就可以了,就会自动调用到,而不需要main 函数中一个一个的顺序执行。

比如i2c控制的初始化放到dev_init 中,下面挂了很多i2c的从设备,只要分别给个从设备用app_init 初始化就行,即使来了一个新的,也用这app_init初始化就行,也不需要更改原来的,高度的分离模块间的耦合度。

这样模拟Linux kenerl 初始化验证成功,最后上库。


关键字:STM32 引用地址:在STM32上模拟Linux自动初始化

上一篇:STM32F103制作FlashDriver的实现过程
下一篇:基于STM32的自平衡机器人设计

推荐阅读最新更新时间:2026-03-21 12:09

linux gpio模拟i2c
/* 这是我模仿其他人写的程序, 仅仅是把他改写成适合自己开发板的程序 */ #include linux/miscdevice.h #include mach/regs-gpio.h #include mach/hardware.h #include linux/fs.h #define DEVICE_NAME FINALL #define RETRY_TIMES 1 #define GPIO_SCL S3C2410_GPF3 #define GPIO_SDA S3C2410_GPF0 #define GPIO_SDA_OUTP S3C2410_GPF0_OUTP #define GPIO_S
[单片机]
STM32外设使用中的五个易错技巧与避坑指南
STM32作为嵌入式开发领域的热门微控制器,功能极为丰富,几乎能够完成所有常见控制任务,如GPIO等外设应有尽有。然而,正因为其功能强大,开发过程中也更容易遇到各种陷阱。许多初学者甚至经验丰富的开发者,常在外设配置上浪费大量时间,调试许久仍难以定位问题。本文总结了5个STM32外设使用中最易出错的技巧,旨在帮助你少走弯路、提高开发效率。 1. GPIO 配置别忘了上拉/下拉 很多初学者在读取按键、外部或中断输入时,会发现输入状态总是不稳定,甚至出现抖动或误触发。这通常是因为 GPIO 输入口浮空造成的。 常见坑: 输入引脚未配置上拉/下拉,导致状态随机波动。 上拉/下拉和外部电路冲突,影响可靠性。 输入误
[嵌入式]
STM32输入捕获
输入捕获是处理器捕获外部输入信号的功能,基于定时器抓取输入信号指定触发方式之间的长度。具体有下面三种触发情况: 1、 上升沿触发 2、 下降沿触发 3、 上下都触发 当触发条件发生后,捕获比较寄存器锁定当前的计数值,如果开启了中断或者DMA,就可以通过中断或DMA及时获得数据进行处理。有时可能遇到上一次触发的标志还没清除,下次触发就发生了,此时会将over-capture标志置位,对于可能出现over-capture的情况,建议先读取数据再清除标志,避免在读取标志后及读取数据前这段时间错过over-capture。 配置输入捕获的步骤: 1. 打开定时器和对应输入引脚的时钟 2. 配置引脚为对应的复用功能 3.
[单片机]
野火STM32学习笔记(构建库函数模型第五节课)
解释初始化函数是怎么运作的 第四节课我们已经了解了如何通过固件库编程的方式初始化一个GPIO口,配置其速度,工作模式等等,这一切都有一个“幕后黑手”就是那一个一百多行的GPIO_Init()函数。我们再来一睹一下芳容: 第一部分 我们从第一行开始看: 大括号内第一二行都是存放这些变量的初始值,可以不用太在意。再看下面模式配置那边,第一行注释是把模式的低四位存入变量currentmode,这样做的目的是取出这些值(与0x0F相与取值),再判断它的bit4位是几(如下图),是0则是输入,是1则是输出,判断方式也很简单,即赋值给currentmode的值和0x10(二进制00010000)与运算,看看是不是0就知
[单片机]
STM32实现软件复位
实现代码(调用该函数即可): //重新启动 void restartSystem(){ __set_FAULTMASK(1); HAL_NVIC_SystemReset(); } cut-off 相关知识: STM32关全局中断: 第一种方法,:操作PRIMASK寄存器 __set_PRIMASK(0) /* 使能全局中断 */ __set_PRIMASK(1) /* 禁止全局中断*/ 或者 __enable_irq(); /* 使能全局中断 */ __disable_irq(); /* 禁止全局中断*/ PRIMASK 用于除能在 NMI 和硬 fault 之
[单片机]
stm32 DMA+IDLE modbus_rtu
没有做3.5T延时判断,直接用IDLE中断,所以时间间距会更小。 此程序只使用了两个功能码:03查询数据,06修改从机地址。 需要发送的数据直接放在data 数组里面,协议直接从此数组中取数据然后发送。 00为广播地址,当忘记从机地址时使用此地址发送命令修改从机地址。 modbus_slave.c #include &# 34;stm32f10x.h" #include &# 34;modbus_slave.h" #include &# 34;bsp_usart_dma.h" #include &# 34;bsp_user_lib.h" static void Modbus_Sen
[单片机]
基于stm32标准库独立按键的多按键状态机的实现
写在前面 一般引用都写在最后,但是这篇博文(https://www.cnblogs.com/ZzJan/p/11334869.html)对我这个状态机的影响很大,我这里有许多借鉴他的思维。所以写在前面,以表敬意 简单按键检测 一开始学习单片机的时候我接触到按键的时候就知道按键有抖动,记得当初按键消抖分为硬件和软件,硬件上常用于复位按键如下图 硬件消抖 软件上来说,最经典的消抖 if(KEY1 == 0) { delay_ms(20); // 延时消抖 if(KEY1 == 0) { while(KEY1 == 0);//堵塞,等待松开 // 按键按下处理代码 } }
[单片机]
基于<font color='red'>stm32</font>标准库独立按键的多按键状态机的实现
STM32 PC13 PC14 PC15 PB3 PB4 PA13 PA14 PA15 做普通IO口笔记
在MCU刚上电时,PC13作为侵入检测TAMPER引脚, PC14、PC15默认是作为连接32.768K低速外部晶振用的脚,PB3、PB4、PA15是JTAG调试脚,不能直接作为普通IO使用,下面是将他们作为普通IO使用的配置方法: STM32的PC13、PC14、PC15用作普通IO口设置方法 1.引脚定义 2.引脚用作普通IO口的条件 注: 1、最新文档中已经删除“在同一时间只有一个引脚能作为输出”也就是三个引脚可以同时作为输出引脚,其他条件未变 。 2、需要备份区域由VDD供电才可以。把VBAT通过一个100nf电容接到VDD上,重新上电3个LED就可以闪烁了。手册说不能驱动LED,我把LED用灌电流的接法(共阳极
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved