stm32江协科技笔记(更新至3-3led流水灯和蜂鸣器)

stm32江协科技笔记

课程链接

创建基础项目

  • 创建一个文件夹



  • 在刚才创建的项目下创建Start,User,Library文件夹

  • 到固件库的如下地址,选中以下文件,复制刚才创建的Start文件夹里

  • 以及下面地址中的文件同样复制到刚才创建的Start文件夹里

  • 下面也是复制到Start文件夹下

  • 以下路径的文件复制到Library文件夹下:

  • 以下文件复制到User文件夹下:
  • 以下按照图片步骤








    USE_STDPERIPH_DRIVER

    下面第三步骤是需要选择第三栏Flash Download,忘记说明了

    把原来main.c文件里的代码删除

GPIO

  • General Purpose Input Output 通用输入输出口
  • 可配置为8种输入输出模式
  • 引脚电平:0v--3.3v,部分引脚可容忍5v,容忍5v表示接收5v正电压而不是发出5v电压
  • 输出模式下可控制端口输出高低电平,用以驱动LED,控制蜂鸣器,模拟通信协议输出时序等
  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入,外接模块电平信号输入,ADC电压采集,模拟通信协议接收数据等
    GPIO基本结构图

    stem32里所有的GPIO外设都是挂载在APB2总线的
    GPIO外设的名称是安装GPIOA,GPIOB,GPIOC等等规则命名,每个GPIO外设,有16个引脚,编号是0~15,GPIOA的第0号引脚,一般被称作为PA0,接着是PA1,然后PA2,以此类推,直到PA15,GPIOB同理,PB0 ~ PB5
    寄存器是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,这样就可以完成输出电平和读取电平的功能了,寄存器的每一位对应于每一位引脚,寄存器的值是0就会输出低电平,1则是高电平,读取同理,0为低电平,1为高电平,因为stm32是32位单片机,所以内部寄存器都是32位的,但是端口只有16位(PA0~PA15),所以只有寄存器的低16位对应端口,驱动器是用来增加驱动能力的

GPIO 位结构


《我们先来看输入部分》

保护二极管

图中两个保护二极管是对输入电压进行限幅的,上面VDD接3.3v,下面接VSS0V,如果输入电压比3.3V还要高,那么上方这个二极管就会导通,输入电压产生的电流会直接流入VDD而不会流入内部电路,防止了高电压对内部器件伤害,若输入的是负电压(相对于VSS电压),下方二极管可以流通,电流直接从VSS流过去,而不会从内部电路流过,保护内部电路和器件。若输入电压在0~3.3v之间,那么那两个二极管都不会导通,这时保护二极管不对流通电流产生影响。

上拉电阻vdd 与 下拉电阻vss,这个开关可以通过程序配置,如果上面导通,下面断开,则是上拉输入模式,如果下面导通,上面断开,就是下拉输入模式,如果两个都断开,就是浮空模式。

上拉和下拉的作用:为了给输入提供一个默认输入电平,对于一个数字端口,输入不是低电平就是高电平,若输入引脚什么都不接,那么算是低电平还是高电平呢,什么都不接的情况下,处于一个浮空状态,引脚的输入电平及其容易受到外界干扰而改变,就像是物体悬浮在太空一样,物体受到一点改变就变化,为了避免引脚悬空导致输入数据的不确定,完美就需要在这里加上拉或下拉电阻,,若接入上拉电阻,若引脚悬空,还有上拉电阻保证引脚的高电平,所以上拉输入又可以称作是(默认为高电平的输入模式),同理,下拉模式默认低电平的输入方式。

施密特触发器

图中所展示的肖特基触发器是施密特触发器,滞回比较器,他的执行逻辑是电压大于某一阈值,输出就会瞬间升为高电平,如果输入电压小于某一阈值,输出就瞬间变为低电平(注意,高低阈值是两个阈值,而不是同一个),因为实际电压会有波动,这一设计可以消除信号抖动

输入数据寄存器

斯密特触发器输出的信号会被存储到寄存器的每一位上

模拟输入/复用功能输入

模拟输入/复用功能输入这两路线路,这些就是连接到片上外设的一些端口线路。

