Flash2833x_API的cmd文件解析

一、先搞懂 F28335 的核心硬件内存概念

在看 cmd 文件之前,必须先明确 F28335 的物理内存布局,这是 cmd 文件的底层依据

1. F28335 的内存分类与 PAGE 机制( PAGE 0/PAGE 1)

F28335 是基于哈佛架构 的 DSP,哈佛架构的核心特点是程序存储器和数据存储器分开寻址 ,TI 为了适配这个架构,在 F28335 中引入了PAGE 0PAGE 1的逻辑分区机制(注意:不是物理上的两个内存芯片,是逻辑分区)。

分区 作用 对应物理内存类型
PAGE 0 程序内存(Program) 存储代码、常量、中断向量、BootROM
PAGE 1 数据内存(Data) 存储变量、堆栈、外设寄存器、DMA 数据

关键注意点

  • 同一个物理内存地址不能同时被定义到 PAGE 0 和 PAGE 1 (比如不能把 RAML0 既放在 PAGE 0 又放在 PAGE 1),否则会导致程序 / 数据冲突、系统崩溃(这也是 cmd 文件中MEMORY部分注释里强调的点)。
  • F28335 的物理内存是统一编址 的(即每个物理内存有唯一的地址),PAGE 0/PAGE 1 只是链接器和 CPU 寻址时的逻辑划分
2. F28335 的物理内存组成(对应 cmd 文件中的MEMORY部分)

F28335 的物理内存主要包括:片内 RAM片内 FlashOTPBoot ROMXINTF(外部接口) 。其中,Flash 是重点 ,因为Flash28_API就是操作它的。

(1) 片内 RAM(高速读写,用于运行代码 / 存储数据)
  • RAMM0/RAMM1:低地址 RAM(0x000000 开始),通常用于堆栈、外设寄存器映射、小量数据。
  • RAML0~RAML7:高地址 RAM(0x008000 开始),分为 8 个块(每块 4KB),是主要的用户 RAM,可用于运行代码(PAGE 0)或存储数据(PAGE 1)。
  • 特点:访问速度快(0 等待状态),但掉电丢失数据。
(2) 片内 Flash(非易失性,用于存储程序 / 常量)

这是Flash28_API操作的核心对象,F28335 的片内 Flash 被划分为多个扇区(Sectors) (也叫 Blocks),每个扇区有独立的地址范围和大小,对应 cmd 文件中的FLASHH~FLASHA

Flash 扇区名称 起始地址(origin) 长度(length) 大小 备注
FLASHH 0x300000 0x008000 32KB 最高地址的 Flash 扇区
FLASHG 0x308000 0x008000 32KB
FLASHF 0x310000 0x008000 32KB
FLASHE 0x318000 0x008000 32KB
FLASHD 0x320000 0x008000 32KB 常用作 ramfuncs/Flash API 的存储区
FLASHC 0x328000 0x008000 32KB
FLASHB 0x330000 0x008000 32KB
FLASHA 0x338000 0x007F80 ~32KB 包含 CSM、启动向量等

Flash 扇区的关键特性

  1. 擦除粒度 :Flash 只能按扇区擦除 (不能按字节擦除),比如擦除 FLASHA 必须擦除整个扇区,而Flash28_API的核心功能之一就是实现扇区擦除。
  2. 编程限制:向 Flash 写入数据前,必须先擦除对应的扇区(擦除后扇区内容为 0xFFFF)。
  3. 执行冲突不能从正在被擦除 / 编程的 Flash 扇区中运行代码 (比如你在 FLASHA 中运行代码,同时尝试擦除 FLASHA,会导致总线错误)。这是Flash28_API需要放到 RAM 中运行的根本原因
(3) 其他内存(辅助了解)
  • OTP:一次性可编程内存,用于存储加密密钥、校准数据。
  • Boot ROM:固化了启动程序、IQ 数学表、FPU 表,不可修改。
  • XINTF:外部内存接口,用于扩展外部 Flash/RAM。

二、cmd 文件的MEMORY部分:将硬件内存映射为链接器的逻辑块

了解了硬件内存后,再看 cmd 文件的MEMORY部分,就会发现它只是将 F28335 的物理内存地址和大小,定义为链接器能识别的逻辑名称 (比如FLASHDRAML0)。

