STM32--GPIO点亮LED灯(手把手,超详细)

news2024/10/5 14:12:21

写在前面:在前面的学习中,我们学习了STM32的编译环境(MDK)、时钟树以及GPIO的8种工作模式;这节我们学习正式入门STM32---点亮第一个LED灯;即利用GPIO进行电灯,尽管是一个十分简单的实现,但是其步骤也是一个完整的STM32项目,可以说是“麻雀虽小,五脏俱全”;因此,作为入门十分合适;

实验介绍:利用STM32开发板,通过HAL库函数对相关寄存器的控制,实现对开发板上LED灯的控制;

实验硬件:正点原子---STM32F1精英版开发板; 

目录

一、GPIO寄存器介绍

1.1 端口配置寄存器(GPIOx_CRL 和 GPIOx_CRH)     

1.2  端口输入数据寄存器(GPIOx_IDR)

1.3 端口输出数据寄存器(GPIOx_ODR)

1.4 端口置位/复位寄存器(GPIOx_BSRR)

1.5 端口位清除寄存器(GPIOx_BRR)

1.6 端口配置锁定寄存器(GPIOx_LCKR)

二、GPIO配置步骤

2.1 使能时钟

2.2 设置工作模式 

2.3 设置输出状态

2.4 读取输入状态

三、硬件设计

3.1 实现功能

3.2 原理图

四、程序设计

4.1 程序流程图

4.2 程序分析

 4.2.1 led驱动函数

4.2.2 main函数

五、实验现象


一、GPIO寄存器介绍

        在前面的学习中,我们对GPIO进行了基本的介绍,基本结构进行了分析以及GPIO的8中工作模式的介绍;详细的内容请大家参考我前面的博客;

STM32--GPIO(8种工作模式)-CSDN博客

         STM32F1 每组(这里是 A~D)通用 GPIO 口有 7 个 32 位寄存器控制; 分别为:CRL、CRH、TDR、ODR、BSRR、BRR以及LCKR寄存器;下面对7个寄存器做出详述(注:每组都有7个寄存器,例如:GPIOA、GPIOB都有7个相关的寄存器);

1.1 端口配置寄存器(GPIOx_CRL 和 GPIOx_CRH)     

        这个寄存器是用于配置GPIO端口的寄存器,其中CRL 控制端口的低八位,CRH 控制端口的 高八位,每个组GPIO有16个端口(例如:GPIOA0-GPIOA15);那么这两个寄存器控制GPIO口的什么呢?用于控制 GPIO 口的工作模式和工作速度;

CRL寄存器:

 CRH寄存器:

        这两个32位的寄存器,一共为64位,64位共控制16个端口(如:PA0-PA15);那么相当于每4位控制一个IO口的工作模式与工作速度;CRL寄存器控制(0-7)端口,CRH寄存器控制(8-15)端口;

以3-0位(控制PX_0端口)为例:

    1-0位控制其输入输出: 

    3-2位控制其对应的模式:

        如0-3位为1010,则表示:端口1工作在复用功能的推挽输出模式,最大速度为2MHz;

        又如,我们需要让Px_7端口工作在浮空输入模式,则就要求CRL寄存器的31-28位赋值为:0100;

        故通过上面两个寄存器,作用是控制端口的输入输出模式以及工作速度;

        但是,我们仔细观察,在上面的寄存器中,如果选择的是输入模式,在上拉/下拉输入模式下,通过上面两个寄存器无法确定其到底为上拉还是下拉模式;此时,就需要另外的寄存器;

1.2  端口输入数据寄存器(GPIOx_IDR)

        该寄存器用于读取GPIOx的输出高电平或低电平;

         寄存器低 16 位有效,分别对应每一组 GPIO 的 16 个引脚。当 CPU 写访问该寄存器,可以读取对应引脚的输入电平(IDRy=0、IDRy=1),该寄存器下方为r,表示为只读寄存器,只可以读取值,不能写入值;

