#include "stm32f10x.h"                  // Device header

int main(void)
{
	/*寄存器操作法
	RCC->APB2ENR =0X00000010;   //开启时钟
	GPIOC->CRH = 0X00300000;	//把 PC13 配置为输出模式
	GPIOC->ODR = 0X00002000;	//给 PC13 引脚输出高电平
	*/
	//库函数版本 / 标准外设库写法
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE);   //先开时钟!!

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOC, &GPIO_InitStructure);    
	GPIO_SetBits(GPIOC, GPIO_Pin_13);	//高电平点亮PC13口LED
	//GPIO_ResetBits(GPIOC, GPIO_Pin_13); //低电平
	
	while(1)
	{
		
	}
}

&+变量名 = 取变量地址; F12或者右键 跳转到定义;

Start放启动文件根据芯片型号选择,Library导入封装好的库函数,User放自己的工程


自测题目:

  1. 启动文件 startup_stm32f10x_hd.s 里的 hd 代表什么?

    A. High Density(大容量芯片)B. Half Duplex(半双工)C. High Speed(高速)D. Hardware Debug(硬件调试)
  2. 你写 GPIO_Init(GPIOC, &GPIO_InitStructure); 时,为什么要加 &

    A. 因为 GPIO_InitStructure 是指针B. 因为函数参数要求传地址& 是取地址符C. 因为库函数规定必须加D. 因为不加会编译报错
  3. 寄存器写法 GPIOC->ODR = 0x00002000; 和库函数写法 GPIO_SetBits(GPIOC, GPIO_Pin_13); 功能是否一致?

    A. 完全一致,都是让 PC13 输出高电平B. 不一致,库函数写法更慢C. 不一致,寄存器写法更安全D. 不一致,只有库函数写法能控制电

请按功能匹配文件:

  • startup_stm32f10x_hd.s
  • stm32f10x_gpio.c
  • main.c
  • stm32f10x_conf.h

功能选项:

A. 存放你的主程序逻辑

B. 芯片启动初始化,负责跳转到 main()

C. 库函数实现文件,封装 GPIO 寄存器操作

D. 库的总开关,控制哪些外设头文件被包含


答案:ABA 、BCAD


加强版

第一部分(基础概念)

  1. 我们用的 STM32F103C8T6LD、MD、HD 哪一种?
  2. 启动文件的作用是什么?一句话说。
  3. GPIO_InitTypeDef 是什么东西?
  4. 函数里看到 * 代表什么?传参要加什么符号?

第二部分(寄存器理解)

  1. GPIOC->ODR 是干嘛的?
  2. GPIOC->CRH 是干嘛的?
  3. RCC->APB2ENR 是干嘛的?
  4. 0x00002000 是控制第几个引脚?

第三部分(库函数理解)

  1. 配置 GPIO 一共分几步?(库函数版)
  2. RCC_APB2PeriphClockCmd 是干嘛的?
  3. GPIO_SetBitsGPIO_ResetBits 区别?
  4. 下面这句对吗?错在哪?GPIO_Init(GPIOC, GPIO_InitStructure);

代码实操题(2 题)
  1. 请写出:用库函数方式,让 PB5 引脚输出低电平的完整代码片段(提示:需要先开时钟 → 配置 GPIO 为推挽输出 → 输出低电平)

  2. 请写出:用寄存器方式,让 PA0 引脚输出高电平的代码片段(提示:需要先开 GPIOA 时钟 → 配置 PA0 为输出模式 → 给 ODR 对应位置 1)



豆老师参考答案:
 

一、概念题(原答题 + 正确答案修正)

第一部分 基础概念

  1. STM32F103C8T6 是 LD/MD/HD?→ MD(中密度)(你答对)
  2. 启动文件的作用?→ 正确答案:初始化芯片堆栈、中断向量表,引导程序从复位跳转到 main () 函数(你答 “配置芯片整体功能”,核心意思对)
  3. GPIO_InitTypeDef 是什么?→ 正确答案:GPIO 配置参数的结构体类型,用于存放引脚、模式、速度等配置信息(你答 “定义一个结构型变量”,混淆了 “类型” 和 “变量”,修正后即懂)
  4. 函数里看到 * 代表什么?传参加什么符号?→ 正确答案:*代表指针(函数要求接收变量地址),传参必须加 **&(取地址符)**(你后半段答对)

第二部分 寄存器理解(全对,附精准描述)

  1. GPIOC->ODR → GPIO 端口输出数据寄存器,直接控制引脚高低电平
  2. GPIOC->CRH → GPIO 端口配置高寄存器,配置 8~15 号引脚的工作模式和速度
  3. RCC->APB2ENR → RCC 的 APB2 总线外设时钟使能寄存器,用于开启对应外设的时钟
  4. 0x00002000 → 控制 13 号引脚(PC13)