模拟输入,连接到ADC上ADC(模数转换器)和DAC(数模转换器)是电子系统中实现模拟信号与数字信号相互转换的核心器件,二者功能互补、应用场景各异,数字信号(Digit)是跳变的,模拟(Analog)信号是连续的,接收模拟量。可以看到模拟输入是接到斯密特触发器的前面,意味着不需要斯密特触发器处理,因为模拟信号本来就是连续的,不需要整形。

复用功能输入,这个是连接到其他需要读取端口的外设的,比如串口的输入引脚等,这根线接收的是数字量,需要结果数字信号整形处理,所以在斯密特触发器后面


《接着我们来看输出部分》

输出数据可以由输出数据寄存器或片上外设输出数据,通过数据选择器(图中连着输出控制的竖着的梯形)控制选择用哪一个作为输出,如果选择通过数据寄存器进行控制,就是普通的IO口输出,写着数据寄存器的某一位就可以控制某一位端口了,

位设置/清除寄存器

可以用来单独操作输出数据寄存器的某一位,而不影响其他位,若没有这个寄存器而想单独控制某一位就需要先读取这个16位寄存器然后按位与/或来修改再赋值。若采用该寄存器修改,我们只需要对位设置寄存器的某一位设置1或清除位置1即可,这样就可以直接对某一位赋值1或0,不影响效率。

  • 设置位(Set Bits):将寄存器中的某一位或多位置为1。
  • 清除位(Clear Bits):将寄存器中的某一位或多位置为0。
    高16位是位清除,低16位是位设置

除了以上两种方式,还有"位带"的方式来设置1/0输出,(你没听错,是大胃袋!),stem32中有"位带"区域,作用和51单片机的寻址差不多,在stm32中,专门分配了一段地址区域,这段地址映射了RAM和外设寄存器的所有位,读或写这段地址中的数据,就相当于读写所映射的某一位。

端口位清除寄存器

和上面的那个不同,低16位和上面的高16位功能一样,用来清除,高16位无用,这个寄存器是为了方便设置的,因为处于低位更直观。

P-MOS/N-MOS

这个mos管就是一种电子开关,我们的信号来控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS,这里可以选择推挽,开漏或关闭三种输出模式

推挽/开漏/关闭 模式

输出模式下,输入模式也是可以有效的, 但是输入模式下,输出都是无效的。读时不可写,因为写将会篡改引脚信号;写时可以读,其实读的目的就是监控写的数据。

在推挽输出模式下,P-MOS和N-MOS均有效,数据寄存器为1时,上管(P-MOS)导通,下管(N-MOS)断开,输出直接接到VDD,就是输出高电平,数据寄存器为0时,就是上管断开,下管导通,输出直接接到VSS,就是输出低电平。这种模式高低电平都有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式,这种模式下,stem32对IO口有绝对控制权,高低电平都由STM32说的算

开漏输出模式下,P-MOS是无效的,只有N-MOS在工作,数据寄存器为1时,下管断开,这时输出相当于断开,也就是高阻模式。数据寄存器为0时,下管导通,输出接到VSS,输出低电平,这种模式下,只有低电平有驱动能力,高电平没有驱动能力。此开漏模式可以作为通信协议的驱动方式,如I2C通信的引脚,就是使用的开漏模式,多机通信的情况下,这个模式可以避免各个设备相互干扰,另外开漏模式可以用于输出5V的电平信号,比如在IO口外接入一个上拉电阻的5V电源,输出低电平时直接接到内部VSS,当输出高电平时,N-MOS和P-MOS都断开,此时由5v电源提供5v电压,用于兼容一些5v的设备

关闭模式,当配置输入模式的时候,两个MOS管都无效,也就是输出关闭,端口的电平都由外部信号来控制,以上是GPIO位结构的全部介绍。

GPIO模式

输出模式下,输入模式也是可以有效的, 但是输入模式下,输出都是无效的。读时不可写,因为写将会篡改引脚信号;写时可以读,其实读的目的就是监控写的数据。

通过配置GPIO的端口配置寄存器,端口可以配置为以下8种模式:

浮空/上拉/下拉输入

前三个输入模式电路结构图:

可以看到输入模式下输出是断开的

前三个输入模式电路结构基本一致,区别是上拉电阻和下拉电阻的连接,上拉在无信号接收时默认是高电平,下拉则是低电平,浮空则是不确定,浮空模式下电平是不确定的,所以使用浮空模式输入时,应该接一个连续的驱动源,不能出现悬空状态。

