STM32启动探秘:Reset_Handler如何一步步引导至main()函数?

STM32启动过程分析 1. 程序和数据在Flash和SRAM上的存储结构 在讲解 STM32 启动过程之前,我们先来了解一下 STM32 的程序和数据在 Flash 和 SRAM 上到底是如何存储的,因为有了这方面的知识之后,非常有助于我们理解 STM32 启动过程中还做了哪些隐藏的工作。关于详细的程序和数据存储分布信息,我们可以从Keil生成的 .map 文件中得到,要生成 .map 文件操作如下: 1.1 STM32的程序在flash上的存储结构 STM32 的程序在 Flash 上的存储结构如下图所示: 栈顶指针 M 阅读详情

1. STM32启动过程全景解析

当你按下STM32开发板的电源按钮,或者重新上电的那一刻,芯片内部就开始了一场精密的"启动芭蕾"。很多人以为程序直接从main()函数开始执行,其实在这之前,芯片已经完成了一系列复杂的准备工作。这个过程就像一场演出的幕后筹备,Reset_Handler就是那个忙碌的舞台总监,确保所有道具、灯光、演员都就位后,才拉开帷幕让main()函数登场。

我在实际项目中曾经遇到过一个问题:一个全局变量莫名其妙地变成了随机值,导致系统运行不稳定。经过深入排查,才发现是启动文件中的BSS段初始化出了问题。这个经历让我深刻认识到,理解STM32的启动过程不是学术研究,而是实实在在的工程需求。

STM32的启动过程严格遵循ARM Cortex-M内核的规范。上电复位后,处理器首先从地址0x00000000读取初始栈指针(MSP),然后从0x00000004读取复位向量的地址。这个复位向量指向的就是Reset_Handler函数。之所以选择这个地址,是因为Cortex-M系列使用Thumb指令集,地址的最低比特位需要设置为1来表示Thumb状态。

2. 向量表:启动过程的路标系统

2.1 向量表的结构与作用

向量表是STM32启动过程中的核心导航系统,它就像高速公路上的指示牌,告诉处理器应该去哪里处理各种事件。这个表包含了系统所有异常和中断的处理函数地址,按照固定的顺序排列。第一个条目是初始栈指针值,第二个条目就是复位向量,指向Reset_Handler。

在实际项目中,我经常需要自定义向量表来满足特殊需求。比如在bootloader设计中,需要重新映射向量表到不同的内存区域。这个过程虽然复杂,但给了我们极大的灵活性。向量表的基地址由VTOR(Vector Table Offset Register)寄存器控制,上电时默认为0,指向Flash的起始地址。

2.2 向量表的实际配置

让我们看一个具体的向量表配置示例。在标准的STM32启动文件中,通常会看到这样的汇编代码:

.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* 外设中断向量继续排列 */

这个数据结构定义了完整的异常处理框架。_estack表示栈顶地址,通常在链接脚本中定义。每个.word条目都对应一个异常处理函数的地址。当发生相应的异常时,处理器会自动跳转到对应的地址执行。

3. Reset_Handler的详细工作流程

3.1 数据段初始化:变量的诞生时刻

Reset_Handler首先要处理的就是数据段的初始化。这包括两个主要部分:已初始化数据段(.data)和未初始化数据段(.bss)。已初始化数据段存储着有明确初始值的全局变量和静态变量,这些初始值存储在Flash中,需要在启动时复制到RAM中。未初始化数据段则需要被清零,确保变量从确定的初始状态开始。

让我分享一个实际调试中遇到的坑。有一次发现一个全局变量的值每次上电都不同,最终发现是因为忘记在Reset_Handler中初始化.data段。编译器把初始值放在Flash中,但运行时需要在RAM中操作,如果没有这个复制过程,变量就会保持RAM中的随机值。

数据段初始化的具体过程如下:首先通过链接脚本提供的符号获取各段的起始和结束地址。_etext指向Flash中存储初始值的位置,_sdata和_edata界定RAM中数据段的范围。初始化代码将数据从Flash复制到RAM,确保所有已初始化变量获得正确的初始值。

3.2 BSS段清零:确保纯净的起点

BSS段的处理相对简单但同样重要。这个段包含所有未初始化的全局变量和静态变量,需要被清零。这是C语言标准的要求,确保这些变量从0开始。链接脚本提供_sbss和_ebss符号来标识这个段的范围。

