资讯详情

面向应用学习stm32(1)-GPIO输出点亮灯

导游:本文的目的和目的是为应用学习单片机,因此不会涉及太多的原理知识,如寄存器。

主要目的是学习单片机,学习单片机的基本用法,开发板采用野火指南f103。

作者大二小白,写得不好的地方轻轻喷洒,欢迎评论区交流 所有代码和笔记 开源在 Gitee仓库

文章目录

  • 1 单片机简介
    • 1.1简单介绍
    • 1.2 STM32
  • 2 开发环境建设
  • 3 GPIO输出
    • 3.1简介
    • 3.2思路分析
    • 3.3 代码实现
  • 4 三个小实验
    • 4.1需求
    • 4.2 亮更多颜色的灯
      • 4.2.1亮灯函数
      • 4.2.2 完整代码
      • 4.2.3分文件改造
    • 4.3 闪烁灯
    • 4.4 流水灯
  • 5 总结

1 单片机简介

1.1简单介绍

简称单片机(MCU(MicrbControl Unit))。它,将这些部件安装在主板上,然后连接电源、屏幕、鼠标、键盘等外设,形成我们的计算机。。然后连接其他外设进行操作。

它比普通的好PC计算机价格更低,占用空间更小,但相应地,性能相对较弱。然而,在许多电子元件中,我们往往不需要如此强大的性能,需要考虑更多的商业因素,如价格和体积。然后单片机就来了。

1.2 STM32

意法半导体(ST)推出一款单片机。是目前各大高校应用教学最常见、最多的单片机。

基于官方提供的简单易用的

同时,由于价格不贵,性能强,生态环境优良,更适合初学者

例如 STM32F103C8T6 指的就是

基本型48引脚,内存容量64kb,QFP工作在-40-85-40-85℃

img

关于单片机地址总线、数据总线等底层硬件知识,因为这里主要是关于应用程序的开发,底层很少涉及,所以不要说。

2 开发环境建设

可以参考开发环境

开发环境建成后,在我的项目目录下复制一份Template你可以准备好开始点亮我们的第一个LED灯啦。

3 GPIO输出

3.1简介

在做我们的第一个项目之前,先做GPIO最简单的介绍

GPIO(General-Purpose IO ports) ,即通用输入输出IO口。

我们可以通过,驱动我们需要的电路或发送数据。

同时,也可以,接收数据、判断状态等。

首先,原理图pdf和stm下载32库函数的中文翻译手册。同时复制我的项目目录Template1。下载地址

到你的文件夹,改成你想要的名字,我改成LED,复制后点击打开,然后点击Project。最后点击这个小绿图标进入项目。

进入项目后,首先引入我们眼中的是这样一个界面。边缘的一串是STM10x_开头的文件是ST未来我们将广泛使用官方为我们提供的库函数

3.2思路分析

看完整体开发环境,我们回到需求,进行如下分析。

根据电路的原理,我们自然需要知道灯泡连接个引脚。

此时大家LED模块,发现如下。

首先,我们可以看到他是三盏灯,分别连接 PB0,PB1,PB5.(后面还写着红、绿、蓝的分布)到目前为止,灯泡引脚已经确定。

接下来能写代码亮灯吗?显然不是,

观察原理图。

可见,。根据电路分析,3.3V电压不能从右侧流向左侧,二极管不能导通。

,3.3V从右到左,经过二极管后,灯泡被点亮,是红灯,因为原理图上注明了R,另外两个是G(Green)和B(Blue)。

  • PB0、1、5需要输出模式
  • 输出电平需要设置为低电平

3.3 代码实现

回到我们的工程代码中,

首先我们需要开启GPIOB为什么不先解释时钟?总之,使用任何一个GPIO或者任何模块,打开他们的时钟。

时钟相关操作位于stm32f10x_rcc.h其中。我们点击他,在里面找到两件事

宏定义可以在这里找到。GPIOB时钟,挂载APB继续向下翻。

发现有行使能APB2总线上的时钟源函数。传输的参数是我们以前看到的宏定义时钟,它们被复制到主函数中while(1)前面

//主函数 int main(void) { 
          //使能GPIOB的时钟  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  while(1)  { 
              } } 

打开时钟后,我们准备开始配置GPIOB0,1,5端口。stm32f10x_gpio.h

看到这样的结构GPIO_InitTypeDef,里面的参数是引脚、速度和模式。我们可以根据结构和内部变量初始化GPIO口。

同样,这些模式,Pin嘴、速度等也定义在里面,如下图所示,枚举中都是定义好的参数,可以用。

