GPIO输出
GPIO简介
- GPIO是general purpose input output的缩写,意思是通用输入输出口
- 可配置为8种输入输出模式
- 引脚电平:0V~3.3V,部分引脚可容忍5V
- 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
- 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
GPIO的基本结构
寄存器就是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,完成输出电平和读取电平的功能了。寄存器的每一位对应一个引脚,其中输出寄存器写1,对应的引脚就会输出高电平;写0,就会输出低电平。输入寄存器读取为1,就证明对应的端口目前是高电平,读取为0,就是低电平。因为STM32是32位的单片机,所以STM32内部的寄存器都是32位的,但这个端口只有16位,所以这个寄存器只有低16位对应的端口,高16位是没有用到的。这个驱动器是用来增加信号的驱动能力的,寄存器只负责存储数据如果要进行点灯这样的操作的话,还是需要驱动器来负责增大驱动能力,这些就是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
- 使用RCC开启GPIO的时钟
- 使用GPIO_Init函数初始化GPIO
- 使用输出或输入的函数控制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语言的知识的学习,就不再详细记录了,基本包括下面几个内容:
- C语言数据类型
- C语言宏定义
- C语言typedef
- C语言结构体
- 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();
}
}
}