示例:MEMORY部分的核心代码解析
bash 复制代码
MEMORY
{
PAGE 0:    /* Program Memory:程序内存,对应哈佛架构的程序空间 */
   RAML0       : origin = 0x008000, length = 0x001000  /* 物理RAML0的地址和大小 */
   FLASHD      : origin = 0x320000, length = 0x008000  /* 物理FlashD的地址和大小 */
   FLASHA      : origin = 0x338000, length = 0x007F80  /* 物理FlashA的地址和大小 */
   // 其他程序内存块...

PAGE 1 :   /* Data Memory:数据内存,对应哈佛架构的数据空间 */
   RAMM1       : origin = 0x000400, length = 0x000400  /* 物理RAMM1的地址和大小 */
   RAML4       : origin = 0x00C000, length = 0x001000  /* 物理RAML4的地址和大小 */
   // 其他数据内存块...
}
  • origin :物理内存的起始地址(对应硬件手册中的地址)。
  • length :内存块的大小(十六进制)。
  • 逻辑名称 :比如RAML0FLASHD,是链接器的别名,方便后续分配段。

在 F28335 的链接器命令文件(.cmd)中,SECTIONS是和MEMORY并列的核心部分,两者是 **"逻辑段" 与 "物理内存" 的映射关系 **。我们可以用一个通俗的比喻来理解:

组件 类比 作用
MEMORY部分 一栋大楼的房间 定义了硬件的物理内存(Flash 扇区、RAM 块),每个 "房间" 有地址(origin)、大小(length)、分区(PAGE 0/1)
SECTIONS部分 大楼里的人和物 定义了程序的各个 "逻辑段"(代码、数据、常量、堆栈等),并指定每个 "人 / 物" 要放进哪个 "房间" 里
2. SECTIONS两个核心概念

理解这两个概念,就能看懂所有段配置的逻辑:

(1) 段(Section):程序的 "逻辑模块"

段是程序的最小逻辑单元,分为两类:

  • 编译器默认段 :由 C/C++ 编译器自动生成的段,是固定名称的(比如.text.cinit.ebss.econst),每个段有固定的功能(比如.text存代码,.ebss存未初始化变量)。
  • 用户自定义段 :用户根据需求创建的段(比如ramfuncsFlash28_APIDMARAML4),用于将特定功能的代码 / 数据整合在一起,方便统一分配内存。
(2) 段的内存属性配置:解决硬件限制的关键

链接器允许为段配置存储地址(LOAD)运行地址(RUN),这是 F28335 解决硬件限制的核心语法:

  • LOAD(加载地址 / 存储地址) :程序被烧录到硬件时,该段的物理存储位置(通常是 Flash,非易失,掉电不丢)。
  • RUN(运行地址) :程序运行时,该段实际在内存中执行 / 存储的位置(通常是 RAM,高速,无硬件冲突)。
  • 两种模式
    • LOAD=RUN(默认) :存储地址和运行地址相同,段直接在存储位置运行(比如.text段存在 FLASHA,直接在 FLASHA 中运行)。
    • LOAD≠RUN(分离模式) :存储地址和运行地址不同,需要手动将段从 LOAD 地址复制到 RUN 地址后,才能运行 (这是ramfuncsFlash28_API的核心设计)。

二、Flash API 是什么(先说清本质)

Flash28335_API_V210.lib 是什么?

它是 TI 官方提供的 Flash 操作库,内部包含:

  • Flash_Erase()

  • Flash_Program()

  • Flash_Verify()

  • 各种状态机、超时、校验逻辑

⚠️ 重点:

  • 这些函数 绝对不能在 Flash 中执行

  • 必须在 SARAM(RAM)中运行


三、cmd 中 Flash API 的"总体结构"

我们从这段开始:

复制代码

Flash28_API: { -lFlash28335_API_V210.lib(.econst) -lFlash28335_API_V210.lib(.text) } LOAD = FLASHD, RUN = RAML0, LOAD_START(_Flash28_API_LoadStart), LOAD_END(_Flash28_API_LoadEnd), RUN_START(_Flash28_API_RunStart), PAGE = 0

接下来 逐字符级拆解


四、Flash28_API: ------ 这是在定义什么?

复制代码

Flash28_API:

含义

👉 定义一个 section 名字

  • section 名:Flash28_API

  • 不是 C 变量

  • 不是函数

  • 链接器内部的逻辑容器

你可以把它理解成:

"把某些目标文件里的某些段,打包成一个整体"