在实际操作中,我推荐使用高效的存储器操作指令来完成这个任务。Cortex-M系列处理器提供了强大的存储器访问指令,可以快速完成大块内存的清零操作。例如,使用STM指令集可以一次存储多个寄存器,显著提高初始化速度。

// 优化的BSS段清零实现
void zero_bss_section(void)
{
    extern uint32_t _sbss, _ebss;
    uint32_t *p = &_sbss;
    
    // 使用字操作提高效率
    while (p < &_ebss) {
        *p++ = 0;
    }
}

4. 系统初始化与环境准备

4.1 时钟系统配置

时钟系统是STM32的心跳,正确的时钟配置至关重要。Reset_Handler通常会调用SystemInit()函数来初始化时钟系统。这个函数配置PLL、设置总线分频器、使能所需的外设时钟。不同的STM32系列有略微不同的时钟树结构,但基本理念相似。

以STM32F4系列为例,默认使用内部16MHz RC振荡器(HSI)作为时钟源。SystemInit()会配置PLL将时钟提升到更高的频率,比如168MHz。这个过程需要仔细配置多个寄存器,包括PLL分频系数、倍频系数等。

我在项目实践中发现,时钟配置的错误往往导致最难以调试的问题。比如曾经遇到USB设备无法正常工作的问题,最终发现是48MHz时钟配置不正确。因此理解时钟树结构并正确配置各个参数非常重要。

4.2 浮点单元初始化

对于带有浮点单元的Cortex-M4/M7内核,还需要初始化FPU。这是很多人容易忽略的一步。FPU的使用需要在启动时设置CPACR寄存器的相应位。如果不正确初始化FPU,使用浮点运算时就会触发硬错误。

// FPU初始化代码
void enable_fpu(void)
{
    // 设置CPACR的CP10和CP11字段为完全访问权限
    SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
}

这个操作必须在任何浮点运算之前完成,否则第一次浮点指令就会导致硬件错误。我建议在SystemInit()函数中尽早完成这个配置。

5. 链接脚本:内存布局的蓝图

5.1 理解链接脚本的作用

链接脚本是启动过程中另一个关键组件,它定义了内存布局和各个段的放置位置。虽然不直接属于Reset_Handler的代码,但它与启动过程密不可分。链接脚本告诉链接器如何组织代码和数据在内存中的布局。

在实际项目中,我经常需要修改链接脚本来满足特殊需求。比如优化内存使用、将关键代码放到更快的内存区域、或者配置多个内存区域等。理解链接脚本的语法和功能是嵌入式开发的重要技能。

5.2 典型链接脚本分析

让我们分析一个简单的链接脚本示例:

MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}

SECTIONS
{
    .isr_vector :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector))
        . = ALIGN(4);
    } >FLASH
    
    .text :
    {
        . = ALIGN(4);
        *(.text)
        *(.text*)
        . = ALIGN(4);
        _etext = .;
    } >FLASH
    
    .data : AT (_etext)
    {
        . = ALIGN(4);
        _sdata = .;
        *(.data)
        *(.data*)
        . = ALIGN(4);
        _edata = .;
    } >RAM
    
    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
        *(.bss)
        *(.bss*)
        . = ALIGN(4);
        _ebss = .;
    } >RAM
}

这个脚本定义了Flash和RAM的内存区域,并安排了各个段的存放位置。特别注意.data段的AT(_etext)语法,这表示.data段的内容在Flash中存储在_etext之后,运行时则加载到RAM中。

6. 从汇编到C:切换的艺术

6.1 汇编启动代码详解

虽然我们可以用C语言实现Reset_Handler,但传统的启动代码通常用汇编编写。这是因为在最开始的启动阶段,有些操作用汇编更加直接和可靠。比如设置栈指针、初始化.data段等。

让我分享一个实用的汇编启动代码示例:

.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
    // 设置栈指针
    ldr sp, =_estack
    
    // 复制.data段从Flash到RAM
    ldr r0, =_sdata
    ldr r1, =_edata
    ldr r2, =_etext
    movs r3, #0
    b copy_data_loop

