位带操作简介
位带操作的概念其实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 口了。