wavebuilt wavebuilt
关注数: 13 粉丝数: 46 发帖数: 820 关注贴吧数: 24
【转】stm32深入浅出——由GPIO谈谈寄存器配置 文章来源于:http://tieba.baidu.com/mo/q/checkurl?url=http%3A%2F%2Fbbs.ickey.cn%2Fgroup-topic-id-35637.html&urlrefer=2228fac0de704c99551ea237534a313c 作者:子朔 相信大家对GPIO的配置并不陌生,只需简单的几个库函数就能完成。而本菜今天要讲的不是怎么用这些库函数,而是要讲讲这些库函数是怎么工作的。本菜留意了下,无论是网上还是书籍,涉及这方面的知识很少,直接抄了使用手册就上了。那么本菜在这里就详细讲一讲,做些补充,希望能帮助到大家。 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOD, &GPIO_InitStructure); 这几行代码是司空见惯了。我们先从它们入手。 GPIO_InitStructure 看名字,就知道是个结构体,在主文件里是这样声明的: GPIO_InitTypeDef GPIO_InitStructure; 于是乎,我们再看看GPIO_InitTypeDef 是什么东西吧。 typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; }GPIO_InitTypeDef; 其中各有声明为: typedef enum { GPIO_Speed_10MHz = 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_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; 我们可以发现,这些配置信息都有它们的固定数值。这些数值,代表什么意义呢?我们接下去看看GPIO_Init(GPIOD, &GPIO_InitStructure)这个函数吧。 先看参数的类型GPIO_TypeDef* (GPIO_InitTypeDef之前已经有讲解) : GPIO_TypeDef : typedef struct { vu32 CRL; vu32 CRH; vu32 IDR; vu32 ODR; vu32 BSRR; vu32 BRR; vu32 LCKR; } GPIO_TypeDef; 这里有几个寄存器需要简单讲解的:CRL是低位配置寄存器,是用来存储低位数据(低8位)的配置情况。CRH是高位寄存器。ODR是写入输出寄存器,在配置为输出时,该寄存器的值就输出到I/O引脚。IDR是输入数据寄存器,在每个APB2时钟周期读取并捕获对应I/O口数据。BSRR寄存器是32位置/复位寄存器,高16位可以对2字节(16位)的ODR上对应位进行位操作,低16位则进行复位操作,一般都用这个寄存器对I/O口的输出进行操作,当然也可以通过修改ODR寄存器来实现,像在这个库函数里就是通过BSRR来实现的。BRR是16位复位寄存器,只能进行复位操作。LCKR是端口锁定寄存器,开锁定以后,对该端口的某位的配置修改不可行。具体请参照STM32F10X使用手册。 然后另一个实参(举例):GPIOD 查找其声明,就可以发现: #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) 就和之前的例子一样,是以GPIOD_BASE为基地址的结构体。那么当你再查找GPIOD_BASE的定义时,就会发现其值为0x40011800,也即:这是什么意思呢?就是指管理GPIOD的寄存器的初始地址就是0x40011800。那么我们再来看看第一个寄存器CRL的信息:我们发现CRL的偏移地址是00h,也即其是基地址上的第一个寄存器组。仔细一看便知,这个寄存器组包含32个寄存器,也即占了32位空间。我们再看CRL在结构体的类型是vu32,就能明白,这些空间分配,是遵循着线性规律的。当我们再看CRH,也就是第二个寄存器组时,不难发现其偏移地址是04h,也即4*8=32位的偏移地址。这和我们之前的分析是完全符合的。 再回到程序中,可以发现I/O口的配置是分高低8位的,原因在于GPIO的配置寄存器分为高位配置寄存器CRH和低位配置CRL。在STM32中通常一个寄存器有32位,在这里却用了2个寄存器来配置GPIO,说明一个I/O口需要4个位来配置。就仅仅以CRL为例,来看继续看上图。 我们再看GPIO_PIN_x的定义:从中不难发现一些规律。看官心中有些明了之后,我们再回到代码中,只看这几句: if (((u32)GPIO_InitStruct->GPIO_Pin & ((u32)0x00FF)) != 0x00) { tmpreg = GPIOx->CRL; for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = ((u32)0x01) << pinpos; currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; 我们大致揣摩,就能得出这几句语句是用来选择当前的引脚的。这也就是为何上面GPIO_PIN_x的定义是以2的倍数递增。当然,CRL这个寄存器的位的排列也是遵循这个规律的:这是PIN_0脚上的4个配置位,可见这是与它们名称的尾号是一致的,MODEx和CNFx控制PIN_x。掌握这个规律,后面几条语句自然容易推敲出来是什么意思。 不过,有些不同寻常的是: // Reset the corresponding ODR bit if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) { GPIOx->BRR = (((u32)0x01) << (pinpos + 0x08)); } //Set the corresponding ODR bit if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) { GPIOx->BSRR = (((u32)0x01) << (pinpos + 0x08)); } 很匪夷所思,根据判断的条件,显然是要选择下拉输入还是上拉输入。但它居然需要对输出数据复位置位寄存器BRR和BSRR来操作。这是为何?不管那么多,先看手册:在CNFX[1:0]中,显然不能选定上拉和下拉(MODEX[1:0]同样也没有),再看输入口配置电路图:必须有一个控制器控制着这个ON/OFF。那么在下图中,我们就可以找到答案:这下了然了吧!ODR寄存器是参与选择上拉和下拉模式的。 现在大家可以通过写寄存器来做一些像点LED灯的简单操作了。虽说直接使用库函数绝对是很快捷的方法,但是了解了寄存器的功能,目的是为了未来要使用这些库函数时能得心应手。我们学习这些寄存器不是为了摆脱库函数,而是为了更好地利用库函数。
希望正视此书缺点。。。。。。。。 我想说,老楚经历过这么多,可以说死都死了好几回了,早该不普通了!大家凭自己逻辑,凭自己良心说,是不是?可是飘火的描写下老楚的思想思维还是一个最普通的人,连“国家大事”都不太过问,天下5族割据,多能亦占一舆,楚门相对瑛弱,这些从来不去打听,尽人皆知的天空城之变(暂且这么叫吧)还是听烧饼有目的的说出口的,这些,个人觉得稍显不妥吧。大家如果听不进去些许缺点,那我重开贴只写优点,但那我想也没什么意思和必要吧,此书优点一大车,也是尽人皆知的,为什么不能讨论缺点呢?如果此书要实体出版,我是作者的话,最盼望知道的,便是缺点了。这句话我要这么说:“如果此书要出版,飘火最盼望的,莫过于缺点了。”相信又会引来很多人:“你是飘火吗?你怎么知道他怎么想?”云云。。。有人描述缺点,wkss却有删贴的欲望(参见致命缺点一贴),此贴不是人身攻击啊,也没违返吧规啊,只因为题目有些夸张?怕引起接下来的骂街,人渗?怎么会有删贴的想法?对于不违规帖,想法不该有吧,有也不该以吧务身份说吧。引发讨论不是更好?不用怕下面骂娘,人渗,骂一个关一个(不是针对wkss,描述个人想法,望见凉,况且你们给我们带来手打,我只有推荐票,
1 下一页