LED跑马灯-位操作

一,什么是位操作

  之前我们已经介绍了库函数和寄存器控制LED跑马灯,其实无论使用哪一种方法(包括操作BSRR,BRR寄存器的方式)最终都是通过操作GPIO_ODR寄存器(32位寄存器只使用低16位)响应的位为该IO口赋值
 
  那么什么是位操作?我们知道GPIO_ODR寄存器的每一位对应一个IO口的电平操作,而每一位实际是一个IO口地址的映射,位操作就是跨越寄存器映射,直接为这个地址进行赋值
  
  在LED跑马灯-位操作的实验中我们将使用位操作的方式控制IO口输出高低电平

  这里写图片描述


二,位与别名映射关系

1,支持位操作的两个内存区范围:

0x2000_0000-0x200F_FFFF     // SRAM区中的最低1M
0x4000_0000-0x400F_FFFF     // 片上外设区中的最低1M

2,地址映射关系计算:
对于SRAM位带区某比特,所在字节地址为A,位n(0<=n<=7),该比特在别名区的地址为:

AliasAddr=0x22000000+((A-0x20000000) * 8+n)*4=0x22000000+(A-0x20000000)*32+n*4

对于片上外设位带区某比特,所在字节地址为A,位n(0<=n<=7),该比特在别名区的地址为:

AliasAddr=0x42000000+((A-0x40000000) * 8+n)*4=0x42000000+(A-0x40000000)*32+n*4

三,位操作

寄存器每一个bit映射为一个32位地址,修改这个位,可直接修改其映射的地址达到操作位的目的
这里写图片描述

这里写图片描述


四,位操作的优越性

位操作的优越性

以前获取某个位的值:
  先获取整个寄存器的值
  掩盖不需要的位
 位操作获取某个位的值:
  从位带别名区读取状态位
  
 位操作对硬件I/O密集型底层程序最有好处
 以前的读-改-写需要三条指令,导致中间有两个可能被中断的空档


五,sys.h介绍

介绍一个sys.h文件对位操作进行了封装

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

//计算寄存器地址addr下,第bitnum位映射的32位地址值
#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))

//GPIOx_ODR寄存器地址
#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

//GPIOx_IDR寄存器地址
#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

//位操作封装
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)    // 操作GPIOA_ODR寄存器的第n个位-输出
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)    // 操作GPIOA_IDR寄存器的第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

stm32f10x.h中找到GPIOA基地址GPIOA_BASE

#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)  // 在APB2总线下+偏移量0x0800

再找到APB2PERIPH基地址

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)

最终操作寄存器的相应的位


六,LED硬件连接:

  这里写图片描述

连接方式:
    LED0连接PB5引脚
    LED1连接PE5引脚

七,LED跑马灯实现流程-位操作

1,使能IO时钟
    调用函数RCC_APB2PeriphClockCmd

2,初始化GPIO
    调用函数GPIO_Init();

3,使用位操作实现操作IO口输出高低电平

八,LED跑马灯-位操作代码

#include "stm32f10x.h"
#include "led.h"
#include "delay.h"  // 此头文件中间接引用了sys.h头文件

 int main(void)
 {

     delay_init();
     LED_Init();

     while(1){

        PBout(5)=1;      // PB5设置为高电平,LED熄灭
        PEout(5)=1;

        delay_ms(500);   // 延迟500毫秒

        PBout(5)=0;      // PB5设置为低电平,LED点亮
        PEout(5)=0;

        delay_ms(500);

     }
 }

以上代码实现了LED0,LED1每间隔500毫秒闪烁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraveWangDev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值