> 文档中心 > 【GD32F427开发板试用】位带操作实现多线程下的跑马灯

【GD32F427开发板试用】位带操作实现多线程下的跑马灯


本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:SmallWhite

一、位带操作

作用:对某一位或者几个连续的位进行操作

前言

我们在使用GD32等单片机时使用到的固件库编程,很经常会遇到位带操作,固件库对外设寄存器的每个关键bit都做了定义,例如宏定义中的:

写1:用1左移n位后和具体位进行|=的运算得到
写0:用1左移n位后取反得到第n位的0值然后和具体位进行&=的运算得到

/* GPIO_OCTL */#define GPIO_OCTL_OCTL0     BIT(0) #define GPIO_OCTL_OCTL1     BIT(1)

例如上述代码中的端口输出控制寄存器(GPIOx_OCTL),和待会还要用到的端口输入状态寄存器(GPIOx_ISTA),想要实现对GPIO的某一位(某一pin)操作的话,需要知道这两个寄存器的地址
1.端口输入状态寄存器(GPIOx_ISTAT, x=A…I)
地址偏移:0x10
复位值:0x0000 XXXX
2.端口输出控制寄存器(GPIOx_OCTL, x=A…I)
地址偏移:0x14
复位值:0x0000 0000

所以说每个端口x的输入状态以及输出控制寄存器的地址=端口x基地址+寄存器的偏移地址:

#define output_offset 0x14#define input_offset  0x10//IO口地址映射#define GPIOA_ODR_Addr    (GPIOA+output_offset) //0X40020000U+0x0U+output_offset#define GPIOB_ODR_Addr    (GPIOB+output_offset) //0X40020000U+0x00000400U+output_offset#define GPIOC_ODR_Addr    (GPIOC+output_offset) //0X40020000U+0x00000800U+output_offset#define GPIOD_ODR_Addr    (GPIOD+output_offset) #define GPIOE_ODR_Addr    (GPIOE+output_offset)    #define GPIOA_IDR_Addr    (GPIOA+input_offset) #define GPIOB_IDR_Addr    (GPIOB+input_offset)  #define GPIOC_IDR_Addr    (GPIOC+input_offset)#define GPIOD_IDR_Addr    (GPIOD+input_offset) #define GPIOE_IDR_Addr    (GPIOE+input_offset)

二、实现位带操作的前提条件

1.支持位带操作的两个内存区的范围是:
0x2000_0000‐0x200F_FFFF(SRAM区中的最低1MB)
0x4000_0000‐0x400F_FFFF(片上外设区中的最低1MB)
而GPIOA~GPIOE的地址刚好在以上范围内(0x4002 0000~0x4002 1000)

2.查阅GD32的数据手册说明:

【GD32F427开发板试用】位带操作实现多线程下的跑马灯

3.对于片上外设位带区的某个bit,我们假设记它所在字节地址为A,位序号为n,则该bit在别名区的地址为:
Addr=0x42000000+(A-0x40000000)_32+4_n

4.回到代码中实现:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
整体宏定义代码如下:

//IO口操作宏定义#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 output_offset 0x14#define input_offset  0x10//IO口地址映射#define GPIOA_ODR_Addr    (GPIOA+output_offset) //0X40020000U+0x0U+output_offset#define GPIOB_ODR_Addr    (GPIOB+output_offset) //0X40020000U+0x00000400U+output_offset#define GPIOC_ODR_Addr    (GPIOC+output_offset) //0X40020000U+0x00000800U+output_offset#define GPIOD_ODR_Addr    (GPIOD+output_offset) #define GPIOE_ODR_Addr    (GPIOE+output_offset)    #define GPIOA_IDR_Addr    (GPIOA+input_offset) #define GPIOB_IDR_Addr    (GPIOB+input_offset)  #define GPIOC_IDR_Addr    (GPIOC+input_offset)#define GPIOD_IDR_Addr    (GPIOD+input_offset) #define GPIOE_IDR_Addr    (GPIOE+input_offset)//IO口操作,只对单一的IO口!//确保n的值小于16!#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入void Led_Flash_Task(void *p_arg){    OS_ERR err;    while(1)    { running_led_out=!running_led_out; OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err);//延时1s    }}

5.现在跑个UCOS多线程下的跑马灯看看:

可以看到正常运行串口发送线程和跑马灯线程,UCOS部分的移植也是看网上的,具体步骤还没时间去完全理清思路,现在就是能跑的状态,目前也是发的第一篇文章,如有错误请多多指教!