STM32基本GPIO操作:点灯(库函数+寄存器)
社团作业=_=
开发版上的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操作:点灯(库函数+寄存器)的更多相关文章
- STM32基本GPIO操作:按键输入(扫描+外部中断)
(涉及专有名词较多,难免解释不到位,若有错误还请指出,谢谢!) 硬件连接图如下: 一.扫描 思路是在main函数中通过死循环来扫描端口电平状态检测,以此判断按键是否按下.实现较为简单. 1.初始化(注 ...
- STM32之GPIO操作
啊哈.没办法.外国人的芯片就喜欢用英文来命名,所以中文的:通用输入/输出 就用GPIO来代替..谁叫哥们都不是外国人呢.好啦.胡扯了一下,借用唐伯虎点秋香的话:小小书童,可笑可笑... 知道了GPI ...
- STM32 常用GPIO操作函数记录
STM32读具体GPIOx的某一位是1还是0 /** * @brief Reads the specified input port pin. * @param GPIOx: where x can ...
- STM32标准库GPIO操作
STM32标准库GPIO操作 STM32任何外围设备的使用都分为两部分:初始化和使用.体现在代码上就是:(1)有一个初始化函数(2)main函数中的使用 1.初始化GPIO 初始化GPIO函数代码: ...
- STM32的GPIO使用的函数剖析
转载http://blog.csdn.net/wuwuhuizheyisheng/article/details/8239599 STM32的GPIO总结 作者:JCY 该文是自己学习了一段STM32 ...
- STM32的GPIO口的输出开漏输出和推挽输出
本文来自cairang45的博客,讲述了STM32的GPIO口的输出开漏输出和推挽输出, 作者博客:http://blog.ednchina.com/cairang45 本文来自: 高校自动化网(Ww ...
- stm32之GPIO(二)
输入上拉:当IO口作为输入时,比如按键输入,而按键是与地连接,按下时为低电平,则没按下时该IO口应为高电平,上拉即是该IO口通过一个电阻与电源相连,则没按下时为高电平,按下即为低电平. 输入下拉:同理 ...
- esp32的GPIO操作
对于任何一款芯片,GPIO接口是其最基本的组成部分,也是一款芯片入门的最基本操作,下面论述下 关于esp32开发版的GPIO操作,本文中重点讲解下 关于如何创建eclipse工程,并通过eclipse ...
- sys下gpio操作
gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射 * 控制GPIO的目录位于/sys/class/gpio * /sys/class/gpio/export文 ...
随机推荐
- ARTS-S sed替换
网上有大量替换的例子,比如 sed 's/aaa/bbb/g' a.txt 其实分隔符可以用别的字符,比如#,所以下面的命令也是正确的 sed 's#aaa#bbb#g' a.txt 用#号在用环境变 ...
- 【ZooKeeper系列】1.ZooKeeper单机版、伪集群和集群环境搭建
ZooKeeper安装模式主要有3种: 单机版(Standalone模式)模式:仅有一个ZooKeeper服务 伪集群模式:单机多个ZooKeeper服务 集群模式:多机多ZooKeeper服务 1 ...
- 【MySQL】ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
问题现象: ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) 拒绝访问root用户 ...
- wxxcx_learn
32个字符组成的一组随即字符串 function getRandChar($length){ $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWX ...
- LNMP-Nginx配置不记录静态文件、过期时间
用户访问web网站,通常日志文件会记录很多web站点上的一些静态文件信息,如果长期不处理,日志文件会越来越大,占用的系统资源也越大,此时就需要我们配置不记录静态文件和过期时间,减少日志文件记录过多不必 ...
- Koa中间件(middleware)级联原理
前言 上次看到了koa-compose的代码,今天来说一下koa中间件的级联以及工作原理. 中间件工作原理 初始化koa实例后,我们会用use方法来加载中间件(middleware),会有一个数组来存 ...
- 深入浅出Object.defineProperty()
深入浅出Object.defineProperty() 红宝书对应知识点页码:139页 红宝书150页:hasOwnProperty( )方法可以检测一个属性是存在于实例中,还是存在于原型中,给定属性 ...
- CCF-CSP题解 201903-4 消息传递接口
求并行的各个进程,且进程内部顺序执行的情况下,会不会出现"死锁". 首先用\(%[^\n]\)将每个进程读入.最后过不了居然是因为\(str[\ ]\)开小了(悲喜交加.存储在\( ...
- 骚年,如果你还不懂一些java常识?中了奖也无法兑换
今天下午约着几个朋友一起去看叶问4,结果碰到了一个有趣的事情,正好和java有关所以写一篇文章来记录一下. 事件:我和朋友小李.小王一起去看电影 时间:2019/12/21 地点:H市某家电影院 起因 ...
- 【CV现状-3.2】纹理与材质
#磨染的初心--计算机视觉的现状 [这一系列文章是关于计算机视觉的反思,希望能引起一些人的共鸣.可以随意传播,随意喷.所涉及的内容过多,将按如下内容划分章节.已经完成的会逐渐加上链接.] 缘起 三维感 ...