1.3 端口输出数据寄存器(GPIOx_ODR)

        该寄存器用于控制 GPIOx 的输出高电平或者低电平;

        该寄存器低 16 位有效,分别对应每一组 GPIO 的 16 个引脚。当 CPU 写访问该寄存器,如 果对应的某位写 0(ODRy=0),则表示设置该 IO 口输出的是低电平,如果写 1(ODRy=1),则表示设置该 IO 口输出的是高电平。

        除了 ODR 寄存器,还有一个寄存器也是用于控制 GPIO 输出的,它就是 BSRR 寄存器。

1.4 端口置位/复位寄存器(GPIOx_BSRR)

         该寄存器也用于控制 GPIOx 的输出高电平或者低电平.

        该寄存器的31-16(BRy)位控制15-0引脚的ODRy的值,如果为0,对ODRy的值不产生影响,如果为1,则将ODRy的值改为0;

        该寄存器的15-0(BSy)位也控制15-0引脚的ODRy的值,如果为0,对ODRy的值不产生影响,如果为1,则将ODRy的值改为1;

        当BRy与BSy同时作用时,以BSy为主;

        此处,我们需要思考一下,为什么输出一个引脚的值,需要两个寄存器对其进行双重控制呢?

官方给出的解释是:在使用ODR在读和修改之间产生中断时,会对ORD的值产生风险,而BSRR无风险;

        我们对其进行简单的分析,ODR是一个可读可写的寄存器,那么对其进行赋值时(确定某个IO口输出电平),首先需要读出ODR寄存器的值,然后再重新为ODR进行赋值。而BSRR是一个只写寄存器,赋值时直接设置即可。BSRR 寄存器改变引脚状态的时候,不会被中断打断,而 ODR 寄存器有被中断打断的风险。

1.5 端口位清除寄存器(GPIOx_BRR)

        该寄存器用于清除ODR寄存器的值,不经常使用;

        该寄存器低 16 位有效,若为0,对应的ODRy位不产生影响;若为1,清除对应位ODRy为0;

1.6 端口配置锁定寄存器(GPIOx_LCKR)

        当执行正确的写序列设置了位16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁 定GPIO端口的配置。在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了 LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。 每个锁定位锁定控制寄存器(CRL, CRH)中相应的4个位。

以上7个寄存器中,我们需要重视的为CRL、CRH、ODR、IDR以及BSRR寄存器

二、GPIO配置步骤

一般外设的配置驱动模型为:

1、初始化

分为:时钟设置(包括时钟源与开启时钟);

           参数设置;

           IO设置(可选);

           中断设置(可选);

2、读函数:从外设读取数据(可选);

3、写函数:往外设写入数据(可选);

4、中断服务函数(可选);

         在此,我学习的时候就有个疑问与大家分享,为什么STM32进行IO口操作时,必须先要进行时钟设置(使能时钟)?

        在学习52单片机的时候,只需要直接将对应的外设寄存器配置好,就直接使用,为什么32要先配置时钟呢?

        经过查阅资料发现,51单片机的做法虽然简单,但是这样对于功耗的浪费很大,也就是说尽管有些地方用不到,还需要配置资源。为了降低功耗与发热,芯片厂商需要精准的控制某个设置的开与关,达到节能省电的目的;此外,51只有一个晶振时钟,好控制,而32的晶振4个起步,对于不同的外设需要分配不同的晶振时钟,故STM32中I/O要先进行时钟配置,在进行IO口操作;

GPIO配置步骤:

1、使能时钟 __HAL_RCC_GPIOB_CLK_ENABLE();//宏定义

2、设置工作模式:HAL_GPIO_Init 函数;

3、设置输出状态:HAL_GPIO_WritePin 函数;

                               HAL_GPIO_TogglePin 函数;

4、读取输入状态:HAL_GPIO_ReadPin函数;

口诀:GPIO配置,一个宏,四个函数;

下面我们对于这四个步骤进行详细说明,包括利用HAL库函数进行相关的配置说明;

2.1 使能时钟

        STM32 在使用任何外设之前,我们都要先使能其时钟(也就是打开时钟),此处利用一个宏定义:

__HAL_RCC_GPIOX_CLK_ENABLE();

此处,我们看看代码种对于其宏定义的说明是什么。打开任意一个例程源码进行搜索;(此处以GPIOA作为举例); 

