(GCC)STM32基础详解之全局资源的使用

注:测试硬件非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,本质是一样的。该文件中主要函数是:

这个函数中主要是汇编操作:

可以看到主要使用的是ldrexstrex独占访问指令(也有叫排他访问),感兴趣的可以自行搜索。

STM提供的原子操作:

https://github.com/STMicroelectronics/STM32CubeF4/blob/4aba24d78fef03d797a82b258f37dbc84728bbb5/Drivers/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h#L230

这里我们使用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__

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值