copy_data_loop:
    ldr r4, [r2, r3]
    str r4, [r0, r3]
    adds r3, #4
    adds r5, r0, r3
    cmp r5, r1
    blt copy_data_loop

    // 清零.bss段
    ldr r0, =_sbss
    ldr r1, =_ebss
    movs r2, #0
    b zero_bss_loop

zero_bss_loop:
    str r2, [r0]
    adds r0, #4
    cmp r0, r1
    blt zero_bss_loop

    // 跳转到main函数
    bl main

    // 如果main返回,进入无限循环
    b .

这段代码完成了所有必要的启动任务,最后跳转到main函数。在实际使用中,可能还需要添加其他初始化代码,比如调用SystemInit()等。

6.2 C语言实现的优化技巧

虽然汇编代码效率高,但用C语言实现Reset_Handler更易于理解和维护。现代编译器的优化能力很强,能够生成相当高效的代码。以下是一些优化技巧:

首先使用register关键字提示编译器使用寄存器存储关键变量。其次使用const和volatile适当修饰变量,帮助编译器优化。还可以使用内联汇编处理特定的底层操作。

void Reset_Handler(void) 
{
    // 声明链接器定义的符号
    extern uint32_t _etext, _sdata, _edata, _sbss, _ebss;
    
    // 复制.data段
    uint32_t *src = &_etext;
    uint32_t *dest = &_sdata;
    uint32_t size = (uint32_t)(&_edata - &_sdata);
    
    for (uint32_t i = 0; i < size; i++) {
        dest[i] = src[i];
    }
    
    // 清零.bss段
    uint32_t *bss_start = &_sbss;
    uint32_t *bss_end = &_ebss;
    size = (uint32_t)(bss_end - bss_start);
    
    for (uint32_t i = 0; i < size; i++) {
        bss_start[i] = 0;
    }
    
    // 系统初始化
    SystemInit();
    
    // 调用main函数
    main();
    
    // 安全处理
    while(1);
}

7. 常见问题与调试技巧

7.1 启动失败的常见原因

在实际开发中,启动过程可能会遇到各种问题。最常见的问题包括栈指针初始化错误、数据段复制不完整、时钟配置错误等。这些问题通常表现为程序跑飞、硬件错误、或者外设工作不正常。

我总结了一个启动问题排查清单:首先检查栈指针是否指向有效的RAM地址;其次确认.data段复制是否正确,可以通过在Reset_Handler中设置断点来验证;然后检查.bss段是否正确清零;最后验证时钟配置是否符合预期。

7.2 使用调试器分析启动过程

现代调试器提供了强大的功能来分析启动过程。我习惯使用J-Link配合Ozone或者STM32CubeIDE的调试功能。设置复位后暂停,然后单步执行启动代码,观察寄存器和内存的变化。

特别有用的技巧是在Reset_Handler开始处设置断点,然后观察栈指针的值。还可以在.data段复制前后检查相关内存区域的内容,确保复制操作正确完成。对于.bss段,可以在清零操作后检查内存是否确实变为0。

另一个有用的技巧是使用调试器查看向量表内容。确认复位向量确实指向Reset_Handler,其他异常向量也指向正确的处理函数。这可以帮助发现链接或编程错误。

8. 自定义启动流程的高级应用

8.1 多核启动与同步

在一些高级应用中,可能需要自定义启动流程。比如在多核系统中,需要协调多个核的启动顺序。或者在一些安全关键应用中,需要在启动过程中进行自检和验证。

我参与过一个双核STM32H7的项目,需要精确控制两个核的启动时序。主核完成基本的硬件初始化后,唤醒从核并同步启动进度。这需要深入理解芯片的启动机制和核间通信方式。

8.2 安全启动与固件验证

在现代嵌入式系统中,安全启动变得越来越重要。可以在Reset_Handler中添加固件验证逻辑,确保只有经过签名的固件才能运行。这通常涉及密码学操作和签名验证。

实现安全启动时,需要在最早可能的时间进行验证,通常在.data和.bss初始化之后,但在跳转到main之前。如果验证失败,可以进入安全模式或者阻止进一步执行。

void Reset_Handler(void)
{
    // 正常的.data和.bss初始化
    initialize_data_sections();
    
    // 固件验证
    if (!verify_firmware_signature()) {
        // 验证失败,进入安全模式
        enter_safe_mode();
        while(1);
    }
    
    // 继续正常启动流程
    SystemInit();
    main();
}

