1-GPIO
GPIO的配置:
GPIO库函数编程:
void LED_init(void)//LED初始化
{
GPIO_InitTypeDef GPIO_InitStructure;//定义一个结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //开相应的时钟--APB2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //GPIOC13
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //上拉方式
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
}
GPIO寄存器配置:
void LED_init(void)
{
RCC->APB2ENR|=<<;//开启时钟
GPIOA->CRH &=0xff3fffff;//推挽输出,50MHz //端口位设置/清除寄存器(GPIOx_BSRR) (x=A..E)
//向相应的BRR写1,则清零
//向相应的BSRR写1,则置位
GPIO->BRR |=<<; //点亮led
GPIO->BSRR |=<<;//关闭led //端口输出数据寄存器(GPIOx_ODR) (x=A..E)
//向相应的ODR写1,则置位
//GPIO->ODR &=~(1<<13); //点亮led
//GPIO->ODR |= (1<<13); //关闭led
//GPIOB->ODR ^= GPIO_Pin_13; //LED_Toggle
//库函数接口
//GPIO_ResetBits(GPIOC,GPIO_Pin_13);//点亮led
//GPIO_SetBits(GPIOC,GPIO_Pin_13);//关闭led
}
BRR:
BSRR:
ODR:
STM32的PA15、PB3、 PB4管脚作普通管脚的解决办法
使用的是SWD方式进行下载程序,仅仅使用到SWDIO(PA13) 和SWCLK(PA14)两个管脚。我将PA15(JTDI)和PB3(JTDO)管脚用于他用(用于点LED使用),发现无法将这两个管脚拉低:
我在网上搜到PA15,PB3,PB4这几个管脚默认功能不是普通管脚,需要进行重映射后,才能普通使用
解决办法:
GPIO_InitTypeDef GPIO_InitStructus; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);//RCC_APB2Periph_AFIO必须开启
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//改变映射(失能JTAG) GPIO_InitStructus.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructus.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructus.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructus); GPIO_InitStructus.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructus.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructus.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructus);
GPIO_SetBits(GPIOB,LED2);
其中:GPIO_Remap_SWJ_JTAGDisable 改变管脚映射(失能JTAG功能)
GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)
什么时候需要开复用时钟,RCC_APB2Periph_AFIO:
(1)使用EXTI
(2)重映射(用到外设的重映射功能时才需要使能AFIO的时钟)
举例:重映射USART2
USART2的TX/RX在PA.2/3.但是,PA.2已经被Timer2的channel3使用.这时,如果还想使用USART2,但又不想影响Timer2的使用,这就需要把USART2的TX/RX重映射到PD.5/6。
映射库函数的调用过程
(1)使能被重新映射到的I/O端口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
(2)使能被重新映射的外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
(3)使能AFIO功能的时钟(勿忘!)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
(4)进行重映射
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
GPIO位带的操作:
首先我们先简单回顾一下,51单片机怎样点亮一盏LED灯呢?
假设51单片机的P3^1口外挂一个带上拉电阻的LED灯,那么我们直接通过一个关键字sbit进行位定义,如:
#include<reg52.h> sbit P31 =P3^; //位定义
int main(void)
{
P31 =; //点亮led
P31 =; //关闭led
}
就可实现led的点亮,但是我们的stm32并没有这样的关键字哦。也就是它不能单独的对一个bit进行读写,那么我们怎样做呐?
可以通过对位带别名区进行访问进而实现对某一个位进行读写。
什么是位带?
将一个单元的某一位,通过简单的地址变换算法,映射到一个地址的空间(简称位带别名区),然后对地址进行操作,进而达到对位操作的效果。
为辅助大家理解,特此制作一张图:
PS: 要想对左边某位进行读写,只需对右边映射后的地址进行操作即可(将RAM和外设中的每一个bit映射到一个独立的地址,对这个地址的32位读写操作实现对一个bit的操作,就像51单片机中的位寻址区域一样)。
在stm32中能实现位带功能的也仅仅有两个地方(简称位带区):
sram区最低1MB空间-0x20000000--0x200fffff
外设区最低1MB空间 -0x40000000--0x400fffff
图:来源Cortex-M3权威指南Cn P84
这里两个位带区的每一位都可以分别通过映射算法映射到某一地址空间(这里的映射关系可以通过一个数学公式表达,即:映射算法=公式):
仅有限范围可以实现位绑定:
sram区: 0x2000 0000 ~0x200f ffff---1M—2^20
公式:AliasAddr =0x22000000 + ((A-0x20000000)*8+n)*4
=0x22000000 + (A-0x20000000)*32 + n*4
片上外设:0x4000 0000 ~0x400f ffff---1M
公式:AliasAddr =0x42000000 + ((A-0x40000000)*8+n)*4
=0x42000000 + (A-0x40000000)*32 + n*4
其中:
A --位带区某个bit所在的地址
0x20000000 --sram区的位带区的起始地址
0x40000000 --外设区的位带区的起始地址
AliasAddr --映射后的位带别名区
0x22000000 --sram区的位带别名区的起始地址
0x42000000 --外设位带别名区的起始地址
n --代表在A地址的第几位(范围:0--7,一个地址单元只有8位)
公式解读:
①这里的A-0x20000000或A-0x40000000代表的意思是,该地址之前有多少个地址单位,又因为STM32的一个地址单元对应8个bit位(一个字节),所以然后乘以8转化为bit;
②因为位带区的1bit映射到位带别名区后就是4字节(32bit),同时位带别名区的一个地址单元也是1Byte,也就是需要4个Byte, 所以后面需要将第一步转换的bit乘以4
③位带别名区起始地址在①②两步偏移后,然后将我们所需要操作的位在此基础上进行偏移
举例:利用位带对GPIOA的PA3进行位设置或位清除 GPIOA->ODR |= (1<<3);或者 GPIOA->ODR &= ~ (1<<3);
①计算(A - 0x40000000) *8 将偏移地址转换为bit
GPIOA_BASE =APB2PERIPH_BASE + 0x0800 = PERIPH_BASE + 0x10000 + 0x0800 = 0x40000000 + 0x10000 + 0x0800 =0x40010800
A= GPIOA_BASE (基地址) + ODR偏移地址 = 0x40010800 + 0x0C = 0x4001080C
(A - 0x40000000) *8 = (0x4001080C - 0x40000000)*8
②然后将第一步bit膨胀到位带别名区,乘以4字节(位带区的1bit 变成了位带别名区的4Byte),并在位带别名区进行偏移
0x42000000 + 0x4001080C - 0x40000000)*8 *4
③那么由①②两步已经将要操作的 ODR寄存器的那个位所在的地址0x4001080C 映射到位带别名区了,这时候我们只需要把位带区该地址(0x4001080C)要操作的位ODR2(bit3)膨胀到位带别名区所在的地址:
(volitile unsigned long *)(0x42000000 +(A-0x40000000)*32 + 3*4)
④然后我们对其定义,就可以对该地址操作,不就是对ODR的bit3位进行操作了吗?从而实现了位操作
u32 *PA_out_3= (volitile unsigned long *)(0x42000000 +(A-0x40000000)*32 + 3*4);
*PA_out_3 = 0;//复位该位
*PA_out_3 =1;//置位该位
The range of value for n, we should pay special attention to this situation:
According to the header file stm32f10x.h, then we know that the selection of ODR offset address directly affects the value of n.
When we only operate on the lower six bits of the port output data register, its offset address is 0x0C, and then n ranges from 0 to 7.
When we only operate on the highest six bits of the port output data register, its offset address can be 0x0C or 0x0D:
If its offset address is 0x0C, and then n ranges from 0 to 15.
If its offset address if 0x0D, and then n ranges from 0 to 7.
图:来源Cortex-M3权威指南Cn P87
位带区的每一个位 1 bit 都与别名区的一个字 32 bit 相对应(由 4 个字节组成)。 位带区的一个bit位需4个字节0x2000 0000的第0位对应别名区地址0x2200 0000~0x2200 0003,第2位对0x22000004~0x2200 0007。这样対别名区进行的字操作, 都会映射到位带区的相应的位上, 从而实现对位带区的一 个位进行快速操作。
我们可以对输出数据寄存器进行操作,将ODR位置1,则该位对应的管脚就会输出高电平。
接下来就可以实现点灯操作:
<1>确定所要操作的寄存器的第几位,确定偏移地址和管脚基地址,最终明确 A 的值和所位于的位带区(sram/外设)
<2>确定 n 的范围
<3>由映射关系公式进行计算得到所操作的地址
<4>对要操作的地址进行访问即可
/***********************************************************************************************************************************/
#define GPIOA_ODR (GPIOC_BASE + 0x0C) //A的地址表示--GPIO的基地址 + 偏移地址
//点PA13的灯
u32 *PC_out_3 =(u32*)(0x42000000 +(GPIOA_ODR-0x40000000)* + *);//定义一个指针来指向该地址
//这里是操作的ODR的高8位,且偏移地址是0x0C --n为13
int main(void)
{
LED_init();//初始化led灯 *PC_out_3=;//关闭led灯
*PC_out_3=;//打开led灯 //这样也可以: 是因为最终经映射所得到的地址,对其操作时,仅关心最低位(也就是最低位有效)
*PC_out_3=;//关闭led灯
*PC_out_3=;//打开led灯
}
通过以上方法,虽然可以实现像51一样进行对某一位进行操作,但是当操作的位不仅在sram的位带区,还可能在外设的位带区,这时候此方法显得捉襟见肘了。那么我们有没有好的办法呢?
简洁的位带宏定义
通过简单的宏定义即可实现两个位带区通用的公式,也就不需要进行多个指针变量进行定义了。
/*********************************************************************************************************************************/
//宏定义
#define GPIOC_ODR (GPIOC_BASE + 0x0C) //A=GPIOC的基地址 + 偏移地址
#define GPIOC_IDR (GPIOC_BASE + 0x08) #define BitBand(Addr,Bitnum) *((volitile unsigned long *)((Addr & 0xF0000000)+0x02000000+((Addr&0xfffff)<<5)+(Bitnum<<2)))//通用公式
#define PC_out(n) BitBand(GPIOC_ODR,n)//对映射后的地址赋值
#define PC_in(n) BitBand(GPIOC_IDR,n) int main(void)
{
LED_init();//初始化led灯
if(PC_in()==)//IDR的第11位进行操作 如果PC11为高电平时,将PC3的电平置高,否则置低
PC_out()=; //ODR的第3位进行操作
else
PC_out()=;
}
这篇文章有没有帮助到你哈,可以关注一下我的微信公众号: 电子小彭友, 了解更为有趣的我!
1-GPIO的更多相关文章
- [转]: stm328种GPIO模式
[原创]:这段时间开始研究stm32,今天撸着一段代码一直追,追到了GPIO口模式的枚举类型这里,遂去网上查看这8种模式到底是什么,网上一查,看到了一个答案被很多博主转载或者原创,那我也就不重复废话了 ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(3)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(2)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- 基於tiny4412的Linux內核移植--- 中斷和GPIO學習(1)
作者 彭東林 pengdonglin137@163.com 平臺 tiny4412 ADK Linux-4.4.4 u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uIma ...
- STM32f10xxx 之 GPIO口配置
背景 配置stm32f103使其完成PWM输出的过程中,在配置GPIO口的时候,按照习惯配置GPIO口的speed为50MHZ,突然就意识到,为什么大部分例程习惯配置为50MHZ,而不是其它值,即有了 ...
- android gpio口控制
android gpio口控制 GPIO口控制方式是在jni层控制的方式实现高低电平输出,类似linux的控制句柄方式,在linux系统下将每个设备看作一个文件,android系统是基于linux内 ...
- STM32F412应用开发笔记之二:基本GPIO控制
NUCLEO-F412ZG板子上的元器件并没有完全焊接,除去ST-LINK部分和电源部分后,还有用一个USB主机接口,三个LED灯和两个按钮,不过很多功能引脚都已经引到了插针.查看原理图可发现,由原理 ...
- 通过数组和枚举简化GPIO操作编码
在工作中,经常遇到大量使用GPIO作为数字量输入输出来控制设备或采集状态,每次定义操作不同的GPIO针脚既麻烦又容易出错,于是就想要简化操作过程.对于数字量输入来说就是采集对应针脚的状态:而输出则是根 ...
- Zybo GPIO Demo Run Embedded Linux
1.Environment Ubuntu 12.04 x86_64 Vivado 2013.4 SDK 2013.4 2.Pre-requisites 2.1 CodeSourcery arm-g ...
- Android(Java)控制GPIO的方法及耗时分析
前面两篇分别介绍了通过脚本和C代码读写/sys/class/gpio以控制GPIO.实际项目调试时经常还需要在Java代码里控制GPIO,其实现与C代码类似,唯一不同是Android权限.本文重点介绍 ...
随机推荐
- 怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
这么说吧,在我眼里,Java 就是最流行的编程语言,没有之一(PHP 往一边站).不仅岗位多,容易找到工作,关键是薪资水平也到位,不学 Java 亏得慌,对吧? 那可能零基础学编程的小伙伴就会头疼了, ...
- python3 中调用post和get接口
用了很多方法都没有这个实用 POST API接口: import jsonimport requestsif __name__ == '__main__': url = "http://12 ...
- Android中的SharedPreferences存储
一.前言 不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的.也就是说,当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把 ...
- beego register db `default`, sql: unknown driver "mysql" (forgotten import?)
首先先去你的目录下找找这个文件里有没有东西,或者有没有这个文件 如果没有 执行下面两个命令: 下载:go get github.com/Go-SQL-Driver/MySQL 安装:go instal ...
- Linux 之Mycat搭建报错 java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException
搭建MyCat环境时出现 错误: 代理抛出异常错误: java.net.MalformedURLException: Local host name unknown: java.net.Unknown ...
- 3.WebPack配置文件
一.为什么需要WebPack配置文件 引用自官方: 在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件.这比在终端( ...
- 三文搞懂学会Docker容器技术(上)
1,Docker简介 1.1 Docker是什么? Docker官网: https://www.docker.com/ Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2. ...
- Java字符串类型详解
Java 字符串类主要有String.StringBuffer.StringBuilder.StringTokenizer 1.字符串类型底层都是使用char数组进行实现. 2.从jdk1.7以后,S ...
- JavaWeb网上图书商城完整项目--25.注册页面之隐藏没有内容的错误信息实现
在上一章中我们显示的效果如下所示: 上面后面都有错误的红色×的显示,这样是不对的,我们要解决该问题 我们要循环遍历每一个错误的信息,看它的内容有没有,如果有内容我们就显示错误的×,如果没有就不显示× ...
- IDEA解决SVN频繁弹出登录框
将HTTP请求改成SVN就可以了,或者请项目经理开启SVN中的HTTP请求