社团作业=_=

开发版上的LED灯负极连接在PB5口,正极串联一510Ω电阻后与3.3V相连

若开发板不带LED灯则需要自行连接,务必串联一个合适的电阻防止LED灯烧坏

零、一个有趣的延时函数

来自于开发板配套资料当中的例程,第一次看到的时候觉得耳目一新,代码如下:

void Delay(u32 count)
{
u32 i = 0;
for (; i < count; i++)
;
}

当中的u32类型是在stm32f10x.h当中的一个宏定义,对应uint32_t,表示32位无符号型整数,在我的开发板当中就是unsigned int类型。

因为STM32的主频比电脑CPU慢得多,因此可以通过这种循环的方式来达到延时的效果

一、库函数版本

1.初始化

以下是初始化PB5端口的代码

// 定义一个类型为GPIO_InitTypeDef,名字叫做GPIO_InitStructure的结构体
GPIO_InitTypeDef GPIO_InitStructure; // PORTB时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置结构体GPIO_InitStructure
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 设置GPIO端口号为5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置端口模式为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置输出速率为50MHz // 初始化
GPIO_Init(GPIOB, &GPIO_InitStructure); // 传入的是结构体的指针

初始化流程:

时钟使能 → 创建一个含端口配置信息的结构体 → 使用该结构体初始化

初始化其它端口:

以PC2端口为例,将以上代码中的两处GPIOB改为GPIOC,把GPIO_Pin_5改为GPIO_Pin_2即可

端口模式(STM32有8种):

输入浮空(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)

其他的目前没弄懂,反正设置为推挽输出模式就能够输出高/低电平了

启动同组的多个端口:

例如要同时启用PB5,PB6端口,

第一种方案是在上面的代码之后添加以下内容

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOB, &GPIO_InitStructure);

因为PB5、PB6同属于GPIOB组,GPIOB的时钟已经使能且PB6端口的其他配置和PB5端口相同,因此改变结构体的端口号之后再次执行初始化函数即可

第二种方案是将上述代码当中的

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

改为

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;

因为GPIO_Pin_0 ~ GPIO_Pin_15分别对应二进制数1、10、100、……,因此使用位运算当中的或运算即可将两个参数叠加起来

同理,如果想同时使能PORTA、PORTB、PORTC时钟,则可将

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

改为

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);

2.使用

由于PB5口与LED负极相连,因此仅当输出低电平时LED灯才会亮

设置为低电平:

GPIO_ResetBits(GPIOB, GPIO_Pin_5);

设置为高电平:

GPIO_SetBits(GPIOB, GPIO_Pin_5);

3.闪灯例程

代码如下:

void Delay(unsigned int count)
{
unsigned int i = 0;
for (; i < count; i++)
;
}
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); while (1)
{
GPIO_SetBits(GPIOB, GPIO_Pin_5);
Delay(10000000); GPIO_ResetBits(GPIOB, GPIO_Pin_5);
Delay(10000000);
}
}

Delay()当中的数字可根据实际设备的运行频率做相应调整

二、寄存器版本(建议和库函数版本对比异同)

这一部分需要对C语言的位运算有一定的了解

0.寄存器

以下是需要用到的寄存器,通过查询STM32中文参考手册7.3和8.2可获得更加详细的信息。

RCC寄存器:

APB2外设时钟使能寄存器(RCC->APB2ENR)

GPIO寄存器:

端口配置低寄存器(GPIOx->CRL)(x=A..E)

端口输出数据寄存器(GPIOx->ODR)(x=A..E)

端口位设置/清除寄存器(GPIOx->BSRR)(x=A..E)

端口位清除寄存器(GPIOx->BRR)(x=A..E)

1.初始化

以下是初始化PB5端口的代码

// PORTB时钟使能
RCC->APB2ENR |= 1<<3; //将寄存器APB2ENR的第3位(与PORTB对应)设为1 // 初始化
GPIOB->CRL &= 0XFF0FFFFF; //清空寄存器CRL第20~23位(Pin5对应的参数)
GPIOB->CRL |= 0X00300000; //将寄存器CRL第20~23位设为0011

初始化流程:

时钟使能 → 直接通过配置寄存器来初始化端口

关于APB2ENR寄存器:

APB2ENR寄存器的各位描述如下:

由图可知,若需要使能PORTC时钟,则需要以下代码

RCC->APB2ENR |= 1<<4;

