STM32学习笔记二——GPIO

GPIO输出

GPIO简介

  1. GPIO是general purpose input output的缩写,意思是通用输入输出口
  2. 可配置为8种输入输出模式
  3. 引脚电平:0V~3.3V,部分引脚可容忍5V
  4. 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
  5. 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等

GPIO的基本结构

在这里插入图片描述
寄存器就是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,完成输出电平和读取电平的功能了。寄存器的每一位对应一个引脚,其中输出寄存器写1,对应的引脚就会输出高电平;写0,就会输出低电平。输入寄存器读取为1,就证明对应的端口目前是高电平,读取为0,就是低电平。因为STM32是32位的单片机,所以STM32内部的寄存器都是32位的,但这个端口只有16位,所以这个寄存器只有低16位对应的端口,高16位是没有用到的。这个驱动器是用来增加信号的驱动能力的,寄存器只负责存储数据如果要进行点灯这样的操作的话,还是需要驱动器来负责增大驱动能力,这些就是GPIO的整体基本结构。

GPIO的位结构

在这里插入图片描述
下图为GPIO位结构的电路图
GPIO位结构的电路图
可以分为上下两个部分:输入和输出部分

GPIO模式

在这里插入图片描述
CNF=端口的配置位
MODE=端口的配置位
在这里插入图片描述
当I/O端口配置为输入时:
● 输出缓冲器被禁止
● 施密特触发输入被激活
● 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接
● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器
● 对输入数据寄存器的读访问可得到I/O状态
在这里插入图片描述
当I/O端口被配置为模拟输入配置时:
● 输出缓冲器被禁止;
● 禁止施密特触发输入,实现了每个模拟I/O引脚上的零消耗。施密特触发输出值被强置
为’0’;
● 弱上拉和下拉电阻被禁止;
● 读取输入数据寄存器时数值为’0’。
在这里插入图片描述
当I/O端口被配置为输出时:
● 输出缓冲器被激活
─ 开漏模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将端口置于高阻状态(PMOS从不被激活)。
─ 推挽模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将激活P-MOS。
● 施密特触发输入被激活
● 弱上拉和下拉电阻被禁止
● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器
● 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态
● 在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。
在这里插入图片描述
当I/O端口被配置为复用功能时:
● 在开漏或推挽式配置中,输出缓冲器被打开
● 内置外设的信号驱动输出缓冲器(复用功能输出)
● 施密特触发输入被激活
● 弱上拉和下拉电阻被禁止
● 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器
● 开漏模式时,读输入数据寄存器时可得到I/O口状态
● 在推挽模式时,读输出数据寄存器时可得到最后一次写的值
在这里插入图片描述
其他可以参考STM32的参考手册8 通用和复用功能I/O(GPIO和AFIO)

LED和蜂鸣器简介

在这里插入图片描述
使用STM32的GPIO口驱动LED的电路如下,分别为低电平驱动的电路和高电平驱动的电路
低电平驱动的电路 高电平驱动的电路
由于很多单片机都使用了高电平弱驱动,低电平的强驱动的规则,所以一般倾向于使用低电平驱动的电路

使用STM32的GPIO口驱动蜂鸣器的电路如下,这里使用了三极管开关的驱动方案,分别是PNP三极管和NPN三极管
在这里插入图片描述 在这里插入图片描述

面包板

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LED闪烁

第一步

新建工程或直接复制工程模板,并且可以把批处理文件keilkill.bat(可以把工程编译产生的中间文件都删除)复制到工程文件夹中。

第二步

连接线路,将所需要的器件连接到面包板上

第三步:编写程序

操作STM32的GPIO

  1. 使用RCC开启GPIO的时钟
  2. 使用GPIO_Init函数初始化GPIO
  3. 使用输出或输入的函数控制GPIO口
    在Library组中找到stm32f10x_rcc.h文件,打开,在文件最后面,是库函数所有函数的声明,在这里我们可以看到RCC有很多的库函数,但实际上这里的大部分函数我们都不会用到,我们最常用的只有这三个函数
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