五、花括号 {} 里面:真正装了什么?

复制代码

{ -lFlash28335_API_V210.lib(.econst) -lFlash28335_API_V210.lib(.text) }

1️⃣ -lFlash28335_API_V210.lib

  • -l = link library

  • 指定一个库文件

  • 等价于在 CCS 里把这个 lib 加入工程


2️⃣ (.text) / (.econst)

这是精华中的精华

.text
  • Flash API 的 函数代码

  • 机器指令

.econst
  • Flash API 用到的:

    • 常量

    • 查表

    • 参数表

👉 TI 非常严谨:

  • 不只搬函数

  • 连常量也一起搬进 RAM


3️⃣ 为什么不用 *(.text)

因为:

只想要 Flash API 的代码

不想把你自己写的 .text 一起拖进来

这是"精准抓取",不是"野蛮复制"。


六、LOAD = FLASHD ------ 编译后放哪?

复制代码

LOAD = FLASHD

含义(非常重要)

链接完成后,这段代码在 FlashD 扇区

也就是说:

  • 烧录文件(.out / .hex)

  • Flash API 实体代码

  • 最初就写在 FLASHD

📌 FLASHD 的角色:

  • API 的"仓库"

  • 上电后复制源


七、RUN = RAML0 ------ 真正执行在哪?

复制代码

RUN = RAML0

含义

CPU 实际取指地址 = RAML0

也就是说:

  • Flash API 从不在 Flash 中运行

  • 永远在 RAML0 中执行

📌 为什么是 RAML0?

  • 4KB 连续

  • 零等待

  • 靠近 Flash

  • TI 官方推荐


八、LOAD_START / LOAD_END / RUN_START

复制代码

LOAD_START(_Flash28_API_LoadStart), LOAD_END(_Flash28_API_LoadEnd), RUN_START(_Flash28_API_RunStart),

这三行是给谁用的?

👉 给 C 代码用的

链接器会自动生成三个符号:

符号 含义
_Flash28_API_LoadStart Flash 中起始地址
_Flash28_API_LoadEnd Flash 中结束地址
_Flash28_API_RunStart RAM 中目标地址

在 C 代码里你会看到类似:

复制代码

extern Uint16 Flash28_API_LoadStart; extern Uint16 Flash28_API_LoadEnd; extern Uint16 Flash28_API_RunStart; memcpy(&Flash28_API_RunStart, &Flash28_API_LoadStart, (Uint32)&Flash28_API_LoadEnd - (Uint32)&Flash28_API_LoadStart);

📌 这一步 = "灵魂搬家"


九、PAGE = 0 是干嘛的?

复制代码

PAGE = 0

含义

  • PAGE0 = 程序空间

  • 这是 取指空间

  • Flash API 是"程序代码",不是数据

👉 即使它运行在 RAM,也仍然属于程序段


十、ramfuncs:和 Flash API 的关系

复制代码

ramfuncs LOAD = FLASHD RUN = RAML0

本质关系:

项目 Flash28_API ramfuncs
来源 TI 官方 你自己
功能 擦写 Flash 任意高优先级
放哪 FLASHD → RAML0 FLASHD → RAML0
本质 完全一样

👉 在链接器眼里,它们是同一类生物


十一、为什么 Flash API 单独占一个 section?

如果不这么做,会发生什么?

❌ 错误做法

  • 把 Flash API 混进 .text

  • .text 全放 FLASHA

❌ 后果

  • Flash API 在 FLASHA

  • 你擦别的扇区时

  • CPU 正在 Flash 里取指

  • 系统死机 / 跑飞

👉 所以 TI 强制隔离


十二、Flash API 与"8 个扇区"的工程关系总结

扇区 用途
FLASHA 主程序 + Boot
FLASHD Flash API / ramfuncs 装载区
FLASHC 数学库
FLASHB / E / F / G / H 用户数据

十三、逐字符速查表(给你当手册用)

符号 含义
Flash28_API: 定义 section
{} section 内容
-lxxx.lib 链接库
(.text) 函数代码
(.econst) 常量
LOAD = 编译后存放地址
RUN = 实际执行地址
LOAD_START() Flash 源地址
RUN_START() RAM 目标地址
PAGE = 0 程序空间

