固件库
(1)固件库
STM32固件库就是函数的集合,固件库向下负责与寄存器打交道,向上提供用户函数调用的API
这种方法当然可以,但是这种方法的劣势就是你不需要掌握寄存器的使用,也可以正确的使用STM32。优势就是开发很方便。
固件将这些寄存器底层操作全部封装起来了,提供了一套API供用户使用,大部分情况下,你不需要去了解寄存器,只需要知道函数的使用
(2)Source insight 4.0管理,编辑工程
(3)STM32F4xx固件库GPIO的操作
3.1 使能GPIO分组的时钟(GPIO都属于AHB1) --rcc.h
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
@RCC_AHB1Periph:指定AHB1上外设的名称 可以位或
RCC_AHB1Periph_GPIOA
RCC_AHB1Periph_GPIOB
......
@NewState:指定该外设的时钟状态
ENABLE 使能
DISABLE 禁止
比如:想使能GPIOF组和GPIOE组的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOE,ENABLE);
3.2 初始化GPIO引脚(完成基本的4个寄存器的配置) --gpio.h
a.定义结构体变量
GPIO_InitTypeDef GPIO_InitStruct;
b.给结构体成员赋值
typedef struct
{
uint32_t GPIO_Pin;//指定要配置的GPIO引脚
GPIO_Pin_0
GPIO_Pin_1
.....
GPIO_Pin_All
GPIOMode_TypeDef GPIO_Mode;//指定要配置的GPIO引脚的功能模式
GPIO_Mode_IN 输入模式
GPIO_Mode_OUT 输出模式
GPIO_Mode_AF 复用模式
GPIO_Mode_AN 模拟模式
GPIOSpeed_TypeDef GPIO_Speed;//指定引脚速率
GPIO_Speed_2MHz
GPIO_Speed_25MHz
GPIO_Speed_50MHz
GPIO_Speed_100MHz
GPIOOType_TypeDef GPIO_OType;//输出类型
GPIO_OType_PP 推挽模式
GPIO_OType_OD 开漏模式
GPIOPuPd_TypeDef GPIO_PuPd;//上下拉配置
GPIO_PuPd_NOPULL 无上下拉
GPIO_PuPd_UP 上拉
GPIO_PuPd_DOWN 下拉
}GPIO_InitTypeDef;
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_0;
.....
c.把刚才写好的结构体写进寄存器
GPIO_Init(GPIOF,&GPIO_InitStruct);
3.3 输入/输出
1.输入:从配置好GPIO引脚,获取外部电平
a.获取输入寄存器的指定的GPIO引脚的值
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
@GPIOx:指定的GPIO分组
GPIOA
GPIOB
.....
@GPIO_Pin:指定的引脚编号
GPIO_Pin_0
GPIO_Pin_1
.....
返回值:
Bit_SET 高电平
Bit_RESET 低电平
b.获取指定分组的输入寄存器整组的值(16位)
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
@GPIOx:指定的GPIO分组
GPIOA
GPIOB
.....
返回值:是一个无符号的16位的整数,这个整数就是这个分组的16个引脚的电平值
c.获取输出数据寄存器的指定的GPIO引脚的值
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
d.获取指定分组的输出寄存器整组的值(16位)
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
2.输出:CPU通过引脚向外部电路输出一个电平值
a.向指定的引脚输出指定的电平值
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
@GPIOx:指定的GPIO分组
GPIOA
GPIOB
.....
@GPIO_Pin:指定的引脚编号
GPIO_Pin_0
GPIO_Pin_1
.....
@BitVal:想输出的电平
Bit_RESET 低电平
Bit_SET 高电平
b.往指定的GPIO分组整组输出(16个引脚一块输出)
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
@GPIOx:指定的GPIO分组
GPIOA
GPIOB
.....
@PortVal:uint16_t类型(16位的整数)
bit0 ---> 引脚0
......
比如:
GPIO_Write(GPIOF,0xF0F0);
将PF15~PF12和PF7~PF4输出高电平,其它的输出低电平
c.向指定的引脚输出一个高电平
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
d.向指定的引脚输出一个低电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
e. 将指定的引脚的输出啊状态进行翻转(1-->0,0-->1)
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
4. 当GPIO引脚被配置为复用模式的时候才需要使用
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
@GPIOx:指定的GPIO分组
GPIOA
GPIOB
.....
@GPIO_PinSource:指定的GPIO引脚(不可以位或)
GPIO_PinSource0
GPIO_PinSource1
.....
@GPIO_AF:指定复用成什么功能
GPIO_AF_TIM1
......
练习1
使用固件库的方式实现流水灯
led_lib.c led_lib.h #include "stm32f4xx.h"
由上图可看出,LED灯低电平状态下才能在电器元件两端形成电势差,产生电压,有电流产生并流过LED灯,使LED灯处于工作状态;高电平状态下LED灯不工作。
led_lib.c
#include "stm32f4xx.h"
#include "led_lib.h"
void led_lib_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_Init(GPIOE,&GPIO_InitStruct);
GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
GPIO_SetBits(GPIOE, GPIO_Pin_13 | GPIO_Pin_14);
}
void led_Ctrl(int led_num,int status )
{
if(led_num == D1)
{
status ? D1_OFF : D1_ON;
}
if(led_num == D2)
{
status ? D2_OFF : D2_ON;
}
if(led_num == D3)
{
status ? D3_OFF : D3_ON;
}
if(led_num == D4)
{
status ? D4_OFF : D4_ON;
}
}
void delay_ms(uint32_t ms)
{
uint32_t i;
for(i = 0; i < ms * 7000; i++);
}
led_lib.h
#ifndef __LED_LIB_H__
#define __LED_LIB_H__
#include "stm32f4xx.h"
/*灯的编号*/
enum LED_NUM
{
D1,
D2,
D3,
D4
};
/*灯的状态*/
enum LED_STATUS
{
ON,
OFF
};
//灯的控制
#define D1_ON GPIO_ResetBits(GPIOF, GPIO_Pin_9)
#define D1_OFF GPIO_SetBits(GPIOF, GPIO_Pin_9)
#define D2_ON GPIO_ResetBits(GPIOF, GPIO_Pin_10)
#define D2_OFF GPIO_SetBits(GPIOF, GPIO_Pin_10)
#define D3_ON GPIO_ResetBits(GPIOE, GPIO_Pin_13)
#define D3_OFF GPIO_SetBits(GPIOE, GPIO_Pin_13)
#define D4_ON GPIO_ResetBits(GPIOE, GPIO_Pin_14)
#define D4_OFF GPIO_SetBits(GPIOE, GPIO_Pin_14)
void led_lib_init(void);
void led_Ctrl(int led_num,int status);
void delay_ms(uint32_t ms);
#endif
main.c
#include "main.h"
#include "stm32f4xx.h"
#include "led_lib.h"
#include "beep_lib.h"
#include "key_lib.h"
#include "exti.h"
int main(void)
{
led_lib_init();
while(1)
{
led_Ctrl(D1, ON);
delay_ms(500);
led_Ctrl(D1, OFF);
led_Ctrl(D2, ON);
delay_ms(500);
led_Ctrl(D2, OFF);
led_Ctrl(D3, ON);
delay_ms(500);
led_Ctrl(D3, OFF);
led_Ctrl(D4, ON);
delay_ms(500);
led_Ctrl(D4, OFF);
}
//定义四个标志位来表示四个按键是被按下还是松开
//默认按键被松开标志位为1
// int S1 = 1;
// int S2 = 1;
// int S3 = 1;
// int S4 = 1;
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// led_lib_init();
// beep_lib_init();
// EXTIX_Init();
// while(1);
// beep_lib_init();
// key_lib_init();
// while(1)
// { //S1按键
// if(KEY0 == 0)//判断按键KEY0是否被按下
// {
// delay_ms(50); //延时消抖
// if(KEY0 == 0) //判断按键KEY0是否被按下
// {
// if(S1)
// {
// S1 = 0;//标志位S1置0
// GPIO_ToggleBits(GPIOF,GPIO_Pin_9);//翻转D1灯的状态
// GPIO_ToggleBits(GPIOF,GPIO_Pin_10);//翻转D2灯的状态
// }
// }
// }
// if(KEY0 == 1)//判断按键KEY0是否被松开
// {
// S1 = 1;//标志位S1置1
// }
// //S2按键
// if(KEY1 == 0)//判断按键KEY1是否被按下
// {
// delay_ms(50);//延时消抖
// if(KEY1 == 0)//判断按键KEY1是否被按下
// {
// if(S2)
// {
// S2 = 0;//标志位S2置0
// GPIO_ToggleBits(GPIOE,GPIO_Pin_13);//翻转D3灯的状态
// GPIO_ToggleBits(GPIOE,GPIO_Pin_14);//翻转D4灯的状态
// }
// }
// }
// if(KEY1 == 1)//判断按键KEY1是否被松开
// {
// S2 = 1;//标志位S2置1
// }
// //S3按键
// if(KEY2 == 0)//判断按键KEY2是否被按下
// {
// delay_ms(50);//延时消抖
// if(KEY2 == 0)//判断按键KEY2是否被按下
// {
// if(S3)
// {
// S3 = 0;//标志位S3置0
// GPIO_ToggleBits(GPIOF,GPIO_Pin_8);//翻转蜂鸣器的状态
// }
// }
// }
// if(KEY2 == 1)//判断按键KEY2是否被松开
// {
// S3 = 1;//标志位S3置1
// }
// //S4按键
// if(KEY3 == 0)//判断按键KEY3是否被按下
// {
// delay_ms(50);//延时消抖
// if(KEY3 == 0)//判断按键KEY3是否被按下
// {
// if(S4)
// {
// S4 = 0;//标志位S4置0
// GPIO_ToggleBits(GPIOF,GPIO_Pin_9);//翻转D1灯的状态
// GPIO_ToggleBits(GPIOF,GPIO_Pin_10);//翻转D2灯的状态
// GPIO_ToggleBits(GPIOE,GPIO_Pin_13);//翻转D3灯的状态
// GPIO_ToggleBits(GPIOE,GPIO_Pin_14);//翻转D4灯的状态
// }
// }
// }
// if(KEY3 == 1)//判断按键KEY3是否被松开
// {
// S4 = 1;//标志位S4置1
// }
// }
}
----------------------------------------------------------------------------
按键的使用:
1.看原理图:
S1 -- PA0
S2 -- PE2
S3 -- PE3
S4 -- PE4
这四个引脚配置为上拉输入
经过分析:
以S1按键为例,按下S1按键,CPU读取PA0是低电平,松开S1按键,CPU读取PA0是高电平
代码为例:
int main()
{
//初始化函数....
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 0)
{
//点灯
}
}
}
以上的代码在实际上行不通。还要进行"按键消抖"
抖动的原因:
单片机上的按键大部分都是机械弹性按键,这种按键在按下或者弹起的时候发生抖动,对实验造成影响
市面上消抖的方法有两种:
1.硬件消抖
在按键的电路上并联一个电容,利用电容的充放电特性对毛刺进行平滑出来,有效果,但是现在基本不会用这种方法
增加了成本
2.软件消抖
利用一个小延时跳过抖动的时间,大部分的按键延时个10ms左右可以跳过抖动
延时可以改成for(i=0;i<0x2000;i++) mydelay(100)近似10ms
把上述的代码进行改动
int main()
{
//初始化函数....
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 0)//S1
{
mydelay(100);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 0)//确定是人为按下
{
//点灯
}
}
if()
{
......
}
}
}
练习2
利用按键去控制灯,蜂鸣器....
第一次按下S1按键的时候,D1和D2亮,第二次按下S1按键的时候,D1和D2灭
S2-->D3和D4
S3-->BEEP
由原理图可知,蜂鸣器在低电平下不工作,高电平下工作
beep_lib.c
#include "stm32f4xx.h"
#include "beep_lib.h"
void beep_lib_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_ResetBits(GPIOF, GPIO_Pin_8);
}
void beep_Ctrl(int status)
{
status ? BEEP_OFF : BEEP_ON;
}
beep_lib.h
#ifndef __BEEP_LIB_H__
#define __BEEP_LIB_H__
#include "stm32f4xx.h"
/*蜂鸣器的状态*/
enum BEEP_STATUS
{
beep_ON,
beep_OFF
};
//蜂鸣器的控制
#define BEEP_OFF GPIO_ResetBits(GPIOF, GPIO_Pin_8)
#define BEEP_ON GPIO_SetBits(GPIOF, GPIO_Pin_8)
void beep_lib_init(void);
void beep_Ctrl(int status);
#endif
main.c
#include "main.h"
#include "stm32f4xx.h"
#include "led_lib.h"
#include "beep_lib.h"
#include "key_lib.h"
#include "exti.h"
int main(void)
{
// led_lib_init();
// while(1)
// {
// led_Ctrl(D1, ON);
// delay_ms(500);
// led_Ctrl(D1, OFF);
//
// led_Ctrl(D2, ON);
// delay_ms(500);
// led_Ctrl(D2, OFF);
// led_Ctrl(D3, ON);
// delay_ms(500);
// led_Ctrl(D3, OFF);
// led_Ctrl(D4, ON);
// delay_ms(500);
// led_Ctrl(D4, OFF);
// }
//定义四个标志位来表示四个按键是被按下还是松开
//默认按键被松开标志位为1
int S1 = 1;
int S2 = 1;
int S3 = 1;
int S4 = 1;
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
led_lib_init();
// beep_lib_init();
// EXTIX_Init();
// while(1);
beep_lib_init();
key_lib_init();
while(1)
{ //S1按键
if(KEY0 == 0)//判断按键KEY0是否被按下
{
delay_ms(50); //延时消抖
if(KEY0 == 0) //判断按键KEY0是否被按下
{
if(S1)
{
S1 = 0;//标志位S1置0
GPIO_ToggleBits(GPIOF,GPIO_Pin_9);//翻转D1灯的状态
GPIO_ToggleBits(GPIOF,GPIO_Pin_10);//翻转D2灯的状态
}
}
}
if(KEY0 == 1)//判断按键KEY0是否被松开
{
S1 = 1;//标志位S1置1
}
//S2按键
if(KEY1 == 0)//判断按键KEY1是否被按下
{
delay_ms(50);//延时消抖
if(KEY1 == 0)//判断按键KEY1是否被按下
{
if(S2)
{
S2 = 0;//标志位S2置0
GPIO_ToggleBits(GPIOE,GPIO_Pin_13);//翻转D3灯的状态
GPIO_ToggleBits(GPIOE,GPIO_Pin_14);//翻转D4灯的状态
}
}
}
if(KEY1 == 1)//判断按键KEY1是否被松开
{
S2 = 1;//标志位S2置1
}
//S3按键
if(KEY2 == 0)//判断按键KEY2是否被按下
{
delay_ms(50);//延时消抖
if(KEY2 == 0)//判断按键KEY2是否被按下
{
if(S3)
{
S3 = 0;//标志位S3置0
GPIO_ToggleBits(GPIOF,GPIO_Pin_8);//翻转蜂鸣器的状态
}
}
}
if(KEY2 == 1)//判断按键KEY2是否被松开
{
S3 = 1;//标志位S3置1
}
//S4按键
if(KEY3 == 0)//判断按键KEY3是否被按下
{
delay_ms(50);//延时消抖
if(KEY3 == 0)//判断按键KEY3是否被按下
{
if(S4)
{
S4 = 0;//标志位S4置0
GPIO_ToggleBits(GPIOF,GPIO_Pin_9);//翻转D1灯的状态
GPIO_ToggleBits(GPIOF,GPIO_Pin_10);//翻转D2灯的状态
GPIO_ToggleBits(GPIOE,GPIO_Pin_13);//翻转D3灯的状态
GPIO_ToggleBits(GPIOE,GPIO_Pin_14);//翻转D4灯的状态
}
}
}
if(KEY3 == 1)//判断按键KEY3是否被松开
{
S4 = 1;//标志位S4置1
}
}
}
由原理图的按键电路可知,S1,S2,S3,S4未被按下时,端口的电平值连接的是VDD3.3V,即高电平1,按下以后电路连通到了GND,测到的是低电平。总而言之,KEY0 == 0时,S1被按下。同时四个按键默认高电平,外部分别接入了一个上拉电阻,因此上拉/下拉寄存器应配置为上拉。
key_lib.c
#include "stm32f4xx.h"
#include "key_lib.h"
void key_lib_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);//KEY1,KEY2,KEY3
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);//KEY0
}
key_lib.h
#ifndef __KEY_LIB_H__
#define __KEY_LIB_H__
#include "stm32f4xx.h"
//按键S1,S2,S3,S4的状态
#define KEY0 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define KEY3 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
void key_lib_init(void);
#endif