==========================================================
《 固件库搭建》
1.开发环境:window+kill5+STM32F407
2.开发准备:(具体下载源码包路径就不提供了,可以下载我已经建好的基本固件库)
(1)连接(已经建好的固件库):链接:https://pan.baidu.com/s/1Ddvcd1LQDZ3V3EOSt4FL3w 提取码:cua6
固件库文件结构说明:
新建一个文件夹叫做 Template ,在 Template 文件夹中新建以下文件夹
CMSIS --->存放启动文件以及内核相关文件
Libraries --->相关外设的头文件以及源文件
Output --->存放编译生成的其他文件
Project --->存放工程
User --->存放自己日后写的代码,mian.c也在里面(该main.c是官方的,后期把函数体删除加自己的函数实现即可)
Doc --->存放 Readme.txt
(2)若想单独建立自己的固件库参考:https://blog.csdn.net/linxiaobo110/article/details/40347689
2.环境搭建:已经弄好的固件库在keill开发平台使用还得需要弄些步骤,直到编译成功才能说明成功。
修改相应的文件以及配置(详细操作另看操作图和视频:)
链接:https://pan.baidu.com/s/1Am9lanrp4IR8Ea0YT2xDqg
提取码:josk (视频大部分内容是多余的)
1、 点击魔术棒 ---》C/C++
在 define 位置添加 STM32F40_41xxx, USE_STDPERIPH_DRIVER
在 Include Paths 中添加工程中所有用到的头文件路径
2、整理 main.c main.h
3、把 stm32f4xx_it.c 中144行注释掉
//TimingDelay_Decrement();
4、设置编译生成的文件输出路径
点击魔术棒 ---》Output --->Select Folder for Objects
5、修改固件库工程时钟 !!!
看图
6、 直到编译成功
=================================================================
《滴答定时器》
1.理论基础:
(1)滴答定时器(内核定时器,SysTick 定时器) ,实现精准延时,是倒计时,用于在每隔一定的时间产生一个中断,即使是系统在睡眠模式下也能工作。
(2)在 STM32F4xx中文参考手册.pdf 文件是找不到,它是属于内核的模块。
(3)相关函数以及宏定义在 跟内核相关(core_开头的那几个头文件)的那几个文件中。
2.使用实例:滴答定时器给流水灯精准定时
----------------------------------------------------------systick-code-----------------------------------------------
-----------------------------------systick.h----------------------------
#ifndef __SYSTICK_H_
#define __SYSTICK_H_
//头文件
#include "stm32f4xx.h"
//宏定义
//函数声明
void Systick_Init(void);//初始化
void delay_us(u32 nus);//定时微妙
void delay_ms(u32 nms);//定时毫秒
void delay_s(u32 ns);//定时秒
#endif
-----------------------------------systick.c----------------------------
#include "systick.h"
u8 my_us = 0;
u16 my_ms = 0;
//初始化滴答定时器
void Systick_Init(void)
{
//得到的Systick时钟 168MHz/8 = 21MHz -->每隔 1/21us计数一次
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
my_us = 21; //21
my_ms = 21*1000;
}
//微秒延时,nus最大值:798915
void delay_us(u32 nus) //1
{
u32 temp = 0;
//往自动重装载除值寄存器写入延时nus SysTick->LOAD最大值0xFFFFFF
SysTick->LOAD = nus*my_us;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启计数,开始倒数
//如果还没有数到 0 就卡死在这个循环中,依据控制寄存器的第 16 位
do
{
//读控制寄存器
temp = SysTick->CTRL;
}while(!(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数
SysTick->VAL = 0x00;
}
//毫秒延时,nms最大值,nms最大值798.915
void delay_ms(u32 nms)
{
u32 temp = 0;
//往自动重装载除值寄存器写入延时nms SysTick->LOAD最大值0xFFFFFF
SysTick->LOAD = nms*my_ms;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启计数
do
{
//读控制寄存器
temp = SysTick->CTRL;
}while(!(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数
SysTick->VAL = 0x00;
}
//秒延时
void delay_s(u32 ns)
{
for(; ns>0; ns--)
{
delay_ms(500);
delay_ms(500);
}
}
------------------------------led.h-------------------
#ifndef __LED_H
#define __LED_H
//头文件
#include "stm32f4xx.h" //这个头文件一定要留
//宏定义
#define LED_ON 0
#define LED_OFF 1
#define LED0(a); if(a) \
GPIO_SetBits(GPIOF,GPIO_Pin_9); \
else \
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
#define LED1(a); if(a) \
GPIO_SetBits(GPIOF,GPIO_Pin_10); \
else \
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
#define LED2(a); if(a) \
GPIO_SetBits(GPIOE,GPIO_Pin_13); \
else \
GPIO_ResetBits(GPIOE,GPIO_Pin_13);
#define LED3(a); if(a) \
GPIO_SetBits(GPIOE,GPIO_Pin_14); \
else \
GPIO_ResetBits(GPIOE,GPIO_Pin_14);
//函数声明
void GPIO_LED_Init(void);
#endif
---------------------------------led.c-------------------------
#include "led.h"
/*
* PF9 --->LED0
* PF10 --->LED1
* PE13 --->LED2
* PE14 --->LED3
* 0 --->LED_ON
* 1 --->LED_OFF
*/
/*
* 功 能:初始化 LED 的 GPIO 口
* 参 数:无
* 返回值:无
*/
void GPIO_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructrue;
//1、开启 GPIOE GPIOF 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
//配置为 输出 推挽 上拉 25MHz
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructrue.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructrue.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_25MHz;
//2、初始化 PF9 PF10
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOF, &GPIO_InitStructrue);
//2、初始化 PE13 PE14
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14;
GPIO_Init(GPIOE, &GPIO_InitStructrue);
//初始灯灭
GPIO_SetBits(GPIOF, GPIO_Pin_9|GPIO_Pin_10);
GPIO_SetBits(GPIOE, GPIO_Pin_13|GPIO_Pin_14);
}
----------------------------------main.h--------------------------
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f4xx.h" //这个头文件一定要留
#endif
---------------------------------main.c----------------------------
/**
******************************************************************************
* @author LLH
* @version V1.0
* @date 05-12-2018
* @brief Main program body
******************************************************************************
*/
#include "stm32f4xx.h" //类似于51单片机 #include <reg52.h>
#include "led.h"
#include "systick.h"
int main(void)
{
//1、硬件初始化
Systick_Init();
GPIO_LED_Init();
while(1)
{
LED0(LED_ON);
delay_s(1);
LED0(LED_OFF);
delay_s(1);
}
}
=====================================================================
《前后台系统及中断》
一 、理论知识点--程序运行方式:
----------------------
方式一:轮询
int main()
{
while(1)
{
}
}
---------------------
方式二:前后台系统 (轮询(后台) + 中断(前台))
int main()
{
while(1)
{
//中断产生
}
}
//中断函数
void IRQ_Handler()
{
}
---------------------
方式三:多任务 --->类似于进程和线程,采用操作系统
二、理论知识点--中断向量:
1、
STM32F407ZE 这个型号有 82 + 10 = 92 个中断
这 92 个中断组成了一个中断向量表,和启动文件中的向量表是一样
2、
NVIC --->嵌套向量中断控制器 ---》用于管理所有的中断(类似于电源总闸)
这个 NVIC 属于内核外设,所有跟 NVIC 相关的函数都在 misc.c 中
3、
优先级分为两种
抢占优先级(主优先级) ---》抢占优先级高的可以打断抢占优先级低的中断
响应优先级(子优先级) ---》当多个中断同时到达时,谁的响应优先级高就先执行谁
4、
中断有哪些:
定时器中断
串口中断
外部中断(本次程序案列使用这个)
三、程序案列:中断+按键+滴答定时器+led灯
案列思路:(1)滴答定时器起到延时作用。(2)按键产生中断源。(3)led灯亮灭是中断执行的功能。(4)中断类似第三方,站在管理者角度,绑定按键作为中断输入来源,制定中断执行函数体中执行点灯延时点灯.....等操作,中断执行函数时间不易过久。关键点在于中断初始化部分。
------------------------按键--key.h----------------------
#ifndef __KEY_H
#define __KEY_H
//头文件
#include "stm32f4xx.h" //这个头文件一定要留
//宏定义
#define KEY_ON 0
#define KEY_OFF 1
#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 GPIO_KEY_Init(void);
#endif
------------------------按键--key.c----------------------
#include "key.h"
/*
* PA0 --->KEY0
* PE2 --->KEY1
* PE3 --->KEY2
* PE4 --->KEY3
* 0 --->按下
* 1 --->松开
*/
/*
* 功 能:初始化 KEY 的 GPIO 口
* 参 数:无
* 返回值:无
*/
void GPIO_KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructrue;
//1、开启 GPIOE GPIOA 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//配置为 输入 上拉
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructrue.GPIO_PuPd = GPIO_PuPd_UP;
//2、初始化 PA0
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructrue);
//2、初始化 PE2 PE3 PE4
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
GPIO_Init(GPIOE, &GPIO_InitStructrue);
}
-------------------led灯-led.h------------
#ifndef __LED_H
#define __LED_H
//头文件
#include "stm32f4xx.h" //这个头文件一定要留
//宏定义
#define LED_ON 0
#define LED_OFF 1
#define LED0(a); if(a) \
GPIO_SetBits(GPIOF,GPIO_Pin_9); \
else \
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
#define LED1(a); if(a) \
GPIO_SetBits(GPIOF,GPIO_Pin_10); \
else \
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
#define LED2(a); if(a) \
GPIO_SetBits(GPIOE,GPIO_Pin_13); \
else \
GPIO_ResetBits(GPIOE,GPIO_Pin_13);
#define LED3(a); if(a) \
GPIO_SetBits(GPIOE,GPIO_Pin_14); \
else \
GPIO_ResetBits(GPIOE,GPIO_Pin_14);
//函数声明
void GPIO_LED_Init(void);
#endif
-------------------led灯-led.c------------
#include "led.h"
/*
* PF9 --->LED0
* PF10 --->LED1
* PE13 --->LED2
* PE14 --->LED3
* 0 --->LED_ON
* 1 --->LED_OFF
*/
/*
* 功 能:初始化 LED 的 GPIO 口
* 参 数:无
* 返回值:无
*/
void GPIO_LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructrue;
//1、开启 GPIOE GPIOF 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
//配置为 输出 推挽 上拉 25MHz
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructrue.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructrue.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_25MHz;
//2、初始化 PF9 PF10
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOF, &GPIO_InitStructrue);
//2、初始化 PE13 PE14
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14;
GPIO_Init(GPIOE, &GPIO_InitStructrue);
//初始灯灭
GPIO_SetBits(GPIOF, GPIO_Pin_9|GPIO_Pin_10);
GPIO_SetBits(GPIOE, GPIO_Pin_13|GPIO_Pin_14);
}
--------------------滴答定时器---systick.h--------------------
#ifndef __SYSTICK_H_
#define __SYSTICK_H_
//头文件
#include "stm32f4xx.h"
//宏定义
//函数声明
void Systick_Init(void);
void delay_us(u32 nus);
void delay_ms(u32 nms);
void delay_s(u32 ns);
#endif
--------------------滴答定时器---systick.c--------------------
#include "systick.h"
u8 my_us = 0;
u16 my_ms = 0;
//初始化滴答定时器
void Systick_Init(void)
{
//得到的Systick时钟 168MHz/8 = 21MHz -->每隔 1/21us计数一次
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
my_us = 21; //21
my_ms = 21*1000;
}
//微秒延时,nus最大值:798915
void delay_us(u32 nus) //1
{
u32 temp = 0;
//往自动重装载除值寄存器写入延时nus SysTick->LOAD最大值0xFFFFFF
SysTick->LOAD = nus*my_us;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启计数,开始倒数
//如果还没有数到 0 就卡死在这个循环中,依据控制寄存器的第 16 位
do
{
//读控制寄存器
temp = SysTick->CTRL;
}while(!(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数
SysTick->VAL = 0x00;
}
//毫秒延时,nms最大值,nms最大值798.915
void delay_ms(u32 nms)
{
u32 temp = 0;
//往自动重装载除值寄存器写入延时nms SysTick->LOAD最大值0xFFFFFF
SysTick->LOAD = nms*my_ms;
SysTick->VAL = 0x00;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启计数
do
{
//读控制寄存器
temp = SysTick->CTRL;
}while(!(temp & (1<<16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数
SysTick->VAL = 0x00;
}
//秒延时
void delay_s(u32 ns)
{
for(; ns>0; ns--)
{
delay_ms(500);
delay_ms(500);
}
}
----------------------中断----exti.h-------------
#ifndef __EXTI_H
#define __EXTI_H
//头文件
#include "stm32f4xx.h" //这个头文件一定要留
#include "key.h"
#include "led.h"
#include "systick.h"
//宏定义
//函数声明
void EXTI_0234_Init(void);
#endif
----------------------中断----exti.c-------------
#include "exti.h"
/*
* EXTI0 --->PA0 --->KEY0
*
*/
void EXTI_0234_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructrue;
NVIC_InitTypeDef NVIC_InitStructrue;
//一定要开启这个时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//1、设置 PA0(KEY0) PE2(KEY1) PE3(KEY2) PE4(KEY3) 引脚设置为上拉输入模式
GPIO_KEY_Init();
//2、选择 PA0 PE2 PE3 PE4 引脚作为外部中断的输入来源
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
//3、初始化 外部中断
EXTI_InitStructrue.EXTI_Line = EXTI_Line0 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4; //外部中断0 2 3 4
EXTI_InitStructrue.EXTI_LineCmd = ENABLE; //打开
EXTI_InitStructrue.EXTI_Mode = EXTI_Mode_Interrupt;//中断
EXTI_InitStructrue.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿(按键默认高电平,按下去的那一瞬间就会由高变低)
EXTI_Init(&EXTI_InitStructrue);
//4、初始化 NVIC
NVIC_InitStructrue.NVIC_IRQChannel = EXTI0_IRQn; //选择外部中断0
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE; //打开
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructrue);
NVIC_InitStructrue.NVIC_IRQChannel = EXTI2_IRQn; //选择外部中断2
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE; //打开
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructrue);
NVIC_InitStructrue.NVIC_IRQChannel = EXTI3_IRQn; //选择外部中断3
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE; //打开
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 0; //响应优先级
NVIC_Init(&NVIC_InitStructrue);
NVIC_InitStructrue.NVIC_IRQChannel = EXTI4_IRQn; //选择外部中断4
NVIC_InitStructrue.NVIC_IRQChannelCmd = ENABLE; //打开
NVIC_InitStructrue.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级
NVIC_InitStructrue.NVIC_IRQChannelSubPriority = 0; //响应优先级
NVIC_Init(&NVIC_InitStructrue);
}
//编写外部中断0执行函数
void EXTI0_IRQHandler(void)
{
//判断中断是否产生
if(EXTI_GetFlagStatus(EXTI_Line0) != RESET)
{
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
LED0(LED_ON);
delay_s(3);
LED0(LED_OFF);
}
}
//编写外部中断2执行函数
void EXTI2_IRQHandler(void)
{
//判断中断是否产生
if(EXTI_GetFlagStatus(EXTI_Line2) != RESET)
{
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line2);
LED1(LED_ON);
delay_s(1);
LED1(LED_OFF);
}
}
//编写外部中断3执行函数
void EXTI3_IRQHandler(void)
{
//判断中断是否产生
if(EXTI_GetFlagStatus(EXTI_Line3) != RESET)
{
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line3);
LED2(LED_ON);
delay_s(1);
LED2(LED_OFF);
}
}
//编写外部中断2执行函数
void EXTI4_IRQHandler(void)
{
//判断中断是否产生
if(EXTI_GetFlagStatus(EXTI_Line4) != RESET)
{
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line4);
LED3(LED_ON);
delay_s(1);
LED3(LED_OFF);
}
}
-------------------------main.h---------------
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f4xx.h" //这个头文件一定要留
#endif
-----------------------main.c-----------------
/**
******************************************************************************
* @author LLH
* @version V1.0
* @date 05-12-2018
* @brief Main program body
******************************************************************************
*/
#include "stm32f4xx.h" //类似于51单片机 #include <reg52.h>
#include "led.h"
#include "systick.h"
#include "key.h"
#include "exti.h"
int main(void)
{
//一般优先级分组写在整个程序的开始,后面尽量不要修改分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //第 2 组是最常用的一组
//1、硬件初始化
Systick_Init();
GPIO_LED_Init();
EXTI_0234_Init();
while(1);
}