宏定义的作用就是替代,将被定义的量用定义的量进行替代;

 代码中对于此宏的定义是这样说的:

其中关键在于划线这句:

 SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);

其中,SET_BIT的意思我们不知,再次进行查找,右键Go to Definiation of "SET_BIT"其又是一个宏定义:其含义为括号中的两个参数做或运算;

 SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN)的参数1:RCC->APB2ENR;参数2:RCC_APB2ENR_IOPAEN;分别代表什么意思呢?

RCC_APB2ENR_IOPAEN:这又是一个宏定义,我们同样跳转过去看一下去其含义:

这三行代码可以说是层层宏定义,其表达的意思就是将0X00000002(二进制为:0x00000000000000000000000000000010)左移一位成为0X00000004(二进制为:0x00000000000000000000000000000100);

RCC->APB2ENR:是APB2外设时钟使能寄存器,让其与0X00000004进行或运算,则该寄存器的第2位进行置1;

查看参考手册,将位2置1的作用就是打开IO口时钟;

例如,我们需要使能IO端口B时钟使能,那么怎么设置呢?

__HAL_RCC_GPIOB_CLK_ENABLE();

综上过程,__HAL_RCC_GPIOX_CLK_ENABLE();宏定义的作用就是打开GPIO端口的时钟;

2.2 设置工作模式 

        设置工作模式:HAL_GPIO_Init 函数;

        那么同样我们对于HAL_GPIO_Init函数进行分析;

对其两个参数进行分析:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)

这两个参数都为结构体指针:

结构体指针1:GPIO_TypeDef其定义格式为:

主要包括一些相关的寄存器,当然库函数已经给我们定义了这些结构体变量,例如下面的:

因此也将GPIOA成为GPIO寄存器基地址

结构体指针2:GPIO_InitTypeDef其定义格式为:

该结构体变量需要在使用时对其结构体成员进行定义使用,包括端口的引脚、工作模式、上

下拉方式以及输出速度;

PIN:引脚设置;

Mode:(工作模式)

Pull上下拉

Speed:速度

例如,我们需要初始化IO端口B的引脚5,使其工作在推挽模式,输出速度为低速,那么怎么设置呢?

GPIO_InitTypeDef  gpio_init_struct;//定义第二个结构体变量

设置变量对应的结构体成员;

gpio_init_struct.Pin=GPIO_PIN_5;
gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW ; 

HAL_GPIO_Init(GPIOB,&gpio_init_struct);、、引用初始化函数;

2.3 设置输出状态

        设置输出状态:HAL_GPIO_WritePin 函数;

        下面我们对于Wirte_Pin进行分析:同样利用右键或者查找找到对应函数的定义;

该函数共有三个参数:GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState

参数1:GPIO_TypeDef *GPIOx结构体指针类型,与设置工作模式的参数相同,此处不进行详述;

参数2:uint16_t GPIO_Pin与上文所说的PIN引脚的相同,选择端口对应的引脚;

参数3:GPIO_PinState此处为一个枚举类型,GPIO的状态0/1;

例如,我们需要设置PB6=1,那么怎么设置呢?

参数1为:GPIOB;参数2为:GPIO_PIN_1;参数3为:GPIO_PIN_SET;   

        HAL_GPIO_TogglePin 函数;

        下面对于TogglePin 函数进行详述:

该函数有两个参数:GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin

参数1:GPIO基地址,上文已经提及;

参数2:GPIO_Pin,端口引脚上文已经提及;

例如,我们需要设置PB10进行翻转,那么怎么设置呢?

参数1为:GPIOB;参数2为:GPIO_PIN_10;

2.4 读取输入状态

        HAL_GPIO_ReadPin函数;

        下面我们对于ReadPin函数进行详述:

该函数共有三个参数:GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin这两个参数上文已经介绍过,分别为:GPIO基地址、GPIO_Pin,端口引脚;

该函数的返回值为枚举类型,返回0代表IO为低电平,返回1代表IO为高电平;

例如,我们需要知道PB10的输入,那么怎么设置呢?

参数1为:GPIOB;参数2为:GPIO_PIN_10;在设置一个变量,将返回值赋值给变量即可;

三、硬件设计

