STM32掌机教程3,工程模板与带灯按键测试

版权声明:希望能帮助你,也希望你帮助别人,哪怕一点点 https://blog.csdn.net/geek_monkey/article/details/86521512

我们需要“脚手架”

  关于代码,我想体现出这么一个过程:我是如何一步一步修改代码的。我认为,从学习的角度来考虑,直接看最终的代码没有什么意义。 写代码就像工人盖房子,盖房子过程中,工人要搭建脚手架;房子盖好以后,脚手架要拆除。直接领着学生看盖好的房子,说,你就照着这个样子来盖房子,学生是做不出来。他不知道怎么搭建脚手架,甚至都不知道什么是脚手架。 所以我打算这个系列的讲义,每一篇代码都在上一篇的基础上做一些改进,保留代码“进化”的过程。有些过程代码在最终的代码中不会体现,但是也很重要,就像房子盖好以后,你看不到脚手架一样。我们需要脚手架。
在这里插入图片描述
  目前用到的代码先用压缩包的形式上传,等教程写完以后,后续代码的维护使用Git。

各种初始化

  下载压缩包并打开工程以后,可以在main.c里找到主函数。目前为止,主函数进行了一些初始化,死循环内什么都没写,后续可以根据我们的需要写业务逻辑代码。

//main.c
int main(void)
{
	LED_Init();
	KEY_Init();
	delay_init();
	initIIC();
	initOLED();
	
	while(1)
	{


	}
}

LED与SLED的初始化

  函数的作用从名字就可以看出来,比较简单。例如LED的初始化。需要初始化哪个引脚,可以从电路图中看出。
在这里插入图片描述

//IO.c
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE);	

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5; 
	GPIO_Init(GPIOC, &GPIO_InitStructure);

	AllLED_ON();
	LED1 = LED_ON;
	LED2 = LED_OFF;
}

  具体LED或者SLED对应的引脚可以在原理图中看出来。为了方便使用,我根据外设与引脚关系做了宏定义。

//IO.h
#define SLED1 PCout(0)
#define SLED2 PCout(1)
#define SLED3 PCout(2)
#define SLED4 PCout(3)
#define LED1 PCout(4)
#define LED2 PCout(5)
#define SLED5 PBout(12)
#define SLED6 PBout(13)
#define SLED7 PBout(14)
#define SLED8 PBout(15)
#define SKEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define SKEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)
#define SKEY3 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
#define SKEY4 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3)
#define SKEY5 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6)
#define SKEY6 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_7)
#define SKEY7 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_8)
#define SKEY8 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_9)

#define PAUSE GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)
#define BEEP PBout(1)

#define KEY1_PRES    1    //KEY1按下
#define KEY2_PRES    2    //KEY2按下
#define KEY3_PRES    3    //KEY3按下
#define KEY4_PRES    4    //KEY4按下
#define KEY5_PRES    5    //KEY5按下
#define KEY6_PRES    6    //KEY6按下
#define KEY7_PRES    7    //KEY7按下
#define KEY8_PRES    8    //KEY8按下
#define PAUSE_PRES   9

#define LED_ON  0
#define LED_OFF 1
#define DOWN  0 //按键按下
#define FREE  1

  然后编写了两个函数,用于点亮所有LED或者关闭所有LED

//IO.c
void AllLED_ON(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
	GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
}
void AllLED_OFF(void)
{
	GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
	GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
}

  这些代码。可能对于某些没有使用过STM32的同学来说不太好看懂,不过没有关系,可以仅仅应用的话,这是很简单的。在我之前的博客里也提过一些库函数的基础。例如设置某引脚为推挽输出GPIO_Mode_Out_PP ,不知道什么事推挽输出可以自己查一下。设置哪个引脚为推挽输出?PB12到PB15,PC0到PC5。
  再比如,让某个LED亮起来GPIO_ResetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);很容易看出来,这个操作是把PB12到PB15置为低电平,结合原理图可知,引脚低电平可以点亮LED。