图中VDD和VDD_FT,FT表示可容忍5.5v的断开

模拟输入

GPIO无效,引脚直接接入内部ADC

开漏输出和推挽输出

主要区别是开漏输出的高电平输出为高阻态,无驱动能力。在GPIO位结构里 推挽/开漏/关闭 模式 介绍了主要细节

复用开漏和复用推挽输出

也就是普通开漏和推挽模式,输入设备变成了外设而已。
在GPIO这8个模式里,除了模拟输入这个模式会关闭数字输入功能,其他7个模式,所有输入都是有效的以上是Stem32的GPIO所有介绍;

基础外设

LED:发光二极管,正向通电点亮,反向通电不亮

有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定

无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音

我们使用是上图的有源蜂鸣器使用,右侧是其内部电路图;

下面左侧是GPIO驱动led灯的电路,右侧是蜂鸣器电路。

左上是低电平驱动,左下是高电平驱动,一般使用上面低电平驱动,一般单片机都是低电平弱驱动,高电平强驱动。

右侧是蜂鸣器电路,使用三极管驱动电路来完成驱动任务,三极管的左边是基极,带箭头的是发射极,剩下的是集电极。右上是PNP三极管驱动,右下是NPN三极管的驱动电路,他的驱动能力和右上的相反,基极给高电平导通,低电平断开。PNP的三极管最好接在上边,NPN三接管最好接在下边,三接管的通断是需要发射极和基极直接产生一点开启电压的,如果负载接在发射极,就可能导致三极管无法开启

面包板

面包板简单示例

点亮led

连接基础单元

注意红灯连接的是芯片A0处,后续调用的也是A0引脚,若接错则不亮!

下面是我本人连接的图片不同型号的stem32接口可能不一样,一定要根据上面图片看清楚再插,否则有可能点亮芯片!

  • 点亮led的步骤
  1. 使用RCC开启GPIO时钟

STM32 RCC(Reset and Clock Control)是STM32微控制器中的复位和时钟控制模块,负责管理系统中的时钟信号和复位操作,是STM32的"心脏"和"管家",对系统的稳定运行和性能优化至关重要。

  1. 使用GPIO_Init函数初始化GPIO
  2. 使用输出或输入的函数控制GPIO口
    以上涉及RCC和GPIO两个外设

初始化GPIO

了解rcc函数头文件

library文件夹下有stm32f10x_rcc.h文件

  • 主要用到以下三个时钟使能控制函数
关于该处使能函数的误区(题外话)
  1. PB1 的含义
    PB1 是 GPIO 引脚的标识,表示 Port B 的第 1 号引脚(即 GPIO_PIN_1)。
    例如,在配置 GPIO 时,PB1 可能用于控制 LED 或读取按钮状态。
    但 RCC_APB2PeriphClockCmd() 函数的参数 RCC_APB2Periph 与具体引脚无关,而是用于控制 整个外设模块的时钟(如 GPIOB、USART1 等)。
  2. RCC_APB2PeriphClockCmd() 的作用
    该函数用于 使能或禁用 APB2 总线上的外设时钟,其参数是 外设模块的标识符,而非具体引脚
  • RCC_APB2Periph_GPIOB 表示 整个 GPIOB 端口的时钟,而不是某个具体引脚(如 PB1 或 PB2)。
  • 时钟使能后,才能操作该外设下的所有引脚(如 PB0~PB15)

在STM32微控制器中,时钟使能是控制外设和引脚的前提条件,这是由其硬件架构和低功耗设计决定的。以下是详细解释:

