什么是位操作
51单片机
对于我么熟悉的51单片机,有了==sbit(特殊功能寄存器位)==关键字后,我们可以直接读写gpio的某一位,像这样就属于位操作:
sbit led0 = P3 ^ 0;
sbit led1 = P3 ^ 1;
sbit led2 = P3 ^ 2;
sbit led3 = P3 ^ 3;
led0 = 1;
led1 = 0;
led2 = 0;
led3 = 0;
STM32
These bits can be read and written by software and can be accessed in Word mode only.
对于stm32的GPIO寄存器,没有sbit关键字,我们无法直接操作寄存器的某一位,只能先一次读出一个Word(16bit) ,然后通过逻辑与或来对位进行操作。使用时很不方便
比如我们要操作GPIOA_ODR 的第0位:
#define GPIOA_ODR_0 *((volatile unsigned long *)(0x4001080C))
GPIOA_ODR_0 = (GPIOA_ODR_0 & (~(1 << 0))) | (1 << 0);
位带别名区
BitBand
为了让stm32 也能像51一样实现端口的位操作,Cotex-M3系列引入了位带 BitBand Alias
膨胀关系
还是以操作GPIOx_ODR 为例,解释如下:
即:位带别名区把这 1MB 的空间的每一个位膨胀成32 位 (STM32 的系统总线是 32 位的,按照 4 个字节访问的时候是最快的,所以膨胀成 4 个字节来访问是最高效的)
形象举例:
就是把某危楼(片内外设区)的一家(一个地址)8口人(8bit)搬到一个另地方(位带别名区),并且,每个人(每一位bit)住上了更大的房子(变成32bit),这样一个大房子(32bit)里只有一个人(一个I\O),不管你去哪个大房子里拜访,一次你只能拜访到一个人(一个I\O)
位带操作的头文件编写
重点在于这几个宏:
#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))
1、基地址宏定义
2、一下参考正点原子
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/8/18
//版本:V1.7
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//
//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持UCOS
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#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))
//IO口地址映射
#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
#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
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#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) //输入
#endif
有了这些,就免去了先读出一个Word(16bit) ,然后通过逻辑与或来对位进行操作。,直接操作bitband即可。(A0输出高电平的前提是配置好端口配置寄存器)
如下:
PAout(0) = 1;
注:本文参考了 stm32之位带操作,特此感谢,部分图片来源于网络,如有侵权请告知