1、实验电路原理图
(1)实验要求说明
主要实现按键对各种简单显示器件的控制。
Key_up:控制蜂鸣器鸣响,当按键被按下时蜂鸣器鸣响
Key_left:控制LED灯,当按键被按下一次时,亮起的一颗LED灯左移一个。
Key_right:控制LED灯,当按键被按下一次时,亮起的一颗LED灯右移一个。
Key_down:控制数码管,按键按下一次时增加数码管的显示数字。
原理图
原理图分析
按键部分的硬件电路涉及的GPIO端口有:
Key_up:PA0 输入高电平有效
Key_left:PE2 输入低电平有效
Key_right:PE4 输入低电平有效
Key_down:PE3 输入低电平有效
涉及的外设总线 APB2
2、电路涉及IO端口分析
(1)端口模式
按键涉及的IO口都必须配置位输入模式。
各种现实外设的IO口都必须设置为输出模式。
(2)端口寄存器描述
配置IO口为输入模式涉及的寄存器叫端口配置寄存器,输入数据传输到端口输入数据寄存器。
端口配置寄存器(CRL),这里涉及到的IO端口都是端口配置低寄存器的配置。
端口输入数据寄存器(IDR),端口输入数据寄存器只有低16有效,而且这些位只能以字(16位)的形式读出。读出的值为对应的IO口的状态。
PA端口的基地址:0x40010800
PA端口的CRL寄存器地址 0x40010800 + 0x00
PA端口的IDR寄存器地址 0x40010800 + 0x08
PE端口的基地址:0x40011800
PE端口的CRL寄存器地址 0x40011800 + 0X00
PE端口的IDR寄存器地址 0x40011800 + 0x08
由于PE端口的三个按键的一段都是接地的,所以我们换需要在程序开始的时候对这三个按键的端口状态设置为高电平状态。这就需要使用到输出数据寄存器ODR。
PE端口的ODR寄存器地址 0x40011800 + 0x0c
除了以上涉及到的端口寄存器之外,如果要使用STM32的外设,还需要对外设总线进行配置。
以上外设的端口都是在APB2总线上的外设,所以只需要对APB2外设进行初始化配置就可以。
APB2外设时钟使能寄存器(RCC_APB2ENR)
RCC时钟总线基地址 0x40021000
APB2外设时钟使能寄存器地址 0x40021000 + 0x18
(3)端口寄存器配置
输入模式的配置
我们这里需要将按键涉及的端口配置为 上拉/下拉输入模式。则每个IO口的CNF位配置为 1 0 ,每个IO口的MODE位配置为 0 0。
PA端口的配置数据为 GPIOA_CRL = 0x44444448
PE端口的配置数据为 GPIOE_CRL = 0x00088800;
3、显示(输出部分)
(1)LED灯部分
LED原理图
相关寄存器
LED灯模块涉及到的外设端口是PC0-PC7.所以涉及到的寄存器就是PC端口ODR输出数据寄存器和CRL端口配置寄存器。
PC端口的基地址 0x40011000
PC端口的CRL寄存器地址 0x40011000 + 0X00
PC端口的ODR寄存器地址 0x40011000 + 0x0c
(2)数码管部分
数码管工作原理
数码管原理图
相关寄存器设置
从原理图中可以看出,数码管的接线方式是共阳极的,并且连接线与LED灯模块的连线一样。所以涉及到的寄存器也和LED灯模块的一致。只要对ODR寄存器输出不同的值就可以使数码管显示相应的数字。
(3)蜂鸣器部分
蜂鸣器工作原理
蜂鸣器的发声原理有振动装置和谐振装置组成,蜂鸣器有分为无源他激型与有源自激型。无源他激型蜂鸣器的工作发声原理是:方波信息输入谐振装置转换为声音信号输出。有源自激型蜂鸣器的工作原理是:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号。
蜂鸣器原理图
相关寄存器设置
从原理图中可以发现,我们使用的蜂鸣器是无源的蜂鸣器,所以需要输入方波信息促使蜂鸣器发声。
涉及IO口为 PB5
PB端口逇基地址 0x4001 0c00
PB端口的CRL寄存器地址 0x4001 0c00 + 0x00
PB端口的ODR寄存器地址 0x4001 0c00 + 0x0C
4、源代码
reg.h
//该文件中声明寄存器相关的定义及初始化寄存器的相关函数声明
#ifndef __REG_H__
#define __REG_H__
/***********************寄存器基地址定义*************************/
#define GPIOA_BASE_ADD 0x40010800//端口A 上键
#define GPIOB_BASE_ADD 0x40010c00 //端口B 蜂鸣器
#define GPIOC_BASE_ADD 0x40011000//端口C led、数码管
#define GPIOE_BASE_ADD 0x40011800 //端口E 左键、右键、下键
#define RCC_BASE_ADD 0x40021000
/***********************寄存器地址定义*************************/
#define GPIOA_CRL_ADD (GPIOA_BASE_ADD + 0x00)
#define GPIOA_IDR_ADD (GPIOA_BASE_ADD + 0x08)
#define GPIOB_CRL_ADD (GPIOB_BASE_ADD + 0x00)
#define GPIOB_ODR_ADD (GPIOB_BASE_ADD + 0x0C)
#define GPIOC_CRL_ADD (GPIOC_BASE_ADD + 0x00)
#define GPIOC_ODR_ADD (GPIOC_BASE_ADD + 0x0C)
#define GPIOE_CRL_ADD (GPIOE_BASE_ADD + 0x00)
#define GPIOE_ODR_ADD (GPIOE_BASE_ADD + 0x0C)
#define GPIOE_IDR_ADD (GPIOE_BASE_ADD + 0x08)
#define RCC_APB2ENR_ADD (RCC_BASE_ADD + 0x18)
/***********************寄存器定义*************************/
#define GPIOA_CRL (*(unsigned int*)GPIOA_CRL_ADD)
#define GPIOA_IDR (*(unsigned int*)GPIOA_IDR_ADD)
#define GPIOB_CRL (*(unsigned int*)GPIOB_CRL_ADD)
#define GPIOB_ODR (*(unsigned int*)GPIOB_ODR_ADD)
#define GPIOC_CRL (*(unsigned int*)GPIOC_CRL_ADD)
#define GPIOC_ODR (*(unsigned int*)GPIOC_ODR_ADD)
#define GPIOE_CRL (*(unsigned int*)GPIOE_CRL_ADD)
#define GPIOE_IDR (*(unsigned int*)GPIOE_IDR_ADD)
#define GPIOE_ODR (*(unsigned int*)GPIOE_ODR_ADD)
#define RCC_APB2ENR (*(unsigned int*)RCC_APB2ENR_ADD)
/***********************初始化函数声明*************************/
void beep_init(void); //蜂鸣器初始化
void bus_init(void);//总线资源初始化
void key_init(void); //按键初始化
void led_init(void); //LED初始化
void smg_init(void);//数码管初始化
#endif
reg.c
#include "reg.h"
/***************初始化函数定义******************/
//总线资源初始化
void bus_init()
{
RCC_APB2ENR |= 1<<2;//使能GPIOA时钟
RCC_APB2ENR |= 1<<3;//使能GPIOB时钟
RCC_APB2ENR |= 1<<4;//使能GPIOC时钟
RCC_APB2ENR |= 1<<6;//使能GPIOE时钟
}
//蜂鸣器初始化
void beep_init()
{
GPIOB_CRL &= 0x00;
GPIOB_CRL |= 0x44344444;//初始化GPIOB5输出模式
GPIOB_ODR |= 1<<5;
}
//按键初始化
void key_init()
{
GPIOA_CRL &= 0x00;
GPIOA_CRL |= 0x44444448;
GPIOE_CRL &= 0xFFF000FF;
GPIOE_CRL |= 0x00088800;
GPIOE_ODR |= 0x0000001C;//对PE端口进行上拉
}
//LED初始化
void led_init()
{
GPIOC_CRL &= 0x00;
GPIOC_CRL |= 0x33333333;
GPIOC_ODR |= 0xffffffff;
}
//数码管初始化
void smg_init()
{
GPIOC_CRL &= 0x00;
GPIOC_CRL |= 0x33333333;
GPIOC_ODR |= 0xff;
}
fun.h
#ifndef __FUN_H__
#define __FUN_H__
#include "reg.h"
/*****************定义按键值*******************/
#define KEY_UP GPIOA_IDR & 0x00000001 //获取A0是否为1,按下为1
#define KEY_LEFT ((GPIOE_IDR & 0x00000004)?1:0)//获取E2的值,按下为0
#define KEY_RIGHT ((GPIOE_IDR & 0x00000010)?1:0)//获取E4的值,按下为0
#define KEY_DOWN((GPIOE_IDR & 0x00000008)?1:0)//获取E3的值,按下为0
/*****************工作标志*************/
#define BEEP_ON 1
#define BEEP_OFF 0
#define LED_RBIT 2
#define LED_LBIT 3
#define SMG_UP 4
/******************功能函数******************/
void beep_on(void);//蜂鸣器发声
unsigned int key_val(void);//获取按键键值
unsigned int led_left_go(unsigned int);//LED一颗亮起 左移
unsigned int led_right_go(unsigned int);//LED一颗亮起 右移
unsigned int smg_add(unsigned int);//数码管数字加1
void delay1(void);//粗略延时1
#endif
fun.c
//文件中实现外设具体功能
#include "fun.h"
void beep_on()
{
//蜂鸣器由NPN三极管作为开关
//PB5 高电平 开关close 蜂鸣器发声
//PB5 低电平 开关open 蜂鸣器不发声
GPIOB_ODR &= ~(1<<5);
delay1();
GPIOB_ODR |= 1<<5;
delay1();
}
unsigned int led_right_go(unsigned int led_val)
{
//led_val 表示一组LED灯的状态
if(led_val == 0x00000080)
{
led_val = 0x00000001;
}else{
led_val <<= 1;
}
return led_val;
}
unsigned int led_left_go(unsigned int led_val)
{
if(led_val == 0x00000001)
{
led_val = 0x00000080;
}else{
led_val >>= 1;
}
return led_val;
}
unsigned int smg_add(unsigned int smg_key)
{
//smg_key 表示数码管当前显示的数字
smg_key ++ ;
if(smg_key == 10)
{
smg_key = 0;
}
return smg_key;
}
unsigned int key_val()
{
if((KEY_UP) == 1)
{
delay1();//延时去抖动
if((KEY_UP) == 1)
{
return BEEP_ON;
}
while((KEY_UP) == 1);
}
if(KEY_LEFT == 0)
{
delay1();
if(KEY_LEFT == 0)
{
while(KEY_LEFT == 0);
return LED_LBIT;
}
}
if(KEY_RIGHT == 0)
{
delay1();
if(KEY_RIGHT == 0)
{
while(KEY_RIGHT == 0);
return LED_RBIT;
}
}
if(KEY_DOWN == 0)
{
delay1();
if(KEY_DOWN == 0)
{
while(KEY_DOWN == 0);
return SMG_UP;
}
}
return 0;
}
void delay1()
{
unsigned int i,j;
for(i = 0;i < 550;i ++)
for(j = 0;j < 250;j ++);
}
main.c
/***********************实验计划*************************/
//主要实现按键对外设的控制
//1、key_up 控制蜂鸣器鸣响,当按键被按下时蜂鸣器发声
//2、key_left 控制LED灯,按键被按下一次时亮起的LED灯左移一个
//3、key_right 控制LED灯,按键被按下一次时亮起的LED灯右移一个
//4、key_down 控制数码管,按键被按下一次时增加数码管的显示数字
//硬件资源:
//1、LED PC0-PC7
//2、数码管 PC0-PC7
//3、蜂鸣器 无源蜂鸣器 PB5
//4、按键
//key_up 高电平有效 PA0
//key_left 低电平有效 PE2
//key_down 低电平有效 PE3
//key_right 低电平有效 PE4
//总线资源:
//APB2
/**********************************************************/
#include "fun.h"
int main()
{
unsigned int key = 0;
unsigned int led_val = 0x00000001;
unsigned int smg_key = 0;
Unsigned char smg_num[10] =
{0xc0,0xcf,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned int smg_on = 0;//标记当下键按下时显示数码管,LED无效显示
unsigned int led_on = 0;//标记当左右键按下时显示LED,数码管无效
bus_init();
key_init();
beep_init();
led_init();
while(1)
{
key = key_val();
switch(key)
{
case BEEP_ON:
beep_on();
break;
case LED_LBIT:
led_val = led_left_go(led_val);
GPIOC_ODR = ~led_val;
led_on = 1;
smg_on = 0;
break;
case LED_RBIT:
led_val = led_right_go(led_val);
GPIOC_ODR = ~led_val;
led_on = 1;
smg_on = 0;
break;
case SMG_UP:
smg_key = smg_add(smg_key);
GPIOC_ODR = smg_num[smg_key];
led_on = 0;
smg_on = 1;
break;
default:
GPIOC_ODR |= 0xFFFFFFFF;
break;
}
if(led_on)
{
GPIOC_ODR = ~led_val;
}else if(smg_on){
GPIOC_ODR = smg_num[smg_key];
}
}
return 0;
}