1. .cinit : > FLASHA PAGE=0

  • 作用 :存储 C 语言初始化数据(.cinit 是编译器生成的常量初始化表)

  • 存储位置FLASHA(Flash 扇区,非易失)

  • 逻辑

    • 这些数据在程序启动时会被复制到 RAM 中对应的变量区域

    • .cinit 是链接器自动生成的,通常包含全局/静态变量的初始值

  • 为什么 PAGE=0

    • Flash 是程序内存,PAGE 0 对应程序存储空间

    • 代码启动时读取 .cinit 数据


2. .pinit : > FLASHA, PAGE=0

  • 作用 :存储 C++ 全局对象或类的构造函数初始化表(.pinit 是初始化函数指针表)

  • 存储位置FLASHA

  • 逻辑

    • 程序启动时,C++ 对象构造函数通过 .pinit 指针表调用

    • 这样可以确保全局对象在 main 之前正确初始化

  • 为什么 PAGE=0:同样是程序内存,FLASH 存储,不会丢失


3. .text : > FLASHA PAGE=0

  • 作用:存储普通程序代码(text section)

  • 存储位置FLASHA

  • 逻辑

    • 程序的主函数、用户函数等都放在 .text

    • FLASH 非易失,掉电不丢失

    • PAGE=0,因为是程序空间


4. codestart : > BEGIN PAGE=0

  • 作用:定义程序入口地址

  • 存储位置BEGIN(FLASH 中极小地址,用于 bootloader 或启动向量)

  • 逻辑

    • CPU 上电或复位时,从这个地址开始执行 _c_int00 初始化程序

    • PAGE=0,因为是程序入口


