本帖最后由 wudianjun2001 于 2018-12-18 13:44 编辑 ; l8 m$ D7 y; y, d# p5 k+ a% e & A" z. H, o- b8 J 结合书上的内容来快速整理一遍从寄存器的地址开始要库的过程,以GPIO为例。 * r# E% i2 F% K3 l( b- k; D. X7 p, v. Y7 Q 1,首先来看下存储器的结构分配,在书上的第5章 这是整个存储器的分配,GPIO外设功能位于block2的片上外设区域,GPIOA的基地址为0x4001 0800,控制GPIOA的几个寄存器在这个地址的基础上偏移4字节的整数倍 ! J7 x- A8 u; M' j. f. c; ?- L% n0 s, s# P, T 2,总线外设和基地址的宏定义,在stm32f10x.h里 #definePERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the aliasregion */ + `/ A1 g) l1 q$ i9 w! Z: o( J$ Y5 f, o- h& M: { /*!< Peripheral memory map */ #define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) 这样就有了各个外设的地址了,下面再看下GPIO对应的每个寄存器的地址 3,GPIO的各个寄存器的地址是通过结构体的的封装来确定的,因为每个GPIOA-E对应的寄存器都是一样的,只是他们的基地址不一样,所以用结构体来封装比较方便 9 O0 ]1 N- k/ B$ g# @. L6 v9 f#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) 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的寄存器太多,寄存器操作哎复杂了,所以要再封装成库就方便了。 & T$ H: v1 \: Z; V8 VGPIO的各个寄存器里面的具体位定义,这样也是为了编写程序的方便 / s0 b2 K7 d9 m+ i 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 */ & G5 o9 J( g( V0 z0 D/ X8 y) b GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. Thisparameter can be a value of @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. Thisparameter can be a value of @ref GPIOMode_TypeDef */ }GPIO_InitTypeDef; , z4 Z+ c# b9 y. K 具体的引脚号宏定义,都是为了方便编程里的理解和记忆,所以弄了很多宏定义,要不光看值的话记不住,也比较乱 ! Y3 z9 K& P& y0 j w' z GPIO的输入输出模式和速度的定义 有了这些以后,对所有的引脚操作都采用结构体来进行了,也对结构体进行初始化,然后通过结构体进行输出输入操作 1 S; q C: j4 Q 5,GPIO的具体操作库函数,内容在stm32f10x_gpio.c里 % l* U9 y0 J' F9 z' `1 U 引脚初始化配置函数 9 c' P: Y' f* ^- D 读取输入引脚值的函数 输出引脚的控制函数 有了这几个函数就可以进行GPIO的读写操作控制输入输出了 8 A f9 e& {# b. R& d 6,具体操作示例 具体的端口定义,使用宏定义是为了程序的阅读和编写方便 #define SPKBSY GPIO_Pin_11 //PA11为语音控制忙引脚 * t/ s9 ]3 |& N% o5 r) [#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文件里完成 ' U8 ?0 R/ V! [/ Q7 z& P' x RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO口时钟使能 //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); //ConfigureGPIO pin : PA11 GPIO_InitStructure.GPIO_Pin= SPKBSY; GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIO_InitStructure); 具体的输入输出初始化,这些在主程序里完成 #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) //°µ , T; Q4 t) G9 e2 u' r* m# x#define SPKBSY_READ() GPIO_ReadInputDataBit(GPIOA, SPKBSY)//读信号 具体IO口的读写操作,可以在main.h里完成,然后主程序里直接调用宏定义就可以了,比较方面。 就介绍到这里了,这就是书上的前几章内容的个人理解总结了 |
谢谢分享,写得很好了 |