专栏首页电子电路开发学习东芝MCU实现位带操作

东芝MCU实现位带操作

位带操作简介

位带操作的概念其实30年前就有了,那还是 8051单片机开创的先河,如今ARM CM3 将此能力进化,可以说,这里的位带操作是8051 位寻址区的威力大幅加强版。即如果要改写某个寄存器的某一位,通过改写这一位映射的地址即可。东芝的TT_M3HQ开发板也是ARM CM3的MCU,实现了位带操作,就可以如同51单片机控制GPIO口一样的方便。

位带操作的优越性

初学51时,对某一个IO口进行输出操作,或者读取输入时,可以通过如下方式:

sbit LED = P1^0;
sbit KEY = P1^2;

LED = 0;        //输出0

if(KEY == 0)        //读取按键输入
{

}

对于东芝TMPM3HQFDFG,如果没有位带操作,我们需要使用如下函数来实现读取和输入。在txz_gpio.c和txz_gpio.h两个库文件中,我们可以了解到写函数和读函数的使用方法。

写函数:

gpio_t port;

//PK4输出低电平
gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_RESET);

//PK4输出高电平
gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_SET);

读函数:

//读取PV3输入

gpio_pinstate_t key_status;
gpio_t port;
gpio_read_bit(&port, KEY_PORT, KEY_PIN, GPIO_Mode_DATA, &key_status);

而如果实现了位带操作,我们只需要使用两个宏就可以实现:

PK4输出:PKout(4) = 0;
读取PV3输入:in = PVin(3);

实现按键按下LED闪烁:

if(PVin(3) == GPIO_PIN_RESET)    //按键按下LED闪烁
    {
        PKout(4) = 1;   //点亮
        delay_ms(50);

        PKout(4) = 0;   //熄灭
        delay_ms(50);
    }

是不是很简单呢?通过查看官方txz_gpio.c库文件中输出和输入函数的实现,可以看出是使用的位带方式,但是看着不是很简洁,有没有更简单一些的实现方法呢?

位带操作的实现

新建sys.h,主要通过宏定义的方式实现IO的输出和输入。

#ifndef  __SYS_H__
#define __SYS_H__

#include "TMPM3HQ.h"
#include "TMPM3Hy.h"

#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 PORTx_BASE(group)       (0x400C0000UL + (uint32_t)((0x0000100UL) * (group)))
#define PORTx_MODE_BASE(group)   ((uint32_t)(PORTx_BASE(group)) + (uint32_t)(GPIO_Mode_DATA))

#define PORTA_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_A)
#define PORTB_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_B)
#define PORTC_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_C)
#define PORTD_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_D)
#define PORTE_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_E)
#define PORTF_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_F)
#define PORTG_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_G)
#define PORTH_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_H)
#define PORTJ_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_J)
#define PORTK_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_K)
#define PORTL_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_L)
#define PORTM_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_M)
#define PORTN_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_N)
#define PORTP_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_P)
#define PORTR_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_R)
#define PORTT_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_T)
#define PORTU_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_U)
#define PORTV_ODR_ADDR      PORTx_MODE_BASE(GPIO_PORT_V)

#define PAout(n)   BIT_ADDR(PORTA_ODR_ADDR, n)   
#define PBout(n)   BIT_ADDR(PORTB_ODR_ADDR, n)   
#define PCout(n)   BIT_ADDR(PORTC_ODR_ADDR, n)   
#define PDout(n)   BIT_ADDR(PORTD_ODR_ADDR, n)   
#define PEout(n)   BIT_ADDR(PORTE_ODR_ADDR, n)   
#define PFout(n)   BIT_ADDR(PORTF_ODR_ADDR, n)   
#define PGout(n)   BIT_ADDR(PORTG_ODR_ADDR, n)   
#define PHout(n)   BIT_ADDR(PORTH_ODR_ADDR, n)   
#define PJout(n)   BIT_ADDR(PORTJ_ODR_ADDR, n)   
#define PKout(n)   BIT_ADDR(PORTK_ODR_ADDR, n)   
#define PLout(n)   BIT_ADDR(PORTL_ODR_ADDR, n)   
#define PMout(n)   BIT_ADDR(PORTM_ODR_ADDR, n)   
#define PNout(n)   BIT_ADDR(PORTN_ODR_ADDR, n)   
#define PPout(n)   BIT_ADDR(PORTP_ODR_ADDR, n)   
#define PRout(n)   BIT_ADDR(PORTR_ODR_ADDR, n)   
#define PTout(n)   BIT_ADDR(PORTT_ODR_ADDR, n)   
#define PUout(n)   BIT_ADDR(PORTU_ODR_ADDR, n)   
#define PVout(n)   BIT_ADDR(PORTV_ODR_ADDR, n)   


//实现指定管脚置位和复位
/*
PORTx_SET(GPIO_PORT_K, 5);
PORTx_CLR(GPIO_PORT_K, 4);
*/
#define PORTx_SET(group, pin)   (*((__IO uint32_t *)PORTx_MODE_BASE(group)) |= (uint32_t)(0x0000001UL<< pin))
#define PORTx_CLR(group, pin)   (*((__IO uint32_t *)PORTx_MODE_BASE(group)) &= ~((uint32_t)(0x0000001UL<< pin)))

/*
//实现指定管脚置位和复位
#define PORTx_SET(group, pin)   (BIT_ADDR(PORTx_MODE_BASE(group), pin)=1)
#define PORTx_CLR(group, pin)   (BIT_ADDR(PORTx_MODE_BASE(group), pin)=0)
*/

