peripherals-from-datasheet

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Peripherals from Datasheet

基于数据手册的外设驱动开发

Purpose

目的

Guide agents through a repeatable methodology for writing peripheral drivers from MCU reference manuals: locating register maps, interpreting bit definitions, following init sequences, respecting timing constraints, and producing maintainable register-level code. Pair with
skills/baremetal/datasheet-and-refmanual-reading
for doc-navigation methodology (kept as separate skills).
指导Agent掌握从MCU参考手册编写外设驱动的可复用方法论:定位寄存器映射、解析位定义、遵循初始化序列、遵守时序约束,并编写可维护的寄存器级代码。可搭配
skills/baremetal/datasheet-and-refmanual-reading
技能使用,后者专注于文档导航方法论(两者为独立技能)。

When to Use

适用场景

  • Starting a driver without vendor HAL
  • Porting a peripheral between MCU families
  • Verifying HAL behavior against the reference manual
  • Debugging a peripheral that "should work" per examples
  • 无厂商HAL支持时开发驱动
  • 在不同MCU系列间移植外设驱动
  • 对照参考手册验证HAL的行为
  • 调试那些“按示例本应正常工作”的外设问题

Workflow

工作流程

1. Reference manual navigation

1. 参考手册导航

Typical RM structure
├── Memory map (peripheral base addresses)
├── Peripheral chapter (UART, SPI, GPIO, ...)
│   ├── Functional description
│   ├── Register map (table of offsets)
│   ├── Register bit definitions
│   └── Timing / electrical notes
└── Electrical characteristics (clock limits, setup/hold)
Start with the programming model section before copying register writes.
典型RM结构
├── 内存映射(外设基地址)
├── 外设章节(UART、SPI、GPIO等)
│   ├── 功能描述
│   ├── 寄存器映射(偏移量表)
│   ├── 寄存器位定义
│   └── 时序/电气说明
└── 电气特性(时钟限制、建立/保持时间)
在复制寄存器写入代码前,先从编程模型部分开始阅读。

2. Extract register map

2. 提取寄存器映射

c
/* From RM — USART base 0x40004400 */
typedef struct {
    volatile uint32_t SR;   /* 0x00 status */
    volatile uint32_t DR;   /* 0x04 data */
    volatile uint32_t BRR;  /* 0x08 baud */
    volatile uint32_t CR1;  /* 0x0C control */
    /* ... */
} USART_TypeDef;

#define USART2 ((USART_TypeDef *)0x40004400UL)
Verify offset column matches struct layout (padding for reserved words).
c
/* 来自RM — USART基地址0x40004400 */
typedef struct {
    volatile uint32_t SR;   /* 0x00 状态寄存器 */
    volatile uint32_t DR;   /* 0x04 数据寄存器 */
    volatile uint32_t BRR;  /* 0x08 波特率寄存器 */
    volatile uint32_t CR1;  /* 0x0C 控制寄存器 */
    /* ... */
} USART_TypeDef;

#define USART2 ((USART_TypeDef *)0x40004400UL)
验证偏移列是否与结构体布局匹配(预留字段需添加填充)。

3. Init sequence checklist

3. 初始化序列检查清单

Peripheral bring-up order
├── 1. Enable bus clock (RCC/APB/AHB register)
├── 2. Reset peripheral (if RM requires)
├── 3. Configure pins (GPIO alternate function)
├── 4. Configure peripheral registers (mode, baud, etc.)
├── 5. Enable peripheral (UE, TE, RE bits)
├── 6. Enable NVIC IRQ (if interrupt-driven)
└── 7. Verify status flags before first transaction
Bad — enable UART before clock:
c
USART2->CR1 |= USART_CR1_UE;  /* USART clock still off — no effect */
外设启动顺序
├── 1. 启用总线时钟(RCC/APB/AHB寄存器)
├── 2. 复位外设(若RM要求)
├── 3. 配置引脚(GPIO复用功能)
├── 4. 配置外设寄存器(模式、波特率等)
├── 5. 启用外设(UE、TE、RE位)
├── 6. 启用NVIC中断(若采用中断驱动)
└── 7. 首次事务前验证状态标志
错误示例 — 先启用UART再开启时钟:
c
USART2->CR1 |= USART_CR1_UE;  /* USART时钟仍未开启 — 无效果 */

