STM32入门系列-使用库函数点亮LED,LED初始化函数
要点亮LED,需要完成LED的驱动, 在工程模板上新建一个led.c和led.h文件,将其存放在led文件夹内。这两个文件需要我们自己编写。
通常xxx.c文件用于存放编写的驱动程序,xxx.h文件用于存放xxx.c内的stm32头文件、管脚定义、全局变量声明、函数声明等内容。
因此在led.c文件内编写如下代码:
#include "led.h"
/*******************************************************************************
* 函 数 名 : LED_Init
* 函数功能 : LED 初始化函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
RCC_APB2PeriphClockCmd(LED_PORT_RCC,ENABLE);
GPIO_InitStructure.GPIO_Pin=LED_PIN; //选择你要设置的 IO 口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //设置推挽输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(LED_PORT,&GPIO_InitStructure); /* 初始化 GPIO */
GPIO_SetBits(LED_PORT,LED_PIN); //将 LED 端口拉高,熄灭所有 LED
}
函数中的LED_PORT_RCC、LED_PIN和LED_PORT是我们定义的宏,其存放在led.h头文件内 。LED_PORT_RCC定义的是LED端口时钟(如RCC_APB2Periph_GPIOC),LED_PIN定义的是LED的引脚(如 GPIO_Pin_0),LED_PORT定义的是LED的端口(如GPIOC)。这样定义宏的好处是有效提高了程序的移植性,即使后续需要换其他端口,只需简单修改这几个宏就可以完成对LED的控制。
在 led.h 文件内编写如下代码:
#ifndef _led_H
#define _led_H
#include "stm32f10x.h"
/* LED 时钟端口、引脚定义 */
#define LED_PORT GPIOC
#define LED_PIN
(GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7)
#define LED_PORT_RCC RCC_APB2Periph_GPIOC
void LED_Init(void);
#endif
LED_Init()函数就是对LED所接端口的初始化,是按照GPIO初始化步骤完成,这些内容在“寄存器点亮一个LED”章节中有介绍。下面我们主要看库函数是如何实现GPIO初始化的。
在库函数中实现 GPIO 的初始化函数是:
void GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct);
这个函数具体有什么功能以及函数形参的意义,我们可以通过库函数帮助文档来查阅。GPIO_Init函数内有两个形参,第一个形参是GPIO_TypeDef类型的指针变量,而GPIO_TypeDef是一个结构体类型,封装了GPIO外设的所有寄存器,所以给它传送GPIO外设基地址即可通过指针操作寄存器内容,第一个参数值可以为GPIOA、GPIOB、...GPIOG等,其实这些就是封装好的GPIO外设基地址,在stm32f10x.h文件中可以找到。第二个形参是GPIO_InitTypeDef类型的指针变量,而GPIO_InitTypeDef也是一个结构体类型,里面封装了GPIO外设的寄存器配置成员。我们初始化GPIO,其实就是对这个结构体配置。
如果想快速查看代码或参数可以用鼠标点击要查找的函数或者参数,然后右键鼠标选择“Go To Definition Of ...”即可进入所要查找的函数或参数内。
查找函数内变量类型也是同样的方法,但是如果发现此方法查找不出内容,那可能就是你所查找的东西在 KEIL5 软件认为是不正确的。
在 LED 初始化函数中最开始调用的一个函数是:
RCC_APB2PeriphClockCmd(LED_PORT_RCC,ENABLE);
此函数功能是使能GPIOC外设时钟, 在STM32中要操作外设必须将其外设时钟使能,否则即使其他的内容都配置好,也是徒劳无功。因为GPIO外设是挂接在APB2总线上,所以是对APB2总线时钟进行使能,函数内有两个参数,一个是用来选择外设时钟,一个是用来选择使能还是失能,使能:ENABLE,失能:DSIABLE。
在LED初始化函数内最后还调用了GPIO_SetBits(LED_PORT,LED_PIN)函数,此函数功能是让GPIOC端口的第0-7个引脚输出高电平,让LED处于熄灭状态,如果要对同一端口的多个引脚输出高电平,可以使用“|”运算符,相应的在对结构体初始化配置时管脚设置那里也要使用“|”将管脚添加进去,即在led.h文件内对LED引脚的定义。(前提条件是:要操作的多个引脚必须是配置同一种工作模式)例如:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;//管脚设置
GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1);
其实从函数名我们大致就可以知道函数的功能。函数内有两个参数,一个是端口的选择,一个是端口管脚的选择。如果要输出低电平的话可以使用如下库函数:
GPIO_ResetBits(GPIOC,GPIO_Pin_0);
这个函数功能和GPIO_SetBits是相反的,一个输出低电平,一个输出高电平,里面参数功能是一样的。
GPIO输出函数还有好几个,例如:
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
功能:设置端口管脚输出电平,这两个函数很少使用。
从 GPIO 内部结构可知,STM32 的 GPIO 还可以读取输入或输出引脚电平状态。其函数如下:
读取输入引脚
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin);
功能:读取端口中的某个管脚输入电平。底层是通过读取 IDR 寄存器。
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
功能:读取某组端口的输入电平。底层是通过读取 IDR 寄存器。
读取输出引脚
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin);
功能:读取端口中的某个管脚输出电平。底层是通过读取 ODR 寄存器。
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
功能:读取某组端口的输出电平。底层是通过读取 ODR 寄存器。
在 led.h 文件中可以看到使用了一个定义头文件的结构,代码如下:
#ifndef _led_H
#define _led_H
//此处省略头文件定义的内容
#endif
它的功能是防止头文件被重复包含,避免引起编译错误。在头文件的开头,使用“#ifndef”关键字,判断标号“ _led_H”是否被定义,若没有被定义,则从“#ifndef”至“ #endif”关键字之间的内容都有效,也就是说,这个头文件若被其它文件“ #include”,它就会被包含到其该文件中,且头文件中紧接着使用“#define”关键字定义上面判断的标号“_led_H”。当这个头文件被同一个文件第二次“#include”包含的时候,由于有了第一次包含中的“ #define _led_H”定义,这时再判断“#ifndef _led_H”,判断的结果就是假了,从“#ifndef”至“#endif”之间的内容都无效,从而防止了同一个头文件被包含多次,编译时就不会出现“redefine(重复定义)”的错误了。
一般来说,我们不会直接在C的源文件写两个“#include”来包含同一个头文件,但可能因为头文件内部的包含导致重复,这种代码主要是避免这样的问题。如“led.h”文件中调用了#include “stm32f10x.h”头文件,可能我们写主程序的时候会在 main 文件开始处调用#include“stm32f10x.h”和“led.h”,这个时候“stm32f10x.h”文件就被包含两次了,如果在头文件中没有这种机制,编译器就会报错。