这里的GPIO_MODE可以看到有个模式,这里我们先记住点亮LED要用推挽输出,也是我们最常见的输出模式

-输入浮空(GPIO_Mode_IN_FLOATING)
-输入上拉(GPIO_Mode_IPU)
-输入下拉(GPIO_Mode_IPD)
-模拟输入(GPIO_Mode_AIN)

-开漏输出(GPIO_Mode_Out_OD)
-开漏复用功能(GPIO_Mode_AF_OD)
-推挽式输出(GPIO_Mode_Out_PP)//最为常用
-推挽式复用功能(GPIO_Mode_AF_PP)

看完以上消息后,我们开始写代码,声明GPIO初始化结构体,并给结构体成员赋值

	//声明GPIO的初始化结构体
	GPIO_InitTypeDef init;
	//使能GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//配置GPIO属性
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;//引脚用 | 一起配置
	init.GPIO_Mode = GPIO_Mode_Out_PP = 0x10; //推挽输出模式
	init.GPIO_Speed = GPIO_Speed_10MHz;//输出速度,亮灯比较不关心这个

现在有个问题,我怎么知道我配置的是哪个0,1,5。我需要的是GPIOB的0,1,5,可是代码里并没有声明啊。

ok我们还是回到头文件里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HrkrsYdk-1651651676133)(1.1GPIO_Output_Led.assets/image-20220504144945462.png)]

在头文件的最后可以发现有这些函数,我用红圈勾出了我们待会需要的

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);// GPIO的初始化。

,也就是说。

这个GPIOx,就是指定你要配置的端口,并把初始化结构体里的配置写入其中

那么主函数加入这行后, 初始化到这里就完成了

//主函数
int main(void)
{ 
        
	//声明GPIO的初始化结构体
	GPIO_InitTypeDef init;
	//使能GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//配置GPIO属性
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;//引脚用 | 一起配置
	init.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
	init.GPIO_Speed = GPIO_Speed_10MHz;//输出速度,亮灯比较不关心这个
	GPIO_Init(GPIOB,&init);//完成初始化
	while(1)
	{ 
        
			
	}
}

,我们准备亮灯了,回到之前说的,PB5低电平亮红色等。

刚刚那个图片中圈出

参数1是GPIOx,参数2是GPIO_Pin,那么我们要的操作是 PB5,低电平,代码如下

//主函数
int main(void)
{ 
        
    //...省略前面初始化部分...
    
    //先全部置高电平,关灯,防止引脚默认悬空电压影响
	GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5);
    //亮红灯
	GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	while(1)
	{ 
        
			
	}
}

这些代码的使用,功能等等,在固件库的中文翻译版里有,大家可以去查询,例子里也有对应用法,例如

4 三个小实验

4.1需求

首先提出以下的需求。

  • 需要亮更多颜色的灯
  • 闪烁灯
  • 流水灯

4.2 亮更多颜色灯

首先第一点我们好解决,他的灯是RGB灯,那么我们只需要排列组合2的三次方,8种情况就可以亮出所有的灯。

看到这其实就想提出来

我们需要使用一个 ,这样的话代码的可读性也高一点,不然看着一堆set,Reset真的很难看懂是亮什么灯

于是我写出了如下函数和宏定义

4.2.1亮灯函数

#define LED_GREEN 0
#define LED_BLUE 1
#define LED_RED 2
#define LED_WHITE 3
#define LED_PURPLE 4
#define LED_YELLOW 5
#define LED_CYAN 6
#define LED_OFF 7

void LED_Color(int color)
{ 
        
	
	if(color==0){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==1){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==2){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==3){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==4){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==5){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==6){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==7)
	{ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5);
	}
}

现在,我们只需要一行函数,就可以控制灯的各自颜色了,根据不同的宏定义亮不同的颜色

//主函数
int main(void)
{ 
        
	//声明GPIO的初始化结构体
	GPIO_InitTypeDef init;
	//使能GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//配置GPIO属性
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;
	init.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
	init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&init);
    //亮红灯
	LED_Color(LED_RED);
	while(1)
	{ 
        
			
	}
}

然后可以发现,配置部分也很长很麻烦,于是我决定把配置部分也给移出来

void LED_Init()
{ 
        
	//声明GPIO的初始化结构体
	GPIO_InitTypeDef init;
	//使能GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//配置GPIO属性
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;
	init.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
	init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&init);
}

4.2.2 完整代码

#include "stm32f10x_gpio.h" 

#define LED_GREEN 0
#define LED_BLUE 1
#define LED_RED 2
#define LED_WHITE 3
#define LED_PURPLE 4
#define LED_YELLOW 5
#define LED_CYAN 6
#define LED_OFF 7