//读取指定引脚的输入状态
#define READ_PIN(group, pin)    ((*((__IO uint32_t *)(PORTx_MODE_BASE(group))) & (uint32_t)(0x0000001UL<< pin)) >> pin)

//输入状态 = GPIO_PIN_RESET or GPIO_PIN_SET
#define PAin(pin)   READ_PIN(GPIO_PORT_A, pin)
#define PBin(pin)   READ_PIN(GPIO_PORT_B, pin)
#define PCin(pin)   READ_PIN(GPIO_PORT_C, pin)
#define PDin(pin)   READ_PIN(GPIO_PORT_D, pin)
#define PEin(pin)   READ_PIN(GPIO_PORT_E, pin)
#define PFin(pin)   READ_PIN(GPIO_PORT_F, pin)
#define PGin(pin)   READ_PIN(GPIO_PORT_G, pin)
#define PHin(pin)   READ_PIN(GPIO_PORT_H, pin)
#define PJin(pin)   READ_PIN(GPIO_PORT_J, pin)
#define PKin(pin)   READ_PIN(GPIO_PORT_K, pin)
#define PLin(pin)   READ_PIN(GPIO_PORT_L, pin)
#define PMin(pin)   READ_PIN(GPIO_PORT_M, pin)
#define PNin(pin)   READ_PIN(GPIO_PORT_N, pin)
#define PPin(pin)   READ_PIN(GPIO_PORT_P, pin)
#define PRin(pin)   READ_PIN(GPIO_PORT_R, pin)
#define PTin(pin)   READ_PIN(GPIO_PORT_T, pin)
#define PUin(pin)   READ_PIN(GPIO_PORT_U, pin)

#define PVin(pin)   READ_PIN(GPIO_PORT_V, pin)


#endif

实际应用

LED初始化为普通输出:

#define LED_ON  PKout(4)=1
#define LED_OFF     PKout(4)=0

void LED_Init(void)
{
    gpio_t port;
    port.p_pk_instance = TSB_PK;    //GPIOK

    gpio_init(&port, GPIO_PORT_K);    //初始化GPIOK
    gpio_func(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_PK4_OUTPUT, GPIO_PIN_OUTPUT);
    //初始化熄灭
    gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_RESET);
       //LED_OFF;    //位带操作方式
}

KEY初始化为上拉输入:

#define KEY_IN  PVin(3)

void KEY_Init(void)
{
    gpio_t port;

    port.p_pv_instance = TSB_PV;    

    gpio_init(&port, GPIO_PORT_V);    

    gpio_func(&port, GPIO_PORT_V, GPIO_PORT_3, GPIO_PV3_INPUT, GPIO_PIN_INPUT);   //输入模式
    gpio_SetPullUp(&port, GPIO_PORT_V, GPIO_PORT_3, GPIO_PIN_SET);        //上拉
}

main.c主函数实现按键按下LED闪烁:

#include "main.h"

int main(void)
{
    LED_Init();
    delay_init();
    KEY_Init();

    while(1)
    {
        if(KEY_IN == GPIO_PIN_RESET)
        {
         LED_ON;
            delay_ms(50);
         LED_OFF;
            delay_ms(50);
        }
    }
}

总结

有了上面的代码,我们就可以像 51/AVR 一样操作东芝TT_M3HQ开发板的 IO 口了。

本文分享自微信公众号 - 电子电路开发学习(mcu149),作者:wcc149

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基于STM32+RT-Thread的新冠肺炎疫情监控平台

    上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台...

    单片机点灯小能手
  • FPGA单比特信号跨时钟域处理

    因为慢速时钟域的最短信号长度为1个时钟时钟周期即:100ns,大于快速时钟域的时钟周期10ns,所以只需要打3拍即可:

    单片机点灯小能手
  • 详解串行通信协议及其FPGA实现(二)

    基于Verilog实现标准串口协议发送8位数据:起始位 + 8位数据位 + 校验位 + 停止位 = 11位,每1位的时间是16个时钟周期,所以输入时钟应该为:波...

    单片机点灯小能手
  • 目标检测_0

    Dean0731
  • 微信小程序 UI界面

    组件通用属性: id class style hidden data-:用法,<view data-test="test" />,获取:e.curre...

    用户5760343
  • 《时代》评选出2014年度25大最佳发明

    大数据文摘
  • 新手,想用Nisight调试CUDA代码,但断点无效怎么破?

    新手,刚接触CUDA编程,搭好了环境,想用nsight来调试,在vs里面,在核函数里面设置了断点,用CUDA Debugging,但断点就是不生效,电脑左下角会...

    GPUS Lady
  • Linux下使用命令行调试Python程序

    Python提供类似于C++ gdb的调试工具pdb,我们可以在Linux下使用pdb在命令行下进行Python程序的调试。 官方参考网站: Pytho...

    卡尔曼和玻尔兹曼谁曼
  • 为什么我们要使用进销存软件来管理?

      除了录入简单的基础数据以外,别的什么都不用做,放心等待报表结果,这就是进销存管理软件应该具有的使用效果,只要通过单据形式把该录入到软件里的数据都正常录入后,...

    明象ERP
  • 【文字识别】百度AI之运行IOS示例工程总结

    中国AI有小帅 哈哈 有兴趣的可以扫码体验一下个人小程序 百度AI主页:http://ai.baidu.com/点击控制台登录或者注册即可 领先的AI服务分类...

    小帅丶

扫码关注云+社区

领取腾讯云代金券