支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在 CM3中,有两个区中实现了位带。其中一个是
SRAM 区的最低
1MB 范围,第二个则是片内外设区的最低
1MB 范围。这两个区中的地址除了可以像普通的
RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
关于位带操作的博客说明有很多,这里主要将代码贴出来,并做详细的注释
/** ****************************************************************************** * @file GPIO.h * @author Duanxx * @version V1.1 * @data 2015-06-07 * @brief 这个头文件是对"stm32f10x_gpio.h"的一个补充 * 主要是对《Cortex-M3权威指南(中文版)》chp05存储器系统->位带操作的一个实现 * 这个功能实现之后,可以使得STM32像51一样对IO口按位操作 * ****************************************************************************** */ #ifndef _GPIO_H_ #define _GPIO_H_ #include "stm32f10x_gpio.h" /** * 这里的实现是从《Cortex-M3权威指南(中文版)》chp05存储器系统->位带操作第92页直接抄过来的 * 其目的是为了简化位带操作,而定义了一些专用的宏 */ ///< 把“位带地址+位序号”转换成别名地址的宏 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) ///< 把该地址转换成一个指针的宏 #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) ///< 对地址的按位操作 #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) /** * 下面是GPIO输入输出数据寄存器的地址 * 这里的地址是根据《STM32F10x Refernce manual(RM0008 英文版)》中 * chp3 memory and bus architecture -> 3.2 Memory organization 以及 * chp9 General-purpose and alternate-function I/Os (GPIOs and AFIOs) -> 9.5 GPIO and AFIO register maps * 中关于GPIO的地址分配表得到的 */ ///< 在GPIO的基址上偏移0x8,就是GPIOx_IDR的地址,即GPIO Input Data Register(输入数据寄存器) #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 ///< 在GPIO的基址上偏移0x8,就是GPIOx_ODR的地址,即GPIO Output Data Register(输出数据寄存器) #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C /** * 下面是基于GPIO对位带操作的使用做了进一步的宏定义 * 其使用方法如下 * 如果GPIO是输出模式: * Step1: 将GPIO初始化为输出,比如: GPIO_InitTypeDef Duanxx_GPIO_InitStructure; //< Enable GPIOA, RCC_APB2Periph_GPIOA RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE); //< Configure and PA.1 as output push-pull Duanxx_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; Duanxx_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; Duanxx_GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &Duanxx_GPIO_InitStructure); *Step2: 控制PA1的输出 PAout(1) = 0; //<PA1输出低电平 PAout(1) = 1; //<PA1输出为高电平 * 当然,我们也可以使用宏定义,对下面的宏定义做进一步的封装 * 这样,就更有利于我们使用有意义的GPIO控制 * 比如PA1链接的是LED,那么就可以有如下的操作 #define LED PAout(1) ///<LED 为PA1 #define LED_ON 1 ///<定义LED亮的值 #define LED_OFF 0 ///<定义LED灭的值 LED = LED_OFF; ///< LED 灭 LED = LED_OFF; ///< LED 亮、 ********************************************************************** * 对于GPIO输入而言,也可有有相似的操作 * 第一步也是初始化,将GPIo初始化位输入 * 第二步则是对GPIO进行读取,比如 * 判断PA1是否是高电平如下: if(PAin(1) == 1) { .... } * */ #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) void GPIO_Disale_JTAG(void); #endif
时间: 2024-10-14 05:14:05