【STM32学习笔记】(6)—— 跑马灯实验详解
跑马灯实验
在前面五篇STM32学习笔记中,我们已经初步认识了STM32芯片,并且了解STM32的常用寄存器,介绍了STM32的GPIO模式,STM32工程文件,以及最终讲解了如何为STM32添加源文件和头文件的步骤。但是前面的知识只能说是理论和基础,但是非常重要,如果STM32的理论知识不扎实,会导致后续开发项目时,总感觉自己并没有学过STM32,就好像自己是一片浮萍一直飘在水上。所以我们在开发STM32的项目过程中,遇到需要学习一些理论知识时,一定要好好补充自己的理论知识,在实践中检验自己学习的效果是最好的。
GPIO简介
GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引 脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103ZET6 型号的芯片有 GPIOA、GPIOB、 GPIOC、GPIOD、GPIOE、GPIOF 至 GPIOG 共 7组 GPIO,芯片一共 144 个引脚,其中 GPIO 就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。
最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO 引脚接入 到 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或 三极管控制外部大功率电路的通断。
最基本的输入功能是检测外部输入电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是 否被按下。
在固件库中,GPIO 端口操作对应的库函数函数以及相关定义在文件 stm32f10x_gpio.h 和 stm32f10x_gpio.c 中。
跑马灯硬件连接
本章用到的硬件只有 LED(DS0 和 DS1)。其电路在 ALIENTEK 精英 STM32F103 开发板 上默认是已经连接好了的。DS0 接 PB5,DS1 接 PE5。所以在硬件上不需要动任何东西。因为LED发光二极管的阳极串联一个510欧姆的电阻与VCC3.3V电源相连。LED发光二极管的阴极与芯片的GPIO口相连。如果要控制发光二极管发光,则需要将GPIO输出的电平拉低,发光二极管就会由电流流过,电流流过时发光二极管就发光了。当两个GPIO输出的电平不断的拉高或者拉低就能实现了LED的闪烁或者跑马灯的效果了。
注意:510欧姆的电阻一共有两个作用:
- 起到上拉电阻的作用。
- 起到限流电阻的作用,防止流过发光二极管的电路过大而烧坏。
跑马灯程序设计
跑马灯实验我们主要用到的固件库文件是:
stm32f10x_gpio.c stm32f10x_gpio.h
stm32f10x_rcc.c stm32f10x_rcc.h
misc.c misc.h
stm32f10x_usart stm32f10x_usart.h
其中 stm32f10x_rcc.h 头文件在每个实验中都要引入,因为系统时钟配置函数以及相关的外设时 钟使能函数都在这个其源文件 stm32f10x_rcc.c 中。stm32f10x_usart.h 和 misc.h 头文件在我们 SYSTEM 文件夹中都需要使用到,所以每个实验都会引用。
跑马灯所要用到的库函数
1、一个GPIO初始化函数
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);/*作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO 口的4个配置寄存器。*/
typedef struct
{uint16_t GPIO_Pin; //指定要配置的 GPIO 引脚。此参数可以是 @ref GPIO_pins_define的任何值 GPIOSpeed_TypeDef GPIO_Speed; //指定所选引脚的速度。此参数可以是ref GPIOSpeed_TypeDef成员变量中的值GPIOMode_TypeDef GPIO_Mode; //指定选定引脚的工作模式。此参数可以是 GPIOMode_TypeDef成员变量中的值
}GPIO_InitTypeDef;#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */#define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || \((PIN) == GPIO_Pin_1) || \((PIN) == GPIO_Pin_2) || \((PIN) == GPIO_Pin_3) || \((PIN) == GPIO_Pin_4) || \((PIN) == GPIO_Pin_5) || \((PIN) == GPIO_Pin_6) || \((PIN) == GPIO_Pin_7) || \((PIN) == GPIO_Pin_8) || \((PIN) == GPIO_Pin_9) || \((PIN) == GPIO_Pin_10) || \((PIN) == GPIO_Pin_11) || \((PIN) == GPIO_Pin_12) || \((PIN) == GPIO_Pin_13) || \((PIN) == GPIO_Pin_14) || \((PIN) == GPIO_Pin_15))
在GPIO_InitTypeDef结构体中已经初始化了成员变量,所以在调用这个初始化函数的时候,我们首先需要对GPIO_InitTypeDef成员变量进行赋值。
但是在给GPIO_InitTypeDef的成员变量赋值时,赋值的内容需要满足对应结构体类型中的成员变量。
例如:GPIO_Mode的结构类型为GPIOMode_TypeDef,那么给它赋值的内容需要在下面这个结构体当中的成员变量中选择。
typedef enum
{ GPIO_Mode_AIN = 0x0,GPIO_Mode_IN_FLOATING = 0x04,GPIO_Mode_IPD = 0x28,GPIO_Mode_IPU = 0x48,GPIO_Mode_Out_OD = 0x14,GPIO_Mode_Out_PP = 0x10,GPIO_Mode_AF_OD = 0x1C,GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;#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_Speed的结构类型为GPIOSpeed_TypeDef,那么给它赋值的内容需要在下面这个结构体当中的成员变量中选择。
typedef enum
{ GPIO_Speed_10MHz = 1,GPIO_Speed_2MHz, GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \((SPEED) == GPIO_Speed_50MHz))
2、两个读取输入电平函数
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、GPIO_Pin:指定要读取的端口位。
返回值:输入端口引脚值。
例如:
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
参数:GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
返回值:GPIO输入数据端口值。
例如:
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平
3、两个读取输出电平函数
uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、GPIO_Pin:指定要读取的端口位。
返回值:输出端口引脚值。
例如:
GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。
参数:GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
返回值:GPIO输入数据端口值。
例如:
GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平
4、四个设置输出电平函数
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为高电平(1)。实际操作BSRRL寄存器
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、GPIO_Pin:指定要写入的端口位。
返回值:无
例如:
GPIO_SetBits(GPIOA, GPIO_Pin_5);//将GPIOA.5的输出电平置为高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
作用:设置某个IO口输出为低电平(0)。实际操作的BSRRH寄存器。
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、GPIO_Pin:指定要写入的端口位
返回值:无
例如:
GPIO_ResetBits (GPIOA, GPIO_Pin_5);//将GPIOA.5的输出电平置为低电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
作用:设置或清除选定的数据端口位。
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、GPIO_Pin:指定要写入的端口位。
3、BitVal:指定要写入选定位的值。
此参数可以是BitAction枚举值之一:
typedef enum
{
Bit_RESET = 0, // Bit_RESET:清除端口引脚
Bit_SET // Bit_SET:设置端口引脚
}BitAction;
#define IS_GPIO_BIT_ACTION(ACTION) (((ACTION) == Bit_RESET) || ((ACTION) == Bit_SET))
返回值:无
例如:
GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);// 清除GPIOA.5数据端口位。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
作用:将数据写入指定的GPIO数据端口。
参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。
2、PortVal:指定要写入端口输出数据寄存器的值。。
返回值:无
例如:
GPIO_Write(GPIOA,1);将数据“1”写入指定的GPIOA数据端口。
后面两个函数不常用,也是用来设置IO口输出电平。
跑马灯实验编写步骤:
1. 使能IO口时钟。调用函数RCC_AHB1PeriphClockCmd();
不同的外设调用的时钟使能函数可能不一样
2. 初始化 GPIO 口模式。调用GPIO_Init();把目标引脚为推挽输出模式;
3. 编写简单测试程序,控制 GPIO 引脚输出高、低电平。
跑马灯源文件和头文件中的代码
头文件中的代码:
#ifndef __LED_H
#define __LED_Hvoid LED_Init(void);//初始化#endif
源文件中的代码:
#include "led.h"//初始化PB5和PE5为输出口.并使能这两个口的时钟
//LED IO初始化
void LED_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 端口配置, 推挽输出GPIO_Init(GPIOE, &GPIO_InitStructure); //推挽输出 ,IO口速度为50MHzGPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}
跑马灯实验中主函数里的代码
main.c里的代码:
#include "delay.h"
#include "led.h"
/*******************下面注视的代码是通过调用库函数来实现IO控制的方法*****************************************
int main(void)
{ delay_init(); //初始化延时函数
LED_Init(); //初始化LED端口
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5); //LED0对应引脚GPIOB.5拉低,亮
GPIO_SetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉高,灭
delay_ms(300); //延时300ms
GPIO_SetBits(GPIOB,GPIO_Pin_5); //LED0对应引脚GPIOB.5拉高,灭 GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮
delay_ms(300); //延时300ms}
}
函数执行流程如下:
(1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。
(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能 LED 灯的 GPIO 端口时钟,这个函数的调用和详细资料会在后面的一篇文章讲解。
(3) 向 GPIO 初始化结构体赋值,把引脚初始化成推挽输出模式。
(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化。
(5)紧接着使用两个GPIO的库函数,分别为GPIO_SetBits()与GPIO_ResetBits(),在置低和置高之间延时一段时间,就实现了两个LED灯轮流转的实验目的。
【STM32学习笔记】(6)—— 跑马灯实验详解相关推荐
- 【stm32】stm32学习笔记(江科大)-详解stm32获取Mpu6050陀螺仪和加速度
目录 I2C 起始条件: 终止条件: 发送一个字节 接收一个字节 接收发送应答 代码 I2C I2C.C I2C.h Mpu6050 Mpu6050.c Mpu6050.h Mpu6050Reg.h ...
- JDBC学习笔记02【ResultSet类详解、JDBC登录案例练习、PreparedStatement类详解】
黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...
- 小猫爪:i.MX RT1050学习笔记26-RT1xxx系列的FlexCAN详解
i.MX RT1050学习笔记26-RT1xxx系列的FlexCAN详解 1 前言 2 FlexCAN简介 2.1 MB(邮箱)系统 2.1.1 正常模式下 2.1.2 激活了CAN FD情况下 2. ...
- IP地址和子网划分学习笔记之《IP地址详解》
在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. IP地址和子网划分学习笔记相关篇章: 1.I ...
- 我的学习笔记——CSS背景渐变(Gradients)详解
我的学习笔记--CSS背景渐变(Gradients)详解 一.线性渐变(Linear Gradients) 1.语法 background-image: linear-gradient(directi ...
- IP地址和子网划分学习笔记之《子网划分详解》
一,子网划分概述 IP地址和子网划分学习笔记相关篇章: 1.IP地址和子网划分学习笔记之<预备知识:进制计数> 2.IP地址和子网划分学习笔记之<IP地址详解> 3.IP地址和 ...
- Apollo星火计划学习笔记——Apollo决策规划技术详解及实现(以交通灯场景检测为例)
文章目录 前言 1. Apollo决策技术详解 1.1 Planing模块运行机制 1.2 Apollo决策功能的设计与实现 1.2.1参考路径 Reference Line 1.2.2 交规决策 T ...
- 对联智能生成的原理(学习笔记附代码实现与详解)
文章均从个人微信公众号" AI牛逼顿"转载,文末扫码,欢迎关注! 过年的脚步越来越近,是不是该给家里贴上一副对联呢?除了买买买,有没有想过自己动手写出一副对联?来吧,撸起袖子加油干 ...
- redis学习笔记(2)之redis主从详解
redis主从详解 主从详解 主从配置 拓扑 原理 数据同步 概念 复制偏移量 复制积压缓冲区 主节点运行ID Psync命令 全量复制流程 部分复制流程 心跳 缓冲大小调节 读写分离 内容来源为六星 ...
- redis学习笔记(7)之redis哨兵详解
redis哨兵详解 sentinel命令 客户端连接 素材代码 思路 实现过程 哨兵的切换实现原理 发布订阅基础 哨兵的实现原理 部署建议 需要关注的问题 代码流程 内容来源为六星教育,这里仅作为学习 ...
最新文章
- linux运维第二讲
- 附pdf下载 | 入门深度学习和GAN的几本书
- 《CUDA C编程权威指南》——3.4 避免分支分化
- Linux crontab 定时任务设置
- dataguard如何实现切换_深度干货 | 如何借助云原生搞定Oracle备份快速恢复?
- mysql5.7存储json_MySQL5.7的json数据格式的问题
- 给apm换一个软件源
- 太赞了!终于有人把怎么在IDEA中使用Java热部署插件JRebel讲清楚了...
- 1小时教你理解HTTP,TCP,UDP,Socket,WebSocket
- PgSQL · 特性分析 · 金融级同步多副本分级配置方法
- 关于HTTP缓存的故事
- 日历2021年日历表|2021年日历表打印版 Excel版
- gbdt算法_双色球最简单的算法
- 高分三号卫星(GF-3)简介
- 沃特世推出SELECT SERIES MRT多反射飞行时间质谱平台,树立高分辨质谱性能新标杆
- VC之获取计算机网卡mac地址
- linux系统如何安装bt5,BT5硬盘安装(多系统linux + win + BT5)
- 冰冻三尺非一日之寒-自学篇 浅谈个人学习方法(转载)
- 天津大学计算机学院李晨曦,【奋斗·青春】最美的不是牵手,而是携手一起读研——访保研至天津大学的情侣学霸许贤哲与李晨曦...
- test 4:假币问题