这里写目录标题
- 一、任务目标
- (一)分析
- 二、设计思路
- (一)开启`KEY1`对应的`GPIOx`时钟
- 1.找到`KEY1(PE3)`所在的`GPIOx`端口
- 2.开启`GPIOE`端口时钟
- 3.清空`PE3`的端口位
- 4.设置`PE3`的端口位为输出模式的上拉模式
- 5.一个易错点!
- 三、完整代码
项目代码-修改前
一、任务目标
上述压缩包内的项目实现了按下KEY UP按键,LED灯熄灭,松开恢复点亮,要修改原文件夹中的main.c文件,使得按下KEY1开关,LED灯熄灭,松开恢复点亮。
(一)分析
首先按钮信号为输入信号,向ODR
寄存器写入值,应该设置KEY
按钮的端口配置地寄存器(GPIOx_CRL
)为输入模式,下面分析KEY1(PE3)
采用哪种输入模式:
下面的main.c
代码的GPIOA_CRL |= (0x08<<4*0); GPIOA_BRR |= 1;
,使得KEY UP
设置为输入模式的下拉模式,当松开开关时,KEY UP
为低电平,端口输入数据寄存器(IDR
)对应的位变为1
,执行if
语句中的GPIOB_ODR &= (0<<5);GPIOE_ODR &= (0<<5);
代码,使小灯点亮,当按下时,WK UP
所处电路与VCC3.3
导通,此时WK
为高电平,端口输入数据寄存器(IDR
)对应的位变为0
,执行else
语句,小灯熄灭。
但是KEY1
按钮所处电路与上述WK UP
所处电路有不同,其右侧接地,如果设置KEY1(PE3)
为输入模式的下拉模式,KEY1
为低电平,按下KEY1
,按下前后,IDR
均为0
,无法通过按下按键与否判断小灯的闪灭,因此设置KEY1
位输入模式的上拉模式
设置其为上拉模式一般步骤是:
①知道其位于哪个GPIO
端口
②开启端口时钟
③清空端口配置低寄存器
④设置端口配置低寄存器。
#include "stm32f10x.h"
typedef unsigned int u32;
void delay(u32 i)
{while(i--);}
int main(void)
{
delay(0xfffff);
// 开启GPIOx端口时钟
RCC_APB2ENR |= (1<<2); //A
RCC_APB2ENR |= (1<<3); //B
RCC_APB2ENR |= (1<<6); //E
// 清空控制PB5、PE5,PA0的端口位
GPIOB_CRL &= ~( 0x0F<< (4*5)); //PB5
GPIOE_CRL &= ~( 0x0F<< (4*5)); //PE5
GPIOA_CRL &= ~( 0x0F<< (4*0)); //PA0
// 配置PB5、PE5位通用推挽输出、速度为10M
GPIOB_CRL |= (1<<4*5);
GPIOE_CRL |= (1<<4*5);
GPIOA_CRL |= (0x08<<4*0); //PA0为“上拉下拉”输入
GPIOA_BRR |= 1; // 通过设置ODR为1为上拉,为0则下拉,设置0,下拉
while(1)
{
if( (GPIOA_IDR & 0x0001) == 0)
{
//输出低,亮灯
GPIOB_ODR &= (0<<5);
GPIOE_ODR &= (0<<5);
}
else
{
//输出高,灭灯
GPIOB_ODR |= (1<<5);
GPIOE_ODR |= (1<<5);
}
}
}
void SystemInit(void)
{
}
二、设计思路
(一)开启KEY1
对应的GPIOx
时钟
1.找到KEY1(PE3)
所在的GPIOx
端口
由图可以看出KEY
在GPIOE
端口
2.开启GPIOE
端口时钟
源文件已经打开,不需要再进行操作
3.清空PE3
的端口位
清空的代码如下图,下图代码的逻辑即为把上图中的[15:12]
位置为0
,这四位即为PE3
的对应的寄存器的位(上图中的x
即为E
,y
即为PE3
中的3
)
其他可以实现此功能的代码均可以
4.设置PE3
的端口位为输出模式的上拉模式
设置为输入模式的上拉/下拉模式需要将MODE3[1:0]
设置为00
,CNF3[1:0]
设置为10
,由于每4
位对应一个端口,PE3
的处于从低往高位数第4
个4
位(从0
开始的),原理同上,因此可以使用如下代码:
但是GPIOE_CRL |= (0x08<<4*3);
只是设置了输入模式的上拉/下拉模式,若想设置为上拉模式,还需要设置PE3
对应的ODR
寄存器,设置ODR
位1
则上拉,为0
则下拉(老师说的,还没来得及思考为什么)。
设置寄存器值时,应避免改变寄存器的其他位的值,常采用端口位清除寄存器(BSRR
)、端口位设置/清除寄存器(BRR
)
所以采用代码:GPIOE_BSRR |= (1<<3);
(3
是因为PE3
中的3
),来使得输入模式的上拉/下拉模式变为上拉模式
5.一个易错点!
下图代码中如果不加入划线两条语句,另外四条语句由于没有使用BSRR
和BRR
寄存器,而是对ODR
寄存器直接进行操作,如果在这个过程中,改变了PE3
对应的ODR
的值,则会导致程序逻辑出错,按钮对灯的亮灭不产生影响。(但是划线两条语句不一定都是必要的,本实验过程中偷懒没有做细致研究,以后可能会再深入看一下),除下图写法外,还可采用使用对BSRR
和BRR
寄存器从而改变PE5
、AE5
的ODR
寄存器的特定位,而不改变其他位的方法,达成实验目的,后续可能会对此部分做补充
(if( (GPIOE_IDR & (0x0001<<3)) == 0)
是判断PE3(KEY1)
对应的输入数据寄存器(IDR
)的第3
位是否为1
,3
是因为PE3
中的3
,IDR
寄存器见下下张图片)
三、完整代码
(可能有多余的内容,非最优方法)
main.c
#include "stm32f10x.h"
typedef unsigned int u32;
lay(u32 i)
{
while(i--);
}
int main(void)
{
delay(0xfffff);
RCC_APB2ENR |= (1<<2); //A
RCC_APB2ENR |= (1<<3); //B
RCC_APB2ENR |= (1<<6); //E
GPIOB_CRL &= ~( 0x0F<< (4*5)); //PB5
GPIOE_CRL &= ~( 0x0F<< (4*5)); //PE5
GPIOA_CRL &= ~( 0x0F<< (4*0)); //PA0
GPIOE_CRL &= ~( 0x0F<< (4*3)); //PE3
GPIOB_CRL |= (1<<4*5);
GPIOE_CRL |= (1<<4*5);
GPIOE_CRL |= (0x08<<4*3);
GPIOE_BSRR |= (1<<3);
while(1)
{
if( (GPIOE_IDR & (0x0001<<3)) == 0)
{
GPIOB_ODR &= (0<<5);
GPIOE_ODR &= (0<<5);
GPIOE_BSRR |= (1<<3);
}
else
{
GPIOB_ODR |= (1<<5);
GPIOE_ODR |= (1<<5);
GPIOE_BSRR |= (1<<3);
}
}
}
void SystemInit(void)
{
}