为什么要时钟使能才可以控制?

  1. 时钟的本质:外设的"电源开关"
    时钟信号(Clock Signal)是数字电路的"心跳",它为外设提供同步工作的时序基准。
    没有时钟,外设的寄存器无法读写,逻辑电路无法运行,相当于"断电"状态。
    时钟使能后,外设才能根据时钟信号执行操作(如读取引脚状态、发送数据、生成PWM等)。
    STM32的时钟树架构:
    STM32的时钟系统分为多个层级(如HSI、HSE、PLL、AHB、APB1/APB2),外设的时钟最终来源于系统时钟(SYSCLK)的分频或倍频。
    APB1/APB2总线:连接低速/高速外设,通过分频器为外设提供时钟(如APB2的PCLK2可达72MHz)。
    外设时钟门控:每个外设的时钟通过独立的开关(如RCC_APB2Periph_GPIOB)控制,实现精细化的功耗管理。
  2. 为什么必须时钟使能?
    (1) 硬件设计要求
    寄存器访问依赖时钟:
    STM32的外设寄存器(如GPIO的ODR、IDR)是同步寄存器,必须有时钟信号才能完成读写操作。
    例如:写入GPIOB->ODR = 0x0001(设置PB0为高电平)时,硬件会检查GPIOB的时钟是否使能。若未使能,写入操作会被忽略。
    动态功耗控制:
    STM32针对低功耗优化,未使用的外设默认关闭时钟以减少电流消耗。
    例如:若不使用USART1,其时钟默认关闭,此时访问USART1寄存器会导致硬件错误(如总线错误或无响应)。

我们可以右键跳到函数的定义处查看函数,下图

若显示下面提醒,可能是删除了中间文件,需要重新编译一下。

跳过来后可以看到函数头部的注释,这种注释风格类似于javadoc,点击此处了解javadoc

此处表明了函数的作用和参数等等

初步了解stm32f10x_gpio.h

  1. 在stm32f10x_gpio.h的349行可以看到GPIO_DeInit函数,调用该函数,所指定的GPIO外设就会被复位。
  2. 下面的GPIO_AFIODeInit也是一样,可以复位AFIO外设
  3. 第三个函数GPIO_Init,其作用是,用结构体的参数来初始化GPIO,先定义一个结构体变量,然后给结构体赋值,最后调用这个函数,这个函数会调用结构体的参数,自动根据结构体配置参数。这种Iinit函数在stm32中基本所有外设都有,初始化外设都是用Init函数来完成。
  4. GPIO_StructInit函数,这个函数可以把结构体变量赋值一个默认值
  5. 接着从GPIO_ReadInputDataBit下面4个是GPIO读入函数,从下面的GPIO_SetBits到GPIO_WriteBit都是GPIO写入函数,这8个函数就可以实现GPIO的读写功能。
  6. 剩下函数暂时用不到

    所以当前主要学习GPIO_Init,和以上8个GPIO读写函数

外设时钟开启

首先调用RCC里面的APB2外设时钟控制函数

RCC_APB2PeriphClockCmd 复制到main函数里,右键跳转到定义处。RCC_APB2PeriphClockCmd 是STM32微控制器中用于控制APB2总线上外设时钟开启或关闭的关键函数

这里我们要点亮PA0的LED,所以选择RCC_APB2外设GPIOA,放入第一个参数

如下这样:

继续跳转该函数的定义处,可以看到第二个参数的解释:

翻译过来就是:

NewState:指定外设时钟的新状态。

*此参数可以是:ENABLE或DISABLE。

这里我们目的是使能ENABLE

如下这样

调用GPIO_Init

第一个参数选择GPIOA,同样跳转定义,可以看到第二个参数解释是一个结构体,该结构体名称是GPIO_InitTypeDef,关于该结构体的介绍,该传入什么样的函数,可以跳转到该结构体定义。
我们也使用结构体实例,加一个点,编译器会提示有哪些成员变量如下图:

那么我们应该创建一个结构体,然后赋值该结构体,最后将结构体传入GPIO_Init注:对于有的老的c语言版本编译器要求所有局部变量定义必须在函数的最前面,根据自己的编译器调整位置,c99以上版本的就不必考虑

