支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在 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
原文地址:http://blog.csdn.net/daunxx/article/details/46404209