野火霸天虎STM32F407笔记(二)——定义外设寄存器的结构体、编写复位置位函数、定义外设初始化结构体和编写外设初始化函数
一、定义外设寄存器的结构体
- 部分头文件stm32f4xx.h
#include <stdint.h>
//要访问这些成员,只需要知道结构体的地址就可以了
typedef struct
{
uint32_t MODER;
uint32_t OTYPR;
uint32_t OSPEEDR;
uint32_t PUPDR;
uint32_t IDR;
uint32_t ODR;
uint16_t BASSL;
uint16_t BASSH;
uint32_t LCKR;
uint32_t AFRL;
uint32_t AFRH;
}GPIO_TypeDef;
#define GPIOF ((GPIO_TypeDef*)GPIOF_BASE) //定义GPIO地址与结构体基地址一致,就可以通过寄存器结构体进行访问了
#endif //
- main.c
#include "stm32f4xx.h"
//软件延迟函数
void delay(unsigned int count)
{
for( ; count !=0;count--);
}
int main()
{
#if 0
//第一步:开GPIO端口时钟
RCC_AHBIENR |= (1<<5);
//第二步:配置GPIO为输出
GPIOF->MODER &= ~(3<<(2*6)); //清除
GPIOF->MODER |= (1<<(2*6));
//第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器
GPIOF->ODR |= (1<<6); //输出1,关灯
while(1)
{
GPIOF->ODR &= ~(1<<6); //输出0,开灯
delay(0x0fffff); //软件延时函数
GPIOF->ODR |= (1<<6); //输出1,关灯
delay(0x0fffff);
}
#elif 1
//第一步:开GPIO端口时钟
RCC_AHBIENR |= (1<<5);
//第二步:配置GPIO为输出
GPIOF->MODER &= ~(3<<(2*6)); //清除
GPIOF->MODER |= (1<<(2*6));
//第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器
GPIOF->ODR |= (1<<6); //输出1,关灯
while(1)
{
GPIOF->ODR &= ~(1<<6); //输出0,开灯
delay(0x0fffff); //软件延时函数
GPIOF->ODR |= (1<<6); //输出1,关灯
delay(0x0fffff);
}
#endif
}
void SystemInit(void)
{
}
- 定义的结构体怎样和端口的寄存器对应上呢,需要将GPIOF的基地址强制类型转换,类型为该结构体的指针类型。
二、编写复位置位函数
-
编写驱动,必定会用到寄存器,我们所有的外设寄存器都要在头文件“stm32f4xx_gpio”中,所以需要在该头文件里包含头文件“stm32f4xx.h”
-
函数体
//置位函数,替换GPIOF->ODR |= (1<<6);
void GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BSRRL= GPIO_Pin;
}
//复位函数,替换GPIOF->ODR &= ~(1<<6);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BSRRH= GPIO_Pin;
}
- 头文件
#ifndef _STM32F4XX_GPIO_H
#define _STM32F4XX_GPIO_H
#include "stm32f4xx.h"
#define GPIO_Pin_0 ((uint16_t)(1<<0))
#define GPIO_Pin_1 ((uint16_t)(1<<1))
#define GPIO_Pin_2 ((uint16_t)(1<<2))
#define GPIO_Pin_3 ((uint16_t)(1<<3))
#define GPIO_Pin_4 ((uint16_t)(1<<4))
#define GPIO_Pin_5 ((uint16_t)(1<<5))
#define GPIO_Pin_6 ((uint16_t)(1<<6))
#define GPIO_Pin_7 ((uint16_t)(1<<7))
#define GPIO_Pin_8 ((uint16_t)(1<<8))
#define GPIO_Pin_9 ((uint16_t)(1<<9))
#define GPIO_Pin_10 ((uint16_t)(1<<10))
#define GPIO_Pin_11 ((uint16_t)(1<<11))
#define GPIO_Pin_12 ((uint16_t)(1<<12))
#define GPIO_Pin_13 ((uint16_t)(1<<13))
#define GPIO_Pin_14 ((uint16_t)(1<<14))
#define GPIO_Pin_15 ((uint16_t)(1<<15))
#define GPIO_Pin_ALL ((uint16_t)(1<<0xffff))
void GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
#endif //
- 加入函数后的部分main.c
//编写端口的复位和置位函数
//第一步:开GPIO端口时钟
RCC_AHBIENR |= (1<<5);
//第二步:配置GPIO为输出
GPIOF->MODER &= ~(3<<(2*6)); //清除
GPIOF->MODER |= (1<<(2*6));
//第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器
GPIOF->ODR |= (1<<6); //输出1,关灯
while(1)
{
//GPIOF->ODR &= ~(1<<6); //输出0,开灯
GPIO_ResetBits(GPIOF,GPIO_Pin_6); //复位函数
delay(0x0fffff); //软件延时函数
//GPIOF->ODR |= (1<<6); //输出1,关灯
GPIO_SetBits(GPIOF,GPIO_Pin_6); //置位函数
delay(0x0fffff);
}
三、定义外设初始化结构体和编写外设初始化函数
- 在使用固件库函数复位函数,置位函数替换原来代码后,可读性提高,在程序中我们的第一步,第二步的初始化端口配置的是输入还是输出,是通用输出还是对外输出,这些代码的实现可以将操作寄存器实现的转化为库函数实现。下面是完整的代码。
- main.c
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
//软件延迟函数
void delay(unsigned int count)
{
for( ; count !=0;count--);
}
int main()
{
#if 0
//第一步:开GPIO端口时钟
RCC_AHBIENR |= (1<<5);
//第二步:配置GPIO为输出
GPIOF->MODER &= ~(3<<(2*6)); //清除
GPIOF->MODER |= (1<<(2*6));
//第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器
GPIOF->ODR |= (1<<6); //输出1,关灯
while(1)
{
GPIOF->ODR &= ~(1<<6); //输出0,开灯
delay(0x0fffff); //软件延时函数
GPIOF->ODR |= (1<<6); //输出1,关灯
delay(0x0fffff);
}
#elif 1 //定义外设初始化结构体,编写外设初始化函数
//第一步:开GPIO端口时钟
RCC_AHBIENR |= (1<<5);
// //第二步:配置GPIO为输出
// GPIOF->MODER &= ~(3<<(2*6)); //清除
// GPIOF->MODER |= (1<<(2*6));
GPIO_InitTypeDef GPIO_InitStruct;
/*初始化PF6引脚*/
/*选择要控制的GPIO引脚*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
/*设置引脚模式为输出模式*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
/*设置引脚的输出类型为推挽输出*/
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
/*设置引脚为上拉模式*/
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
/*设置引脚速率为2MHz */
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOF, &GPIO_InitStruct);
//第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器
GPIOF->ODR |= (1<<6); //输出1,关灯
while(1)
{
//GPIOF->ODR &= ~(1<<6); //输出0,开灯
GPIO_ResetBits(GPIOF,GPIO_Pin_6); //固件库函数:复位函数
delay(0x0fffff); //软件延时函数
//GPIOF->ODR |= (1<<6); //输出1,关灯
GPIO_SetBits(GPIOF,GPIO_Pin_6); //置位函数
delay(0x0fffff);
}
#endif
}
void SystemInit(void)
{
}
- stm32f4xx.h
#ifndef _STM32F4XX_H
#define _STM32F4XX_H
#include <stdint.h>
#define GPIOF_BASE ((unsigned int)0x40021400)
#define RCC_BASE ((unsigned int)0x40023800)
#define GPIO_MODER *(unsigned int*)(GPIOF_BASE + 0x00)
#define GPIO_OTYPER *(unsigned int*)(GPIOF_BASE + 0x04)
#define GPIOF_ODR *(unsigned int*)(GPIOF_BASE + 0x14)
#define RCC_AHBIENR *(unsigned int*)(RCC_BASE + 0x30)
//外设寄存器结构体定义
//typedef unsigned int uint32_t;
//typedef unsigned short int uint16_t;
//要访问这些成员,只需要知道结构体的地址就可以了
typedef struct
{
uint32_t MODER;
uint32_t OTYPER;
uint32_t OSPEEDR;
uint32_t PUPDR;
uint32_t IDR;
uint32_t ODR;
uint16_t BSRRL;
uint16_t BSRRH;
uint32_t LCKR;
uint32_t AFRL;
uint32_t AFRH;
}GPIO_TypeDef;
#define GPIOF ((GPIO_TypeDef*)GPIOF_BASE) //定义GPIO地址与结构体基地址一致,就可以通过寄存器结构体进行访问了
#endif //
- stm32f4xx_gpio.c
#include "stm32f4xx_gpio.h"
#include "stm32f4xx.h"
//写函数要有可读性,一看这个函数名就知道这个函数是干嘛的
//置位函数,替换GPIOF->ODR |= (1<<6);
void GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BSRRL= GPIO_Pin;
}
//复位函数,替换GPIOF->ODR &= ~(1<<6);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
GPIOx->BSRRH= GPIO_Pin;
}
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/*-- GPIO Mode Configuration --*/
for (pinpos = 0x00; pinpos < 16; pinpos++)
{
/*以下运算是为了通过 GPIO_InitStruct->GPIO_Pin 算出引脚号0-15*/
/*经过运算后pos的pinpos位为1,其余为0,与GPIO_Pin_x宏对应。pinpos变量每次循环加1,*/
pos = ((uint32_t)0x01) << pinpos;
/* pos与GPIO_InitStruct->GPIO_Pin做 & 运算,若运算结果currentpin == pos,
则表示GPIO_InitStruct->GPIO_Pin的pinpos位也为1,
从而可知pinpos就是GPIO_InitStruct->GPIO_Pin对应的引脚号:0-15*/
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
/*currentpin == pos时执行初始化*/
if (currentpin == pos)
{
/*GPIOx端口,MODER寄存器的GPIO_InitStruct->GPIO_Pin对应的引脚,MODER位清空*/
GPIOx->MODER &= ~(3 << (2 *pinpos));
/*GPIOx端口,MODER寄存器的GPIO_Pin引脚,MODER位设置"输入/输出/复用输出/模拟"模式*/
GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (2 *pinpos));
/*GPIOx端口,PUPDR寄存器的GPIO_Pin引脚,PUPDR位清空*/
GPIOx->PUPDR &= ~(3 << ((2 *pinpos)));
/*GPIOx端口,PUPDR寄存器的GPIO_Pin引脚,PUPDR位设置"上/下拉"模式*/
GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (2 *pinpos));
/*若模式为"输出/复用输出"模式,则设置速度与输出类型*/
if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
{
/*GPIOx端口,OSPEEDR寄存器的GPIO_Pin引脚,OSPEEDR位清空*/
GPIOx->OSPEEDR &= ~(3 << (2 *pinpos));
/*GPIOx端口,OSPEEDR寄存器的GPIO_Pin引脚,OSPEEDR位设置输出速度*/
GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (2 *pinpos));
/*GPIOx端口,OTYPER寄存器的GPIO_Pin引脚,OTYPER位清空*/
GPIOx->OTYPER &= ~(1 << (pinpos)) ;
/*GPIOx端口,OTYPER位寄存器的GPIO_Pin引脚,OTYPER位设置"推挽/开漏"输出类型*/
GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << (pinpos));
}
}
}
}
- stm32f4xx_gpio.h
#ifndef _STM32F4XX_GPIO_H
#define _STM32F4XX_GPIO_H
#include "stm32f4xx.h"
#define GPIO_Pin_0 ((uint16_t)(1<<0))
#define GPIO_Pin_1 ((uint16_t)(1<<1))
#define GPIO_Pin_2 ((uint16_t)(1<<2))
#define GPIO_Pin_3 ((uint16_t)(1<<3))
#define GPIO_Pin_4 ((uint16_t)(1<<4))
#define GPIO_Pin_5 ((uint16_t)(1<<5))
#define GPIO_Pin_6 ((uint16_t)(1<<6))
#define GPIO_Pin_7 ((uint16_t)(1<<7))
#define GPIO_Pin_8 ((uint16_t)(1<<8))
#define GPIO_Pin_9 ((uint16_t)(1<<9))
#define GPIO_Pin_10 ((uint16_t)(1<<10))
#define GPIO_Pin_11 ((uint16_t)(1<<11))
#define GPIO_Pin_12 ((uint16_t)(1<<12))
#define GPIO_Pin_13 ((uint16_t)(1<<13))
#define GPIO_Pin_14 ((uint16_t)(1<<14))
#define GPIO_Pin_15 ((uint16_t)(1<<15))
#define GPIO_Pin_ALL ((uint16_t)(1<<0xffff))
/**
* GPIO端口配置模式的枚举定义
*/
typedef enum
{
GPIO_Mode_IN = 0x00, /*!< 输入模式 */
GPIO_Mode_OUT = 0x01, /*!< 输出模式 */
GPIO_Mode_AF = 0x02, /*!< 复用模式 */
GPIO_Mode_AN = 0x03 /*!< 模拟模式 */
}GPIOMode_TypeDef;
/**
* GPIO输出类型枚举定义
*/
typedef enum
{
GPIO_OType_PP = 0x00, /*!< 推挽模式 */
GPIO_OType_OD = 0x01 /*!< 开漏模式 */
}GPIOOType_TypeDef;
/**
* GPIO输出速率枚举定义
*/
typedef enum
{
GPIO_Speed_2MHz = 0x00, /*!< 2MHz */
GPIO_Speed_25MHz = 0x01, /*!< 25MHz */
GPIO_Speed_50MHz = 0x02, /*!< 50MHz */
GPIO_Speed_100MHz = 0x03 /*!<100MHz */
}GPIOSpeed_TypeDef;
/**
*GPIO上/下拉配置枚举定义
*/
typedef enum
{
GPIO_PuPd_NOPULL = 0x00,/*浮空*/
GPIO_PuPd_UP = 0x01, /*上拉*/
GPIO_PuPd_DOWN = 0x02 /*下拉*/
}GPIOPuPd_TypeDef;
/**
* GPIO初始化结构体类型定义
*/
typedef struct
{
uint32_t GPIO_Pin; /*!< 选择要配置的GPIO引脚
可输入 GPIO_Pin_ 定义的宏 */
GPIOMode_TypeDef GPIO_Mode; /*!< 选择GPIO引脚的工作模式
可输入 GPIOMode_TypeDef 定义的枚举值*/
GPIOSpeed_TypeDef GPIO_Speed; /*!< 选择GPIO引脚的速率
可输入 GPIOSpeed_TypeDef 定义的枚举值 */
GPIOOType_TypeDef GPIO_OType; /*!< 选择GPIO引脚输出类型
可输入 GPIOOType_TypeDef 定义的枚举值*/
GPIOPuPd_TypeDef GPIO_PuPd; /*!<选择GPIO引脚的上/下拉模式
可输入 GPIOPuPd_TypeDef 定义的枚举值*/
}GPIO_InitTypeDef;
void GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
#endif //
- 这一节是一个过渡,为了更好地学习固件库,后期学习重点在于学会并熟练调用函数,根据外设的初始化结构体在手册里面找到寄存器说明来看具体外设的功能。Persistence is victory!