RCC_AHB外设时钟控制、RCC_APB2外设使用控制、RCC_APB2外设使用控制。我们可以右键跳到定义,这时就来到了.C文件里的函数定义,上面这有一个函数的介绍,这个AHB外设时钟控制的函数就是使能或者失能AHB的外设时钟的,下面介绍第一个参数就是选择哪个外设,这里说STM32互联型的设备可以在这个列表选择,其他设备在下面这个列表选择;接着第二个参数就是enable或者disable,然后下面的AHB2外设时钟控制和AHB1外设时钟控制都是一样的操作方法,第一个参数选择外设,第二个参数使能或失能,如果你不清楚哪个外设是连接在哪个总线上的,还可以在注释的列表找一下,列表中出现的就肯定是这个总线的外设,这就是RCC的介绍,最主要的就是这三个函数。

/**
  * @brief  Enables or disables the AHB peripheral clock.
  * @param  RCC_AHBPeriph: specifies the AHB peripheral to gates its clock.
  *   
  *   For @b STM32_Connectivity_line_devices, this parameter can be any combination
  *   of the following values:        
  *     @arg RCC_AHBPeriph_DMA1
  *     @arg RCC_AHBPeriph_DMA2
  *     @arg RCC_AHBPeriph_SRAM
  *     @arg RCC_AHBPeriph_FLITF
  *     @arg RCC_AHBPeriph_CRC
  *     @arg RCC_AHBPeriph_OTG_FS    
  *     @arg RCC_AHBPeriph_ETH_MAC   
  *     @arg RCC_AHBPeriph_ETH_MAC_Tx
  *     @arg RCC_AHBPeriph_ETH_MAC_Rx
  * 
  *   For @b other_STM32_devices, this parameter can be any combination of the 
  *   following values:        
  *     @arg RCC_AHBPeriph_DMA1
  *     @arg RCC_AHBPeriph_DMA2
  *     @arg RCC_AHBPeriph_SRAM
  *     @arg RCC_AHBPeriph_FLITF
  *     @arg RCC_AHBPeriph_CRC
  *     @arg RCC_AHBPeriph_FSMC
  *     @arg RCC_AHBPeriph_SDIO
  *   
  * @note SRAM and FLITF clock can be disabled only during sleep mode.
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)
  {
    RCC->AHBENR |= RCC_AHBPeriph;
  }
  else
  {
    RCC->AHBENR &= ~RCC_AHBPeriph;
  }
}

/**
  * @brief  Enables or disables the High Speed APB (APB2) peripheral clock.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11     
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2ENR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2ENR &= ~RCC_APB2Periph;
  }
}

/**
  * @brief  Enables or disables the Low Speed APB (APB1) peripheral clock.
  * @param  RCC_APB1Periph: specifies the APB1 peripheral to gates its clock.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
  *          RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
  *          RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
  *          RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, 
  *          RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
  *          RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
  *          RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
  *          RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB1ENR |= RCC_APB1Periph;
  }
  else
  {
    RCC->APB1ENR &= ~RCC_APB1Periph;
  }
}

#ifdef STM32F10X_CL
/**
  * @brief  Forces or releases AHB peripheral reset.
  * @note   This function applies only to STM32 Connectivity line devices.
  * @param  RCC_AHBPeriph: specifies the AHB peripheral to reset.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_AHBPeriph_OTG_FS 
  *     @arg RCC_AHBPeriph_ETH_MAC
  * @param  NewState: new state of the specified peripheral reset.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_AHBPeriphResetCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_AHB_PERIPH_RESET(RCC_AHBPeriph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));

  if (NewState != DISABLE)
  {
    RCC->AHBRSTR |= RCC_AHBPeriph;
  }
  else
  {
    RCC->AHBRSTR &= ~RCC_AHBPeriph;
  }
}
#endif /* STM32F10X_CL */ 