第三部分 库函数理解(全对,附精准步骤)

  1. 库函数配置 GPIO 步骤 → ①开对应 GPIO 时钟 ②定义 GPIO_InitTypeDef 结构体变量 ③给结构体赋值(引脚 / 模式 / 速度) ④调用 GPIO_Init () 初始化 ⑤设置引脚高低电平
  2. RCC_APB2PeriphClockCmd → 库函数版的 APB2 总线外设时钟使能函数,用于开启指定 GPIO / 外设的时钟
  3. GPIO_SetBits & GPIO_ResetBits 区别 → 前者给指定引脚输出高电平,后者给指定引脚输出低电平
  4. 错误代码GPIO_Init(GPIOC, GPIO_InitStructure); → 错误原因:第二个参数是指针类型,必须加 & 取地址,正确写法为 GPIO_Init (GPIOC, &GPIO_InitStructure);

二、代码实操题(原错误代码 + 完整正确代码)

题 1:库函数方式 让 PB5 引脚输出低电平

#include "stm32f10x.h"  // 器件头文件

int main(void)
{
    // 1. 开启GPIOB时钟(必须匹配引脚组,不能开GPIOC)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    // 2. 定义GPIO配置结构体变量
    GPIO_InitTypeDef GPIO_InitStructure;
    // 3. 给结构体赋值:PB5、推挽输出、50MHz
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;        // 引脚号匹配组内编号5
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 通用推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// 引脚最大输出速度50MHz
    // 4. 初始化GPIOB(传地址&)
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // 5. PB5输出低电平
    GPIO_ResetBits(GPIOB, GPIO_Pin_5);
    
    while(1)  // 死循环,保持程序运行
    {
    }
}

题 2:寄存器方式 让 PA0 引脚输出高电平

#include "stm32f10x.h"  // 器件头文件

int main(void)
{
    // 1. 开启GPIOA时钟:APB2ENR的bit2置1,对应值0x00000004
    RCC->APB2ENR = 0x00000004;
    // 2. 配置PA0为推挽输出50MHz:PA0是0号引脚用CRL,0~3位置0011(0x3)
    GPIOA->CRL = 0x00000003;
    // 3. PA0输出高电平:ODR的bit0置1,对应值0x00000001
    GPIOA->ODR = 0x00000001;
    
    while(1)  // 死循环,保持程序运行
    {
    }
}

三、核心困惑点精准解答

困惑 1:物理引脚号 41(PB5),代码为什么写 GPIO_Pin_5?

核心规则:代码中的GPIO_Pin_xx 是引脚在组内的编号和芯片物理引脚号(1~48)完全无关,只看引脚名称最后的数字!

  • 物理引脚 41 → 引脚名称 PB5 → 组内编号 5 → 代码写GPIO_Pin_5
  • 物理引脚 18 → 引脚名称 PB0 → 组内编号 0 → 代码写GPIO_Pin_0
  • 物理引脚 2 → 引脚名称 PC13 → 组内编号 13 → 代码写GPIO_Pin_13

困惑 2:RCC->APB2ENR 的数值怎么确定?(如 GPIOA=0x00000004)

核心规则:APB2ENR 寄存器的每一位对应一个外设,置 1 即开启对应外设时钟,GPIO 对应位固定,数值为 2 的对应位次方:

  • GPIOA → 对应 bit2 → 2²=4 → 0x00000004
  • GPIOB → 对应 bit3 → 2³=8 → 0x00000008
  • GPIOC → 对应 bit4 → 2⁴=16 → 0x00000010口诀:A4/B8/C16,直接背会即可。

困惑 3:什么时候用 GPIOx->CRL,什么时候用 GPIOx->CRH?

核心规则:按组内引脚编号划分,寄存器分工固定,无例外:

  • CRL(配置低寄存器):控制组内0~7 号引脚(如 PA0、PB5、PC7)
  • CRH(配置高寄存器):控制组内8~15 号引脚(如 PA8、PB10、PC13)举例:PA0(0 号)→ 用 CRL;PC13(13 号)→ 用 CRH。

困惑 4:GPIOx->ODR 的数值怎么确定?(如 PA0=0x00000001,PC13=0x00002000)

核心规则:ODR 寄存器的bitx 对应组内 x 号引脚,该位置 1 输出高电平,置 0 输出低电平,数值为 2 的 x 次方:

  • PA0 → bit0 → 2⁰=1 → 0x00000001
  • PB5 → bit5 → 2⁵=32 → 0x00000020
  • PC13 → bit13 → 2¹³=8192 → 0x00002000

困惑 5:为什么 GPIO_Init () 第二个参数必须加 &?

核心规则:函数声明

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

中,第二个参数GPIO_InitTypeDef**代表指针类型,要求接收变量的地址,而&变量名是 C 语言的取地址符,用于获取变量在内存中的地址:

  • 定义的GPIO_InitStructure结构体变量,不是地址
  • &&GPIO_InitStructure才是变量的地址,匹配函数的指针参数要求口诀:函数参数带 *,传参必须带 &!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