之前用寄存器操作实现了跑马灯和蜂鸣器。这次接着输入寄存器IDR捕获按键。并且综合亮灯和发出声音,作为寄存器GPIO小综合练习。
首先查KEY的电路图,分别用到了ACD几个时钟。KEY0对应PC8,KEY1对应PC9,KEY2对应PD2,KEYUP对应PA0
然后对应的时钟就是APB2的245位
按文档设置为上下拉输出模式(即1000对应16进制的8)
按IDR寄存器读相应端口的电平即可
代码实现如下:
key.h
//定义宏,防止头文件重复引用
#ifndef __KEY
#define __KEY
//初始化按键的环境
void InitKeyEnv(void);
//测试按键
void TestKey(void);
//得到按下的按键
//continuePress:是否允许连按。0:必须从不按状态按才算。 1:只要是按着的就算
//返回0就是按下KEY0,返回1就是按下KEY1,返回2就是按下KEY2,返回3就是按下KEY_UP,返回-1就是没按
int GetPressKey(int continuePress);
#endif
key.c
#include "key.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
//初始化按键的环境
void InitKeyEnv(void)
{
//设置ABP2外设时钟第2位为1,即可用,对应GPIOA时钟(KEY_UP)
//1<<2:1左移2位为0010,与APB2ENR寄存器值或运算就是第2位设置1,其他不变的意思
RCC->APB2ENR|=1<<2;
//设置ABP2外设时钟第4位为1,即可用,对应GPIOC时钟(KEY0,KEY1)
//1<<4:1左移4位为1000,与APB2ENR寄存器值或运算就是第4位设置1,其他不变的意思
RCC->APB2ENR|=1<<4;
//设置ABP2外设时钟第5位为1,即可用,对应GPIOD时钟(KEY2)
//1<<5:1左移5位为10000,与APB2ENR寄存器值或运算就是第5位设置1,其他不变的意思
RCC->APB2ENR|=1<<5;
//KEY_UP PA0对应CRL低位寄存器
//设置1000上下拉输入模式,对应16进制8
GPIOA->CRL&=0XFFFFFFF0;
GPIOA->CRL|=0X00000008;
//KEY0 PC8对应CRH高位寄存器
//设置1000上下拉输入模式,对应16进制8
GPIOC->CRH&=0XFFFFFFF0;
GPIOC->CRH|=0X00000008;
//默认设置高电平,没按下按键
GPIOC->ODR|=1<<8;
//KEY1 PC9对应CRH高位寄存器
//设置1000上下拉输入模式,对应16进制8
GPIOC->CRH&=0XFFFFFF0F;
GPIOC->CRH|=0X00000080;
//默认设置高电平,没按下按键
GPIOC->ODR|=1<<9;
//KEY2 PD2对应CRL低位寄存器
//设置1000上下拉输入模式,对应16进制8
GPIOD->CRL&=0XFFFFF0FF;
GPIOD->CRL|=0X00000800;
//默认设置高电平,没按下按键
GPIOD->ODR|=1<<2;
}
//测试按键
void TestKey(void)
{
//初始化LED灯
InitLedEnv();
//初始化蜂鸣器灯
InitBeepEnv();
InitKeyEnv();
//LED0状态
int LED0Stat=0;
//LED1状态
int LED1Stat=0;
//LED2状态
int LED2Stat=0;
//LED3状态
int LED3Stat=0;
//死循环
while(1)
{
//得到按键值
int key=GetPressKey(0);
//按了KEY0
if(key>=0)
{
//按下了KEY0,点亮LED0
if(key==0)
{
//点亮灯鸣喇叭
if(LED0Stat==0)
{
//点亮LED0
GPIOC->ODR&=~(1<<0);
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED0Stat=1;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
else
{
//熄灭LED0
GPIOC->ODR|=1<<0;
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED0Stat=0;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
}
//按下了KEY1,点亮LED1
if(key==1)
{
//点亮灯鸣喇叭
if(LED1Stat==0)
{
//点亮LED1
GPIOC->ODR&=~(1<<1);
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED1Stat=1;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
else
{
//熄灭LED1
GPIOC->ODR|=1<<1;
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED1Stat=0;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
}
//按下了KEY2,点亮LED2
if(key==2)
{
//点亮灯鸣喇叭
if(LED2Stat==0)
{
//点亮LED2
GPIOC->ODR&=~(1<<2);
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED2Stat=1;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
else
{
//熄灭LED2
GPIOC->ODR|=1<<2;
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED2Stat=0;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
}
//按下了KEYUP,点亮LED3
if(key==3)
{
//点亮灯鸣喇叭
if(LED3Stat==0)
{
//点亮LED3
GPIOC->ODR&=~(1<<3);
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED3Stat=1;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
else
{
//熄灭LED3
GPIOC->ODR|=1<<3;
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
LED3Stat=0;
delay_ms(100);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
}
}
//延迟
delay_ms(10);
}
}
//得到按下的按键
//continuePress:是否允许连按。0:必须从不按状态按才算。 1:只要是按着的就算
//返回0就是按下KEY0,返回1就是按下KEY1,返回2就是按下KEY2,返回3就是按下KEY_UP,返回-1就是没按
int GetPressKey(int continuePress)
{
static int keyNotPress=1;
//允许连着按就任务按键之前一直是没按
if(continuePress==1)
{
keyNotPress=1;
}
//之前不是按键状态
if(keyNotPress==1)
{
//判断KEY0,把IDR寄存器值和0X100000000与操作,那么其他位置都是0,第9位原来是什么就是什么
int key0=(GPIOC->IDR&(1<<8))>>8;
//key0是按下的
if(key0==0)
{
//延迟10毫秒
delay_ms(10);
key0=(GPIOC->IDR&(1<<8))>>8;
if(key0==0)
{
keyNotPress=0;
return 0;
}
}
//判断KEY1,把IDR寄存器值和0X1000000000与操作,那么其他位置都是0,第10位原来是什么就是什么,然后再右移一位
int key1=(GPIOC->IDR&(1<<9))>>9;
//key1是按下的
if(key1==0)
{
//延迟10毫秒
delay_ms(10);
key1=(GPIOC->IDR&(1<<9))>>9;
if(key1==0)
{
keyNotPress=0;
return 1;
}
}
//判断KEY2,把IDR寄存器值和0X00000100与操作,那么其他位置都是0,第3位原来是什么就是什么,然后再右移2位
int key2=(GPIOD->IDR&(1<<2))>>2;
//key2是按下的
if(key2==0)
{
//延迟10毫秒
delay_ms(10);
key2=(GPIOD->IDR&(1<<2))>>2;
if(key2==0)
{
keyNotPress=0;
return 2;
}
}
//判断KEY_UP,把IDR寄存器值和0X00000001与操作,那么其他位置都是0,第1位原来是什么就是什么
int keyUp=(GPIOA->IDR&(1<<0))>>0;
//keyUp是按下的
if(keyUp==1)
{
//延迟10毫秒
delay_ms(10);
keyUp=(GPIOA->IDR&(1<<0))>>0;
if(keyUp==1)
{
keyNotPress=0;
return 3;
}
}
}
//没有按键就恢复keyNotPress值
else
{
int key0=(GPIOC->IDR&(1<<8))>>8;
int key1=(GPIOC->IDR&(1<<9))>>9;
int key2=(GPIOD->IDR&(1<<2))>>2;
int keyUp=(GPIOA->IDR&(1<<0))>>0;
if(key0==1&&key1==1&&key2==1&&keyUp==0)
{
keyNotPress=1;
}
}
return -1;
}
led.h
//定义宏,防止头文件重复引用
#ifndef __LED
#define __LED
//初始化LED的环境
void InitLedEnv(void);
//测试LED
void TestLed(void);
#endif
led.c
#include "led.h"
#include "sys.h"
#include "delay.h"
//初始化LED环境
void InitLedEnv(void)
{
//设置ABP2外设时钟第4位为1,即可用,对应GPIOC时钟
//1<<4:1左移4位位1000,与APB2ENR寄存器值或运算就是第4位设置1,其他不变的意思
RCC->APB2ENR|=1<<4;
//设置LED0为推挽输出模式
//CRL寄存器和十六进制的值且运算,设置指定位置的电平为指定4位位0000
GPIOC->CRL&=0XFFFFFFF0;
//CRL寄存器和十六进制的值或运算,设置指定位置的电平为指定4位位0011,四位里面高两位00表示通用推挽输出。低两位11表示输出模式,最大速度50MHz
GPIOC->CRL|=0X00000003;
//依次设置其他7个LED输出空为推挽输出模式
//LED1
GPIOC->CRL&=0XFFFFFF0F;
GPIOC->CRL|=0X00000030;
//LED2
GPIOC->CRL&=0XFFFFF0FF;
GPIOC->CRL|=0X00000300;
//LED3
GPIOC->CRL&=0XFFFF0FFF;
GPIOC->CRL|=0X00003000;
//LED4
GPIOC->CRL&=0XFFF0FFFF;
GPIOC->CRL|=0X00030000;
//LED5
GPIOC->CRL&=0XFF0FFFFF;
GPIOC->CRL|=0X00300000;
//LED6
GPIOC->CRL&=0XF0FFFFFF;
GPIOC->CRL|=0X03000000;
//LED7
GPIOC->CRL&=0X0FFFFFFF;
GPIOC->CRL|=0X30000000;
//通过操作ODR寄存器设置每个LED位置为1即高电平,不亮
GPIOC->ODR|=1<<0;
GPIOC->ODR|=1<<1;
GPIOC->ODR|=1<<2;
GPIOC->ODR|=1<<3;
GPIOC->ODR|=1<<4;
GPIOC->ODR|=1<<5;
GPIOC->ODR|=1<<6;
GPIOC->ODR|=1<<7;
}
//测试LED
void TestLed(void)
{
//初始化LED灯
InitLedEnv();
//当前该点亮的LED索引
int curIndex=0;
//死循环
while(1)
{
//改版为点亮所以led做跑马灯
for(int i=0;i<8;i++)
{
//当前该亮的灯点亮
if(i==curIndex)
{
//1左移当前索引位数后取非再做且运算就是把当前位数的电平设置低位。比如i=2即LED2,00000100取非之后为11111011再和ODR的值与运算
//第3位电平设置低,其他不变
GPIOC->ODR&=~(1<<i);
}
//其他熄灭
else
{
//1左移当前索引位数再做或运算就是把当前位数的电平设置高位。比如i=2即LED2,ODR寄存器值和00000100或运算
//第3位电平设置高,其他不变
GPIOC->ODR|=1<<i;
}
}
//下次点亮下一个灯
curIndex++;
//8个灯取余数
curIndex=curIndex%8;
//延迟半秒
delay_ms(500);
}
}
beep.h
//定义宏,防止头文件重复引用
#ifndef __BEEP
#define __BEEP
//初始化蜂鸣器的环境
void InitBeepEnv(void);
//测试蜂鸣器
void TestBeep(void);
#endif
beep.c
#include "beep.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
//初始化蜂鸣器的环境
void InitBeepEnv(void)
{
//设置ABP2外设时钟第2位为1,即可用,对应GPIOB时钟
//1<<3:1左移3位位0100,与APB2ENR寄存器值或运算就是第3位设置1,其他不变的意思
RCC->APB2ENR|=1<<3;
//设置PB8为推挽输出模式
//CRH寄存器和十六进制的值且运算,设置指定位置的电平为指定4位位0000
GPIOB->CRH&=0XFFFFFFF0;
//CRH寄存器和十六进制的值或运算,设置指定位置的电平为指定4位位0011,四位里面高两位00表示通用推挽输出。低两位11表示输出模式,最大速度50MHz
GPIOB->CRH|=0X00000003;
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
}
//测试蜂鸣器
void TestBeep(void)
{
//初始化LED灯
InitLedEnv();
//初始化蜂鸣器
InitBeepEnv();
//死循环
while(1)
{
//通过操作ODR寄存器设置9位置为0即低电平,响
GPIOB->ODR&=~(1<<8);
//点亮LED0
GPIOC->ODR&=~(1<<0);
//延迟半秒
delay_ms(500);
//通过操作ODR寄存器设置9位置为1即高电平,不响
GPIOB->ODR|=1<<8;
//关闭LED0
GPIOC->ODR|=1<<0;
//延迟半秒
delay_ms(500);
}
}
mian.c
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
//zlz的stm32学习
int main(void)
{
//初始化时钟
Stm32_Clock_Init(9);
//初始化延时函数
delay_init(72);
//测试LED
//TestLed();
//测试蜂鸣器
//TestBeep();
//测试按键
TestKey();
}
实现起来费劲些,搞了个把小时终于搞定了