路径细节
#include "./LED/bsp_led.h" ./表示当前路径 ../表示上一层路径
#include "bsp_led.h" 需要在魔术棒里添加路径
按键初始化 模式是输入
void key_config(){
//初始化GPIO按键
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;可以不用
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_50MHz;可不用
GPIO_Init(GPIOE,&GPIO_InitStruct);
}
按键检测:
#define KEY_ON 0
#define KEY_OFF 1
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;
}
}
主程序:
while(1)
{
if(KEY_Scan(GPIOE,GPIO_Pin_2)==KEY_ON)
{
GPIO_ToggleBits(GPIOF,GPIO_Pin_9);
}
}
位操作原理:(像指针)
把每个比特膨胀为一个32位的字(地址),当访问这些字的时候就可以达到访问比特的目的。
改变地址的值1或0就是位的值
位带跑马灯:
while(1)
{
PFout(9)=1;
PFout(10)=1;
delay_ms(500);
PFout(9)=0;
PFout(10)=0;
delay_ms(500);
}
启动文件
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __mainLDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
执行完时,已经执行了SystemInit和main 函数,先执行SystemInit
蜂鸣器
注意下拉,默认是低电平
void BEEP_Config()
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;//输出
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//推挽
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉,默认是0
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOF,&GPIO_InitStruct);
}while(1)
{
PFout(9)=1;
PFout(8)=1;
delay_ms(500);
PFout(9)=0;
PFout(8)=0;
delay_ms(500);
}
按键输入
KEY0 上拉输入
KEY1 上拉输入
KEY2 上拉输入
上拉输入默认电平是1
读取输入的三种方法:
int getValue(void) int getValue(void)
{ {
int flag=0; static int flag=0;
flag++; flag++;
return flag; return flag;
} }
int flag=0; 每次调用都会是1
static int flag=0; 每次调用都会是1,2,3,4,5,6 相当于flag+1
u8 KEY_Scan(void)
{
if(KEY 按下)
{
delay_ms(10); //延迟10-20ms,防抖
if(KEY 确实按下)
{
return KEY_Vaule;
}
return 无效值;
}
}
u8 KEY_Scan(void)
{就是说key_up必须为1才能进行循环,必须是松开的时候才能进入if
static u8 key_up=1; //松开没按是1,按下是0
if( key_up&&KEY 按下) //前一次松开且按下
{
delay_ms(10); //延迟10-20ms,防抖
key_up=0;
if(KEY 确实按下)
{
return KEY_Vaule;
}
}else if(KEY没有被按下) key_up=1;
}
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;
if(mode==1) key_up=1;//支持连按
if(key_up &&KEY按下)
{
delay_ms(10);
key_up=0;
if(KEY 确定按下)
{
return KEY_Value;
}
}else if(KEY没有被按下) key_up=1;
return 没有按下;
}
位操作:6种位操作运算符
& 0 1 ~~> 0
左移<< 10101010 ~>01010100 移动补0
右移>>10101010 ~>01010101
#ifndef _LED_H
#define _LED_H
#endif
结构体作用:
注:PLL是锁相环,用来分频和倍频的
系统时钟System clock 大多数来源于PLLCLK,因为PLLCLK可以到168MHz
一.STM32有5个时钟源:HSI,HSE,LSI,LSE,PLL
1.HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高.可以直接作为系统时钟或者用作PLL时钟输入
2.HSE是高速外部时钟,可接振荡器,或者外接外部时钟,频率范围为4MHz~26N=MHz
正点原子板子是8M
3.LSI是低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟,主要供应独立看门狗和自动唤醒单元使用
4.LSE是低速外部时钟,接频率为32.768MHz的石英晶体
5.PLL为锁相环倍频输出,STM32F4有两个PLL:
主PLL由HSE或者HSI提供时钟,并具有两个不同的输出时钟
第一个输出PLLP用于生成高速的系统时钟(最高168MHz)
第二个输出PLLQ用于生成USB SDIO时钟
二.系统时钟SYSCLK可来源于三个时钟源
1.HSI振荡器时钟
2.HSE振荡器时钟
3.PLL时钟
任何外设在使用时,必须首先使能其相应的时钟
CTRL SysTick控制和状态寄存器
LOAD SysTick自动重装载寄存器
VAL SysTick当前值寄存器
CALIB SysTick校准寄存器
1 Mhz=1微秒
1.使能中断请求
2.配置中断优先级分组
3.配置NVIC寄存器,初始化NVIC_InitTypeDef;
4,编写中断服务函数
typedef struct
{
uint8_t NVIC_IRQChannel; 中断源uint8_t NVIC_IRQChannelPreemptionPriority; 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; 子优先级
FunctionalState NVIC_IRQChannelCmd; 使能
} NVIC_InitTypeDef;
必须跟中断向量表(vectors)一样,文件在startup_stm32f40xx.s启动文件中
写错函数则执行不了,只会执行startup_stm32f40xx,s启动文件弱定义
stm32f4xx_it.c 因为启动文件中是弱定义,写了就不会在startup_stm32f40xx.s执行
外部中断配置寄存器
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9-5_IRQHandler
EXTI15-10_IRQHan dler
1.使能SYSCFG时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
2.初始化IO口为输入
GPIO_Init();
3.设置IO口与中断线的映射关系
void SYSCFG_EXTILineConfig():
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);//PE2 连接到中短线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);//PE3 连接到中短线3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);//PE4 连接到中短线44.初始化线上中断,设置触发条件
EXTI_Init();
5.配置中断分组(NVIC),并使能中断
NVIC_Init();
6.编写中断服务函数
EXTIx_IRQHandler();
7.清除中断标志位
EXTI_ClearITPendingBit();
void EXTIX_Init(void)
{
//1.使能SYSCFG时钟
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//2.初始化IO口为输入
KEY_Config();//按键对应的IO口初始化
//3.设置IO口与中断线的映射关系
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
//4.初始化线上中断,设置触发条件
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line4;
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//中断事件
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//默认上拉,用下降沿
EXTI_Init(&EXTI_InitStruct);
//5.配置中断分组(NVIC),并使能中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
//6.编写中断服务函数
//7.清除中断标志位
}
void EXTI4_IRQHandler(void)
{
delay_ms(20);//消抖
if(KEY0==0)
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位
}
#include "exit.h"
#include "key.h"
#include "delay.h"
#include "bsp_led.h"
#include "beep.h"6.编写中断服务函数
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
delay_ms(10);
if(KEY2==0)
{
LED1=!LED1;
LED0=!LED0;
}7.清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line2);//清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1==0)
{
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line3);//清除LINE3上的中断标志位
}
//外部中断4服务程序
void EXTI4_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY0==0)
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位
}
//按键对应的IO口初始化
void GPIO_KEY_Config(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
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_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);
}
//外部中断初始化程序
//初始化PE2~4,PA0为中断输入.
void EXTIX_Init(void)
{1.使能SYSCFG时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能SYSCFG时钟
2.初始化IO口为输入
GPIO_KEY_Config();//按键对应的IO口初始化
3.设置IO口与中断线的映射关系
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2); //PE2 连接到中断线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3); //PE3 连接到中断线3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4); //PE4 连接到中断线4
4.初始化线上中断,设置触发条件
/* 配置EXTI_Line2,3,4 */
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line=EXTI_Line2|EXTI_Line3|EXTI_Line4;
EXTI_InitStruct.EXTI_LineCmd=ENABLE;//中断线使能
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//中断事件
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStruct);//注:每一个都需要被配置
5.配置中断分组(NVIC),并使能中断
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=EXTI2_IRQn;//外部中断2
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0x01; //子优先级1
NVIC_Init(&NVIC_InitStruct);//注:每一个都需要被配置NVIC_InitStruct.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0x02;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0x01;
NVIC_Init(&NVIC_InitStruct);//注:每一个都需要被配置
NVIC_InitStruct.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0x03;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0x01;
NVIC_Init(&NVIC_InitStruct);//注:每一个都需要被配置
}
void exti_init(void)
{
//GPIO使能
GPIO_InitTypeDef GPIO_Init;
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_Init.Mode=GPIO_MODE_IT_FALLING; //下降沿中断
GPIO_Init.Pin=GPIO_PIN_2;
GPIO_Init.Pull=GPIO_PULLUP;
HAL_GPIO_Init(GPIOE,&GPIO_Init);
//NVIC中断
HAL_NVIC_SetPriority(EXTI2_IRQn,2 ,2 );
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
}void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);//公共处理函数:清楚中断并跳转HAL_GPIO_EXTI_Callback
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(10);
if(KEY2==0)
{
HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9);
}}