5. ramfuncs : LOAD=FLASHD RUN=RAML0 ... PAGE=0

  • 作用:存储用户高速函数(RAM 执行)

  • 配置解释

    • LOAD=FLASHD → 编译器/链接器把代码存储在 FLASHD 扇区

    • RUN=RAML0 → 程序运行时把这些函数复制到 RAML0 执行

    • _RamfuncsLoadStart / _RamfuncsLoadEnd → FLASHD 中函数的起止地址

    • _RamfuncsRunStart → RAML0 中运行起始地址

    • PAGE=0 → 属于程序内存空间

  • 为什么要这么做

    • 避免从 Flash 中运行高速函数或在操作 Flash 时造成冲突

    • 类似 Flash28_API 的"Flash存储,RAM运行"方案

      完整的cmd文件:

      bash 复制代码
      /*
      //###########################################################################
      //
      // FILE:	F28335.cmd
      //
      // TITLE:	Linker Command File For F28335 Device
      //
      //###########################################################################
      // $TI Release: F28335 API Release V2.10 $
      // $Release Date: August 18, 2008 $
      //###########################################################################
      */
      
      /* ======================================================
      // For Code Composer Studio V2.2 and later
      // ---------------------------------------
      // In addition to this memory linker command file, 
      // add the header linker command file directly to the project. 
      // The header linker command file is required to link the
      // peripheral structures to the proper locations within 
      // the memory map.
      //
      // The header linker files are found in <base>\DSP2833x_Headers\cmd
      //   
      // For BIOS applications add:      DSP2833x_Headers_BIOS.cmd
      // For nonBIOS applications add:   DSP2833x_Headers_nonBIOS.cmd    
      ========================================================= */
      
      /* ======================================================
      // For Code Composer Studio prior to V2.2
      // --------------------------------------
      // 1) Use one of the following -l statements to include the 
      // header linker command file in the project. The header linker
      // file is required to link the peripheral structures to the proper 
      // locations within the memory map                                    */
      
      /* Uncomment this line to include file only for non-BIOS applications */
      /* -l DSP2833x_Headers_nonBIOS.cmd */
      
      /* Uncomment this line to include file only for BIOS applications */
      /* -l DSP2833x_Headers_BIOS.cmd */
      
      /* 2) In your project add the path to <base>\DSP2833x_headers\cmd to the
         library search path under project->build options, linker tab, 
         library search path (-i).
      /*========================================================= */
      
      /* Define the memory block start/length for the F28335  
         PAGE 0 will be used to organize program sections
         PAGE 1 will be used to organize data sections
      
          Notes: 
                Memory blocks on F28335 are uniform (ie same
                physical memory) in both PAGE 0 and PAGE 1.  
                That is the same memory region should not be
                defined for both PAGE 0 and PAGE 1.
                Doing so will result in corruption of program 
                and/or data. 
                
                L0/L1/L2 and L3 memory blocks are mirrored - that is
                they can be accessed in high memory or low memory.
                For simplicity only one instance is used in this
                linker file. 
                
                Contiguous SARAM memory blocks can be combined 
                if required to create a larger memory block. 
       */
      
      
      MEMORY
      {
      PAGE 0:    /* Program Memory */
                 /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE1 for data allocation */
      
         ZONE0       : origin = 0x004000, length = 0x001000     /* XINTF zone 0 */
         RAML0       : origin = 0x008000, length = 0x001000     /* on-chip RAM block L0 */
         RAML1       : origin = 0x009000, length = 0x001000     /* on-chip RAM block L1 */
         RAML2       : origin = 0x00A000, length = 0x001000     /* on-chip RAM block L2 */
         RAML3       : origin = 0x00B000, length = 0x001000     /* on-chip RAM block L3 */
         ZONE6       : origin = 0x0100000, length = 0x100000    /* XINTF zone 6 */ 
         ZONE7A      : origin = 0x0200000, length = 0x00FC00    /* XINTF zone 7 - program space */ 
         FLASHH      : origin = 0x300000, length = 0x008000     /* on-chip FLASH */
         FLASHG      : origin = 0x308000, length = 0x008000     /* on-chip FLASH */
         FLASHF      : origin = 0x310000, length = 0x008000     /* on-chip FLASH */
         FLASHE      : origin = 0x318000, length = 0x008000     /* on-chip FLASH */
         FLASHD      : origin = 0x320000, length = 0x008000     /* on-chip FLASH */
         FLASHC      : origin = 0x328000, length = 0x008000     /* on-chip FLASH */
         FLASHA      : origin = 0x338000, length = 0x007F80     /* on-chip FLASH */
         CSM_RSVD    : origin = 0x33FF80, length = 0x000076     /* Part of FLASHA.  Program with all 0x0000 when CSM is in use. */
         BEGIN       : origin = 0x33FFF6, length = 0x000002     /* Part of FLASHA.  Used for "boot to Flash" bootloader mode. */
         CSM_PWL     : origin = 0x33FFF8, length = 0x000008     /* Part of FLASHA.  CSM password locations in FLASHA */
         OTP         : origin = 0x380400, length = 0x000400     /* on-chip OTP */
         ADC_CAL     : origin = 0x380080, length = 0x000009     /* ADC_cal function in Reserved memory */
         
         IQTABLES    : origin = 0x3FE000, length = 0x000b50     /* IQ Math Tables in Boot ROM */
         IQTABLES2   : origin = 0x3FEB50, length = 0x00008c     /* IQ Math Tables in Boot ROM */  
         FPUTABLES   : origin = 0x3FEBDC, length = 0x0006A0     /* FPU Tables in Boot ROM */
         ROM         : origin = 0x3FF27C, length = 0x000D44     /* Boot ROM */        
         RESET       : origin = 0x3FFFC0, length = 0x000002     /* part of boot ROM  */
         VECTORS     : origin = 0x3FFFC2, length = 0x00003E     /* part of boot ROM  */
      
      PAGE 1 :   /* Data Memory */
                 /* Memory (RAM/FLASH/OTP) blocks can be moved to PAGE0 for program allocation */
                 /* Registers remain on PAGE1                                                  */
         
         BOOT_RSVD   : origin = 0x000000, length = 0x000050     /* Part of M0, BOOT rom will use this for stack */
         RAMM0       : origin = 0x000050, length = 0x0003B0     /* on-chip RAM block M0 */
         RAMM1       : origin = 0x000400, length = 0x000400     /* on-chip RAM block M1 */
         RAML4       : origin = 0x00C000, length = 0x001000     /* on-chip RAM block L1 */
         RAML5       : origin = 0x00D000, length = 0x001000     /* on-chip RAM block L1 */
         RAML6       : origin = 0x00E000, length = 0x001000     /* on-chip RAM block L1 */
         RAML7       : origin = 0x00F000, length = 0x001000     /* on-chip RAM block L1 */
         ZONE7B      : origin = 0x20FC00, length = 0x000400     /* XINTF zone 7 - data space */
         FLASHB      : origin = 0x330000, length = 0x008000     /* on-chip FLASH */
      }
      
      /* Allocate sections to memory blocks.
         Note:
               codestart user defined section in DSP28_CodeStartBranch.asm used to redirect code 
                         execution when booting to flash
               ramfuncs  user defined section to store functions that will be copied from Flash into RAM
      */ 
       
      SECTIONS
      {
       
         /* Allocate program areas: */
         /* The Flash API functions can be grouped together as shown below.
            The defined symbols _Flash28_API_LoadStart, _Flash28_API_LoadEnd
            and _Flash28_API_RunStart are used to copy the API functions out
            of flash memory and into SARAM */
      
         Flash28_API:
         {
              -lFlash28335_API_V210.lib(.econst)
              -lFlash28335_API_V210.lib(.text)
         }                   LOAD = FLASHD,
                             RUN = RAML0,
                             LOAD_START(_Flash28_API_LoadStart),
                             LOAD_END(_Flash28_API_LoadEnd),
                             RUN_START(_Flash28_API_RunStart),
                             PAGE = 0
         .cinit              : > FLASHA      PAGE = 0
         .pinit              : > FLASHA,     PAGE = 0
         .text               : > FLASHA      PAGE = 0
         codestart           : > BEGIN       PAGE = 0
         ramfuncs            : LOAD = FLASHD, 
                               RUN = RAML0, 
                               LOAD_START(_RamfuncsLoadStart),
                               LOAD_END(_RamfuncsLoadEnd),
                               RUN_START(_RamfuncsRunStart),
                               PAGE = 0
      
         csmpasswds          : > CSM_PWL     PAGE = 0
         csm_rsvd            : > CSM_RSVD    PAGE = 0
         
         /* Allocate uninitalized data sections: */
         .stack              : > RAMM1       PAGE = 1
         .ebss               : > RAML4       PAGE = 1
         .esysmem            : > RAMM1       PAGE = 1
      
         /* Initalized sections go in Flash */
         /* For SDFlash to program these, they must be allocated to page 0 */
         .econst             : > FLASHA      PAGE = 0
         .switch             : > FLASHA      PAGE = 0      
      
         /* Allocate IQ math areas: */
         IQmath              : > FLASHC      PAGE = 0                  /* Math Code */
         IQmathTables     : > IQTABLES,  PAGE = 0, TYPE = NOLOAD 
         IQmathTables2    : > IQTABLES2, PAGE = 0, TYPE = NOLOAD 
         FPUmathTables    : > FPUTABLES, PAGE = 0, TYPE = NOLOAD 
               
         /* Allocate DMA-accessible RAM sections: */
         DMARAML4         : > RAML4,     PAGE = 1
         DMARAML5         : > RAML5,     PAGE = 1
         DMARAML6         : > RAML6,     PAGE = 1
         DMARAML7         : > RAML7,     PAGE = 1
         
         /* Allocate 0x400 of XINTF Zone 7 to storing data */
         ZONE7DATA        : > ZONE7B,    PAGE = 1
      
         /* .reset is a standard section used by the compiler.  It contains the */ 
         /* the address of the start of _c_int00 for C Code.   /*
         /* When using the boot ROM this section and the CPU vector */
         /* table is not needed.  Thus the default type is set here to  */
         /* DSECT  */ 
         .reset              : > RESET,      PAGE = 0, TYPE = DSECT
         vectors             : > VECTORS     PAGE = 0, TYPE = DSECT
         
         /* Allocate ADC_cal function (pre-programmed by factory into TI reserved memory) */
         .adc_cal     : load = ADC_CAL,   PAGE = 0, TYPE = NOLOAD
      
      }
      
      /*
      //===========================================================================
      // End of file.
      //===========================================================================
      */
相关推荐
易水寒陈2 小时前
定时器计数溢出引发的bug
stm32·单片机
染予2 小时前
串口发送之中断方式
单片机·嵌入式硬件
TangDuoduo00052 小时前
【I2C协议】
stm32·单片机
Rorsion2 小时前
第七章:串行总线与接口技术
单片机·嵌入式硬件·串口通信·通信协议·备考ing
anghost1502 小时前
基于MSP430单片机的老人睡眠质量监测系统设计
单片机·嵌入式硬件
一杯原谅绿茶3 小时前
单片机的软件串口通信
单片机·嵌入式硬件
d111111111d3 小时前
在STM32中,中断服务函数的命名有什么要求?
笔记·stm32·单片机·嵌入式硬件·学习·c#
易水寒陈3 小时前
MultiTimer源码分析
stm32·单片机
白羽陌3 小时前
STM32入门教程
stm32·单片机·嵌入式硬件