• 东芝MCU实现位带操作


    位带操作简介

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

    位带操作的优越性

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

    #define LED P1^0
    #define 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 口了。


    推荐阅读


    • 我的个人博客:www.wangchaochao.top
    • 我的公众号:mcu149

  • 相关阅读:
    uni-app中动态设置头部颜色及字体
    微信小程序中 showToast 真机下一闪而过相关问题
    uni-app踩坑记
    配置git提交规范跟规范校验(ESLint、commitLint、husky)
    vscode中配置git终端
    vue插槽学习之——作用域插槽
    布局小技巧集合之——动态列表固定列数固定间距自适应布局
    写入自定义 ASP.NET Core 中间件
    [转]NET实现RSA AES DES 字符串 加密解密以及SHA1 MD5加密
    JavaScript事件循环机制
  • 原文地址:https://www.cnblogs.com/whik/p/11488709.html
Copyright © 2020-2023  润新知