这种设计确保了即使攻击者能够更新固件,也无法运行未经验证的代码。

stm32启动文件里面的__main和主函数main() 但由于__main函数除了调用main()以外在前后还有初始化堆栈和库函数、调用exit()的操作,而我这里直接把__main函数替换成自己想要运行的函数则不包含那些操作,换句话说启动文件前面设置的堆和栈大小都白设置了,库里面有的值如果有设置的也白设置了,现在都成了默认的值,还有exit()里面有啥特别的用处也不了解,等于把一个本来该有但未知用途的模块删了,这肯定是不行的。__rt_entry()该函数先初始化堆栈和库函数,然后即调用主函数main(),从而进入用户程序。二、修改主函数名称的方法。 阅读详情

相关推荐

STM32F103启动过程分析:从CPU上电到main()

自留

Hippogypi的博客 1948

STM32的启动堆栈初始化

有几个问题,众多博文中抄来抄去,内容一样,却没有解释清楚 上电初始化堆栈,在进入_main后又说初始化堆栈,有什么不同 堆栈的地址是怎么得出来的 关于这两个问题,先借用一下要标准的启动流程 一般而言,系统上电后第一个执行的是由汇编所编写的启动文件,其主要工作为一下五部分: (1)、初始化堆栈指针SP=_initial_sp (2)、初始化PC指针,令其=Reset_Handler ...

qq_23899395的博客 7431

自己写stm32的引导启动文件

项目硬件验证环境: STM32F205 项目码云git地址: https://gitee.com/liwandong/ShareRTOS 通常情况下可以直接使用ST公司提供的startup_stm32f2xx.s 这个文件进行mcu启动的引导,不是很喜欢汇编,所以将其启动过程改为使用C语言来实现,首先分析一下startup_stm32f2xx.s这个文件的启动流程大概如下所示:...

做一个有技术追求的人 2110

STM32启动文件初探之startup_stm32f10x_hd.s(Reset_Handler函数)

该文件主要实现目的: 设置初始SP 设置初始PC=Reset_Handler 设置向量表入口地址,并初始化向量表 调用SystemInit,把系统时钟配制成72M,SystemInit在库文件system_stm32f10.c定义 调转到标号_main,最终来到C程序文件 ; Reset handler Reset_Handler PROC EXPORT Res

无名的博客 8083

深入解析STM32启动流程:从Reset_Handler到main()的幕后故事

本文深入解析STM32启动流程,从Reset_Handler到main()的幕后机制。详细介绍了硬件复位、初始SP和PC值获取、Reset_Handler执行、SystemInit调用、__main运行时环境初始化等关键步骤,帮助开发者理解STM32启动过程,解决调试中的常见问题。

weixin_28256233的博客 414

一文看懂Stm32程序结构和启动过程

Stm32程序启动过程分析 程序在Flash的结构 使用Keil编译程序后,程序分为4部分: Code:代码区,指程序中代码即函数体的大小,注意程序中未使用的函数也会算在CODE中,也即会占用FLASH空间,因此不用的函数最好删除掉,以免占用过多FLASH空间; RO-data:RO就是只读的意思,程序中只读的变量(也就是带Const的)和已初始化的字符串等; RW-data:特指已初始化的可读可写全局/静态变量; ZI-data:未初始化的可读可写全局/静态变量,注意初始化为0也算做未初始化,用到的堆

baidu_38808356的博客 4147

STM32启动模式

通过判断BOOT引脚的连接情况识别启动模式,如设置为BOOT0 = 0 ,BOOT1 = x 时,选择主闪存存储器,把0x08000000映射到0x00000000,从0x08000000获取MSP的初始值,这个值就是栈顶地址;在__main函数中,会初始化内核的寄存器,划分堆空间(如果使用到的话)和栈空间。2:从地址0x0000 0004处取出程序计数器指针PC的初始值,这个值是复位向量。对于H1系列:在系统复位后,SYSCLK的第四个上升沿,BOOT引脚的值会被锁存。PROC: 定义子程序。

L_Naive的博客 1013

ARM Cortex-M内核启动探秘:STM32如何从硬件复位跳转到main()