4. Bit definition discipline

4. 位定义规范

c
/* From RM: CR1 M[1:0], PCE, PS, TE, RE, UE */
#define USART_CR1_UE   (1U << 13)
#define USART_CR1_TE   (1U << 3)
#define USART_CR1_RE   (1U << 2)
Document RM section number in comment for audit trail.
c
/* 来自RM: CR1寄存器的M[1:0]、PCE、PS、TE、RE、UE位 */
#define USART_CR1_UE   (1U << 13)
#define USART_CR1_TE   (1U << 3)
#define USART_CR1_RE   (1U << 2)
在注释中记录RM章节编号,便于审计追溯。

5. Timing and busy-wait

5. 时序与忙等待

c
/* RM: poll BUSY flag until reset complete */
while (RCC->CR & RCC_CR_PLLRDY == 0)
    ;
Respect startup times (oscillator settle, PLL lock) from electrical characteristics chapter.
c
/* RM要求:轮询BUSY标志直到复位完成 */
while (RCC->CR & RCC_CR_PLLRDY == 0)
    ;
需遵守电气特性章节中规定的启动时间(振荡器稳定、PLL锁定等)。

6. Good vs bad driver structure

6. 驱动结构的优劣对比

Good — layered, RM-referenced:
c
void usart2_init(uint32_t baud) {
    rcc_enable_usart2();
    gpio_config_usart2_pins();
    usart2_set_baud(baud);
    USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
Bad — magic numbers, no clock enable:
c
*(uint32_t*)0x4000440C = 0x2000;  /* what peripheral? which bit? */
良好示例 — 分层设计、参考RM:
c
void usart2_init(uint32_t baud) {
    rcc_enable_usart2();
    gpio_config_usart2_pins();
    usart2_set_baud(baud);
    USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
糟糕示例 — 魔法数字、未启用时钟:
c
*(uint32_t*)0x4000440C = 0x2000;  /* 这是什么外设?对应哪一位? */

7. Agent usage examples

7. Agent使用示例

/peripherals-from-datasheet Walk me through USART init from STM32 RM
/peripherals-from-datasheet What sections of the ref manual matter for SPI timing?
/peripherals-from-datasheet 引导我根据STM32 RM完成USART初始化
/peripherals-from-datasheet 参考手册中哪些章节对SPI时序至关重要?

Common Problems

常见问题

SymptomCauseFix
Peripheral deadClock not enabledRCC/APB enable bit first
Wrong baud ratePCLK assumption wrongRecompute using actual clock tree
GPIO AF wrongMUX value from wrong tableCross-check pinout + AF table
IRQ stuckStatus flag clear sequence wrongRM "clearing flags" subsection
Silent data corruptionEndian or width mismatchMatch register access size
症状原因解决方法
外设无响应未启用时钟先设置RCC/APB使能位
波特率错误PCLK假设错误根据实际时钟树重新计算
GPIO复用功能错误复用值来自错误表格交叉核对引脚分配表与AF表
中断卡住状态标志清除顺序错误查看RM中“标志清除”小节
数据静默损坏字节序或宽度不匹配匹配寄存器访问宽度

Related Skills

相关技能

  • skills/baremetal/datasheet-and-refmanual-reading
    — fast RM navigation
  • skills/baremetal/mmio-and-bit-manipulation
    — register access patterns
  • skills/baremetal/gpio-baremetal
    — pin mux before peripheral enable
  • skills/embedded/linker-scripts
    — memory map alignment
  • skills/baremetal/datasheet-and-refmanual-reading
    — 快速导航RM
  • skills/baremetal/mmio-and-bit-manipulation
    — 寄存器访问模式
  • skills/baremetal/gpio-baremetal
    — 外设启用前的引脚复用配置
  • skills/embedded/linker-scripts
    — 内存映射对齐