注:测试硬件非STM32(而是nRF52832),但是笔者认为这在所有Cortex-M3、M4单片机上是一样的情况,所以标题为了与之前系列相同而取了STM32。
1.问题描述
全局资源在此处通常指一个全局变量,它可以是一个全局的buf、结构体变量、指针等等。如果你的代码里只有一个while,没有使用任何RTOS或中断,那是不会存在资源竟争问题的,因为不存在打断操作,代码执行流程永远是从上到下顺序执行。(MCU上好像没有听说有使用乱序执行技术)
之前也有遇到过相似的问题,不过没有完整记录,此次遇到就正好完整记录一下。
代码很简单,笔者在main函数中while(1)中使用发送函数发送一段数据,使用3个变量:
发送次数累加,发送成功一次累加,发送失败一次累加
正常我们很容易知道:
发送次数 = 发送成功次数 + 发送失败次数
然后笔者使用一个定时器,每隔1秒在定时器中断回调中打印发送的次数、成功的次数、失败的次数,然后清0,但是会出现:
发送次数 != 发送成功次数+发送失败次数
而且相差很大。
2.原因分析
变量声明:
main的循环中有:
很简单明了的代码,调用 nrf_esb_write_payload 这个发送函数,要么成功,要么失败,所以无论如何总有:
writeCount = writeSuccess + writeFailed
然后笔者使用了一个定时器,定时时间为1秒,也就是1秒触发一次中断回调,然后在中断回调里对这三个变量打印,定时器配置不再赘述,直接给中断回调函数内容:
可以看到,打印完这三个变量后,就把3个变量置0,重新开始累计。
实际代码运行打印:
可以看到出现异常的时候,一共发送了628886次,却失败了1253521次或是发送了1257731次,却成功2025次,失败626839次,剩余那60多万次哪去了?
其实仔细观察,可以发现,异常的时候,均是那个异常变量没有清0,其值是此次值和上次的值相加!
难道是清0那行代码没有生效?
不可能,首先只要正常打印了,它后面清0的代码也必定执行,而且如果没有清0,必定3个变量都不会清0,而且即使是在定时器中断回调执行时被其他中断抢占,在其他中断结束后,也会继续执行定时器中断。
遇事不决,查看汇编。
上图是中断中如何令writeCount这个变量清0。
上图中已经描述了变量累加以及清0是如何用汇编实现的。看到这你应该明白:
writeCount++;
这个看似只有一行的代码,实际用了3行汇编去实现。这就有个问题,假如在执行:
LDR R3, [R6]
这行代码后,被定时器中断打断,转而去执行了中断处理函数会发生什么?
查看变量地址我们知道:
writeCount这个变量其实地址是0x2000 0248,一开始
R6 = writeCount
其实就是R6 = 0x2000 0248,然后
R3 = *R6
即R3 = *(0x2000 0248),假如此时已经发送了60 0000次了,那也就是R3 = 60 0000,这个时候定时器定时时间到了,跑去执行定时器中断处理函数,再假如此时发送成功了2000次,失败了59 8000次,那么定时器中断处理函数中会打印:
发送60 0000次,成功2000次,失败59 8000次
然后把writeCount、writeSuccess、writeFailed都清0了,也就是此时你再去取0x2000 0248这个地址上的值,已经变成0了,但是,在处理完定时器中断回调后,我们继续执行的是:
ADDS R3, #1
STR R3, [R6]
因为在中断前我们已经取过 0x2000 0248这个地址上的值到R3中了,即使再回来继续执行,我们用的之前保存到R3里面的值,此时R3还是之前读进去的60 0000,继续执行则变成:
R3 = 60 0000+1
*(0x2000 0248) = 60 0001
此时我们把刚清0的writeCount赋值成了60 0001!这导致我们在中断中的清0操作失效了!
3.解决办法
3.1原子操作
由上一节分析我们知道了为啥清0会失败,就是因为
writeCount++
这个操作是分为3步执行的!假如这个操作必须完整的执行,不能拆分,那也就不存在上面的异常情况。不能拆分的操作,我们称作原子操作,在nRF的SDK中有提供原子操作的库函数,它们被放在 nrf_atomic.c中,如果你使用的是GCC编译器,那么这些函数稍作修改也同样可以运用到STM32中,因为它们都是cortex-M4内核的MCU,本质是一样的。该文件中主要函数是:
这个函数中主要是汇编操作:
可以看到主要使用的是ldrex和strex独占访问指令(也有叫排他访问),感兴趣的可以自行搜索。
STM提供的原子操作:
这里我们使用nRF的SDK中提供的原子操作函数试一下:
经过长时间测试发现不再会出现上述问题,相关打印如下:
但是可以看到,因为原子操作需要更多的指令,效率有所下降。
3.2互斥量
互斥量常常用于多线程中访问共享资源的情况,在nRF的SDK中互斥量的实现在nrf_mtx.h中。
其实互斥量很简单,同一时刻,互斥量只能被一个地方拥有(这个地方可以是中断处理回调或者RTOS中的任务循环函数)。当我们在任何需要修改一个共享资源的时候,先去获取一个互斥量,如果可以获取到,证明这个资源没有在被使用,如果没有获取到证明有其他修改它的地方已经获取到了。其实互斥量的实现也使用了原子操作:
可以看到每个从互斥量操作都使用了__DMB() 这个内联汇编函数 ,它的函数原型是:
在Cortex-M3权威指南里有:
这个和Cortex-M的3级流水线设计息息相关,感兴趣的自行搜索。
最后main函数类似这样:
但是注意,如果是在RTOS中使用互斥锁很方便,但是在中断中使用互斥锁就不太好,原因如下:
1.中断等待与死锁
在你刚开始学习中断的时候肯定有人告诉你,中断中不能延时,这里如果是一个任务使用while等待一点问题没有,但是因为不清楚while到底会等待多久,所以这里肯定不可以使用while等待。
其次,while在这里会造成死锁,因为假如我们在main函数中已经获取到了互斥锁,这时候被定时器中断回调打断,定时器中断就算一直等待其他地方互斥锁释放也是徒劳,因为中断回调没有执行完,永远不会转头回去继续执行main函数中的while,而main中的while那次循环执行不完,又永远不会释放互斥锁,这就陷入了死循环,也可以叫死锁。
2.跳过
在这个例子中,因为中断每次进入都有一秒的间隔,这里如果获取不到就跳过,那么每次都会出现2秒才打印1次的现象,所以这里跳过也不太合理。当然,如果这是在一个会一直循环的任务里影响就很小了。
综上所述,在中断中使用互斥量不是一个好的选择,但是如果是在RTOS的任务中会有效的多。
3.3关中断
如果你使用过RTOS,你应该知道临界区,它就是保护资源的一种方式,临界区最简单的做法就是关闭中断。上述原子操作的解决思路是把操作压缩到一个不可分割的整体,是从自身出发,而关中断会让定时器这个可能打断我们操作的异常给kill掉,这是从外部出发。
nRF的SDK中有提供打开和关闭中断的方法,在cmsis_gcc.h中有:
而我们需要做的只是:
这样,在操作这几个全局变量的时候就不会被定时器中断所打断,打印如下:
可以看到,虽然有所损失,但是比原子操作耗时短得多,几乎和没有关中断差不太多。但是这儿有个问题,在笔者这个例子中关闭中断和再次开启中断中间只有一个发送函数,如果你所需要保护的区域有很多操作甚至是等待时,长时间的关闭中断很有可能带来其他问题。我们需要尽可能的把临界区弄的短一点:
最好是只在最关键的操作使用中断的开关。当然这势必造成效率下降:
很显然,在这个例子中,只在开头和结尾开关中断好像比分割成3次更加合理。
3.4屏蔽中断
Cortex-M内核很贴心的提供了可屏蔽优先级寄存器BASEPRI:
关于这个寄存器,更为详细的资料可以参考宋岩译的那本《Cortex-M3权威指南》。
而在cmsis_gcc.h中也有设置这个寄存器的函数:
另一个带MAX的是:
所以我们只需要这样操作:
和上一个关中断差不多,但是这里笔者配置的定时器中断优先级是6,而其他中断优先级都在4以上,所以这里我设置为屏蔽4及以下的中断(优先级号越大,优先级越低) 。这里如果是STM32,则需要根据所选的中断分组再判断是设置高4位的哪几位,因为STM32分抢占优先级和响应优先级,这个设置只对抢占优先级有效。而nRF只有0~7八个优先级,不分抢占和响应,所以直接设置即可,至于为什么是高4位,因为芯片设计如此。
这里同样解决了上述问题,打印如下:
可以看到设置此寄存器比只开关中断要耗时的多。如果我们按照上一节思路,把开关屏蔽中断分为3次,应该更加耗时:
打印如下:
可以看到和关闭全局中断差不多。
4.总结
在使用一个共享的全局资源时,多个地方修改这个资源可能会引发问题。可以使用原子操作、互斥量、关闭中断或者屏蔽中断解决。它们各有利弊:
1.原子操作并不一定是一条指令,而可能是很多条指令,只是通过独占访问等实现,所以原子操作可能更加耗时,对于时间敏感的地方需要衡量是否会影响功能。但是好处是它不对其他中断有影响,比如这个例子中的定时器中断,它的响应并不会被延迟。
2.关闭所有中断简单粗暴,但在实际项目中,可能使用了很多中断,并不是所有中断都可以随意关闭,有些中断被关闭可能引发一些问题。虽然关闭期间触发的中断会在中断打开后继续执行,但是这无疑加大了中断延迟,对于一些对响应速度要求很高的中断来说,这无疑是灭顶之灾。所以关闭中断使用次数越少越好,关闭时间越短越好。
3.屏蔽部分中断解决了一部分关闭所有中断的问题,我们可以只屏蔽那些优先级没那么高的中断,但是加重了对不同中断优先级分配的复杂度。像此例子中nRF只有0~7八种中断优先级,而STM32中屏蔽中断只能对抢占优先级生效,所以实际设置时要根据4种主次优先级分配的位数设置。
4.互斥量在中断中使用会引起很多问题,应该在RTOS的任务中使用。
附:
nrf_atomic.c
/**
* Copyright (c) 2018 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "nrf_atomic.h"
#ifndef NRF_ATOMIC_USE_BUILD_IN
#if (defined(__GNUC__) && defined(WIN32))
#define NRF_ATOMIC_USE_BUILD_IN 1
#else
#define NRF_ATOMIC_USE_BUILD_IN 0
#endif
#endif // NRF_ATOMIC_USE_BUILD_IN
#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U))
#define STREX_LDREX_PRESENT
#else
#include "app_util_platform.h"
#endif
#if (NRF_ATOMIC_USE_BUILD_IN == 0) && defined(STREX_LDREX_PRESENT)
#include "nrf_atomic_internal.h"
#endif
uint32_t nrf_atomic_u32_fetch_store(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_exchange_n(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(mov, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data = value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_store(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
__atomic_store_n(p_data, value, __ATOMIC_SEQ_CST);
return value;
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(mov, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data = value;
CRITICAL_REGION_EXIT();
return value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_fetch_or(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_fetch_or(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(orr, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data |= value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_or(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_or_fetch(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(orr, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data |= value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_fetch_and(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_fetch_and(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(and, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data &= value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_and(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_and_fetch(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(and, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data &= value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_fetch_xor(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_fetch_xor(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(eor, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data ^= value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_xor(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_xor_fetch(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(eor, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data ^= value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_fetch_add(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_fetch_add(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(add, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data += value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_add(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_add_fetch(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(add, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data += value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_fetch_sub(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_fetch_sub(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(sub, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data -= value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_sub(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_sub_fetch(p_data, value, __ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(sub, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data -= value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
bool nrf_atomic_u32_cmp_exch(nrf_atomic_u32_t * p_data,
uint32_t * p_expected,
uint32_t desired)
{
#if NRF_ATOMIC_USE_BUILD_IN
return __atomic_compare_exchange(p_data,
p_expected,
&desired,
1,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
#elif defined(STREX_LDREX_PRESENT)
return nrf_atomic_internal_cmp_exch(p_data, p_expected, desired);
#else
bool ret;
CRITICAL_REGION_ENTER();
if (*p_data == *p_expected)
{
*p_data = desired;
ret = true;
}
else
{
*p_expected = *p_data;
ret = false;
}
CRITICAL_REGION_EXIT();
return ret;
#endif
}
uint32_t nrf_atomic_u32_fetch_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
uint32_t expected = *p_data;
uint32_t new_val;
bool success;
do
{
if (expected >= value)
{
new_val = expected - value;
}
else
{
new_val = expected;
}
success = __atomic_compare_exchange(p_data,
&expected,
&new_val,
1,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
} while(!success);
return expected;
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(sub_hs, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return old_val;
#else
CRITICAL_REGION_ENTER();
uint32_t old_val = *p_data;
*p_data -= value;
CRITICAL_REGION_EXIT();
return old_val;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_u32_sub_hs(nrf_atomic_u32_t * p_data, uint32_t value)
{
#if NRF_ATOMIC_USE_BUILD_IN
uint32_t expected = *p_data;
uint32_t new_val;
bool success;
do
{
if (expected >= value)
{
new_val = expected - value;
}
else
{
new_val = expected;
}
success = __atomic_compare_exchange(p_data,
&expected,
&new_val,
1,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
} while(!success);
return new_val;
#elif defined(STREX_LDREX_PRESENT)
uint32_t old_val;
uint32_t new_val;
NRF_ATOMIC_OP(sub_hs, old_val, new_val, p_data, value);
UNUSED_PARAMETER(old_val);
UNUSED_PARAMETER(new_val);
return new_val;
#else
CRITICAL_REGION_ENTER();
*p_data -= value;
uint32_t new_value = *p_data;
CRITICAL_REGION_EXIT();
return new_value;
#endif //NRF_ATOMIC_USE_BUILD_IN
}
uint32_t nrf_atomic_flag_set_fetch(nrf_atomic_flag_t * p_data)
{
return nrf_atomic_u32_fetch_or(p_data, 1);
}
uint32_t nrf_atomic_flag_set(nrf_atomic_flag_t * p_data)
{
return nrf_atomic_u32_or(p_data, 1);
}
uint32_t nrf_atomic_flag_clear_fetch(nrf_atomic_flag_t * p_data)
{
return nrf_atomic_u32_fetch_and(p_data, 0);
}
uint32_t nrf_atomic_flag_clear(nrf_atomic_flag_t * p_data)
{
return nrf_atomic_u32_and(p_data, 0);
}
nrf_atomic_internal.h
/**
* Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_ATOMIC_INTERNAL_H__
#define NRF_ATOMIC_INTERNAL_H__
#include "sdk_common.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
*
* @defgroup nrf_atomic_internal Atomic operations internals
* @ingroup nrf_atomic
* @{
*
*/
/* Only Cortex M cores > 3 support LDREX/STREX instructions*/
#if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0
#error "Unsupported core version"
#endif
#if defined ( __CC_ARM )
static __asm uint32_t nrf_atomic_internal_mov(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
/* The base standard provides for passing arguments in core registers (r0-r3) and on the stack.
* Registers r4 and r5 have to be saved on stack. Note that only even number of register push are
* allowed. This is a requirement of the Procedure Call Standard for the ARM Architecture [AAPCS].
* */
push {r4, r5}
mov r4, r0
loop_mov
ldrex r0, [r4]
mov r5, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_mov
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm uint32_t nrf_atomic_internal_orr(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_orr
ldrex r0, [r4]
orr r5, r0, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_orr
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm uint32_t nrf_atomic_internal_and(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_and
ldrex r0, [r4]
and r5, r0, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_and
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm uint32_t nrf_atomic_internal_eor(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_eor
ldrex r0, [r4]
eor r5, r0, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_eor
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm uint32_t nrf_atomic_internal_add(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_add
ldrex r0, [r4]
add r5, r0, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_add
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm uint32_t nrf_atomic_internal_sub(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_sub
ldrex r0, [r4]
sub r5, r0, r1
strex r3, r5, [r4]
cmp r3, #0
bne loop_sub
str r5, [r2]
pop {r4, r5}
bx lr
}
static __asm bool nrf_atomic_internal_cmp_exch(nrf_atomic_u32_t * p_data,
uint32_t * p_expected,
uint32_t value)
{
#define RET_REG r0
#define P_EXPC r1
#define VALUE r2
#define STR_RES r3
#define P_DATA r4
#define EXPC_VAL r5
#define ACT_VAL r6
push {r4-r6}
mov P_DATA, r0
mov RET_REG, #0
loop_cmp_exch
ldrex ACT_VAL, [P_DATA]
ldr EXPC_VAL, [P_EXPC]
cmp ACT_VAL, EXPC_VAL
ittee eq
strexeq STR_RES, VALUE, [P_DATA]
moveq RET_REG, #1
strexne STR_RES, ACT_VAL, [P_DATA]
strne ACT_VAL, [P_EXPC]
cmp STR_RES, #0
itt ne
movne RET_REG, #0
bne loop_cmp_exch
pop {r4-r6}
bx lr
#undef RET_REG
#undef P_EXPC
#undef VALUE
#undef STR_RES
#undef P_DATA
#undef EXPC_VAL
#undef ACT_VAL
}
static __asm uint32_t nrf_atomic_internal_sub_hs(nrf_atomic_u32_t * p_ptr,
uint32_t value,
uint32_t * p_new)
{
push {r4, r5}
mov r4, r0
loop_sub_ge
ldrex r0, [r4]
cmp r0, r1
ite hs
subhs r5, r0, r1
movlo r5, r0
strex r3, r5, [r4]
cmp r3, #0
bne loop_sub_ge
str r5, [r2]
pop {r4, r5}
bx lr
}
#define NRF_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \
old_val = nrf_atomic_internal_##asm_op(ptr, value, &new_val)
#elif defined ( __ICCARM__ ) || defined ( __GNUC__ )
/**
* @brief Atomic operation generic macro
* @param[in] asm_op operation: mov, orr, and, eor, add, sub
* @param[out] old_val atomic object output (uint32_t), value before operation
* @param[out] new_val atomic object output (uint32_t), value after operation
* @param[in] value atomic operation operand
* */
#define NRF_ATOMIC_OP(asm_op, old_val, new_val, ptr, value) \
{ \
uint32_t str_res; \
__ASM volatile( \
"1: ldrex %["#old_val"], [%["#ptr"]]\n" \
NRF_ATOMIC_OP_##asm_op(new_val, old_val, value) \
" strex %[str_res], %["#new_val"], [%["#ptr"]]\n" \
" teq %[str_res], #0\n" \
" bne.n 1b" \
: \
[old_val]"=&r" (old_val), \
[new_val]"=&r" (new_val), \
[str_res]"=&r" (str_res) \
: \
[ptr]"r" (ptr), \
[value]"r" (value) \
: "cc"); \
UNUSED_PARAMETER(str_res); \
}
#define NRF_ATOMIC_OP_mov(new_val, old_val, value) "mov %["#new_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_orr(new_val, old_val, value) "orr %["#new_val"], %["#old_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_and(new_val, old_val, value) "and %["#new_val"], %["#old_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_eor(new_val, old_val, value) "eor %["#new_val"], %["#old_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_add(new_val, old_val, value) "add %["#new_val"], %["#old_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_sub(new_val, old_val, value) "sub %["#new_val"], %["#old_val"], %["#value"]\n"
#define NRF_ATOMIC_OP_sub_hs(new_val, old_val, value) \
"cmp %["#old_val"], %["#value"]\n " \
"ite hs\n" \
"subhs %["#new_val"], %["#old_val"], %["#value"]\n" \
"movlo %["#new_val"], %["#old_val"]\n"
static inline bool nrf_atomic_internal_cmp_exch(nrf_atomic_u32_t * p_data,
uint32_t * p_expected,
uint32_t value)
{
bool res = false;
uint32_t str_res = 0;
uint32_t act_val = 0;
uint32_t exp_val = 0;
UNUSED_VARIABLE(str_res);
UNUSED_VARIABLE(act_val);
UNUSED_VARIABLE(exp_val);
__ASM volatile(
"1: ldrex %[act_val], [%[ptr]]\n"
" ldr %[exp_val], [%[expc]]\n"
" cmp %[act_val], %[exp_val]\n"
" ittee eq\n"
" strexeq %[str_res], %[value], [%[ptr]]\n"
" moveq %[res], #1\n"
" strexne %[str_res], %[act_val], [%[ptr]]\n"
" strne %[act_val], [%[expc]]\n"
" cmp %[str_res], #0\n"
" itt ne\n"
" movne %[res], #0\n"
" bne.n 1b"
:
[res] "=&r" (res),
[exp_val] "=&r" (exp_val),
[act_val] "=&r" (act_val),
[str_res] "=&r" (str_res)
:
"0" (res),
"1" (exp_val),
"2" (act_val),
[expc] "r" (p_expected),
[ptr] "r" (p_data),
[value] "r" (value)
: "cc");
return res;
}
#else
#error "Unsupported compiler"
#endif
#ifdef __cplusplus
}
#endif
#endif /* NRF_ATOMIC_INTERNAL_H__ */
/** @} */
nrf_mtx.h
/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
* @defgroup nrf_mtx nRF Mutex
* @{
* @ingroup app_common
* @brief Mutex used for protecting resources.
*
* This module provides a mutex that can be used to ensure only one context may enter a critical
* section holding the lock.
*/
#ifndef NRF_MTX_H__
#define NRF_MTX_H__
#include <stdint.h>
#include <stdbool.h>
#include "nrf.h"
#include "nrf_atomic.h"
#include "nrf_assert.h"
#define NRF_MTX_LOCKED 1
#define NRF_MTX_UNLOCKED 0
/**
* @brief Mutex data type.
*
* All fields in this struct are internal, and should never be modified outside of the nrf_mtx_*
* functions.
*/
typedef nrf_atomic_u32_t nrf_mtx_t;
/**
* @brief Initialize mutex.
*
* This function _must_ be called before nrf_mtx_trylock() and nrf_mtx_unlock() functions.
*
* @param[in, out] p_mtx The mutex to be initialized.
*/
__STATIC_INLINE void nrf_mtx_init(nrf_mtx_t * p_mtx);
/**
* @brief Destroy mutex.
*
* This function can be used in abort scenarios or when the mutex is no longer to be used.
*
* @param[in] p_mtx The mutex to be destroy.
*/
__STATIC_INLINE void nrf_mtx_destroy(nrf_mtx_t * p_mtx);
/**
* @brief Try to lock a mutex.
*
* If the mutex is already held by another context, this function will return immediately.
*
* @param[in, out] p_mtx The mutex to be locked.
* @return true if lock was acquired, false if not
*/
__STATIC_INLINE bool nrf_mtx_trylock(nrf_mtx_t * p_mtx);
/**
* @brief Unlock a mutex.
*
* This function _must_ only be called when holding the lock. Unlocking a mutex which you do not
* hold will give undefined behavior.
*
* @note Unlock must happen from the same context as the one used to lock the mutex.
*
* @param[in, out] p_mtx The mutex to be unlocked.
*/
__STATIC_INLINE void nrf_mtx_unlock(nrf_mtx_t * p_mtx);
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE void nrf_mtx_init(nrf_mtx_t * p_mtx)
{
ASSERT(p_mtx != NULL);
*p_mtx = NRF_MTX_UNLOCKED;
__DMB();
}
__STATIC_INLINE void nrf_mtx_destroy(nrf_mtx_t * p_mtx)
{
ASSERT(p_mtx != NULL);
// Add memory barrier to ensure that any memory operations protected by the mutex complete
// before the mutex is destroyed.
__DMB();
*p_mtx = NRF_MTX_UNLOCKED;
}
__STATIC_INLINE bool nrf_mtx_trylock(nrf_mtx_t * p_mtx)
{
ASSERT(p_mtx != NULL);
uint32_t old_val = nrf_atomic_u32_fetch_store(p_mtx, NRF_MTX_LOCKED);
// Add memory barrier to ensure that the mutex is locked before any memory operations protected
// by the mutex are started.
__DMB();
return (old_val == NRF_MTX_UNLOCKED);
}
__STATIC_INLINE void nrf_mtx_unlock(nrf_mtx_t * p_mtx)
{
ASSERT(p_mtx != NULL);
ASSERT(*p_mtx == NRF_MTX_LOCKED);
// Add memory barrier to ensure that any memory operations protected by the mutex complete
// before the mutex is unlocked.
__DMB();
*p_mtx = NRF_MTX_UNLOCKED;
}
#endif //SUPPRESS_INLINE_IMPLEMENTATION
#endif // NRF_MTX_H__
/** @} */
nordic_common.h
/**
* Copyright (c) 2008 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
* @brief Common defines and macros for firmware developed by Nordic Semiconductor.
*/
#ifndef NORDIC_COMMON_H__
#define NORDIC_COMMON_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Check if selected module is enabled
*
* This is save function for driver enable checking.
* Correct from Lint point of view (not using default of undefined value).
*
* Usage:
* @code
#if NRF_MODULE_ENABLED(UART)
...
#endif
* @endcode
*
* @param module The module name.
*
* @retval 1 The macro <module>_ENABLE is defined and is non-zero.
* @retval 0 The macro <module>_ENABLE is not defined or it equals zero.
*
* @note
* This macro intentionally does not implement second expansion level.
* The name of the module to be checked has to be given directly as a parameter.
* And given parameter would be connected with @c _ENABLED postfix directly
* without evaluating its value.
*/
//lint -emacro(491,NRF_MODULE_ENABLED) // Suppers warning 491 "non-standard use of 'defined' preprocessor operator"
#ifdef NRF_MODULE_ENABLE_ALL
#warning "Do not use NRF_MODULE_ENABLE_ALL for real builds."
#define NRF_MODULE_ENABLED(module) 1
#else
#define NRF_MODULE_ENABLED(module) \
((defined(module ## _ENABLED) && (module ## _ENABLED)) ? 1 : 0)
#endif
/** The upper 8 bits of a 32 bit value */
//lint -emacro(572,MSB_32) // Suppress warning 572 "Excessive shift value"
#define MSB_32(a) (((a) & 0xFF000000) >> 24)
/** The lower 8 bits (of a 32 bit value) */
#define LSB_32(a) ((a) & 0x000000FF)
/** The upper 8 bits of a 16 bit value */
//lint -emacro(572,MSB_16) // Suppress warning 572 "Excessive shift value"
#define MSB_16(a) (((a) & 0xFF00) >> 8)
/** The lower 8 bits (of a 16 bit value) */
#define LSB_16(a) ((a) & 0x00FF)
/** Leaves the minimum of the two 32-bit arguments */
/*lint -emacro(506, MIN) */ /* Suppress "Constant value Boolean */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
/** Leaves the maximum of the two 32-bit arguments */
/*lint -emacro(506, MAX) */ /* Suppress "Constant value Boolean */
#define MAX(a, b) ((a) < (b) ? (b) : (a))
/**@brief Concatenates two parameters.
*
* It realizes two level expansion to make it sure that all the parameters
* are actually expanded before gluing them together.
*
* @param p1 First parameter to concatenating
* @param p2 Second parameter to concatenating
*
* @return Two parameters glued together.
* They have to create correct C mnemonic in other case
* preprocessor error would be generated.
*
* @sa CONCAT_3
*/
#define CONCAT_2(p1, p2) CONCAT_2_(p1, p2)
/** Auxiliary macro used by @ref CONCAT_2 */
#define CONCAT_2_(p1, p2) p1##p2
/**@brief Concatenates three parameters.
*
* It realizes two level expansion to make it sure that all the parameters
* are actually expanded before gluing them together.
*
* @param p1 First parameter to concatenating
* @param p2 Second parameter to concatenating
* @param p3 Third parameter to concatenating
*
* @return Three parameters glued together.
* They have to create correct C mnemonic in other case
* preprocessor error would be generated.
*
* @sa CONCAT_2
*/
#define CONCAT_3(p1, p2, p3) CONCAT_3_(p1, p2, p3)
/** Auxiliary macro used by @ref CONCAT_3 */
#define CONCAT_3_(p1, p2, p3) p1##p2##p3
#define STRINGIFY_(val) #val
/** Converts a macro argument into a character constant.
*/
#define STRINGIFY(val) STRINGIFY_(val)
/** Counts number of elements inside the array
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/**@brief Set a bit in the uint32 word.
*
* @param[in] W Word whose bit is being set.
* @param[in] B Bit number in the word to be set.
*/
#define SET_BIT(W, B) ((W) |= (uint32_t)(1U << (B)))
/**@brief Clears a bit in the uint32 word.
*
* @param[in] W Word whose bit is to be cleared.
* @param[in] B Bit number in the word to be cleared.
*/
#define CLR_BIT(W, B) ((W) &= (~(uint32_t)(1U << (B))))
/**@brief Checks if a bit is set.
*
* @param[in] W Word whose bit is to be checked.
* @param[in] B Bit number in the word to be checked.
*
* @retval 1 if bit is set.
* @retval 0 if bit is not set.
*/
#define IS_SET(W, B) (((W) >> (B)) & 1)
#define BIT_0 0x01 /**< The value of bit 0 */
#define BIT_1 0x02 /**< The value of bit 1 */
#define BIT_2 0x04 /**< The value of bit 2 */
#define BIT_3 0x08 /**< The value of bit 3 */
#define BIT_4 0x10 /**< The value of bit 4 */
#define BIT_5 0x20 /**< The value of bit 5 */
#define BIT_6 0x40 /**< The value of bit 6 */
#define BIT_7 0x80 /**< The value of bit 7 */
#define BIT_8 0x0100 /**< The value of bit 8 */
#define BIT_9 0x0200 /**< The value of bit 9 */
#define BIT_10 0x0400 /**< The value of bit 10 */
#define BIT_11 0x0800 /**< The value of bit 11 */
#define BIT_12 0x1000 /**< The value of bit 12 */
#define BIT_13 0x2000 /**< The value of bit 13 */
#define BIT_14 0x4000 /**< The value of bit 14 */
#define BIT_15 0x8000 /**< The value of bit 15 */
#define BIT_16 0x00010000 /**< The value of bit 16 */
#define BIT_17 0x00020000 /**< The value of bit 17 */
#define BIT_18 0x00040000 /**< The value of bit 18 */
#define BIT_19 0x00080000 /**< The value of bit 19 */
#define BIT_20 0x00100000 /**< The value of bit 20 */
#define BIT_21 0x00200000 /**< The value of bit 21 */
#define BIT_22 0x00400000 /**< The value of bit 22 */
#define BIT_23 0x00800000 /**< The value of bit 23 */
#define BIT_24 0x01000000 /**< The value of bit 24 */
#define BIT_25 0x02000000 /**< The value of bit 25 */
#define BIT_26 0x04000000 /**< The value of bit 26 */
#define BIT_27 0x08000000 /**< The value of bit 27 */
#define BIT_28 0x10000000 /**< The value of bit 28 */
#define BIT_29 0x20000000 /**< The value of bit 29 */
#define BIT_30 0x40000000 /**< The value of bit 30 */
#define BIT_31 0x80000000 /**< The value of bit 31 */
#define UNUSED_VARIABLE(X) ((void)(X))
#define UNUSED_PARAMETER(X) UNUSED_VARIABLE(X)
#define UNUSED_RETURN_VALUE(X) UNUSED_VARIABLE(X)
#ifdef __cplusplus
}
#endif
#endif // NORDIC_COMMON_H__