位带

在说到位带和位操作前,先要复习一下指针的概念。指针就是地址,地址就是编号!

看这一条语句:*((int *) 0x10000000) = 0x100;,在stm32单片机中,完全裸写的寄存器去访问地址我们可以通过*((int *) 0x10000000)我们就可以将编号转换成地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Memory mapping of Cortex-M3 Hardware */
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
#define ITM_BASE (0xE0000000) /*!< ITM Base Address */
#define CoreDebug_BASE (0xE000EDF0) /*!< Core Debug Base Address */
#define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */
#define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */
#define SCB_BASE (SCS_BASE + 0x0D00) /*!< System Control Block Base Address */

#define InterruptType ((InterruptType_Type *) SCS_BASE) /*!< Interrupt Type Register */
#define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */
#define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */
#define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */
#define ITM ((ITM_Type *) ITM_BASE) /*!< ITM configuration struct */
#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */

core_cm3.h中可以看到类似(SCB_Type *) SCB_BASE)的操作,通过强制类型转换的方式将编号转换成地址。然后再通过基址+偏移来确定指定寄存器的地址。

在工作中一般要加上volatile关键字,防止编译器的过度优化,让cpu不再去缓存取数据,,而是每次都小心翼翼的从内存中取数据!

volatile的作用原理

在说之前要先明确,库函数和系统调用相比,库函数的执行效率一定是高于系统调用的。比如:库函数fread、系统调用read、内核中sys_read,三个不同的函数read是访问内存读取,而fread是先从缓冲区(cache)中读。当我们调用fread时,要先调用read将内存中的数据读到缓冲区中,而此时缓冲区中已经有数据就直接从缓冲区中读。就节省了read再到内核态调用sys_read的过程。

现在如果你没有加volatile,系统会自动将对应的数据拷贝到缓冲区中,此时就直接访问缓冲区。

假设内存中有一个数据i=0,将来执行时,cpu会从内存中把i取出来放到cpu内部的缓存cache,

image-20230331224604267

位操作