本文深入解析ARM Cortex-M内核在STM32中的启动流程,从硬件复位到执行main()函数的完整过程。详细探讨启动文件结构、异常向量表构建、C运行时环境初始化及系统时钟配置,帮助开发者掌握底层运行机制,解决常见启动问题并优化启动性能。

h9j8k7l6m5n的博客 717

【Cortex-M内核篇】--自复位

本文深入探讨了ARM Cortex-M内核的复位机制,包括三种复位类型:上电复位(影响整个芯片)、系统复位(影响处理器和外设)和处理器复位(仅影响核心)。文章详细介绍了通过CMSIS接口实现软件复位的方法,解析了复位流程的三个关键步骤:初始化栈指针、获取复位向量和执行系统初始化。此外,还通过KEIL调试示例展示了复位过程,并分析了启动文件和栈顶指针的计算原理,解释了实际栈顶地址与理论值的差异原因。这些知识对嵌入式系统开发和调试具有重要指导意义。同时也是做个记录,后续自己方便查看

weixin_42857472的博客 2803

STM32启动文件解析:Reset_Handler到main()的完整调用链(附逆向分析)

本文深入解析了STM32从Reset_Handler到main()的完整启动流程,详细拆解了启动文件、__main函数及C运行时环境构建的关键步骤。通过逆向分析揭示了数据段搬运、BSS段清零等底层机制,并探讨了定制启动流程的风险与正确优化方法,帮助开发者从根本上理解并掌控嵌入式系统初始化过程。

weixin_29172963的博客 555

STM32的启动过程 — startup_xxxx

无论是是何种MCU,从简单的51,MSP430,到ARM9,ARM11,A7 都必须有启动文件,因为对于嵌入式开发,绝大部分情况都是使用C语言,而C语言一般都是从main 函数开始,但是对于MCU来说,他是如何找到并执行main函数的,就需要用到“启动文件”,就是各种 startup_xxxx.s 文件。启动文件是使用机器认识的汇编语言,经过一些必要的配置,最终能够调用 main 函数,使得用户程序能够在 MCU上正常运行起来的必备文件。

2401_89225025的博客 1438

【32单片机学习】(11)STM32启动过程详解

以stm32f1x系列单片机为参照,经过查阅官方手册和对代码进行单步调试,详细地叙述了STM32上电启动的具体过程。对于关键性的知识都指明了出处。

sudoeast的博客 8794

STM32启动文件(startup)深度解析:从Reset_Handler到main()

* 修改后的链接脚本 */MEMORY/* Bootloader区域:0x08000000 ~ 0x08003000 (12KB) *//* APP区域:0x08003000 ~ 0x08010000 (52KB) *//* Bootloader链接脚本 */SECTIONS/* APP链接脚本 */SECTIONSAPP的启动文件修改;APP的startup_app.s使用独立的复位处理函数;外部中断向量需要重定位;...;关键:重定位向量表到APP区域。

从事移动开发八年,csdn博客专家,阿里云特邀专家,华为云云享专家,高级网络信息安全工程师,大数据工程师(高级),python技术开发(高级),HarmonyOS 应用开发者高级认证,现在某学校任教。 1万+

单片机STM32 从上电到 main()函数之间到底发生了什么?

从 STM32 芯片上电到 main() 函数运行,到底发生了什么?这篇文章,我们从零开始,手把手带你搞懂 STM32 的裸机 C 启动过程,顺便点亮一块开发板上的 LED 小灯,体验一下从“裸奔”到“优雅运行”的快感。

奋斗在路上的博客 1278

STM32启动过程浅析(MAP文件、STM32启动过程、启动模式、Reset_Handler函数、__initial_sp堆栈的起始地址、堆栈)

STM32启动过程浅析(MAP文件、STM32启动过程、启动模式、Reset_Handler函数、__initial_sp堆栈的起始地址、堆栈)

扬帆起航 1654

Stable Diffusion跑图总爆显存?别急着换显卡,试试这个PYTORCH_CUDA_ALLOC_CONF参数调优

本文深入解析了Stable Diffusion运行中常见的显存不足问题,特别是由显存碎片化导致的假性OOM现象。通过详细讲解PYTORCH_CUDA_ALLOC_CONF参数中的max_split_size_mb设置技巧,提供了一套完整的显存优化方案,帮助用户在无需升级硬件的情况下显著提升AI绘画的稳定性与效率。

weixin_42524100的博客 347