c 复制代码
#include "stm32f1ox.h
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//如果你的编译器是老版本,该定义需要放到最前面。
GPIO_InitTypeDef GPIO_InitStructure;
//下面的成员变量是通过上图的方式来调出对,下一步需要右键下面每个,跳转到定义处,查看如何赋值
GPIO_InitStructure.GPIO_Mode
GPIO_InitStructure.GPIO_Pin
GPIO_InitStructure.GPIO_Speed
GPIO_Init(GPIOA,

以下是跳转到GPIO_InitStructure.GPIO_Mode的定义处

翻译:指定所选引脚的工作模式。此参数可以是 @ref GPIOMode_TypeDef 中定义的某个值。

所以我们搜索GPIOMode_TypeDef,更简单的可以直接右键他,然后跳转到该定义,下图,红字是解释

AIN(Analog IN)是模拟输入
IN_GLOATING是浮空输入
IPD(In Pull Down 是下拉输入
IPU(In Pull Up)是上拉输入
Out_OD(Out Open Drain)是开漏输出
Out_PP(Atl Push Pull)是复用推挽输出
AF_OD(Atl Open Drain)是复用开漏
AF_PP(Atl Push Pull)是复用推挽

关于该处的知识点在GPIO的结构介绍过各种输出和输入的知识点

由于是电灯,所以我们使用强驱动力的推挽输出,选择Out_PP这一项!

所以代码Mode赋值为:

c 复制代码
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

同理第二个参数GPIO_InitStructure.GPIO_Pin跳转到定义处,由于GPIO_Pin有多处定义,不会直接跳转,而是出现下面场景,我们选择带有member的一项

双击跳转:

所以同样,右键GPIO_pins_define,跳转定义,但是不知道为什么,弹窗,于是我们可以使用cctrl+F搜索该类型

搜索过来后是这个样子,由于灯是接在0号引脚,于是使用GPIO_Pin_0

所以代码赋值是:

c 复制代码
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;

同样跳转第三个参数GPIO_InitStructure.GPIO_Speed的定义处

同理跳转@ref类型,如下图选择50MHz的就行:

所以代码是注意:GPIO_Init的参数都是指针,所以传入结构体应该取地址&

c 复制代码
#include "stm32f10x.h"                  // Device header
int main(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	while(1){
	}
}

当以上GPIO_Init函数执行完,GPIOA外设的0号引脚就自动被配置为推挽输出,50MHz的速度了。其执行逻辑就是读取结构体参数,自动写入GPIO配置寄存器。有一定的封装思想

调用GPIO输入输出函数(上面提到的8种)

stm32f10x_gpio.h的353~360行为这8种函数

GPIO4个输出函数

文档介绍4种函数

第一个函数是GPIO_SetBits,第一个参数是GPIOx,第二个参数是GPIO_Pin,功能:将指定端口设置为高电平

第二个GPIO_ResetBits,参数和上面的一样,这个可以把指定的端口设置为低电平

第三个GPIO_WriteBit,这个函数有三个参数,前两个也是指定端口,第三个是BitValue,这个是根据第三个参数的值来设置指定的端口

第四个是GPIO_Write,第一个参数是GPIOx,选择外设,第二个参数是PortValue,这个函数可以同时对16个端口进行读写操作

  1. GPIO_SetBits
    功能
    将指定的GPIO引脚设置为高电平(逻辑1)。

函数原型

c 复制代码
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

参数说明

参数 类型 描述
GPIOx GPIO_TypeDef* GPIO端口号,可选:GPIOA、GPIOB、GPIOC等(取决于具体型号)。
GPIO_Pin uint16_t 引脚号,使用库定义的宏(如GPIO_Pin_5表示第5号引脚),可多选(如`GPIO_Pin_5

示例

c 复制代码
// 将GPIOA的第5号引脚设置为高电平
GPIO_SetBits(GPIOA, GPIO_Pin_5);
  1. GPIO_ResetBits
    功能
    将指定的GPIO引脚设置为低电平(逻辑0)。

函数原型

c 复制代码
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

参数说明

(与GPIO_SetBits完全相同)

参数 类型 描述
GPIOx GPIO_TypeDef* GPIO端口号(如GPIOA、GPIOB)。
GPIO_Pin uint16_t 引脚号(如GPIO_Pin_5),支持多引脚组合(如`GPIO_Pin_5

示例

c 复制代码
// 将GPIOA的第5号引脚设置为低电平
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
  1. GPIO_WriteBit
    功能
    根据第三个参数BitValue的值,动态设置GPIO引脚为高电平或低电平。

函数原型

c 复制代码
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitValue);

参数说明

参数 类型 描述
GPIOx GPIO_TypeDef* GPIO端口号(如GPIOA、GPIOB)。
GPIO_Pin uint16_t 引脚号(如GPIO_Pin_5),仅支持单个引脚(不能多选)。
BitValue BitAction 电平值,可选:- Bit_SET(高电平)- Bit_RESET(低电平)。可以是0或1,但是有警告。

若强行传入0或1作为第三个参数,需要强制转换类型(BitAction)0或(BitAction)1

示例

c 复制代码
// 将GPIOA的第5号引脚设置为高电平
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_SET);

// 将GPIOA的第5号引脚设置为低电平
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);
  1. GPIO_Write
    在STM32标准外设库(Standard Peripheral Library)中,GPIO_Write 是一个用于写入整个GPIO端口输出数据寄存器(ODR)的函数,它可以一次性设置某个GPIO端口的所有引脚的电平状态(高或低)。与 GPIO_WriteBit(单引脚控制)不同,GPIO_Write 直接操作整个端口的16位数据(适用于STM32的16位GPIO端口)。

