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口操作宏定义, 参考<<CM3权威指南>>第五章(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灯
当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位对应的别名地址
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 (最后两位不同)
6. sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS
// IO口操作宏定义, 参考<<CM3权威指南>>第五章(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