一、如何用DAP仿真器下载程序
在上面链接学习整理
二、 初始STM32
1.什么是STM32
2.STM32有什么
三、 什么是寄存器
四、新建工程-寄存器版
1、使用Keil新建一个工程
- 1.设置文字
- 2.懂太语法检测
- 3.配置仿真器
以霸天虎为例,不是ULINK而是DAP
还有视频里的:
main.c
#include "stm32f4xx.h"
int main(void){
while(1){
/*在这里添加程序的主题代码*/
}
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
stm32f4xx.h
/*用来存放寄存器映射相关的代码*/
要多一行,不然编译报错
2、初步入门,像51单片机一样点亮LED
main.c
#include "stm32f4xx.h"
#if 0
#include <reg51.>
void main(void)
{
PAO = 0XFE;
while(1);
}
#endif
int main(void){
#if 0
/*第一步:开启GPIO端口的时钟*/
/*打开GPIOF端口的时钟*/
*( unsigned int * )(0x40023800+0x30) |= ( 1<<5 );
/*第二步:控制GPIO的方向*/
/*GPIO 配置为输出*/
*( unsigned int * )(0x40021400+0x00) &=~ ( (0x03) << (2*6) );
*( unsigned int * )(0x40021400+0x00) |= ( 1<< (2*6) );
/*第三步:控制GPIO的数据输出寄存器*/
/* PF6输出高电平 */
*( unsigned int * )(0x40021400+0x14) |= (1<<6);
/* PF6输出低电平 */
*( unsigned int * )(0x40021400+0x14) &= ~(1<<6);
#elif 1
/*第一步:开启GPIO端口的时钟*/
/*打开GPIOF端口的时钟*/
RCC_AHB1ENR |= ( 1<<5 );
/*第二步:控制GPIO的方向*/
/*GPIO 配置为输出*/
GPIOF_MODER &=~ ( (0x03) << (2*6) );
GPIOF_MODER |= ( 1<< (2*6) );
/*第三步:控制GPIO的数据输出寄存器*/
/* PF6输出高电平 */
GPIOF_ODR |= (1<<6);
/* PF6输出低电平 */
GPIOF_ODR &= ~(1<<6);
#endif
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
stm32f4xx.h
/*用来存放寄存器映射相关的代码*/
#define RCC_AHB1ENR *(unsigned int *)(0x40023800+0x30)
#define GPIOF_MODER *( unsigned int * )(0x40021400+0x00)
#define GPIOF_ODR *( unsigned int * )(0x40021400+0x14)
五、作业---1.--2.
1.把其他俩个灯也点亮
/*作业1*/
/*第一步:开启GPIO端口的时钟*/
/*打开GPIOF端口的时钟*/
RCC_AHB1ENR |= ( 1<<5 );
/*第二步:控制GPIO的方向*/
/*GPIO 配置为输出*/
GPIOF_MODER &=~ ( (0x03) << (2*6) );
GPIOF_MODER |= ( 1<< (2*6) );
GPIOF_MODER &=~ ( (0x03) << (2*7) );
GPIOF_MODER |= ( 1<< (2*7) );
GPIOF_MODER &=~ ( (0x03) << (2*8) );
GPIOF_MODER |= ( 1<< (2*8) );
/*第三步:控制GPIO的数据输出寄存器*/
/* PF6输出高电平 */
GPIOF_ODR |= (1<<6);
/* PF6输出低电平 */
GPIOF_ODR &= ~(1<<6);
/* PF7输出高电平 */
GPIOF_ODR |= (1<<7);
/* PF7输出低电平 */
GPIOF_ODR &= ~(1<<7);
/* PF8输出高电平 */
GPIOF_ODR |= (1<<8);
/* PF8输出低电平 */
GPIOF_ODR &= ~(1<<8);
2.实现三个灯闪烁(时间的控制使用软件延迟)
/*作业2*/
void Delay(unsigned int count){
for(;count!=0;count--);
}
/*第一步:开启GPIO端口的时钟*/
/*打开GPIOF端口的时钟*/
RCC_AHB1ENR |= ( 1<<5 );
/*第二步:控制GPIO的方向*/
/*GPIO 配置为输出*/
GPIOF_MODER &=~ ( (0x03) << (2*6) );
GPIOF_MODER |= ( 1<< (2*6) );
GPIOF_MODER &=~ ( (0x03) << (2*7) );
GPIOF_MODER |= ( 1<< (2*7) );
GPIOF_MODER &=~ ( (0x03) << (2*8) );
GPIOF_MODER |= ( 1<< (2*8) );
/*第三步:控制GPIO的数据输出寄存器*/
/* PF6输出高电平 */
GPIOF_ODR |= (1<<6);
/* PF6输出低电平 */
GPIOF_ODR &= ~(1<<6);
/* PF7输出高电平 */
GPIOF_ODR |= (1<<7);
/* PF7输出低电平 */
GPIOF_ODR &= ~(1<<7);
/* PF8输出高电平 */
GPIOF_ODR |= (1<<8);
/* PF8输出低电平 */
GPIOF_ODR &= ~(1<<8);
while(1){
GPIOF_ODR &= ~(1<<6);
Delay(0x0fffff);
GPIOF_ODR |= (1<<6);
Delay(0x0fffff);
GPIOF_ODR &= ~(1<<7);
Delay(0x0fffff);
GPIOF_ODR |= (1<<7);
Delay(0x0fffff);
GPIOF_ODR &= ~(1<<8);
Delay(0x0fffff);
GPIOF_ODR |= (1<<8);
Delay(0x0fffff);
}
六、使用寄存器点亮LED
1、GPIO功能框图讲解
GPIO跟引脚有什么区别
2、从0开始写代码讲解实验
main.c
#include "stm32f4xx.h"
/*
*注意事项
*要在 Options for target 选项里的Use MicroLIB这个勾选上
*这样才能执行C文件里的main函数
*/
/*软件延时函数*/
void delay(unsigned int count){
for(;count!=0;count--);
}
int main(void){
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF_MOOER &= ~( 3<<(2*6) ); //清零
GPIOF_MOOER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF_ODR |= (1<<6);
while(1){
GPIOF_ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF_ODR |= (1<<6);
delay(0x0fffff);
}
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
stm32f4xx.h
/*用来存放寄存器映射相关的代码*/
#define GPIOF_BASE ((unsigned int )0x40021400)
#define RCC_BASE ((unsigned int )0x40023800)
#define GPIOF_MOOER *( unsigned int * )(GPIOF_BASE+0x00)
#define GPIOF_OTYPER *( unsigned int * )(GPIOF_BASE+0x04)
#define GPIOF_ODR *( unsigned int * )(GPIOF_BASE+0x14)
#define RCC_AHB1ENR *( unsigned int * )(RCC_BASE+0x30)
七、自己写库-构建库函数雏形
1、外设寄存器结构体定义
main.c
#include "stm32f4xx.h"
/*自己写库*/
/*
*注意事项
*要在 Options for target 选项里的Use MicroLIB这个勾选上
*这样才能执行C文件里的main函数
*/
/*软件延时函数*/
void delay(unsigned int count){
for(;count!=0;count--);
}
int main(void){
#if 0
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF_MOOER &= ~( 3<<(2*6) ); //清零
GPIOF_MOOER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF_ODR |= (1<<6);
while(1){
GPIOF_ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF_ODR |= (1<<6);
delay(0x0fffff);
}
#elif 1 /*定义外设的寄存器结构体*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF->MODER &= ~( 3<<(2*6) ); //清零
GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIOF->ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF->ODR |= (1<<6);
delay(0x0fffff);
}
#endif
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
stm32f4xx.h
//#include <stdint.h>
#ifndef _STM32F4XX_H
#define _STM32F4XX_H
/*用来存放寄存器映射相关的代码*/
#define GPIOF_BASE ((unsigned int )0x40021400)
#define RCC_BASE ((unsigned int )0x40023800)
#define GPIOF_MOOER *( unsigned int * )(GPIOF_BASE+0x00)
#define GPIOF_OTYPER *( unsigned int * )(GPIOF_BASE+0x04)
#define GPIOF_ODR *( unsigned int * )(GPIOF_BASE+0x14)
#define RCC_AHB1ENR *( 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)
#endif /*_STM32F4XX_H*/
2、编写端口复位和置位函数
新建了stm32f4xx_gpio.c和stm32f4xx_gpio.h文件,用来编写端口的复位和置位函数
stm32f4xx_gpio.c
#include "stm32f4xx_gpio.h"
/*置位函数*/
void GPIO_SetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin){
GPIOx->BSRRL = GPIO_Pin;
}
/*复位函数*/
void GPIO_ResetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin){
GPIOx->BSRRH = GPIO_Pin;
}
stm32f4xx_gpio.h
#ifndef _STM32F4XX_GPIO_H
#define _STM32F4XX_GPIO_H
#include "stm32f4xx.h"
#include <stdint.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_A11 ((uint16_t)(0xffff))
/*置位函数*/
void GPIO_SetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
//复位函数
void GPIO_ResetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
#endif /*_STM32F4XX_GPIO_H*/
main.c函数调用函数实现功能
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
/*自己写库*/
/*
*注意事项
*要在 Options for target 选项里的Use MicroLIB这个勾选上
*这样才能执行C文件里的main函数
*/
/*软件延时函数*/
void delay(unsigned int count){
for(;count!=0;count--);
}
int main(void){
#if 0
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF_MOOER &= ~( 3<<(2*6) ); //清零
GPIOF_MOOER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF_ODR |= (1<<6);
while(1){
GPIOF_ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF_ODR |= (1<<6);
delay(0x0fffff);
}
#elif 0 /*定义外设的寄存器结构体*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF->MODER &= ~( 3<<(2*6) ); //清零
GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIOF->ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF->ODR |= (1<<6);
delay(0x0fffff);
}
#elif 1 /*编写端口的置位和复位函数*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF->MODER &= ~( 3<<(2*6) ); //清零
GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
GPIO_SetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
}
#endif
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
3、定义外设初始化结构体和编写外设初始化函数
stm32f4xx_gpio.c
#include "stm32f4xx_gpio.h"
/*置位函数*/
void GPIO_SetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin){
GPIOx->BSRRL = GPIO_Pin;
}
/*复位函数*/
void GPIO_ResetBits(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin){
GPIOx->BSRRH = GPIO_Pin;
}
/**
*函数功能:初始化引脚模式
*参数说明:GPIOx,该参数为GPIO_TypeDef类型的指针,指向GPIO端口的地址
* GPIO_InitTypeDef:GPIO_InitTypeDef结构体指针,指向初始化变量
*/
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)(( GPIO_InitStruct->GPIO_OType)<< (pinpos));
}
}
}
}
stm32f4xx_gpio.h
#ifndef _STM32F4XX_GPIO_H
#define _STM32F4XX_GPIO_H
#include "stm32f4xx.h"
#include <stdint.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_A11 ((uint16_t)(0xffff))
typedef enum{
GPIO_Mode_IN = 0X00,
GPIO_Mode_OUT = 0X01,
GPIO_Mode_AF = 0X02,
GPIO_Mode_AN = 0X03
}GPIOMode_TypeDef;
typedef enum{
GPIO_PuPd_NOPULL = 0X00,
GPIO_PuPd_UP = 0X01,
GPIO_PuPd_DOWN = 0X02
}GPIOPuPd_TypeDef;
typedef enum{
GPIO_OType_PP = 0X00,
GPIO_OType_OD = 0X01
}GPIOOType_TypeDef;
typedef enum{
GPIO_Speed_2MHz = 0X00,
GPIO_Speed_25MHz = 0X01,
GPIO_Speed_20MHz = 0X02,
GPIO_Speed_100MHz = 0X03
}GPIOSpeed_TypeDef;
/*定义GPIO的初始化结构体*/
typedef struct{
uint16_t GPIO_Pin;
GPIOMode_TypeDef GPIO_Mode;
GPIOPuPd_TypeDef GPIO_PuPd;
GPIOOType_TypeDef GPIO_OType;
GPIOSpeed_TypeDef GPIO_Speed;
}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 /*_STM32F4XX_GPIO_H*/
main.c
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
/*自己写库*/
/*
*注意事项
*要在 Options for target 选项里的Use MicroLIB这个勾选上
*这样才能执行C文件里的main函数
*/
/*软件延时函数*/
void delay(unsigned int count){
for(;count!=0;count--);
}
int main(void){
#if 0
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF_MOOER &= ~( 3<<(2*6) ); //清零
GPIOF_MOOER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF_ODR |= (1<<6);
while(1){
GPIOF_ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF_ODR |= (1<<6);
delay(0x0fffff);
}
#elif 0 /*定义外设的寄存器结构体*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF->MODER &= ~( 3<<(2*6) ); //清零
GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIOF->ODR &= ~(1<<6);
delay(0x0fffff);
GPIOF->ODR |= (1<<6);
delay(0x0fffff);
}
#elif 1 /*编写端口的置位和复位函数*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
// GPIOF->MODER &= ~( 3<<(2*6) ); //清零
// GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
//c89的语法格式就是变量必须紧跟大括号
//c99不需要
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOF, &GPIO_InitStruct);
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
GPIO_SetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
}
#elif 1 /*定义外设的初始化结构体和编写外设的初始化函数*/
/*第一步:开GPIO端口时钟*/
RCC_AHB1ENR |= (1<<5);
/*第二步:配置GPIO为输出*/
GPIOF->MODER &= ~( 3<<(2*6) ); //清零
GPIOF->MODER |= ( 1<<(2*6) ); //配置成 01
/*第三步:让GPIO输出0或者1,ODR寄存器或者BSRR寄存器*/
GPIOF->ODR |= (1<<6);
while(1){
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
GPIO_SetBits(GPIOF,GPIO_Pin_6);
delay(0x0fffff);
}
#endif
}
void SystemInit(void){
/*函数体为空,目的是为了骗过编译器不报错*/
}
八、自己写库—构建库函数雏形
1、 什么是STM32固件库
固件库是指“STM32标准函数库”,它是由ST公司针对STM32提供的函数接口,即API (Application Program Interface), 开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。
当我们调用库API的时候不需要挖空心思去了解库底层的寄存器操作,就像当年我们刚开始学习C语言的时候,用prinft()函数时只是学习它的使用格式, 并没有去研究它的源码实现,但需要深入研究的时候,经过千锤百炼的库源码就是最佳学习范例。
实际上,库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。 库开发方式与直接配置寄存器方式的区别见图 固件库开发与寄存器开发对比图 。
2、新建工程-库函数版
- 新建一个文件夹FWLIB-TEMPALTE,然后在里面新建以下几个文件夹
- 在Libraries文件夹下拷贝过来以下俩个文件,在固件库里拷贝
- 在这个位置拷贝的:D:\1野火嵌入式\固件库stm32f4_dsp_stdperiph_lib_V1.8.0\STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Libraries
- 在User文件夹下拷贝以下四个文件:
- 路径:D:\1野火嵌入式\固件库stm32f4_dsp_stdperiph_lib_V1.8.0\STM32F4xx_DSP_StdPeriph_Lib_V1.8.0\Project\STM32F4xx_StdPeriph_Templates
- 需要把stm32f4xx_it.c文件里的俩行删除
- 把main.c文件全部删除,然后编写以下代码
#include "stm32f4xx.h"
int main(void){
/*在这里添加你自己的程序*/
while(1);
}
- 在D:\1野火嵌入式\FWLIB-TEMPALTE\Libraries\CMSIS 这个文件里保留俩个文件(Device、Include),其他全部删除
- 在D:\1野火嵌入式\FWLIB-TEMPALTE\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates 保留俩个文件,其他的全部删除
- 在D:\1野火嵌入式\FWLIB-TEMPALTE\Project 新建RVMDK(uv5)文件夹
打开Keil,执行如下步骤新建工程:
点击保存弹出这个框:选择芯片型号
点击OK,弹出下面这个框,然后点击×,给它去掉
然后就新建好了:如下图:
然后修改名称,并添加以下文件:
在STARTUP文件里添加启动文件:路径:
D:\1野火嵌入式\FWLIB-TEMPALTE\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm
在CMSIS下添加文件:D:\1野火嵌入式\FWLIB-TEMPALTE\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
在STM32F4xx_StdPeriph_Driver文件夹添加这个路径的D:\1野火嵌入式\FWLIB-TEMPALTE\Libraries\STM32F4xx_StdPeriph_Driver\src全部文件
在USER文件夹添加俩个文件:D:\1野火嵌入式\FWLIB-TEMPALTE\User
最后需要指定头文件的目录,不然报错
还需要添加这个:USE_STDPERIPH_DRIVER.STM32F40_41xxx.(这个错了)
应该是:USE_STDPERIPH_DRIVER,STM32F40_41xxx,
然后需要取消三个文件:
运行出来有错误和警告,原因是
3、固件库编程-GPIO输出-使用固件库点亮LED灯
复制上述建立的文件FWLIB-TEMPALTE,改名为GPIO输出——使用固件库点亮LED灯
在USER文件夹下新建LED文件夹,用来存放跟LED相关的代码,然后再在这个文件夹下新建俩个驱动文件用来存放代码(bsp_led.c、bsp_led.h)
打开程序
外设初始化分四步:bsp_led.c:
/*以下四个步骤适合所有外设的初始化*/
/*第一步:开GPIO的时钟*/
/*第二步:定义一个GPIO初始化结构体*/
/*第三步:配置GPIO初始化结构体的成员*/
/*第四步:调用GPIO初始化函数,把配置好的结构体的成员的参数写入寄存器*/
bsp_led.c:
//bsp:board support package 板级支持包
#include "bsp_led.h"
/*外设初始化函数*/
void LED_GPIO_Config(void){
/*以下四个步骤适合所有外设的初始化*/
/*第一步:开GPIO的时钟*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
/*第二步:定义一个GPIO初始化结构体*/
GPIO_InitTypeDef GPIO_InitStruct;
/*第三步:配置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_Speed = GPIO_Low_Speed;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
/*第四步:调用GPIO初始化函数,把配置好的结构体的成员的参数写入寄存器*/
GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
}
bsp_led.h:
#ifndef _BSP_LED_H
#define _BSP_LED_H
#include "stm32f4xx.h"
void LED_GPIO_Config(void);
#endif /*_BSP_LED_H*/
main.c出现警告
#include "stm32f4xx.h"
#include "bsp_led.h"
void Delay(uint32_t count){
for(; count!=0; count-- );
}
int main(void){
/*在这里添加你自己的程序*/
LED_GPIO_Config();
//实现闪烁
while(1){
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
Delay(0xffffff);
GPIO_SetBits(GPIOF,GPIO_Pin_6);
Delay(0xffffff);
}
}
4、GPIO输入-按键检测
复制上述文件GPIO输出——使用固件库点亮LED灯,在USER文件夹新建KEY文件夹,然后新建俩个文件:c文件bsp_key.c和头文件bsp_key.h
需要修改以下四个文件:
bsp_key.c:
#include "bsp_key.h"
/*外设初始化函数*/
void KEY_GPIO_Config(void){
/*以下四个步骤适合所有外设的初始化*/
/*第一步:开GPIO的时钟*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE);
/*第二步:定义一个GPIO初始化结构体*/
GPIO_InitTypeDef GPIO_InitStruct;
/*第三步:配置GPIO初始化结构体的成员*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
/*只在输入下用到*/
//GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
//GPIO_InitStruct.GPIO_Speed = GPIO_Low_Speed;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
/*第四步:调用GPIO初始化函数,把配置好的结构体的成员的参数写入寄存器*/
GPIO_Init(GPIOA,&GPIO_InitStruct);
/*第三步:配置GPIO初始化结构体的成员*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
/*只在输入下用到*/
//GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
//GPIO_InitStruct.GPIO_Speed = GPIO_Low_Speed;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
/*第四步:调用GPIO初始化函数,把配置好的结构体的成员的参数写入寄存器*/
GPIO_Init(GPIOC,&GPIO_InitStruct);
//GPIO_ResetBits(GPIOF,GPIO_Pin_6);
}
//按键扫描函数
uint8_t KEY_Scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin){
if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON ){
while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON);
return KEY_ON;
} else {
return KEY_OFF;
}
}
bsp_key.h:
#ifndef _BSP_KEY_H #define _BSP_KEY_H //包含所有寄存器映射的文件 #include "stm32f4xx.h" #define KEY_ON 1 #define KEY_OFF 0 void KEY_GPIO_Config(void); uint8_t KEY_Scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin); #endif /*_BSP_KEY_H*/
重点是bsp_led.h这个文件,从人家写好的文件拷贝的:
#ifndef _BSP_LED_H #define _BSP_LED_H #include "stm32f4xx.h" //引脚定义 /*******************************************************/ //R 红色灯 #define LED1_PIN GPIO_Pin_6 #define LED1_GPIO_PORT GPIOF #define LED1_GPIO_CLK RCC_AHB1Periph_GPIOF //G 绿色灯 #define LED2_PIN GPIO_Pin_7 #define LED2_GPIO_PORT GPIOF #define LED2_GPIO_CLK RCC_AHB1Periph_GPIOF //B 蓝色灯 #define LED3_PIN GPIO_Pin_8 #define LED3_GPIO_PORT GPIOF #define LED3_GPIO_CLK RCC_AHB1Periph_GPIOF /************************************************************/ /** 控制LED灯亮灭的宏, * LED低电平亮,设置ON=0,OFF=1 * 若LED高电平亮,把宏设置成ON=1 ,OFF=0 即可 */ #define ON 0 #define OFF 1 /* 带参宏,可以像内联函数一样使用 */ #define LED1(a) if (a) \ GPIO_SetBits(LED1_GPIO_PORT,LED1_PIN);\ else \ GPIO_ResetBits(LED1_GPIO_PORT,LED1_PIN) #define LED2(a) if (a) \ GPIO_SetBits(LED2_GPIO_PORT,LED2_PIN);\ else \ GPIO_ResetBits(LED2_GPIO_PORT,LED2_PIN) #define LED3(a) if (a) \ GPIO_SetBits(LED3_GPIO_PORT,LED3_PIN);\ else \ GPIO_ResetBits(LED3_GPIO_PORT,LED3_PIN) /* 直接操作寄存器的方法控制IO */ #define digitalHi(p,i) {p->BSRRL=i;} //设置为高电平 #define digitalLo(p,i) {p->BSRRH=i;} //输出低电平 #define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态 /* 定义控制IO的宏 */ #define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_PIN) #define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_PIN) #define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_PIN) #define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_PIN) #define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_PIN) #define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_PIN) #define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_PIN) #define LED3_OFF digitalHi(LED3_GPIO_PORT,LED3_PIN) #define LED3_ON digitalLo(LED3_GPIO_PORT,LED3_PIN) /* 基本混色,后面高级用法使用PWM可混出全彩颜色,且效果更好 */ //红 #define LED_RED \ LED1_ON;\ LED2_OFF;\ LED3_OFF //绿 #define LED_GREEN \ LED1_OFF;\ LED2_ON;\ LED3_OFF //蓝 #define LED_BLUE \ LED1_OFF;\ LED2_OFF;\ LED3_ON //黄(红+绿) #define LED_YELLOW \ LED1_ON;\ LED2_ON;\ LED3_OFF //紫(红+蓝) #define LED_PURPLE \ LED1_ON;\ LED2_OFF;\ LED3_ON //青(绿+蓝) #define LED_CYAN \ LED1_OFF;\ LED2_ON;\ LED3_ON //白(红+绿+蓝) #define LED_WHITE \ LED1_ON;\ LED2_ON;\ LED3_ON //黑(全部关闭) #define LED_RGBOFF \ LED1_OFF;\ LED2_OFF;\ LED3_OFF void LED_GPIO_Config(void); #endif /* _BSP_LED_H */