本篇文章来自极术社区与兆易创新组织的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的数据手册说明:
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部分的移植也是看网上的,具体步骤还没时间去完全理清思路,现在就是能跑的状态,目前也是发的第一篇文章,如有错误请多多指教!