你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32库开发实战指南--感悟2:寄存器地址映射到库操作

[复制链接]
wudianjun2001 发布时间:2018-12-18 13:43
本帖最后由 wudianjun2001 于 2018-12-18 13:44 编辑
9 o1 ]& X/ `' G) p3 S& ?- p1 d- L/ p
7 H+ Y3 x8 R5 @/ c" f
结合书上的内容来快速整理一遍从寄存器的地址开始要库的过程,以GPIO为例。

" }+ n8 W# \1 C6 `
/ O  I& ]1 G( W3 n
1,首先来看下存储器的结构分配,在书上的第5章
1.png
这是整个存储器的分配,GPIO外设功能位于block2的片上外设区域,GPIOA的基地址为0x4001 0800,控制GPIOA的几个寄存器在这个地址的基础上偏移4字节的整数倍

. _6 h/ E0 n7 ^' X8 y  {( Y3 V8 H' Y' t
2,总线外设和基地址的宏定义,在stm32f10x.h里
#definePERIPH_BASE          ((uint32_t)0x40000000) /*!< Peripheral base address in the aliasregion */
! o' w( W0 _4 Z  N- S/ U' w
$ E6 P" W- j- s0 `& T' m- }
/*!< Peripheral memory map */
#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
  [; u" z/ ?5 m, x
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
这样就有了各个外设的地址了,下面再看下GPIO对应的每个寄存器的地址

+ K6 V! E7 \5 [* \" P. a1 I
' }* g* e4 p$ x
3,GPIO的各个寄存器的地址是通过结构体的的封装来确定的,因为每个GPIOA-E对应的寄存器都是一样的,只是他们的基地址不一样,所以用结构体来封装比较方便

+ |6 W. v) Z& V; N* I
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

* }- l9 Z/ f' o5 k( {4 j/ x2 s  z4 Q1 J) J. a
typedef struct
{
__IO uint32_t CRL;
  __IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
这样就知道了每个寄存器的地址了,直接对结构体操作就会操作到各个寄存器了,不会弄错。有了这个就可以直接进行寄存器编程了,但是STM32的寄存器太多,寄存器操作哎复杂了,所以要再封装成库就方便了。

: H3 q' i  A$ V6 @/ o
+ a/ b/ M5 T( g+ Y3 y
GPIO的各个寄存器里面的具体位定义,这样也是为了编写程序的方便
2.png
0 Y: b" o( i5 {/ y6 F1 P! I
, ~- _7 e% Q5 m
4,GPIO的每个引脚基本的设置参数有输入输出模式,速度,引脚号等,所以又定义了一个GPIO初始化结构体,这些定义都在在stm32f10x_gpio.h里
typedef struct
{
uint16_t GPIO_Pin;            /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be anyvalue of @ref GPIO_pins_define */

2 r( g: ~' x0 ?3 Z" k1 @( E- ~
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
                                      Thisparameter can be a value of @ref GPIOSpeed_TypeDef */

1 o5 g9 o/ Q; E+ u8 t
GPIOMode_TypeDef GPIO_Mode;   /*!< Specifies the operating mode for the selected pins.
                                      Thisparameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
2 M9 C8 `& l2 b* G; [# H
1 }% y3 }4 y, o: p8 x4 ]) e' V" M
具体的引脚号宏定义,都是为了方便编程里的理解和记忆,所以弄了很多宏定义,要不光看值的话记不住,也比较乱
3.png

8 w( ?7 |% E7 s. {* ]( l
/ Q2 t- }* ^6 K& B/ s0 V: [' e
GPIO的输入输出模式和速度的定义
4.png
有了这些以后,对所有的引脚操作都采用结构体来进行了,也对结构体进行初始化,然后通过结构体进行输出输入操作
+ o: w) b1 I' `2 W
0 i6 b$ ?9 x/ R; f2 U# a
5,GPIO的具体操作库函数,内容在stm32f10x_gpio.c里

! z  b, x" k- o# Y
引脚初始化配置函数
5.png

" P2 w1 M7 P/ f5 p: x/ k
8 G% u; [3 I/ z
读取输入引脚值的函数
6.png

$ Y7 N2 J/ D( W
* R2 R, Z- C( J: S7 s  }- {( k
输出引脚的控制函数
7.png
有了这几个函数就可以进行GPIO的读写操作控制输入输出了

! N6 u# j. o7 K, v0 R9 B3 w, @; N+ t# f0 H/ E) n
6,具体操作示例
具体的端口定义,使用宏定义是为了程序的阅读和编写方便
#define SPKBSY       GPIO_Pin_11                      //PA11为语音控制忙引脚

; v# D5 F( ~# f8 y+ F* q
#define LED1  GPIO_Pin_12                               //PB12为发光二极管控制引脚
#define LED2  GPIO_Pin_13                               //PB13为发光二极管控制引脚
#define LED3  GPIO_Pin_14                               //PB14为发光二极管控制引脚
#define LED4  GPIO_Pin_15                               //PB15为发光二极管控制引脚
上面的定义可以在main.h文件里完成

$ I6 k) N1 W; v9 G! X6 ?; b& I4 v5 I/ X
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO口时钟使能

* }6 @% [4 T4 O
         //ConfigureGPIO pins : PB0 PB6 PB7 PB12 PB13 PB14 PB15
         GPIO_InitStructure.GPIO_Pin= LED1 | LED2 | LED3 | LED4;
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
         GPIO_InitStructure.GPIO_Speed= GPIO_Speed_10MHz;
         GPIO_Init(GPIOB,&GPIO_InitStructure);
. f' J( Y: N* ~$ W+ z! B
         //ConfigureGPIO pin : PA11
         GPIO_InitStructure.GPIO_Pin= SPKBSY;
         GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU;
         GPIO_Init(GPIOA,&GPIO_InitStructure);
具体的输入输出初始化,这些在主程序里完成
+ e1 n4 {; X* D4 G3 f9 e: b

/ R. B% X  d1 C+ \+ h# M
#define LED1_ON()          GPIO_ResetBits(GPIOB, LED1)                //ÁÁ
#define LED1_OFF()         GPIO_SetBits(GPIOB, LED1)                     //°µ
#define LED2_ON()          GPIO_ResetBits(GPIOB, LED2)                //ÁÁ
#define LED2_OFF()         GPIO_SetBits(GPIOB, LED2)                     //°µ
#define LED3_ON()          GPIO_ResetBits(GPIOB, LED3)                //ÁÁ
#define LED3_OFF()         GPIO_SetBits(GPIOB, LED3)                     //°µ
#define LED4_ON()          GPIO_ResetBits(GPIOB, LED4)                //ÁÁ
#define LED4_OFF()         GPIO_SetBits(GPIOB, LED4)                     //°µ

7 M7 F2 ~% |+ U8 C, ]$ B) Y
#define SPKBSY_READ()  GPIO_ReadInputDataBit(GPIOA, SPKBSY)//读信号
具体IO口的读写操作,可以在main.h里完成,然后主程序里直接调用宏定义就可以了,比较方面。
4 E( P& r, k+ u, T

8 E4 ^+ |6 Y! V- r' c" m) m$ N5 @! z
就介绍到这里了,这就是书上的前几章内容的个人理解总结了

. j+ L. f% P8 _; q" }  k% F2 w
赞 收藏 评论2 发布时间:2018-12-18 13:43

举报

2个回答
zccdyfw 回答时间:2018-12-18 14:02:55
sincomaster 回答时间:2018-12-18 14:34:41
谢谢分享,写得很好了
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版