/**
  * @brief  Forces or releases High Speed APB (APB2) peripheral reset.
  * @param  RCC_APB2Periph: specifies the APB2 peripheral to reset.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
  *          RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
  *          RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
  *          RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
  *          RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
  *          RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
  *          RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11  
  * @param  NewState: new state of the specified peripheral reset.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB2RSTR |= RCC_APB2Periph;
  }
  else
  {
    RCC->APB2RSTR &= ~RCC_APB2Periph;
  }
}

/**
  * @brief  Forces or releases Low Speed APB (APB1) peripheral reset.
  * @param  RCC_APB1Periph: specifies the APB1 peripheral to reset.
  *   This parameter can be any combination of the following values:
  *     @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
  *          RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
  *          RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
  *          RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4, 
  *          RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
  *          RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
  *          RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
  *          RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14  
  * @param  NewState: new state of the specified peripheral clock.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    RCC->APB1RSTR |= RCC_APB1Periph;
  }
  else
  {
    RCC->APB1RSTR &= ~RCC_APB1Periph;
  }
}

接着,我们再看一下GPIO的库函数,我们打开GPIO.h的文件,然后拖到最后这些就是GPIO的全部库函数。我们目前需要了解的就是前面的这些函数,第一个就是GPIO_DeInit,参数可以写GPIOA、GPIOB等等,调用这个函数之后所指定的GPIO外设就会被复位,这就是这个函数的用途;第二个GPIO_AFIODeInit也是一样,可以复位AFIO外设;第三个GPIO_Init就是非常重要的函数,这个函数的作用是用结构体的参数来初始化GPIO口,我们需要先定义一个结构体变量,然后再给结构体赋值,最后调用这个函数,这个函数内部就会自动读取结构体的值,然后自动把外设的各个参数配置好,这种Init函数在STM32中基本所有的外设都有,一般我们初始化外设都是使用这个Init函数来完成的;第四个是GPIO_StructInit函数,这个函数可以把结构体变量赋一个默认值;这下面四个就是GPIO的读取函数了;

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

下面跟着的四个就是GPIO的写入函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

这些函数可以实现读写GPIO口的功能,最后剩下的这些函数我们暂时不会用到。

接下来使用这些函数来操作GPIO:
在main.c文件中,首先调用的是RCC里面的APB2外设时钟控制函数,复制粘贴。由于我们需要点亮的是PA0口的LED,所以选择RCC_APB2Periph_GPIOA作为第一个参数,并且选择ENABLE作为第二个参数,这样时钟就开启了。接着调用GPIO_Init函数,初始化,第一个参数选择GPIOA,第二个参数是一个结构体,首先复制结构体的类型GPIO_InitTypeDef,并命名结构体为GPIO_InitStructure,接着定义结构体的成员,由于点灯需要的是推挽输出,所以GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;,由于使用的是GPIOA的0号引脚,所以GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;,并且GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;,最后,将GPIO_InitStructure的地址作为第二个参数。
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);可以把指定的端口设置为高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);可以把指定的端口设置为高电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);有三个参数,前两个也是指定端口的,第三个是根据参数的值来设置指定的端口。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);第一个参数是选择外设,第二个参数是PortVal,这个函数可以同时对16个端口进行写入操作。
那我们分别来用一下试试,首先试一下ResetBits,LED灯亮,说明PA0口输出为低电平。

#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);
	
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	
	while(1)
	{
		
	}
}

再试一下SetBits,将GPIO_ResetBits(GPIOA,GPIO_Pin_0);改为GPIO_SetBits(GPIOA,GPIO_Pin_0);,LED灯灭,说明PA0口输出为高电平。
再试一下void GPIO_WriteBit,使用GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);,LED灯亮;使用GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);,LED灯灭。
这就是这三个函数的用法。

接下来,我们来实现LED闪烁。我们需要再while循环函数中写上点亮LED,延时一段时间;熄灭LED,延时一段时间的代码。
使用下面的代码来实现点亮和熄灭LED:

GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//点亮LED
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭LED		

GPIO_ResetBits(GPIOA,GPIO_Pin_0);//点亮LED
GPIO_SetBits(GPIOA,GPIO_Pin_0);//熄灭LED		

	GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
	GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);

在中间加上延时函数来进行延时,在工程文件夹中,新建文件夹System,将延时函数的.c和.h文件(使用SYSTICK延时器来实现的延时)粘贴进来,在keil中点击三个箱子的按钮,新建组System,添加延时函数的.c和.h文件,并点击魔术棒按钮,在C/C++中的include path选项中添加文件夹System的路径。
使用Delay_ms(500);实现500ms的延时。

全部代码如下:

#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);
		Delay_ms(500);
		GPIO_SetBits(GPIOA, GPIO_Pin_0);
		Delay_ms(500);
		
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
		Delay_ms(500);
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
		Delay_ms(500);
		
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
		Delay_ms(500);
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
		Delay_ms(500);
	}
}

最后我们再研究一下推挽输出和开漏输出的驱动问题,我们把这个LED拔掉,然后把长脚插到PA0口,短脚插到负极,这样LED就是高电平点亮的方式,可以看到LED也是正常闪烁,说明在推挽模式下高低电平都是有驱动能力的,那我们把这个端口的模式换成Out_OD,开漏输出模式,编译下载,可以看到LED就不亮了,现在LED还是高电平点亮的方式,LED不亮说明开漏输出的模式高电平是没有驱动的,我们把LED再改回低电平驱动的方式,可以看到LED又亮起来了,这说明开漏模式的低电平是有驱动能力的,这就是推挽输出和开漏输出的特性,推挽输出高低电平均有驱动能力,开漏输出高电平相当于高阻态,没有驱动能力,低电平有驱动能力,那我们把这个改回推挽输出,一般输出用推挽模式就行了,特殊的地方才会用到开漏模式。

LED流水灯

第一步:新建工程

第二步:连接线路

第三步:编写程序

可以使用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//初始化16个端口初始化16个端口,也可以用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2…;进行初始化

#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_All;//初始化16个端口
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	while(1)
	{
		GPIO_Write(GPIOA,~0x0001);//点亮LED  0000 0000 0000 0001
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0002);//点亮LED  0000 0000 0000 0010
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0004);//点亮LED  0000 0000 0000 0100
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0008);//点亮LED  0000 0000 0000 1000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0010);//点亮LED  0000 0000 0001 0000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0020);//点亮LED  0000 0000 0010 0000		
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0040);//点亮LED  0000 0000 0100 0000
		Delay_ms(500);
		GPIO_Write(GPIOA,~0x0080);//点亮LED  0000 0000 1000 0000
		Delay_ms(500);
		
	}
}

蜂鸣器

第一步:新建工程

第二步:连接线路

第三步:编写程序

蜂鸣器连接到B12端口上

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main (void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	while(1)
	{
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(100);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(100);
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(100);
		GPIO_SetBits(GPIOB,GPIO_Pin_12);
		Delay_ms(700);

	}
}

那最后再给大家介绍几种使用库函数的方法,那第一种就是像我这样先打开点击.h文件的最后,看一下都有哪些函,数然后再右键转到定义查看一下函数和参数的用法,这里全都是英文的,如果看不懂的话借助一下翻译软件。第二种就是打开资料文件夹里的这个库函数用户手册,这里面有所有函数的介绍和使用方法,这个文档是中文的看起来比较好理解,而且这个函数下面都还给了例子,要用的话直接复制过来就行了,不过这个用户手册的版本并不对应我们现在用的这个库函数的版本,我们使用的库函数是V3.5.0版本的,这个用户手册是老版本,库函数的有部分用法会有些出入,但是整体上的差异都不大,参考这个用户手册也是没有问题的。V3.5.0的库函数目前还是没有用户手册的,St公司并没有发布,V3.5.0版本的库函数用户手册,而是在这个固件库压缩包中给了一个帮助文档,我们可以打开看一下可以找到GPIO这一节来看一下这种帮助文档,不过这个文档只有英文的版本。最后一种方式就是百度搜索参考一下别人的代码。

GPIO输入

按键介绍

在这里插入图片描述

传感器模块简介

在这里插入图片描述
套件中提供了四种传感器模块,分别是光敏电阻传感器、热敏电阻传感器、对外式红外传感器、反射式红外传感器
上面的电路图是传感器的基本电路

硬件电路

按键的连接电路如下图所示,上面两个为下接按键的方式,下面两个为上接按键的方式,一般来说,按键都是使用下接的方式,是电路设计的习惯和规范。
在这里插入图片描述
那我们先来看一下第一个图,这种接法是按键的最常用的接法了,在这里随便选取一个GPIO口,比如PA0,然后通过K1接到地,当按键按下时,PA0被直接下达到GMT,此时读取PA0口的电压就是低电平,当按键松手时,PA0被悬空悬空,引脚的电压不确定,所以在这种解法下,必须要求PA0是上拉输入的模式,否则就会出现引脚电压不确定的错误现象,如果K0是上拉输入的模式,那引脚在悬空时就是高电平。所以这种方式下。按下按键。引脚为低电平。松手。引脚为高电平。
再看一下第二个图,相比较第一个图在这里外部接了一个上拉电阻,这个上拉电阻可以想象成一个弹簧把这个端口往屋顶上来,然后按键松手时,引脚由于上拉作用,自然保持为高电平,当按键按下时,引脚直接接到GND,也就是一股无穷大的力,把这个引脚往下拉,那弹簧肯定对抗不了无穷大的力,所以引脚就为低电平,这种状态下引脚不会出现悬空状态。所以此时PA0引脚可以配置为浮空输入或者上拉输入,如果是上拉输入,那就是内外两个上拉电阻共同作用,这时高电平就会更强一些,对应高电平就更加稳定,当然这样的话,当引脚被强行拉到低时,损耗也就会大一些。
那接着第三个图,PA0通过按键接到3.3V,这样也是可以的,不过要求PA0必须要配置成下拉输入的模式,当按键按下时,引脚为高电平,松手时,引脚回到默认值低电平,要求单片机的引脚可以配置为下拉输入的模式,一般单片机可能不一定有下拉输入的模式,所以最好还是用上面的方法,下面的作为扩展部分,了解一下即可。
那最后一种接法,就是在刚才的这种接法下面再外接一个下拉电阻,这个接法PA0需要配置为下拉输入模式或者浮空输入模式。

那总结一下就是上面这两种接法按键按下时,引脚是低电平,松手是高电平;下面这两种接法按键按下时,是高电平,松手,是低电平。左边两种接法必须要求引脚是上拉或者下拉输入的模式,右边两种接法可以允许引脚是浮空输入的模式,因为已经外置了上拉电阻和下拉电阻;一般我们都用上面两种接法,下面两种接法用的比较少。

传感器模块的连接电路如下图所示,
在这里插入图片描述
下面是C语言的知识的学习,就不再详细记录了,基本包括下面几个内容:

  1. C语言数据类型
  2. C语言宏定义
  3. C语言typedef
  4. C语言结构体
  5. C语言枚举

按键控制LED

新建工程

复制并粘贴之前的工程,更改名字,并在工程文件夹中新建文件夹Hardware,并在keil中进行相关设置。

连接线路

编写程序

实验现象:按下按键1,LED1亮,按下按键2,LED2亮,再按一下按键,LED灯灭。

编写LED模块的程序

右键点击Hardware组,添加新文件,新建LED.c文件,路径选择Hardware文件夹,点击Add;右键点击Hardware组,添加新文件,新建LED.h文件,路径选择Hardware文件夹,点击Add;分别编写LED.c和LED.h文件

#include "stm32f10x.h"                  // Device header

void LED_Init(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_1 | GPIO_Pin_2; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOA ,&GPIO_InitStructure );
	
	GPIO_SetBits (GPIOA ,GPIO_Pin_1 | GPIO_Pin_2);
}


void LED1_ON(void)
{
	GPIO_ResetBits (GPIOA ,GPIO_Pin_1 );
}

void LED1_OFF(void)
{
	GPIO_SetBits (GPIOA ,GPIO_Pin_1 );
}

void LED1_Turn(void)
{
	if(GPIO_ReadOutputDataBit (GPIOA ,GPIO_Pin_1 )== 0)
	{
		GPIO_SetBits (GPIOA ,GPIO_Pin_1 );
	}
	else 
	{
		GPIO_ResetBits (GPIOA ,GPIO_Pin_1 );
	}
}

void LED2_ON(void)
{
	GPIO_ResetBits (GPIOA ,GPIO_Pin_2 );
}

void LED2_OFF(void)
{
	GPIO_SetBits (GPIOA ,GPIO_Pin_2 );
}

void LED2_Turn(void)
{
	if(GPIO_ReadOutputDataBit (GPIOA ,GPIO_Pin_2 )== 0)
	{
		GPIO_SetBits (GPIOA ,GPIO_Pin_2 );
	}
	else 
	{
		GPIO_ResetBits (GPIOA ,GPIO_Pin_2 );
	}
}
#ifndef __LED_H
//是否存在LED.H的字符串
#define __LED_H
//若不存在,定义LED.H的字符串

void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);

#endif

编写按键模块的程序

右键点击Hardware组,添加新文件,新建Key.c文件,路径选择Hardware文件夹,点击Add;右键点击Hardware组,添加新文件,新建Key.h文件,路径选择Hardware文件夹,点击Add;分别编写Key.c和Key.h文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOB ,&GPIO_InitStructure );
	
}

uint8_t Key_GetNum(void )
{
	uint8_t KeyNum = 0;
	if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_1 ) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_1 ) ==0 );
		Delay_ms (20);
		KeyNum = 1;
	}
	
	if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_11 ) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_11 ) ==0 );
		Delay_ms (20);
		KeyNum = 2;
	}
	
	return KeyNum ;
}
#ifndef __KEY_H
#define __KEY_H

void Key_Init(void);
uint8_t Key_GetNum(void );

#endif

编写主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

uint8_t KeyNum;

int main (void)
{
	LED_Init();
	Key_Init();
	
	while(1)
	{
		KeyNum = Key_GetNum() ;
		if(KeyNum == 1)
		{
			LED1_Turn ();
		}
		if(KeyNum == 2)
		{
			LED2_Turn ();
		}
	}
}

光敏传感器控制蜂鸣器

新建工程

连接电路

编写程序

实验现象:遮住光敏电阻,蜂鸣器响,不遮挡光敏电阻,蜂鸣器不响。

编写蜂鸣器模块的程序

右键点击Hardware组,添加新文件,新建Buzzer.c文件,路径选择Hardware文件夹,点击Add;右键点击Hardware组,添加新文件,新建Buzzer.h文件,路径选择Hardware文件夹,点击Add;分别编写Buzzer.c和Buzzer.h文件

#include "stm32f10x.h"                  // Device header

#include "stm32f10x.h"                  // Device header

void Buzzer_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOB ,&GPIO_InitStructure );
	
	GPIO_SetBits (GPIOB ,GPIO_Pin_12);
}


void Buzzer_ON(void)
{
	GPIO_ResetBits (GPIOB ,GPIO_Pin_12 );
}

void Buzzer_OFF(void)
{
	GPIO_SetBits (GPIOB ,GPIO_Pin_12 );
}

void Buzzer_Turn(void)
{
	if(GPIO_ReadOutputDataBit (GPIOB ,GPIO_Pin_12 )== 0)
	{
		GPIO_SetBits (GPIOB ,GPIO_Pin_12 );
	}
	else 
	{
		GPIO_ResetBits (GPIOB ,GPIO_Pin_12 );
	}
}
#ifndef __BUZZER_H
#define __BUZZER_H

void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);

#endif

编写蜂鸣器模块的程序

右键点击Hardware组,添加新文件,新建LightSensor.c文件,路径选择Hardware文件夹,点击Add;右键点击Hardware组,添加新文件,新建LightSensor.h文件,路径选择Hardware文件夹,点击Add;分别编写LightSensor.c和LightSensor.h文件

#include "stm32f10x.h"                  // Device header

void LightSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
	GPIO_Init(GPIOB ,&GPIO_InitStructure );
}

uint8_t LightSensor_Get(void)
{
	return GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_13);
}

#ifndef __LIGHTSENSOR_H
#define __LIGHTSENSOR_H

void LightSensor_Init(void);
uint8_t LightSensor_Get(void);

#endif

编写主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"

uint8_t KeyNum;

int main (void)
{
	Buzzer_Init ();
	LightSensor_Init ();
	
	while(1)
	{
		if(LightSensor_Get() == 1)
		{
			Buzzer_ON();
		}
		else
		{
			Buzzer_OFF();
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值