STM32入门系列-使用库函数点亮LED,LED初始化函数的更多相关文章
- STM32入门系列-使用库函数点亮LED软硬件分析
		
电路图分析 首先找来单片机的原理图,根据原理图进行相关的设计工作. 例如在上图中相同网络标号表示它们是连接在一起的,因此D1发光二极管阴极是连接在STM32的PC0管脚上,D2指示灯阴极连接在PC1管 ...
 - STM32入门系列-STM32时钟系统,STM32时钟树
		
时钟对于单片机来说是非常重要的,它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行.时钟系统犹如人的心脏,一旦有问题整个系统就崩溃.我们知道STM32属于高级单片机,其内部有很多的外设,但不是 ...
 - STM32入门系列-STM32时钟系统,自定义系统时钟
		
在时钟树的讲解中我们知道,通过修改PLLMUL中的倍系数值(2-16)可以改变系统的时钟频率.在库函数中也有对时钟倍频因子配置的函数,如下: void RCC_PLLConfig(uint32_t R ...
 - STM32入门系列-CMSIS标准
		
使用寄存器点亮开发板上LED,这种开发方式显然是不适合大众,对于STM32这样庞大的芯片,内部寄存器实在太多,如果操作的外设比较多,那么就需要花很多时间查询底层寄存器内容,而且即使程序写好,如果要换其 ...
 - STM32入门系列-库帮助文档使用
		
在前面文件夹介绍时,提到了stm32f10x_stdperiph_lib_um.chm文件,此文件是库函数使用帮助文档,可以直接打开如下图. 因为STM32库函数非常多,我们不可能把所有的外设函数都记 ...
 - STM32入门系列-GPIO工作模式及LED电路原理
		
GPIO工作模式 由于GPIO内部的结构关系,决定了GPIO可配置成以下几种模式. 输入模式 在输入模式时,施密特触发器打开,输出被禁止.可通过输入数据寄存器GPIOx_IDR读取I/O状态.输入模式 ...
 - STM32入门系列-STM32时钟系统,时钟使能配置函数
		
之前的推文中说到,当使用一个外设时,必须先使能它的时钟.怎么通过库函数使能时钟呢?如需了解寄存器配置时钟,可以参考<STM32F10x中文参考手册>"复位和时钟控制(RCC)&q ...
 - STM32入门系列-STM32时钟系统,时钟初始化配置函数
		
在前面推文的介绍中,我们知道STM32系统复位后首先进入SystemInit函数进行时钟的设置,然后进入主函数main.那么我们就来看下SystemInit()函数到底做了哪些操作,首先打开我们前面使 ...
 - STM32入门系列-启动文件介绍
		
在启动文件内部使用的都是汇编语言,这个文件的作用是负责执行微控制器从"复位"到"开始执行 main 函数"中间这段启动时间所必须进行的工作.它完成的具体工作有: ...
 
随机推荐
- System Verilog随笔(1)
			
测试文件该怎么写? 首先看一个简单代码案例: `timescale 1ns/10ps //1 module test; //2 intput wire[15:0] a; output reg[15 ...
 - @Autowired,@Resource,@Qualifier,@Primary,@Inject的作用和区别
			
@Autowired注解的用法:可以用于构造器,方法,参数,字段进行属性注入,有一个required属性,默认是true,当改成false时,如果注入的属性在容器中不存在也不会报错@Resource该 ...
 - Python-列表推导式、生成器、字典推导式
			
列表推导式 [] + for + if 算子.循环.条件, 对相同的数据进行处理, 算子也可以是函数 number = [i for i in range(1, 101) if i % 2 == 0] ...
 - Python-对字典进行排序
			
案例: 某班英语成绩以字典的形式存储为: {'lili':78, 'jin':50, 'liming': 30, ......} 依据成绩高低,进行学生成绩排名 如何对字典排序? 方法1: #!/us ...
 - nessus 本地扫描(一)
			
第一次使用nessus ,so 适合小白看看 1.新建扫描.配置策略:起个名字,description是详细记录,类似于说明:targets是要访问的主机ip地址或者网段,必填项 选择好之后sav ...
 - 总线SPI的Arduino库函数
			
来源参考:https://www.cnblogs.com/MyAutomation/p/9348480.html 总线SPI的Arduino库函数 SPI基本知识 SPI:高速同步串行口.是一种标准的 ...
 - P5911 [POI2004]PRZ (状态压缩dp+枚举子集)
			
题目背景 一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 题目描述 桥已经很旧了, 所以它不能承受太重的东西.任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍过桥时 ...
 - Linux 下 svn 场景实例及常用命令详解
			
一.SVN使用场景实例 问题: 在使用svn做为版本控制系统的软件开发中,经常会有这样的需求:在工作复本目录树的不同目录中增加了很多文件,但未纳入版本控制系统,这时如果使用svn add命令一个一个的 ...
 - 访问 LNMP 报 502 Bad Gateway 错误的解决办法
			
LNMP : Linux + Nginx + MySQL + PHP Nginx 出现502有很多原因,但大部分原因可以归结为资源数量不够用,也就是说后端 PHP-FPM 处理有问题,Nginx 将正 ...
 - 拜托,别再问我怎么自学 Java 了!和盘托出
			
假如有那么残酷的一天,我不小心喝错了一瓶药,一下子抹掉了我这十多年的编程经验,把我变成了一只小白.我想自学 Java,并且想要找到一份工作,我预计需要 6 个月的时间,前提条件是每天都处于高效率的学习 ...