void Delay(int i){ 
        
	for(;i>0;i--);
}

void LED_Init();
void LED_Color(int color);
//主函数
int main(void)
{ 
        

	LED_Init();
	LED_Color(LED_YELLOW);
	while(1)
	{ 
        
			
	}
}

void LED_Init()
{ 
        
	//声明GPIO的初始化结构体
	GPIO_InitTypeDef init;
	//使能GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	//配置GPIO属性
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;
	init.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出模式
	init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&init);
}
void LED_Color(int color)
{ 
        
	
	if(color==0){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==1){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==2){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==3){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==4){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==5){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==6){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==7)
	{ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5);
	}
}

4.2.3分文件改造

其实你看到末尾的函数实现体,还是会觉得很长,毕竟一个程序来到了84行的长度,那未来呢?

  • 去到项目的目录下,进入User,新建led.c,led.h。
  • 在工程中添加刚刚新建的led.c文件

双击User,将刚刚的led.c添加就去

在led.c里先包含led.h然后编译一遍

编译完后可以看到

#include "stm32f10x_gpio.h" 

#define LED_GREEN 0
#define LED_BLUE 1
#define LED_RED 2
#define LED_WHITE 3
#define LED_PURPLE 4
#define LED_YELLOW 5
#define LED_CYAN 6
#define LED_OFF 7

void LED_Init();
void LED_Color(int color);

#include "led.h"

void LED_Init()
{ 
        
	GPIO_InitTypeDef init;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5;
	init.GPIO_Mode = GPIO_Mode_Out_PP;
	init.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&init);
}
void LED_Color(int color)
{ 
        
	
	if(color==0){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==1){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==2){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==3){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==4){ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==5){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_1);
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==6){ 
        
		GPIO_ResetBits(GPIOB,GPIO_Pin_0);
		GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
	}
	if(color==7)
	{ 
        
		GPIO_SetBits(GPIOB,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_5);
	}
}

最后,我们回到主函数,,整个主函数结构如下,现在主函数真正的简洁了,

并且

先调用LED_Init();进行GPIO的配置和初始化

再调用LED_Color(int color)这里面传入你要的颜色(在led.h里宏定义了),就可以对应显示了。

4.3 闪烁灯

有了刚刚的函数,闪烁灯的颜色其实就很好控制了,闪烁本质上就是开和关。

而我们需要不断的开和关就好了,那我就要在while(1)这个死循环里不断开关了,我写出第一份代码

#include "led.h"
void Delay(int i){ 
        
	for(;i>0;i--);
}
int main(void)
{ 
        
	LED_Init();
	while(1)
	{ 
        
			LED_Color(LED_YELLOW);
			LED_Color(LED_OFF);
	}
}

不断开关完成了,但我们会发现没有效果,为什么?

因为

所以我们,开一会儿,关一会儿,再重新开一会儿,关一会儿。这里的延时采用最简单的for循环延时,传入参数,减到0,for循环才能退出。

#include "led.h"
void Delay(int i){ 
        
	for(;i>0;i--);
}
//主函数
int main(void)
{ 
        
	LED_Init();
	while(1)
	{ 
        
		LED_Color(LED_YELLOW);
		Delay(5000000);
		LED_Color(LED_OFF);
		Delay(5000000);
	}
}

到这,我们的效果就已经完成了,但我还是不满意。

所以我了(如之前led一样的操作),因为延时这个功能本质上是不属于led的。

然后

延时分文件这里不再讲了,详细操作和前面的led是差不多的。讲一讲闪烁函数的实现吧。

我希望

参数类型就和我前面LED_Color();这个颜色控制函数的参数值一样就行。

首先去led.h里声明函数,增加一行,由于我们还需要包含延时,所以延时的头文件也包括进来

#include "delay.h"
void LED_Flashing(int color);//闪烁函数

在led.c里增加实现体

void LED_Flashing(int color)
{ 
        
		LED_Color(color);//亮对应颜色
		Delay(5000000);
		LED_Color(LED_OFF);
		Delay(5000000);
}

主函数调用

#include "led.h"

int main(void)
{ 
        
	LED_Init();
	while(1)
	{ 
        
		 LED_Flashing(LED_Red);
	}
}

4.4 流水灯

在有了前面那些基础过后,我们直接分析如何写流水灯函数吧

首先,流水灯函数要做到的就是把所有颜色流水显示一遍,这里

可以看到,写这个

led.h增加一行

void LED_Stream();

led.c增加函数

void LED_Stream()
{ 
        
	int i
        标签: 红绿蓝白二极管

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台