历史上的今天
返回首页

历史上的今天

今天是:2025年03月12日(星期三)

正在发生

2019年03月12日 | STM32之点亮LED

2019-03-12 来源:eefocus

学习一个新的处理器,第一个程序肯定就是点亮LED,它可以让我们较快的、较清晰的了解到一个处理器的程序结构,学习32也不例外,首先第一个程序我们就来点亮LED,点亮LED程序有很多种,这里我们用库函数来实现LED的闪烁 


本人使用的开发板是正点原子的精英版开发板。 


首先复制一下我们之前建好的工程模板,然后把文件夹命名为LED。 


然后打开这个工程文件,第一步我们需要再建立两个空白页,并按照我上一篇博客的方法把他俩存到HARDWARE文件夹里面,一个命名为led.c,另一个命名为led.h,然后再在keil MDK软件里面把这两个文件给添加到工程中去,添加完之后,我们的工程结构如下图 


  


添加完之后,我们不要忘记要把这个.h文件路径给添加进去。点击keil MDK软件中的那个魔术棒标志,然后进入到c/c++那一栏,点击include Paths后面的那三个点 


 
 
 
 


经过以上几个步骤之后,然后找到HARDWARE文件夹,双击一下那个LED文件夹就可以了(请忽略我里面已经添加好的路径),注意32添加路径的时候,必须精确到最后一级文件夹 



其实其它的几个文件夹都需要添加路径,具体的做法正点原子官方论坛提供的资料中的不完全手册里面都讲的很清楚,大家可以去看一下。 

附上正点原子官方论坛网址:http://www.openedv.com/thread-13912-1-1.html


这些基本工作做好之后,我们就可以开始写程序了 

首先在led.h文件中写如下程序


#ifndef __LED_H

#define __LED_H

#include"sys.h"

#define LED0 PBout(5)



void led_init(void);


#endif


(注意在每一个.c和.h文件的最后,我们都需要加上一个回车,这可能是keil MDK软件自身的一个BUG,如果不加的话,编译的时候会有警告) 

其中这个.h文件中程序就是用来定义一个led.h头文件,以便我们在led.c文件里面可以进行调用。 

头文件的声明也是固定的格式,格式如下:


#ifndef __LED_H

#define __LED_H



#endif


意思就是如果没有定义led.h头文件的话,我们就定义一个led.h头文件,然后最后加上#endif表示定义完毕


然后这个led.h文件里面的#include “sys.h”是调用工程中的”sys.h”头文件,以后每个新定义的头文件都需要调用这个头文件,这个”’sys.h”头文件里面定义了一些32程序所需要的东西,有兴趣的你可以进入这个头文件里面看一下。


#define LED0 PBout(5)


这一条语句就是一个宏定义,你可以理解为它就相当于我们51单片机中写的sbit的作用,就是用LED0来代替了PB5引脚,并且PB5引脚为输出,即表达格式为PBout(5)


然后再在led.c文件里面写程序


#include"led.h"


void led_init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;

    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

    GPIO_Init(GPIOB,&GPIO_InitStructure);

}


这个led.c文件主要就是定义了一个GPIO口初始化的程序,就是来配置一下我们点亮LED所用到的GPIO口,(注意32在定义函数的时候,如果没有参数需要传递的话,那么必须要写入 void,即我们要写成led_init(void),否则编译也会出现警告,但是在函数调用的时候,如果没有参数传递,我们就不用再写入void ,我们可以直接调用led_init()即可)另外对于32来说,一般所有的配置都是用结构体来实现的,结构体中的成员就代表了这个功能所工作的方式。就拿我们这次点亮LED来说,我们使用的是LED0,他在正点原子的精英版上是接在了PB5引脚上,也就是说我们需要把PB5引脚配置成可以让LED点亮的工作方式。 

首先我们要声明一个结构体 GPIO_InitStructure


GPIO_InitTypeDef GPIO_InitStructure;


其中GPIO_InitStructure这个结构体的名字我们是可以随意写的,但是GPIO_InitTypeDef这个名字就只能写成这种形式,因为它是在固件库中已经定义好的,所以说我们不能随意改动。注意,结构体的定义必须在函数的开头,我们不能在函数中间进行定义变量(其实所有的定义都必须要在函数的开头,这一点也和我们当初学的C语言是一样的) 

然后我们就需要开启GPIOB的时钟,因为32基本上每一部分都有自己独立的时钟,我们要想让某一个模块工作,我们就必须使能它的时钟,使能时钟的函数也是固定的,我们只需要调用这个函数,然后传入对应的参数就行,原函数如下


RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)