与库函数版本类似,若需要同时使能PORTA、PORTB、PORTC时钟,则需要以下代码

RCC->APB2ENR |= (1<<3) | (1<<4) | (1<<5);

关于CRL寄存器:

CRL寄存器的各位描述如下:

当中的每4个位对应1个输出端口,查阅资料可得推挽输出对应的配置位(CNFx)为00,50MHz输出速率对应的模式位(MODEx)为11

4个位(bit)刚好与1个16进制数相对应(2^4 = 16^1 = 16),因此一个16进制数0 ~ F刚好对应了一个GPIO端口的配置。二进制数0011对应的16进制数为0x3,因此上面的初始化代码可将寄存器的第20 ~ 23位设为0011

同理,若需要将PC2口初始化为推挽输出+50MHz输出速率,则需要以下代码

GPIOC->CRL &= 0XFFFFF0FF;  //清空CRL寄存器第8~11位(Pin2对应的参数)
GPIOC->CRL |= 0X00000300; //将CRL寄存器第8~11位设为0011

设置Pin0 ~ Pin7时用的是CRL寄存器,设置Pin8 ~ Pin15时用的是CRH寄存器,CRH寄存器的各位描述如下,具体设置方法和CRL寄存器类似

2.使用(多种方案)

2.1 传统操作
2.1.1 通过ODR寄存器操作(麻烦)

ODR名为端口输出数据寄存器,向其0 ~ 16位写入1则对应端口为高电位,反之为低电位

这种特性意味着每次设置ODR寄存器需要给出0号端口 ~ 15号端口的高低电位

由于看开发手册没有看全,首先想到的是这种操作方法

设置为低电平:

GPIOB->ODR &= 0xffffffff-(1<<5); //将第5位(bit)清空
GPIOB->ODR |= 0<<5; //将第5位(bit)设置为0

设置为高电平:

GPIOB->ODR &= 0xffffffff-(1<<5); //将第5位(bit)清空
GPIOB->ODR |= 1<<5; //将第5位(bit)设置为1
2.1.2 通过BSRR/BRR寄存器操作(简单)

BSRR名为端口位设置/清除寄存器,向其0 ~ 16位(bit)写入1则ODR对应位变为1(对应端口为高电位),写入0则不变

BRR名为端口位清除寄存器,向其0 ~ 16位写入1则ODR对应位变为0(对应端口为低电位),写入0则不变

后期发现了这种更方便的操作方法

设置为低电平:

GPIOB->BRR &= 1<<5; //将第5位设置为0

设置为高电平:

GPIOB->BSRR &= 1<<5; //将第5位设置为1

不使用BRR寄存器,仅使用BSRR寄存器来将ODR对应位变为0/1也是可以的,具体方法可参考STM32中文参考手册8.2.5

2.2 位带操作

个人理解是通过访问一个32位长度的地址区间(类似于直接操作一个unsigned int)来达到访问1个位的效果,各种资料上说这种操作更优越

具体实现还不会,不过开发板资料中有现成的头文件可供使用

头文件中的代码如下:

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n)
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n)
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n)
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n)
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n)
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n)
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n)

include该头文件后即可使用位带操作

设置为低电平:

PBout(5) = 0;

设置为高电平:

PBout(5) = 1;

3.闪灯例程

代码如下:

void Delay(unsigned int count)
{
unsigned int i = 0;
for (; i < count; i++)
;
}
int main(void)
{
RCC->APB2ENR |= 1<<3; GPIOB->CRL &= 0XFF0FFFFF;
GPIOB->CRL |= 0X00300000; while (1)
{
GPIOB->BSRR = 1<<5;
Delay(10000000); GPIOB->BRR = 1<<5;
Delay(10000000);
}
}

Delay()当中的数字可根据实际设备的运行频率做相应调整

2019.11.28