按键初始化与扫描函数

  按键设置为上拉输入,检测到低电平,说明按键被按下。按键扫描函数课可以返回被按下的按键值。

void KEY_Init(void)  //PB0-PB3
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	/*	
	//PB3与PB4默认用作调试口,如果用作普通的IO,需要加上以下两句 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//上拉输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; 
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	  
}
unsigned char KEY_Scan(unsigned char mode)
{    
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按          
	if(key_up&&(SKEY1==DOWN||SKEY2==DOWN||SKEY3==DOWN||SKEY4==DOWN||SKEY5==DOWN||SKEY6==DOWN||SKEY7==DOWN||SKEY8==DOWN||PAUSE==DOWN))
	{
		delay_ms(10);//去抖动
		key_up=0;
		if(PAUSE==DOWN)return PAUSE_PRES;
		else if(SKEY1==DOWN)return KEY1_PRES;
		else if(SKEY2==DOWN)return KEY2_PRES;
		else if(SKEY3==DOWN)return KEY3_PRES;
		else if(SKEY4==DOWN)return KEY4_PRES;
		else if(SKEY5==DOWN)return KEY5_PRES;
		else if(SKEY6==DOWN)return KEY6_PRES;
		else if(SKEY7==DOWN)return KEY7_PRES;
		else if(SKEY8==DOWN)return KEY8_PRES;
	}else if(SKEY1==FREE && SKEY2==FREE && SKEY3==FREE && SKEY4==FREE && SKEY5==FREE && SKEY6==FREE && SKEY7==FREE && SKEY8==FREE && PAUSE==FREE)key_up=1;         
	return 0;// 无按键按下
}

其它初始化

  延时函数的初始化借用了别人的代码,就不贴了。
  0.96OLED屏幕用到了IIC总线,所以既需要初始化IIC总线,也需要初始化OLED屏幕。其实初始化的代码也不是我写的,借用的。有的读者可能会说,屏幕的初始化好像挺难的,我不知道这些初始化函数怎么写出来。这个问题很好解决——如果厂家不提供初始化的代码,我们不买它的屏幕就行了。谁提供代码,提供技术支持,我们选谁的屏幕。毕竟
在这里插入图片描述
  另外,对于定时器和蜂鸣器,我也写好了初始化代码。用到再说吧。

带灯按键检测

  在LED的初始化函数中,已经点亮了所有的带灯按键。如果按键上的灯不亮,就只能找硬件问题了。接下来写一小段测试代码,判断按键按下能否检测到。
  我的思路是,如果按下了某个按键,那么按键对应的LED灯状态翻转。STM32中,让引脚状态翻转其实是比较高阶的操作,原理比较复杂,感兴趣的可以搜下STM32的位带操作。但是用起来很简单,把寄存器当数据操作,假装寄存器可以取反。例如,LED1的状态取反LED1 = !LED1,如此操作,可以不用关心LED之前是亮是灭,看到的现象就是状态变了。
  我们可以写一个switch case语句,根据按键值来操作对应的LED。

while(1)
	{
		switch(KEY_Scan(0))
		{
			case PAUSE_PRES:
				LED1 = !LED1;
				LED2 = !LED2;
			break;
			case KEY1_PRES:
				SLED1 = !SLED1;
			break;
			case KEY2_PRES:
				SLED2 = !SLED2;
			break;
			case KEY3_PRES:
				SLED3 = !SLED3;
			break;
			case KEY4_PRES:
				SLED4 = !SLED4;
			break;
			case KEY5_PRES:
				SLED5 = !SLED5;
			break;
			case KEY6_PRES:
				SLED6 = !SLED6;
			break;
			case KEY7_PRES:
				SLED7 = !SLED7;
			break;
			case KEY8_PRES:
				SLED8 = !SLED8;
			break;
			default:
			break;
		}
	}

  现象就是,上电以后,所有的按键灯都亮,如果按下某个按键,那么按键对应的灯状态翻转。

猜你喜欢

转载自blog.csdn.net/geek_monkey/article/details/86521512