在这里有段宏定义如下:
#define LED_GPIO_PORT GPIOB
#define LED_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_GPIO_PIN GPIO_Pin_5
#define Toggle(p,i) {p->ODR ^=i;}
#define LED_TOGGLE Toggle(LED_GPIO_PORT,LED_GPIO_PIN)
现在我们来讲解上述代码段中的:#define Toggle(p,i) {p->ODR ^=i;}
这里运用到了结构体指针与位运算的知识,在这里我默认读者已经掌握相关知识。
在stm32f10x.h文件中我们可以找到如下代码段:
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;
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
由此可见GPIOB是一个结构体,其中包含成员32位的输出寄存器ODR。
同时我们还可以在stm32f10x_gpio.h文件中找到有关管脚偏移地址的介绍:
注:这里的偏移地址不是C语言中所谓的地址,而是16bit数。
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
在stm32参考手册中我们找到有关GPIO_ODR寄存器的说明:
由上图可知,各编号ODR的位数与GPIO_PIN的偏移地址是相等的。
例如:GPIO_PIN_5定义为0x0020即二进制的0000 0000 0010 0000。其中的第六位是1,而其他位均为0,恰好是与ODR5是ODR寄存器中的第六位相对应。
所以 GPIOB->ODR ^=GPIO_PIN_5 就是GPIOB的ODR中的低十六位与GPIO_PIN_5(16bit偏移地址)相异或,即实现ODR5即GPIO_PIN5输出电平的反转,而其他位保持不变。
例如GPIOB_ODR=0001 001 0010 0001
GPIOB->ODR ^=GPIO_PIN_5即GPIOB_ODR = GPIOB_ODR^GPIO_PIN_5
GPIOB_ODR = (0001 0010 0010 0001)^(0000 0000 0010 0000)
GPIOB_ODR = 0001 0010 0000 0001 (与1异或取反,与0异或保持不变)
注意:我看到有文章将 #define Toggle(p,i) {p->ODR ^=i;} 中的i表述为管脚i的输出电平,这是错误的,i是所代表管脚的偏移地址。