函数原型

c 复制代码
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

参数说明

参数 类型 描述
GPIOx GPIO_TypeDef* GPIO端口号,可选:GPIOA、GPIOB、GPIOC 等(取决于具体型号)。
PortVal uint16_t 16位值,每位对应端口的一个引脚(如 0xFFFF 表示所有引脚高电平,0x0000 表示所有引脚低电平)。

功能

  • 一次性设置整个GPIO端口的所有引脚电平(16位并行控制)。
  • PortVal 的每一位(bit0 ~ bit15)对应 GPIOx 的 Pin_0 ~ Pin_15:
    bit=1 → 对应引脚输出高电平。
    bit=0 → 对应引脚输出低电平。

示例

  1. 设置整个GPIOA端口的电平
c 复制代码
// 将GPIOA的所有引脚设置为高电平(0xFFFF = 16位全1)
GPIO_Write(GPIOA, 0xFFFF);

// 将GPIOA的所有引脚设置为低电平(0x0000 = 16位全0)
GPIO_Write(GPIOA, 0x0000);

// 设置GPIOA的部分引脚(如 Pin_0 和 Pin_1 高电平,其余低电平)
GPIO_Write(GPIOA, 0x0003);  // 0000 0000 0000 0011 (二进制)
  1. 动态控制多个引脚
c 复制代码
uint16_t port_value = 0;

// 设置 Pin_5 和 Pin_7 高电平,其余低电平
port_value |= (1 << 5);  // Pin_5 高
port_value |= (1 << 7);  // Pin_7 高

GPIO_Write(GPIOA, port_value);  // 写入GPIOA
调用ResetBits点亮led灯

1.GPIO_ResetBits 把指定的端口设置为低电平

第一个GPIOx,x可以是A-->G

第二个GPIO_Pin_x,x可以是0~15

c 复制代码
//将GPIOA 0号引脚赋值低电平
GPIO_ResetBits(GPIOA,GPIO_Pin_0);

编译然后,下载

由于A0输出低电平,所以灯就会亮

  1. GPIO_SetBits

同理,调用GPIO_SetBits给高电平就会熄灭

3.GPIO_WriteBit 前两个参数一样,第三个参数注释翻译:参数可以是BitAction这个枚举中的一个值,其枚举包含Bit_RESET和Bit_SET,前者是低电平,后者是高电平

c 复制代码
//高电平设置
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
//低电平设置
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);

这样也可以设置A0为低电平,点亮led

  1. GPIO_Write 在STM32标准外设库(Standard Peripheral Library, SPL)中,GPIO_Write 是一个用于同时控制多个GPIO引脚的函数,与 GPIO_WriteBit(单引脚控制)不同,它可以一次性设置整个端口的输出状态。

函数原型

c 复制代码
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

参数

  • GPIOx:GPIO端口(如 GPIOA、GPIOB)。
  • PortVal:16位无符号整数(uint16_t),每一位对应端口的一个引脚(0=低电平,1=高电平)。
    功能
  • 一次性设置整个GPIO端口的输出状态(所有引脚)。
  • 适用于需要同时更新多个引脚的场景(如控制多位LED、数据总线等)。

led闪烁

我们可以使用while循环点亮led,需要用到延时,在学80c51单片机课程教学了Delay函数,这里直接提供延时函数库点击跳转复制库函数

在项目下创建System文件夹,用来存放系统资源,将Delay.h和Delay.c复制到文件夹下

同样点击三个箱子按钮