其中这个函数的第一个参数就是选择你是要使能哪一个模块的时钟,第二个参数就是选择使能或者不使能,使能就是ENABLE,不使能就是DISABLE。(这里我们使用的是APB2总线,32还有APB1总线,APB2是高速总线,APB1是低速总线,有的模块是挂在APB1总线,有的是挂在APB2总线,我们应该具体情况具体分析) 

所以我们现在要用PB5引脚,即我们要使能GPIOB的时钟,所以我们要写成如下的形式


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


接下来我们就需要对刚才定义过的结构体中的成员进行赋值了, 

首先我们输入GPIO_InitStructure,然后再输入一个点“.”也就是输入 

GPIO_InitStructure.,这个时候,程序会自动出现这个结构体中的各个成员变量供你选择(这一点和我们的C语言是一样的)。其中这个结构体我们需要对三个成员进行配置,分解为GPIO_Mode、GPIO_Pin、GPIO_Speed`


然后我们选中GPIO_Mode,然后右鼠标点击右键,进入Go To Definition of “GPIO_Mode”,然后我们就会转入到一个 gpio.h 文件,在里面我们找到关于GPIO_Mode的相关定义,


#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \

                            ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \

                            ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \

                            ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))


我们可以看到GPIO有很多工作模式,我们需要根据自己的实际需要来选择具体的工作模式,这里我们要实现的是点亮LED灯,所以我们选择推挽输出工作模式,也就是选择GPIO_Mode_Out_PP 

然后另外的两个结构体成员的配置同理,最后三个成员配置如下


GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;


(关于GPIO_Speed其实我也不是很理解他的意思,他好像是配置GPIO口的最大输出速度,这里我们选择最大的速度,也就是50MHz) 

对各个成员变量赋值之后,我们要把这些值给导入到结构体当中去,此时我们又需要另外一个函数


GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)


这个函数就是把结构体中的成员利用下面这个函数给导入到相应的寄存器中,(如果暂时不理解,可以先记住,以后每一次配置都会用到这个函数),这个函数的第一个参数就是填入你要配置的是哪一个GPIO口,第二个参数就是你刚才定义的那个结构体名字加上符号“&”,最后这条程序如下


GPIO_Init(GPIOB,&GPIO_InitStructure);


到此,我们就完成了GPIO口的初始化过程,然后就是在主函数中调用了。注意你在.c文件中所定义的函数,不要忘记在这个.c文件所对应的.h文件中进行声明,这里我们在led.c里面定义了一个函数led_init(void),那么我们就需要把这个函数在led.h文件中进行声明,led.h文件如下


#ifndef __LED_H

#define __LED_H

#include "sys.h"

#define LED0 PBout(5)



void led_init(void);


#endif


接下来,我们在main.c里面进行操作,程序如下


#include "sys.h"

#include "delay.h"

#include "led.h"


int main(void)

{

    led_init();

    delay_init();

    while(1)

    {

        LED0=0;

      delay_ms(100);

      LED0=1;

      delay_ms(100);

    }

}


首先就是先添加我们程序中所用到的头文件


#include "sys.h"

#include "delay.h"

#include "led.h"


其中delay.h也是在建工程模板的时候添加的,里面定义了一些延时函数,我们一般都在主函数中添加这个delay.h文件,以便我们随时调用延时函数(你在哪个,c文件中调用了对应的函数,你就必须把这个函数所对应的的.h文件给添加到当前的.c文件中去,可能我说的比较乱,以后慢慢理解)。 

然后就是写主函数,注意32 的主函数不能是void main(void),必须是int main(void)(我也不知道为啥) 

在主函数中,我们首先调用我们在led.c文件中所写的LED初始化函数,然后再调用延时函数,然后写一个while循环,让LED在循环里面不断地进行状态翻转,程序如下


int main(void)

{

    led_init();

    delay_init();

    while(1)

    {

        LED0=0;

      delay_ms(100);

      LED0=1;

      delay_ms(100);

    }

}


到此为止,我们这个点亮LED的程序就写完了,编译之后,如果没有错误就可以把它烧录到开发板进行测试了,如果都正确的话,我们可以看到开发板上的一个小灯会不断地闪烁,闪烁间隔就是我们在 while循环里面所写的100ms。


点亮LED实验到此结束!

推荐阅读

史海拾趣

EasySync公司的发展小趣事

随着数字化和智能化技术的不断发展,EasySync公司也加快了数字化转型的步伐。公司引入先进的生产管理系统和智能制造设备,实现了生产过程的自动化和智能化。同时,公司还加强了对大数据和人工智能技术的研发和应用,为客户提供更加智能化、个性化的同步解决方案。

Hitachi (Renesas )公司的发展小趣事

随着数字化和智能化技术的不断发展,EasySync公司也加快了数字化转型的步伐。公司引入先进的生产管理系统和智能制造设备,实现了生产过程的自动化和智能化。同时,公司还加强了对大数据和人工智能技术的研发和应用,为客户提供更加智能化、个性化的同步解决方案。

思博科技(Cybermax)公司的发展小趣事

为了进一步扩大市场份额,思博科技开始积极拓展国际市场。公司参加了多个国际电子展会,与全球各地的客户建立了联系。同时,思博科技还与国际知名企业展开了合作,共同研发新产品。这些合作不仅提升了公司的技术水平,还为公司带来了更多的商机。在国际市场的拓展过程中,思博科技逐渐成为了全球电子行业的佼佼者。

百蓉(ECE)公司的发展小趣事

面对智能制造的浪潮,百蓉公司积极拥抱变革。他们引进了先进的自动化设备,对生产线进行了全面升级,实现了从手工制造向智能制造的转变。这一转型不仅提高了生产效率,还大幅提升了产品质量和产品的一致性,使得百蓉的产品在市场上更具竞争力。

Anytek Technology Corporation Ltd公司的发展小趣事

Anytek始终坚持以创新为驱动,不断推出具有市场竞争力的新产品。2007年,公司自主研发的创新型产品“高清汽车行车记录仪”面世,这款产品以其高清画质、稳定性能和易用性等特点,迅速占领了市场制高点。随后,Anytek又于2008年推出了行业唯一一款双镜头的“双镜头行车记录仪”,这一创新设计进一步提升了行车记录仪的功能性和实用性,赢得了市场的广泛认可。

富瀚(Fullhan)公司的发展小趣事
如果以上步骤均无法解决问题,建议联系新中兴的技术支持团队寻求帮助。

问答坊 | AI 解惑

我们做的音频信号分析仪

本帖最后由 paulhyde 于 2014-9-15 03:38 编辑 做得还行 交流一下  …

查看全部问答>

EVC 编译TCPMP的时候,没有的选择ARMV4,是什么原因?

EVC 编译TCPMP的时候,没有的选择ARMV4,是什么原因? 是下载的是0.72RC1的TCPMP源码,请问该如何处理?有做过的吗?网上的编译过程没有说到这个问题的。 请过的DX麻烦了…

查看全部问答>

2440+WINCE5.0 扩充128M SDRAM的问题。

    我使用2440+wince5.0的平台,原先使用2片共64M字节的SDRAM,每片256bit,16bit位宽;现在想使用128M SDRAM,所以用了4片和原先一模一样的SDRAM芯片,在硬件连接上4片sdram芯片的BA0都与addr24连接,BA1都与addr25连接,另外其中两片 ...…

查看全部问答>

用WinCE开发车载倒车视频系统,如何添加倒车参考线?

如题,有些GPS可以在显示倒车视频的时候,同时在画面上显示倒车参考线或者点,以表示与车后障碍物的距离,怎样用编程方式实现呢?…

查看全部问答>

有道题想请教一下大家,参考一下你们的答案

串的操作 要求: (1)字符串采用数组存储,建立两个字符串String1和String2。输出两个字符串。 (2)将字符串String2的头n个字符添加到String1的尾部。输出结果。 (3)查找串String3在串String1中的位置,若String3在String1中不存在,则插入 ...…

查看全部问答>

电力行业企业高薪招聘嵌入式开发人员!!

    欢迎满足以下条件之一的有识之士与我们联系,公司网址:     www.nerc.com.cn,发送简历邮箱地址:whl668@epri.ac.cn。     1.具有2年以上电力行业嵌入式产品开发经验。         2. ...…

查看全部问答>

软件工程专业应届生找不到工作

我是上海一大学软件工程专业的09届应届生,本科,不是很会说话。 在学校里学的是单片机开发之类的(偏软件 C语言),接触过ARM+Wince之类的嵌入式软件的开发。(其实学校里嵌入式软件方面的课基本就没有,都是我凭着兴趣自学的)单片机的水平达到自 ...…

查看全部问答>

OSSchedLock()函数透析

最近网友问到OSSchedLock()这个函数的问题,当时我也搞得半昏迷状态。     这个函数又叫上锁函数,如果在一个任务里面调用了上锁函数,那么OSSched()这个任务切换函数就不会执行也就是说不会进任务调度。 调用OSSchedLock()以后,用户 ...…

查看全部问答>

F149复位后寄存器状态?

我最近开始学430单片机,用的是F149的板子,请问哪位能提供我一些关于F149复位后各模块寄存器的状态  的资料。尤其是时钟模块三个寄存器  DCOCTL,BCSCTL1和BCSCTL2复位后是全1还是全0?…

查看全部问答>