stm32 位带操作

什么是位操作

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之位带操作,特此感谢,部分图片来源于网络,如有侵权请告知

猜你喜欢

转载自blog.csdn.net/qq_28816873/article/details/105212146