C语言支持的6种位操作符如下:
方法:先对需要设置的位用&操作符(对应位&0)进行清零操作,然后用|操作符设值(对应位|你想要设定的值)。
举例:改变 GPIOA-> BSRRL 的状态
GPIOA-> BSRRL &=0XFF0F; //将第 4-7 位清 0
GPIOA-> BSRRL |=0X0040; //设置相应位的值,不改变其他位的值
位操作在单片机开发中也非常重要,我们来看看下面一行代码
GPIOx->ODR |= (((uint32_t)0x01) << pinpos);
这个操作就是将 ODR 寄存器的第 pinpos 位设置为 1,为什么要通过左移而不是直接设置一个固定的值呢?
其实,这是为了提高代码的可读性以及可重用性。这行代码可以很直观明了的知道,是将第 pinpos 位设置为 1。
如果你写成GPIOx->ODR =0x0040; 这样的代码就不好看也不好重用了。
SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为 0,同时其他位都保留为 1,
简单的作法是直接给寄存器设置一个值:
TIMx->SR=0xFFF7;
这样的作法设置第 3 位为 0,但是这样的作法同样不好看,并且可读性很差。看看库函数
代码中怎样使用的:
TIMx->SR &= (uint16_t)~TIM_FLAG;
而 TIM_FLAG 是通过宏定义定义的值:
#define TIM_FLAG ((uint16_t)0x0001)
看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_ 就是设置的第 0位了,可读性非常强。
define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供
方便。常见的格式:
#define 标识符 字符串
“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如:定义标识符 PLL_M 的值为 8。至于 define 宏定义的其他一些知识,比如宏定义带参数这里我们就不多讲解。
这个条件编译在MDK里面是用得很多的,在stm32f4xx.h这个头文件中经常会看到这样的语句:
#if defined (STM32F40_41xxx)
STM32F40x 系列和 STM32F41x 系列芯片需要的一些变量定义
#end
而STM32F40_41xxx 则是我们通过 #define 来定义的。
条件编译经常在头文件中使用,是为了防止一些.c文件重复包含头文件,导致重复编译出错。
具体看我的另外一篇博客点击打开链接。
译器遇到此变量和函数时在其他模块中寻找其定义。 这里面要注意,对于 extern 声明变量可以多
次,但定义只有一次。在我们的代码中你会看到看到这样的语句:
extern u16 USART_RX_STA;
这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,在这里要使用到。所以,你肯定
可以找到在某个地方有变量定义的语句:
u16 USART_RX_STA;
对于函数也是同样的应用。
struct _GPIO
{
__IO uint32_t MODER;
__IO uint32_t OTYPER;
…
}
struct _GPIO GPIOA;//定义结构体变量 GPIOA
但是这样很繁琐,MDK 中有很多这样的结构体变量需要定义。这里我们可以为结体定义一个别
名 GPIO_TypeDef,这样我们就可以在其他地方通过别名 GPIO_TypeDef 来定义结构体变量了。
方法如下:
typedef struct
{
__IO uint32_t MODER;
__IO uint32_t OTYPER;
…
} GPIO_TypeDef
Typedef 为结构体定义一个别名 GPIO_TypeDef,这样我们可以通过 GPIO_TypeDef 来定义结构体
这里的 GPIO_TypeDef 就跟 struct _GPIO 是等同的作用了。
当然typedef在程序代码可移植性上也有很大的帮助。比如在一些头文件中使用typedef如下:
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed __INT64 int64_t;
声明结构体类型:
Struct 结构体名{
成员列表;
}变量名列表;
例如:
Struct U_TYPE {
Int BaudRate
Int WordLength;
}usart1,usart2;
在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:
Struct 结构体名字 结构体变量列表 ;
例如:struct U_TYPE usart1,usart2;
结构体成员变量的引用方法是:
结构体变量名字.成员名
比如要引用 usart1 的成员 BaudRate,方法是:usart1.BaudRate;
结构体指针变量定义也是一样的,跟其他变量没有啥区别。
例如:struct U_TYPE *usart3;//定义结构体指针变量 usart1;
结构体指针成员变量引用方法是通过“->”符号实现,比如要访问 usart3 结构体指针指向的结
构体的成员变量 BaudRate,方法是:
Usart3->BaudRate;
使用结构体的好处是防止函数的入口参数过多,当然也利于增加变量时不用修改函数定义,对于一组描述
同一对象的参数,用结构体使他们形成一个整体,也有利于代码的可读性,不会使变量定义显得混乱。