kevinc

主攻ZYNQ及RTOS开发,关注Flash存储

stm32的寄存器映射方法

0
阅读(2907)

小目标是对几个io口延时翻转,即实现闪灯。

最终控制:GPIOA端口的ODR寄存器,对应基地址0x4001 0800 - 0x4001 0BFF,偏移地址0x0C。


如何把类似的几百个寄存器映射成方便操作的指针或变量?


下面从顶部向下逐步分解。

直接用cube产生代码如下,调用HAL_GPIO_TogglePin函数

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

HAL_GPIO_TogglePin(GPIOA,user_led_1_Pin);

HAL_GPIO_TogglePin(GPIOA,user_led_2_Pin);

HAL_GPIO_TogglePin(GPIOA,user_led_3_Pin);

HAL_GPIO_TogglePin(GPIOA,user_led_4_Pin);

HAL_Delay(1000);

}

/* USER CODE END 3 */


Toggle函数把指定bit逐次异或(bit=0与1异或=1,bit=1与1异或=0,即实现翻转)

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

/* Check the parameters */

assert_param(IS_GPIO_PIN(GPIO_Pin));

GPIOx->ODR ^= GPIO_Pin;

}


参数user_led_1_Pin,根据如下宏定义,把io对应位标为1

#define user_led_1_Pin GPIO_PIN_0

#define user_led_1_GPIO_Port GPIOA

#define user_led_2_Pin GPIO_PIN_1

#define user_led_2_GPIO_Port GPIOA

#define user_led_3_Pin GPIO_PIN_2

#define user_led_3_GPIO_Port GPIOA

#define user_led_4_Pin GPIO_PIN_3

#define user_led_4_GPIO_Port GPIOA


#define GPIO_PIN_0 ((uint16_t)0x0001)

#define GPIO_PIN_1 ((uint16_t)0x0002)

#define GPIO_PIN_2 ((uint16_t)0x0004)

#define GPIO_PIN_3 ((uint16_t)0x0008)

#define GPIO_PIN_4 ((uint16_t)0x0010)

#define GPIO_PIN_5 ((uint16_t)0x0020)

#define GPIO_PIN_6 ((uint16_t)0x0040)

#define GPIO_PIN_7 ((uint16_t)0x0080)

#define GPIO_PIN_8 ((uint16_t)0x0100)

#define GPIO_PIN_9 ((uint16_t)0x0200)

#define GPIO_PIN_10 ((uint16_t)0x0400)

#define GPIO_PIN_11 ((uint16_t)0x0800)

#define GPIO_PIN_12 ((uint16_t)0x1000)

#define GPIO_PIN_13 ((uint16_t)0x2000)

#define GPIO_PIN_14 ((uint16_t)0x4000)

#define GPIO_PIN_15 ((uint16_t)0x8000)

#define GPIO_PIN_All ((uint16_t)0xFFFF)



参数GPIOA,是个struct型指针,指向GPIOA_BASE表示的地址。

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)

#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)

#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)


GPIOA_BASE的地址如下表示(查手册 RM0008 Reference manual)

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)

#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)

#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)

#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)

#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)


#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)


#define PERIPH_BASE ((uint32_t)0x40000000)



st的工程师合理运用结构体的特点,将寄存器的偏移地址与结构体变量对应。

Port configuration register low (GPIOx_CRL) (x=A..G)
Address offset: 0x00
Port configuration register high (GPIOx_CRH) (x=A..G)
Address offset: 0x04
Port input data register (GPIOx_IDR) (x=A..G)
Address offset: 0x08h
Port output data register (GPIOx_ODR) (x=A..G)
Address offset: 0x0C


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;


定义一个struct就会对应一个存储地址,内部的变量依次占用存储空间。

如果#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE),则定义的GPIOA是一个结构体指针,指向基地址。

内部的变量依次对应有其偏移地址,uint32_t 占32bit即4Byte,那么CRL偏移0x00,CRH偏移0x04,IDR偏移0x08,ODR偏移0x0C 。(结构体有默认的对齐方式,分配空间一定是最大变量的整数倍)

用struct的方式很好地将端口以及其寄存器匹配起来,层次分明。



对REG内单个bit位的处理


1、将bit7位置0,其他位不变。

GPIOx->CRL &= ~ (uint32_t)(1<<7);

分析: (1<<7)即0000_0000_0000_0000_0000_0000_1000_0000,

取反后变成1111_1111_1111_1111_1111_1111_0111_1111,跟原reg值与,即实现置0

2、将bit7位置1,其他位不变。

GPIOx->CRL |= (uint32_t)(1<<7);

3、将bit7位取反,其他位不变。

GPIOx->CRL ^= (uint32_t)(1<<7);


继续把移位值define成常量,#define CNF1_1 7,这样每个bit位都实现了定义。


\Drivers\CMSIS\Device\ST\STM32F1xx\Include\stm32f103xb.h

可以参考这个文件里面position和mask的做法。

/******************* Bit definition for GPIO_CRL register *******************/

#define GPIO_CRL_MODE_Pos (0U)

#define GPIO_CRL_MODE_Msk (0x33333333U << GPIO_CRL_MODE_Pos)

#define GPIO_CRL_MODE GPIO_CRL_MODE_Msk


Baidu
map