从Reset_Handler到main():详解STM32F103启动文件的跳转逻辑

本文详细解析了STM32F103启动文件startup_stm32f103xe.s中从Reset_Handler到main()函数的跳转逻辑,包括栈堆初始化、中断向量表建立、系统时钟配置及C运行时环境初始化等关键步骤,并提供了MDK-ARM调试实践与常见问题排查方法,帮助开发者深入理解嵌入式系统启动机制。

pytorch8learner的博客 632

同步辐射材料科学应用指南:从XAFS原位实验到数据处理

材料结构分析是材料研究的核心环节,但传统实验室X射线受限于能量固定、亮度不足,往往难以回答原子局域配位、非晶结构及动态演变等关键问题。同步辐射光源以高通量、连续可调、偏振可控及脉冲时间结构等特性,将材料表征从静态观察推进到原位动态追踪层次。基于X射线吸收精细结构谱(XAFS)、原位实验、X射线衍射与散射、微区成像等技术,研究人员可实现元素选择性分析、近邻原子环境解析及真实工况下的结构演变监测。本文从技术原理出发,覆盖实验方案设计、样品制备、现场对光、数据处理与常见故障排查,并结合典型应用场景,为材料科学领域

weixin_32612263的博客 174

STM32的启动过程 — startup_xxxx.s文件解析(MDK和GCC双环境)

了解STM32在MDK和GCC开发环境的下的启动过程,过分析STM32 的startup_xxxx.s

不浮夸,不将就,认真对待学知识的我们 3万+

StarUML最新版汉化与破解二合一教程:5分钟搞定永久使用

本文提供StarUML最新版的高效使用指南,涵盖从安装到深度定制的全流程优化方案。包括软件获取最佳实践、界面个性化设置、核心建模技巧、团队协作策略及性能优化建议,帮助架构师和开发团队提升UML建模效率。特别推荐版本控制集成和文档自动化输出方案,适用于各类规模的项目开发。

weixin_42530732的博客 454

Ubuntu环境下rknn-toolkit2的安装与兼容性解析

本文详细解析了在Ubuntu系统上安装瑞芯微rknn-toolkit2工具链的完整流程与关键注意事项。文章重点剖析了rknn-toolkit2与旧版rknn-toolkit的核心兼容性问题,强调两者在API、模型格式及运行时库上互不兼容,并推荐使用Conda虚拟环境进行隔离管理,为RK3566、RK3588等开发板的AI模型部署提供清晰的实践指南。

weixin_29041767的博客 377

【正点原子STM32】STM32启动过程浅析(MAP文件、STM32启动过程、启动模式、Reset_Handler函数、__initial_sp堆栈的起始地址、堆栈)

一、MAP文件浅析二、STM32启动过程三、总结参考资料:STM32 MAP文件浅析.pdf、STM32 启动文件浅析.pdf、Cortex-M3权威指南(中文).pdf 路径:战舰 V4\资料\1,入门资料\STM32 MAP文件浅析.pdf、STM32 启动文件浅析.pdf    战舰 V4\资料\8,STM32参考资料\Cortex-M3权威指南(中文).pdf MAP文件(Memory Map File)是编译器生成的连接地图文件,提供了有关程序在内存中的布局信息。MAP文件包含了代码、数据、堆栈等

咖喱年糕的博客 2978

【超详细】磁力计数据读取--以IST8310为例

第一章讲了IST8310的特性,I2C通信,最高支持400kHz通信速率,14位或者16位自适应数据输出等特性第二章讲了内部结构第三章讲了电气特性第四章讲了如何联系他们然后数据手册就没了。。。在大疆的开发手册中找到了寄存器的手册,如下:点开I2C配置选项, 下图是配好的:观察IST8310的数据手册,发现其支持最大400kHz的I2C通信速率,也就是快速I2C模式,所以第一行I2C Speed Mode我们选Fast Mode同时不要忘记了在C板中I2C3的两个IO口分别是PA8和PC9(一般来说都是这两个

lmh_12138的博客 1万+
上一篇: 低功耗与数据稳定:不变模式在IoT边缘设备中的隐形价值
下一篇: 从零到省一:蓝桥杯嵌入式竞赛的代码架构设计与调试心法
gitlab7runner
博客等级 码龄4年 78粉丝 1747原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值