PCout(n) -- STM32F103RCT6 位带操作

1. 使用位带操作控制GPIO口的输入、输出模式,以及输出的电平高、低

注:位带操作一般是操作单独的一个bit 位,而&=,|= 则可操作多个bit位,看自己的需求吧。(不懂&=,|= 是什么意思的自行问度娘)

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)

#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))  // 传如要操作的实际地址及其bit位

#define LED0  PCout(3)

LED0  = 1;// GPIOC_3 为输出模式,且输出高电平(输出寄存器ODR 置1)

LED0  = 0;// GPIOC_3 为输出模式,且输出低电平(输出寄存器ODR 置0)

LED0  = !LED0  ;// LED0 状态翻转(亮的话就熄灭,熄灭的话就点亮)

2. 宏定义

sys.h:位带操作, 参考《Cortex-M3/M4 权威指南》 第五章(87页~92页)

// IO口操作宏定义, 参考<>第五章(87页~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))

#define GPIOC_ODR_Addr    (GPIOC_BASE + 12) // 0x4001 100C

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)

stm32f10x.h:GPIOC 的地址

#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)

3. 位带操作的宏定义解释

3.1 计算位段区地址对应的别名区地址,总之,就是计算要操作的寄存的地址,计算出来了寄存器的地址,就可以直接操作寄存器了

#define    BITBAND(addr, bitnum)          ((addr & 0xF000 0000)+0x200 0000+((addr &0xF FFFF)<<5)+(bitnum<<2))

这一句定义了位带存储地址的计算方法
addr & 0xF000 0000: 取地址的最高4位,用来区分段的,是片内外设段还是SRAM段。
+0x200 0000:(值为32M)是别名区相对位段区的地址偏移量,别名区在相应位段上方的32M处(别名区 = 位段区 + 0x200 0000)
(addr & 0xF FFFF) << 5): 别名区的地址值等于位段区的地址膨胀32倍,左移5位即可;
(bitnum<<2): 由于每1比特膨胀为32位,32位占用4个字节的存储位置,所以计算地址时要乘以4,左移2位即是;

3.2 取地址里面的值(操作寄存器里面的值)

#define   MEM_ADDR(addr)            *((volatile unsigned long  *)(addr))

在 3.1 中计算出来寄存器的地址后,就可以对地址里面的值进行修改了,至于中间加一个volatile关键字,则是为了告诉编译器不要对该值进行优化,必须每次老老实实地去直接访问这个地址!!

3.3 操作的具体地址及其bit位

#define BIT_ADDR(addr, bitnum)       MEM_ADDR(BITBAND(addr, bitnum))

addr:要操作的寄存器的实际地址,例:GPIOC_ODR_Addr(GPIOC_x的数据寄存器)
bitnum:要操作的寄存器的实际地址的某个bit位,例:GPIOC_3(GPIOC_3的数据寄存器)

4. 例:点亮或者熄灭LED灯

PCout(n) -- STM32F103RCT6 位带操作_第1张图片
当GPIOC_3 输出高电平的时候,LED23 点亮
当GPIOC_3 输出低电平的时候,LED23 熄灭

#define LED0  PCout(3)

LED0  = 1;   // GPIOC_3 为输出模式,且输出高电平(输出寄存器ODR 置1)

LED0  = 0;    // GPIOC_3 为输出模式,且输出低电平(输出寄存器ODR 置0)

LED0  = !LED0;    // LED0 状态翻转(亮的话就熄灭,熄灭的话就点亮)

5. 例如,在《Cortex-M3 权威指南》第88页有张图,显示的是实际地址的每个bit位对应的别名地址

PCout(n) -- STM32F103RCT6 位带操作_第2张图片

5.1 看图可知:

0x200F FFFF 的地址有8个bit位,每个bit位映射了一个新的地址,操作新地址就相当于在操作实际地址的每个bit位,对应关系:
0x200F FFFF bit[0] --> 0x23FF FFE0
0x200F FFFF bit[1] --> 0x23FF FFE4
0x200F FFFF bit[2] --> 0x23FF FFE8
0x200F FFFF bit[3] --> 0x23FF FFEC
0x200F FFFF bit[4] --> 0x23FF FFF0
0x200F FFFF bit[5] --> 0x23FF FFF4
0x200F FFFF bit[6] --> 0x23FF FFF8
0x200F FFFF bit[7] --> 0x23FF FFFC

5.2 根据实际地址的bit位来计算映射地址

#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))

其中:addr = 0x200F FFFF, bitnum = 0, 1, 2, 3, 4, 5, 6, 7

(addr & 0xF0000000):    0x200F FFFF & 0xF0000000 = 0x2000 0000
+ 0x2000000
(addr & 0xF FFFF) << 5:    (0x200F FFFF & 0xFFFFF) << 5 =  0xF FFFF << 5 = 0x1FF FFE0
+ (bitnum << 2)

0 << 2 = 00;     bit[0]
1 << 2 = 04;     bit[1]
2 << 2 = 08;     bit[2]
3 << 2 = 0C;     bit[3]
4 << 2 = 10;     bit[4]
5 << 2 = 14;     bit[5]
6 << 2 = 18;     bit[6]
7 << 2 = 1C;     bit[7]

所以

((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0

((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (0 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x00  = 0x23FF FFE0
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (1 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x04  = 0x23FF FFE4
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (2 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x08  = 0x23FF FFE8
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (3 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x0C  = 0x23FF FFEC
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (4 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x10  = 0x23FF FFF0
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (5 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x14  = 0x23FF FFF4
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (6 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x18  = 0x23FF FFF8
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5)  + (7 << 2))  = 0x2000 0000 + 0x200 0000  + 0x1FF FFE0 + 0x1C  = 0x23FF FFFC

即下图所示: E0、E4、E8、EC、F0、F4、F8、FC (最后两位不同)

PCout(n) -- STM32F103RCT6 位带操作_第3张图片

6. sys.h

#ifndef __SYS_H
#define __SYS_H	
#include "stm32f10x.h"

//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS		1		//定义系统文件夹是否支持OS

// IO口操作宏定义, 参考<>第五章(87页~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)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE + 12) // 0x4001 080C
#define GPIOB_ODR_Addr    (GPIOB_BASE + 12) // 0x4001 0C0C
#define GPIOC_ODR_Addr    (GPIOC_BASE + 12) // 0x4001 100C
#define GPIOD_ODR_Addr    (GPIOD_BASE + 12) // 0x4001 140C
#define GPIOE_ODR_Addr    (GPIOE_BASE + 12) // 0x4001 180C
#define GPIOF_ODR_Addr    (GPIOF_BASE + 12) // 0x4001 1A0C
#define GPIOG_ODR_Addr    (GPIOG_BASE + 12) // 0x4001 1E0C

#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口,GPIO_n 设置成输出模式或者输入模式
//确保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)  //输入

//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址

#endif

你可能感兴趣的:(stm32,单片机,嵌入式硬件,mcu)