3.1 实现功能

        使STM32F1精英版上的LED1进行闪烁;

3.2 原理图

        在该开发板上的LED灯共有3个,本次我们点亮LED0,即对PB5进行操作;我们看到LED0右端接的是VCC3.3V,那么只需要控制左端的PB5输出即可;(输出0点亮,输出1熄灭);

        我们还需要确定GPIOB_5的工作模式;我们采用推挽输出,其特点为:输出引脚电平,高电平为VDD,低电平为VSS;

四、程序设计

4.1 程序流程图

4.2 程序分析

 4.2.1 led驱动函数

void led_init(void)//led初始化函数
{

     __HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟
    GPIO_InitTypeDef  gpio_init_struct; //定义结构体变量  
    gpio_init_struct.Pin=GPIO_PIN_5;//定义结构体变量成员-引脚号为5;
    gpio_init_struct.Mode=GPIO_MODE_OUTPUT_PP;//定义结构体变量成员-工作模式为推挽输出;
    gpio_init_struct.Speed=GPIO_SPEED_FREQ_LOW ; 定义结构体变量成员-输出速度为低速;
    HAL_GPIO_Init(GPIOB,&gpio_init_struct);//引用GPIO初始化函数;
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);//引用GPIO写入函数;

}

4.2.2 main函数

int main(void)
{
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);      /* 设置时钟, 72Mhz */
    delay_init(72);                          /* 延时初始化 */
    led_init();                              /* LED初始化 */
    
    while(1)
    { 
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);    /* PB5置1 */ 
        delay_ms(500);
        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);  /* PB5置0 */
        delay_ms(500); 
    }
}

五、实验现象

32点灯

总结:本文主要讲解了如何在STM32开发板上点亮第一个LED灯,主要的学习内容为:GPIO配置步骤以及GPIO相关的寄存器,硬件设计部分入代码分析;尽管本文的代码难度不难,但是对于程序中相关函数以及寄存器的理解,对于初学者还有一定的难度因此还是需要多加练习;

创作不易,还请大家多多点赞支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1297556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2020年第九届数学建模国际赛小美赛A题自由泳解题全过程文档及程序

2020年第九届数学建模国际赛小美赛 A题 自由泳 原题再现: 在所有常见的游泳泳姿中,哪一种最快?哪个冲程推力最大?在自由泳项目中,游泳者可以选择他们的泳姿,他们通常选择前面的爬行。然而,游泳…

知识付费商城7.0.3开心学习版,新增供货商功能

彩虹知识付费商城7.0.3小森升级版新增供货商开心学习版。仅供开发参考,切勿正式商用。 1.新增邮件提醒功能,支持给用户发订单、结算等邮件通知 2.支持给管理员发送提现、域名审核等邮件通知 3.支持设置手续费最低扣除金额 4.修复了其他一些已知问题 …

激光雷达标定板提高扫地机器人感知环境能力和清洁效率

智能扫地机器人的激光雷达标定板是一种用于校准激光雷达的设备,它通常由不同反射率的涂料涂覆在板面上,用于接收激光雷达发出的激光束并将其反射回来,从而帮助校准激光雷达的测量参数。在自动驾驶和机器人领域,激光雷达和相机的联…

HDFS Java API 基本操作实验