添加组System,将System所有文件添加进来

别忘记点击魔术棒按钮,c/c++一栏添加这个新文件夹的头文件路径


以后每次添加新库文件夹都要这么做

Delay库里有这三个函数,分别是微秒,毫秒,秒延时。这里使用SysTick实现延时,具体怎么实现的,可以不用管。

调用延时函数 #include "Delay.h",以下代码就可以实现闪烁led

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	while(1){
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	//延时500毫秒
	Delay_ms(500);
	GPIO_SetBits(GPIOA,GPIO_Pin_0);
	Delay_ms(500);
	}
}

关于推挽输出和开漏输出的驱动问题

运行上面代码后,若此时拔掉led灯,将正极接到A0上,负极接到负极线上(在面包板是是蓝线),此时是正极驱动led,仍然可以发亮,说明推挽高低电平驱动能力都强。若换成Out_OD(开漏模式)则不亮,说明开漏模式高电平驱动能力弱。一般情况电灯用推挽模式就行了。

插入流水灯

由于不方便,我少插了一个,如果你要全插,注意,A0的正极位于倒数第4个孔,否则插口不够

流水灯项目

复制以上项目,重命名为3-2LED流水灯,省去创建项目的功夫。

双击副本下带有keil图标的文件

整理复制的代码

要改动的只有main函数:

第一句:打开GPIOA时钟,我们连接的都是GPIOA的端口,不用删除

c 复制代码
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

添加GPIO端口

c 复制代码
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;

之所以可以这样设置,是因为每个参数位于二进制的不同bit位上,0号是1,1号是0b10,2号是0b100,以此类推,GPIO_Pin_All是0b111111....表示选中所有引脚

除了引脚可以这样设置,GPIO时钟控制也可以这样类似:

以及GPIO_setBits等函数的第二个参数,也可以通过或来批量选择引脚,带有Bits表示可以设置多个引脚,一般都可以。

现在为了控制多个端口,我们使用GPIO_Write函数c语言不支持直接写二进制,由于低电平为亮,所以在while函数里,使用了取反~

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
	//添加其他7个端口
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_0);
	while(1){
	//依次点亮0~7端口
GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0001
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002); //0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004); //0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008); //0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010); //0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020); //0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040); //0000 0000 0100 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080); //0000 0000 1000 0000
Delay_ms(500);
	}
}

点响蜂鸣器

如图GND接到负极,I/O接到正极,vcc接到随便一个I/O口,这里选中PB12,注意:A15,B3,B4先别选,这三个口是JTAG的调试端口,如果想变成普通端口,还需要配置一下。

大致是这样接的

依旧复制上一个程序,改名3-3 蜂鸣器

以下代码基于上一个代码修改,是可以触发蜂鸣器的,更改的部分有注释和c51的无缘蜂鸣器不同,这个只要给了低电平信号就会发出声音,而不是给与脉冲信号

c 复制代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
int main(void){
	//更改为RCC_APB2Periph_GPIOB
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
	//改为12口
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	//更改为GPIOB
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	//代码逻辑更改
	while(1){
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(500);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(500);
	}
}

使用库函数的方法

1.转到.h头文件,跳转定义处,使用翻译软件查看解释。

2.查看库函数用户手册,里面有所有库函数的使用方法

相关推荐
charlie1145141913 小时前
SSH X11 转发排查与解决指南(Windows + Xming + Ubuntu)
arm开发·windows·笔记·ubuntu·ssh·个人开发·环境配置
日更嵌入式的打工仔12 小时前
LAN9253中文注释第三章
笔记
Struggle to dream12 小时前
STM32对于中断的简单理解
stm32·单片机·嵌入式硬件
玩c#的小杜同学12 小时前
源代码保卫战:给C# 程序(混淆、加壳与反逆向实战)
开发语言·笔记·c#
Yeh20205813 小时前
2月7日笔记
笔记
Aliex_git14 小时前
浏览器 API 兼容性解决方案
前端·笔记·学习
四谎真好看14 小时前
SSM学习笔记(Spring篇 Day02)
笔记·学习·学习笔记·ssm
gsls20080817 小时前
vue3学习笔记
笔记·vue3
闪闪发亮的小星星17 小时前
asin和atan2的区别 (CPA指向相关)
笔记·其他