
文章目錄
-
-
- 位帶操作簡介
- 位帶操作的優越性
- 位帶操作的實作
- 實際應用
- 總結
- 推薦閱讀
-
位帶操作簡介
位帶操作的概念其實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