查看原文
其他

东芝MCU实现位带操作

wcc149 电子电路开发学习 2021-01-31


位带操作简介

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

位带操作的优越性

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

  1. sbit LED = P1^0;

  2. sbit KEY = P1^2;


  3. LED = 0; //输出0


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

  5. {


  6. }

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

写函数:

  1. gpio_t port;


  2. //PK4输出低电平

  3. gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_RESET);


  4. //PK4输出高电平

  5. gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_SET);

读函数:

  1. //读取PV3输入


  2. gpio_pinstate_t key_status;

  3. gpio_t port;

  4. gpio_read_bit(&port, KEY_PORT, KEY_PIN, GPIO_Mode_DATA, &key_status);

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

  1. PK4输出:PKout(4) = 0;

  2. 读取PV3输入:in = PVin(3);

实现按键按下LED闪烁:

  1. if(PVin(3) == GPIO_PIN_RESET) //按键按下LED闪烁

  2. {

  3. PKout(4) = 1; //点亮

  4. delay_ms(50);


  5. PKout(4) = 0; //熄灭

  6. delay_ms(50);

  7. }

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

位带操作的实现

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

  1. #ifndef __SYS_H__

  2. #define __SYS_H__


  3. #include "TMPM3HQ.h"

  4. #include "TMPM3Hy.h"


  5. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))

  6. #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

  7. #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))


  8. #define PORTx_BASE(group) (0x400C0000UL + (uint32_t)((0x0000100UL) * (group)))

  9. #define PORTx_MODE_BASE(group) ((uint32_t)(PORTx_BASE(group)) + (uint32_t)(GPIO_Mode_DATA))


  10. #define PORTA_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_A)

  11. #define PORTB_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_B)

  12. #define PORTC_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_C)

  13. #define PORTD_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_D)

  14. #define PORTE_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_E)

  15. #define PORTF_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_F)

  16. #define PORTG_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_G)

  17. #define PORTH_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_H)

  18. #define PORTJ_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_J)

  19. #define PORTK_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_K)

  20. #define PORTL_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_L)

  21. #define PORTM_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_M)

  22. #define PORTN_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_N)

  23. #define PORTP_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_P)

  24. #define PORTR_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_R)

  25. #define PORTT_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_T)

  26. #define PORTU_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_U)

  27. #define PORTV_ODR_ADDR PORTx_MODE_BASE(GPIO_PORT_V)


  28. #define PAout(n) BIT_ADDR(PORTA_ODR_ADDR, n)

  29. #define PBout(n) BIT_ADDR(PORTB_ODR_ADDR, n)

  30. #define PCout(n) BIT_ADDR(PORTC_ODR_ADDR, n)

  31. #define PDout(n) BIT_ADDR(PORTD_ODR_ADDR, n)

  32. #define PEout(n) BIT_ADDR(PORTE_ODR_ADDR, n)

  33. #define PFout(n) BIT_ADDR(PORTF_ODR_ADDR, n)

  34. #define PGout(n) BIT_ADDR(PORTG_ODR_ADDR, n)

  35. #define PHout(n) BIT_ADDR(PORTH_ODR_ADDR, n)

  36. #define PJout(n) BIT_ADDR(PORTJ_ODR_ADDR, n)

  37. #define PKout(n) BIT_ADDR(PORTK_ODR_ADDR, n)

  38. #define PLout(n) BIT_ADDR(PORTL_ODR_ADDR, n)

  39. #define PMout(n) BIT_ADDR(PORTM_ODR_ADDR, n)

  40. #define PNout(n) BIT_ADDR(PORTN_ODR_ADDR, n)

  41. #define PPout(n) BIT_ADDR(PORTP_ODR_ADDR, n)

  42. #define PRout(n) BIT_ADDR(PORTR_ODR_ADDR, n)

  43. #define PTout(n) BIT_ADDR(PORTT_ODR_ADDR, n)

  44. #define PUout(n) BIT_ADDR(PORTU_ODR_ADDR, n)

  45. #define PVout(n) BIT_ADDR(PORTV_ODR_ADDR, n)



  46. //实现指定管脚置位和复位

  47. /*

  48. PORTx_SET(GPIO_PORT_K, 5);

  49. PORTx_CLR(GPIO_PORT_K, 4);

  50. */

  51. #define PORTx_SET(group, pin) (*((__IO uint32_t *)PORTx_MODE_BASE(group)) |= (uint32_t)(0x0000001UL<< pin))

  52. #define PORTx_CLR(group, pin) (*((__IO uint32_t *)PORTx_MODE_BASE(group)) &= ~((uint32_t)(0x0000001UL<< pin)))


  53. /*

  54. //实现指定管脚置位和复位

  55. #define PORTx_SET(group, pin) (BIT_ADDR(PORTx_MODE_BASE(group), pin)=1)

  56. #define PORTx_CLR(group, pin) (BIT_ADDR(PORTx_MODE_BASE(group), pin)=0)

  57. */


  58. //读取指定引脚的输入状态

  59. #define READ_PIN(group, pin) ((*((__IO uint32_t *)(PORTx_MODE_BASE(group))) & (uint32_t)(0x0000001UL<< pin)) >> pin)


  60. //输入状态 = GPIO_PIN_RESET or GPIO_PIN_SET

  61. #define PAin(pin) READ_PIN(GPIO_PORT_A, pin)

  62. #define PBin(pin) READ_PIN(GPIO_PORT_B, pin)

  63. #define PCin(pin) READ_PIN(GPIO_PORT_C, pin)

  64. #define PDin(pin) READ_PIN(GPIO_PORT_D, pin)

  65. #define PEin(pin) READ_PIN(GPIO_PORT_E, pin)

  66. #define PFin(pin) READ_PIN(GPIO_PORT_F, pin)

  67. #define PGin(pin) READ_PIN(GPIO_PORT_G, pin)

  68. #define PHin(pin) READ_PIN(GPIO_PORT_H, pin)

  69. #define PJin(pin) READ_PIN(GPIO_PORT_J, pin)

  70. #define PKin(pin) READ_PIN(GPIO_PORT_K, pin)

  71. #define PLin(pin) READ_PIN(GPIO_PORT_L, pin)

  72. #define PMin(pin) READ_PIN(GPIO_PORT_M, pin)

  73. #define PNin(pin) READ_PIN(GPIO_PORT_N, pin)

  74. #define PPin(pin) READ_PIN(GPIO_PORT_P, pin)

  75. #define PRin(pin) READ_PIN(GPIO_PORT_R, pin)

  76. #define PTin(pin) READ_PIN(GPIO_PORT_T, pin)

  77. #define PUin(pin) READ_PIN(GPIO_PORT_U, pin)


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



  79. #endif

