一、位带区与位带别名区
(一)位带介绍
1、位带操作
在学习51单片机时就已经使用过位操作,比如使用sbit对单片机IO口的定义,但是STM32中并没有这类关键字,而是通过访问位带别名区来实现,即通过将每个比特位膨胀成一个32位字,当访问这些字的时候就达到了访问比特的目的。比方说BSRR寄存器有32个位,那么可以映射到32个地址上,当我们去访问这32个地址就达到访问32个比特的目的。
2、STM32位带及位带别名区域
支持位带操作的区域是SRAM区的最低1MB范围(APB1/2,AHB外设)和片内外设区的最低1MB范围。
1MB的位就扩展为了32MB的位带别名区
(二)位带区与位带别名区地址转换
外设位带区与外设位带别名区的地址转换公式:AliasAddr = 0x42000000+ (A-0x40000000)*8*4+n*4
SRAM位带区与SRAM位带别名区的地址转换公式:AliasAddr = 0x22000000+ (A-0x20000000)*8*4 +n*4
A:表示我们要操作的那个位所在的寄存器的地址,n:位序号
理解要点:位带区的一个位在位带别名区会被膨胀成四个字节
根据上述两个公式特点,将其统一为一个公式表示:((A & 0xF0000000)+0x02000000+((A &0x000FFFFF)<<5)+(n<<2))
A:要操作的位所在寄存器的地址;n:位号,即在寄存器的第几位。
(三)位带操作的优点
(1)控制GPIO口输入输出非常简单。
(2)操作串行接口芯片非常方便(DS1302、74HC595等)
(3)代码简洁,阅读方便。
二、GPIO位带操作
//addr是地址,bitnum是该地址的第几位
#define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//volatile提示该变量是随时可变的,每次使用都要读取
#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)//0x4001280C
#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 GPTOE_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 GPTOF_IDR_Addr (GPIOF_BASE+8)//0x40011A08
#define GPTOG_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)//输入
三、LED闪烁
在点亮LED灯的文件六、使用库函数点亮一个LED_并不傻的袍子-CSDN博客中,新建system.h和system.c文件,并添加到工程中,在system.h文件中键入以下代码:
#ifndef _system_H
#define _system_H
#include "stm32f10x.h"
//addr是地址,bitnum是该地址的第几位
#define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//volatile提示该变量是随时可变的,每次使用都要读取
#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)//0x4001280C
#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 GPTOE_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 GPTOF_IDR_Addr (GPIOF_BASE+8)//0x40011A08
#define GPTOG_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
在system.c中键入以下代码:
#include "system.h"
在main.c中键入以下代码:
#include "system.h"
#include "led.h"
//延时函数
void delay(u32 i)
{
u16 j=0;
while(i--)
{
//1ms
j=12000;
while(j--);
}
}
int main()
{
LED_Init();
while(1)
{
PCout(0)=0;
delay(1000);
PCout(0)=1;
delay(1000);
}
}
下载到普中STM32F103ZET6开发板中,即可观察LED灯闪烁。如果不是普中的开发板,只要把LED在自己板子相应的引脚替换主函数while(1)中的PCout(0)即可