STM32基本GPIO操作:点灯(库函数+寄存器)的更多相关文章

  1. STM32基本GPIO操作:按键输入(扫描+外部中断)

    (涉及专有名词较多,难免解释不到位,若有错误还请指出,谢谢!) 硬件连接图如下: 一.扫描 思路是在main函数中通过死循环来扫描端口电平状态检测,以此判断按键是否按下.实现较为简单. 1.初始化(注 ...

  2. STM32之GPIO操作

    啊哈.没办法.外国人的芯片就喜欢用英文来命名,所以中文的:通用输入/输出  就用GPIO来代替..谁叫哥们都不是外国人呢.好啦.胡扯了一下,借用唐伯虎点秋香的话:小小书童,可笑可笑... 知道了GPI ...

  3. STM32 常用GPIO操作函数记录

    STM32读具体GPIOx的某一位是1还是0 /** * @brief Reads the specified input port pin. * @param GPIOx: where x can ...

  4. STM32标准库GPIO操作

    STM32标准库GPIO操作 STM32任何外围设备的使用都分为两部分:初始化和使用.体现在代码上就是:(1)有一个初始化函数(2)main函数中的使用 1.初始化GPIO 初始化GPIO函数代码: ...

  5. STM32的GPIO使用的函数剖析

    转载http://blog.csdn.net/wuwuhuizheyisheng/article/details/8239599 STM32的GPIO总结 作者:JCY 该文是自己学习了一段STM32 ...

  6. STM32的GPIO口的输出开漏输出和推挽输出

    本文来自cairang45的博客,讲述了STM32的GPIO口的输出开漏输出和推挽输出, 作者博客:http://blog.ednchina.com/cairang45 本文来自: 高校自动化网(Ww ...

  7. stm32之GPIO(二)

    输入上拉:当IO口作为输入时,比如按键输入,而按键是与地连接,按下时为低电平,则没按下时该IO口应为高电平,上拉即是该IO口通过一个电阻与电源相连,则没按下时为高电平,按下即为低电平. 输入下拉:同理 ...

  8. esp32的GPIO操作

    对于任何一款芯片,GPIO接口是其最基本的组成部分,也是一款芯片入门的最基本操作,下面论述下 关于esp32开发版的GPIO操作,本文中重点讲解下 关于如何创建eclipse工程,并通过eclipse ...

  9. sys下gpio操作

    gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射 * 控制GPIO的目录位于/sys/class/gpio * /sys/class/gpio/export文 ...

随机推荐

  1. unity3d 柏林噪声 PerlinNoise 规律 算法

    测试 每个小数值取100次 print(0.1); LaTest3(0.1f, 0.1f); print("Max:" + La.Max() + "|Min:" ...

  2. nmap扫描、信息收集(网安全实训第一天)

    本期内容:网站信息收集.nmap扫描 1. 信息收集 2. nmap扫描1.信息收集 (1)确定目标 首先,我们确定攻击目标,在这里,我们随便找一个网站做测试,我以码云为例. (2)nslookup查 ...

  3. ueEditor第一次赋值失败

    var ue=null; //在初始化富文本的地方 if (ue == null) { ue = new baidu.editor.ui.Editor(); ue.render('inspection ...

  4. windows10查看电脑已经保存的wifi密码

    1,打开windows的命令窗口,输入    netsh wlan show profiles,如下图,这个命令仅仅只是查看一下电脑保存的所有的wifi名字 2,需要查看密码的话,则需要输入这个命令, ...

  5. hdu 1394 Minimum Inversion Number (树状数组求逆序对)

    The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that ...

  6. CSRF与auth模块

    目录 一.模拟实现中间件的编程思想 (一)impotlib模块 (二)实现功能的配置使用 二.跨站请求伪造CSRF (一)由来 (二)form表单的CSRF (三)ajax中的CSRF (1)通过da ...

  7. 《Java基础知识》Java锁详解(volatile,synchronized等)

    volatile: 让变量每次在使用的时候,都从主存中取. volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执 ...

  8. scrapy框架(二)

    scrapy框架(二) 一.scrapy 选择器 概述: Scrapy提供基于lxml库的解析机制,它们被称为选择器. 因为,它们“选择”由XPath或CSS表达式指定的HTML文档的某部分. Sca ...

  9. 转自自己的关于落谷计数器【p1239】的题解

    本蒟蒻写这道题用了两天半里大概五六个小时.(我太弱了) 然后这篇题解将写写我经历的沟沟坎坎,详细的分析一下, 但是由于它很长,因此一定还有多余的地方,比如说我的 预处理,可能比较多余.但是我觉得,信息 ...

  10. Vue之使用JsonView来展示Json树

    前两天干活儿有个需求,在前端需要展示可折叠的Json树,供开发人员查看,这里采用JsonView组件来实现,它是一款用于展示Json的Vue组件,支持大体积的Json文件快速解析渲染,下面记录一下实现 ...