实际应用

LED初始化为普通输出:

  1. #define LED_ON PKout(4)=1

  2. #define LED_OFF PKout(4)=0


  3. void LED_Init(void)

  4. {

  5. gpio_t port;

  6. port.p_pk_instance = TSB_PK; //GPIOK


  7. gpio_init(&port, GPIO_PORT_K); //初始化GPIOK

  8. gpio_func(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_PK4_OUTPUT, GPIO_PIN_OUTPUT);

  9. //初始化熄灭

  10. gpio_write_bit(&port, GPIO_PORT_K, GPIO_PORT_4, GPIO_Mode_DATA, GPIO_PIN_RESET);

  11. //LED_OFF; //位带操作方式

  12. }

KEY初始化为上拉输入:

  1. #define KEY_IN PVin(3)


  2. void KEY_Init(void)

  3. {

  4. gpio_t port;


  5. port.p_pv_instance = TSB_PV;


  6. gpio_init(&port, GPIO_PORT_V);


  7. gpio_func(&port, GPIO_PORT_V, GPIO_PORT_3, GPIO_PV3_INPUT, GPIO_PIN_INPUT); //输入模式

  8. gpio_SetPullUp(&port, GPIO_PORT_V, GPIO_PORT_3, GPIO_PIN_SET); //上拉

  9. }

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

  1. #include "main.h"


  2. int main(void)

  3. {

  4. LED_Init();

  5. delay_init();

  6. KEY_Init();


  7. while(1)

  8. {

  9. if(KEY_IN == GPIO_PIN_RESET)

  10. {

  11. LED_ON;

  12. delay_ms(50);

  13. LED_OFF;

  14. delay_ms(50);

  15. }

  16. }

  17. }

总结

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


推荐阅读


  • 我的个人博客:www.wangchaochao.top

  • 我的公众号:mcu149

由于微信文章不支持超链接,文中出现的软件、程序等文件下载,可以点击"阅读原文",跳转到我的博客文章进行下载。



原创不易,如果觉得我的文章对你有所帮助,可以随手点“好看”分享,你的支持将是我持续更新的动力。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存