文章目录 一、实验环境二、实验内容(一)数据准备(二)编程环境准备(三)使用Hadoop API操作HDFS文件系统(四)使用Hadoop API Java IO流操作HDFS文件系统 三、实验步骤(一&…

OpenCL学习笔记(二)手动编译开发库(win10+vs2019)

前言 有时需求比较特别,可能需要重新编译opencl的sdk库。本文档简单记录下win10下,使用vs2019编译的过程,有需要的小伙伴可以参考下 一、获取源码 项目地址:GitHub - KhronosGroup/OpenCL-SDK: OpenCL SDK 可以直接使用git命令…

【剑指offer|图解|数组】寻找文件副本 + 螺旋遍历二维数组

🌈个人主页:聆风吟 🔥系列专栏:数据结构、剑指offer每日一练 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 一. ⛳️寻找文件副本(题目难度:简单)1.1 题目1.2 示例1.3 限制1.4 解题思路一c代…

2023年第一次系统架构师备考经历

12月7日成绩已经出来,果然最担心的案例分析没有过,才考了40分,还差5分,我就可以一次拿下证书,好可惜啊,今年的题出的有点偏,大数据居然成为必答题,平常是搞Java,大数据方…

做抖店代发,新手如何定类目?五大类目优缺点分析!

我是电商珠珠 类目是店铺的方向,只有将店铺的定位确定好,才能超越大部分的同行。 我经常跟我的学生讲,选择类目的时候不能瞎选,要学会去分析市场,由于大部分的学员前期都是新手小白,所以我们这边会负责给…

[Linux] 用LNMP网站框架搭建论坛

一、nginx在其中工作原理 原理: php-fpm.conf是控制php-fpm守护进程 它是php.ini是一个php解析器 工作过程: 1.当客户端通过域名请求访问时,Nginx会找到对应的虚拟主机 2. Nginx将确定请求。 对于静态请求,Nginx会自行处理…

听GPT 讲Rust源代码--src/tools(9)

File: rust/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs 在Rust源代码中,apply_demorgan.rs文件位于rust-analyzer工具的ide-assists库中,其作用是实现一个辅助函数,用于在代码中应用De Morgan定律的变换。 …

第二十一章网络通信总结

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信,必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称,是一种网络协议。Internet 网络采用的协议是TCP/IP协议,其全称是Transmissi…

SPRD Android 13 下拉状态栏菜单添加静音快捷键简单记录

SPRD Android 13 下拉状态栏菜单添加静音快捷键简单记录 需要修改文件具体修改补丁吐槽需要修改文件 frameworks/base/packages/SystemUI/res/values/config.xml frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java frameworks/base…

Java入门 EditPlus的安装与配置讲解

写Java程序不建议使用EditPlus,首选idea社区版,其次是vscode, 然后是eclipse 。editplus说实话排不上号。 但既然小伙伴想了解一下怎么配置,这里就简单说一下。 下载 首先是jdk,jdk是Java开发和运行的基础&#xff…

【MySQL】:库的操作

库的操作 一.创建和删除数据库二.认识编码三.库的查改1.查找2.修改 四.库的备份和恢复1.备份2.还原 一.创建和删除数据库 说明: 1.大写的表示关键字 2.[] 是可选项 3.CHARACTER SET: 指定数据库采用的字符集 4.COLLATE: 指定数据库字符集的校验规则首先进入MySQL 创…

Verilog基础:时序调度中的竞争(三)(#0的使用)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 作为一个硬件描述语言,Verilog HDL常常需要使用语句描述并行执行的电路,但其实在仿真器的底层,这些并行执行的语句是有先后顺…

IDEA构建springBoot新项目时JDK只有17和21,无法选择JDK8解决方案

今天创建springboot新项目时,发现IDEA里JDK选项只有17和21,无法选择本机的JDK8,网上查资料后发现是springboot2.7于11.24号后停止维护,基于2.7和java8的spring Initializ官方不再维护,解决方案是在server URL栏&#x…

【WebRTC】【Unity】Unity Web RTC1-Unity中简单实现远程画面

【项目资源下载】 本篇配套直接打开可用的项目包地址,欢迎下载: https://download.csdn.net/download/weixin_41697242/88612084 【背景】 想要在Unity中实现实时远程桌面,找到了Render Streaming这个手段,本篇介绍相应的使用方法和实例。 【要求】 平台:基本常用平台…

【数据结构和算法】--- 栈

目录 栈的概念及结构栈的实现初始化栈入栈出栈其他一些栈函数 小结栈相关的题目 栈的概念及结构 栈是一种特殊的线性表。相比于链表和顺序表,栈只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的…

机器人学习目标

学习目标: 若干年后,我们都将化为尘土,无人铭记我们的存在。那么,何不趁现在,尽己所能,在这个世界上留下一些痕迹,让未来的时光里,仍有人能感知到我们的存在。 机器人协会每届每个阶…

【开源】基于Vue.js的公司货物订单管理系统

文末获取源码,项目编号: S 082 。 \color{red}{文末获取源码,项目编号:S082。} 文末获取源码,项目编号:S082。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供…