nrf51822 --- 外部中断 (按键)

1.目的

   nrf51822外部中断

2.分析

    在实际应用中经常要用到外部中断,比如按键唤醒。

3.平台:

协议栈版本:SDK10.0.0

编译软件:keil 5.12

硬件平台:微雪开发板nrf51822

例子:SDK 10.0.0\examples\ble_peripheral\ble_app_uart\pca10028\s110\arm4

4.步骤

     首先来分析下主要的代码


/**@brief Function for application main entry.
 */
int main(void)
{
    uint32_t err_code;
    bool erase_bonds;

    // Initialize.
    app_trace_init();
    timers_init();
    buttons_leds_init(&erase_bonds); //按键函数初始化
    ble_stack_init();
    device_manager_init(erase_bonds);
    gap_params_init();
    advertising_init();
    services_init();
    sensor_simulator_init();
    conn_params_init();

    // Start execution.
    application_timers_start();
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);

    // Enter main loop.
    for (;;)
    {
        power_manage();
    }
}

/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    bsp_event_t startup_event;
    //按键和灯初始化函数,APP_TIMER_TICKS(100, APP_TIMER_PRESCALER):消抖时间 bsp_event_handler:回调函数
    uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS,
                                 APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), 
                                 bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}

<pre name="code" class="cpp">uint32_t bsp_init(uint32_t type, uint32_t ticks_per_100ms, bsp_event_callback_t callback)
{
    uint32_t err_code = NRF_SUCCESS;
//判断是否定义了LEDS_UNMBER数量和 是否定义BSP_SIMPLE
#if LEDS_NUMBER > 0 && !(defined BSP_SIMPLE)
    m_app_ticks_per_100ms = ticks_per_100ms; //消抖动时间赋值
    m_indication_type     = type;//事件类型
#else
    UNUSED_VARIABLE(ticks_per_100ms);
#endif // LEDS_NUMBER > 0 && !(defined BSP_SIMPLE)
//判断是否定义了按键和是否定义BSP_SIMPLE
#if (BUTTONS_NUMBER > 0) && !(defined BSP_SIMPLE)
    m_registered_callback = callback;//回调函数赋值 及bsp_event_handler

    // BSP will support buttons and generate events  //判断是否按键事件
    if (type & BSP_INIT_BUTTONS)   {
        uint32_t num;
        //BUTTONS_NUMBER是定义了几个中断, 中断类型 回调函数初始化
        for (num = 0; ((num < BUTTONS_NUMBER) && (err_code == NRF_SUCCESS)); num++)
        {
            err_code = bsp_event_to_button_action_assign(num, BSP_BUTTON_ACTION_PUSH, BSP_EVENT_DEFAULT);
        }

        if (err_code == NRF_SUCCESS)
        {    //app_buttons 中断表  BUTTONS_NUMBER:中断数目 ticks_per_100ms:消抖动时间  这里是初始化按键设置
            err_code = app_button_init((app_button_cfg_t *)app_buttons,
                                       BUTTONS_NUMBER,
                                       ticks_per_100ms / 2);
        }

        if (err_code == NRF_SUCCESS)
        {  //使能中断
            err_code = app_button_enable();
        }
        if (err_code == NRF_SUCCESS)
        {  //建立m_button_timer_id事件
            err_code = app_timer_create(&m_button_timer_id,
                                        APP_TIMER_MODE_SINGLE_SHOT,
                                        button_timer_handler);
        }
    }
#elif (BUTTONS_NUMBER > 0) && (defined BSP_SIMPLE)

    if (type & BSP_INIT_BUTTONS)
    {
        uint32_t cnt;
        uint32_t buttons[] = BUTTONS_LIST;
        for (cnt = 0; cnt < BUTTONS_NUMBER; cnt++)
        {
            nrf_gpio_cfg_input(buttons[cnt], BUTTON_PULL);
        }
    }
#endif // (BUTTONS_NUMBER > 0) && !(defined BSP_SIMPLE)

#if LEDS_NUMBER > 0 && !(defined BSP_SIMPLE)

    if (type & BSP_INIT_LED)
    {
        LEDS_OFF(LEDS_MASK);
        NRF_GPIO->DIRSET = LEDS_MASK;
    }

    // timers module must be already initialized!
    if (err_code == NRF_SUCCESS)
    {
        err_code =
            app_timer_create(&m_leds_timer_id, APP_TIMER_MODE_SINGLE_SHOT, leds_timer_handler);
    }

    if (err_code == NRF_SUCCESS)
    {
        err_code =
            app_timer_create(&m_alert_timer_id, APP_TIMER_MODE_REPEATED, alert_timer_handler);
    }
#endif // LEDS_NUMBER > 0 && !(defined BSP_SIMPLE)

    return err_code;
}<span>	</span>
<span style="font-family: Arial;">	</span>
<span style="font-family: Arial;">										</span>
 

 这里来分析下bsp_event_to_button_action_assign()函数, 其中uint32_t button 是数组m_events_list的下标。可见如果在次初始化的时候可能把 event给改变。

  

#ifndef BSP_SIMPLE
/**@brief Assign specific event to button.
 */
uint32_t bsp_event_to_button_action_assign(uint32_t button, bsp_button_action_t action, bsp_event_t event)
{
    uint32_t err_code = NRF_SUCCESS;

#if BUTTONS_NUMBER > 0
    if (button < BUTTONS_NUMBER) //判断是否定义了按键
    {
        if (event == BSP_EVENT_DEFAULT) //判断事件是否BSP_EVENT_DEFAULT事件
        {  //找到定义的哪个按键的事件,
            // Setting default action: BSP_EVENT_KEY_x for PUSH actions, BSP_EVENT_NOTHING for RELEASE and LONG_PUSH actions.
            event = (action == BSP_BUTTON_ACTION_PUSH) ? (bsp_event_t)(BSP_EVENT_KEY_0 + button) : BSP_EVENT_NOTHING;
        } 
        switch (action)
        {            case BSP_BUTTON_ACTION_PUSH:
                m_events_list[button].push_event = event; //给m_events_list[button].push_event 赋值
                break;
            case BSP_BUTTON_ACTION_LONG_PUSH:
                m_events_list[button].long_push_event = event;
                break;
            case BSP_BUTTON_ACTION_RELEASE:
                m_events_list[button].release_event = event;
                break;
            default:
                err_code = NRF_ERROR_INVALID_PARAM;
                break;
        }
    }
    else
    {
        err_code = NRF_ERROR_INVALID_PARAM;
    }
#else
    err_code = NRF_ERROR_INVALID_PARAM;
#endif // BUTTONS_NUMBER > 0

    return err_code;
}

#endif // BSP_SIMPLE

为什么这里 event = (action == BSP_BUTTON_ACTION_PUSH) ? (bsp_event_t)(BSP_EVENT_KEY_0 + button) : BSP_EVENT_NOTHING;是这样 ,请看下面

typedef enum
{
    BSP_EVENT_NOTHING = 0,                  /**< Assign this event to an action to prevent the action from generating an event (disable the action). */
    BSP_EVENT_DEFAULT,                      /**< Assign this event to an action to assign the default event to the action. */
    BSP_EVENT_CLEAR_BONDING_DATA,           /**< Persistent bonding data should be erased. */
    BSP_EVENT_CLEAR_ALERT,                  /**< An alert should be cleared. */
    BSP_EVENT_DISCONNECT,                   /**< A link should be disconnected. */
    BSP_EVENT_ADVERTISING_START,            /**< The device should start advertising. */
    BSP_EVENT_ADVERTISING_STOP,             /**< The device should stop advertising. */
    BSP_EVENT_WHITELIST_OFF,                /**< The device should remove its advertising whitelist. */                                                       BSP_EVENT_BOND,                         /**< The device should bond to the currently connected peer. */ 
    BSP_EVENT_RESET,                        /**< The device should reset. */
    BSP_EVENT_SLEEP,                        /**< The device should enter sleep mode. */
    BSP_EVENT_WAKEUP,                       /**< The device should wake up from sleep mode. */
    BSP_EVENT_DFU,                          /**< The device should enter DFU mode. */
    BSP_EVENT_KEY_0,                        /**< Default event of the push action of BSP_BUTTON_0 (only if this button is present). */
    BSP_EVENT_KEY_1,                        /**< Default event of the push action of BSP_BUTTON_1 (only if this button is present). */
    BSP_EVENT_KEY_2,                        /**< Default event of the push action of BSP_BUTTON_2 (only if this button is present). */                        BSP_EVENT_KEY_3,                        /**< Default event of the push action of BSP_BUTTON_3 (only if this button is present). */
    BSP_EVENT_KEY_4,                        /**< Default event of the push action of BSP_BUTTON_4 (only if this button is present). */
    BSP_EVENT_KEY_5,                        /**< Default event of the push action of BSP_BUTTON_5 (only if this button is present). */
    BSP_EVENT_KEY_6,                        /**< Default event of the push action of BSP_BUTTON_6 (only if this button is present). */
    BSP_EVENT_KEY_7,                        /**< Default event of the push action of BSP_BUTTON_7 (only if this button is present). */
    BSP_EVENT_KEY_LAST = BSP_EVENT_KEY_7,
} bsp_event_t;
这样就可以找到对应的小标 事件的case事件

上面既是给对应的长按,释放,短按事件赋值对应的事件。。同一个m_events_list只能同时有一个赋值。

  配置端口:

 app_button_init((app_button_cfg_t *)app_buttons,
                                       BUTTONS_NUMBER,
                                       ticks_per_100ms / 2);

BUTTONS_NUMBER:设置按键的个数,

ticks_per_100ms / 2 :消痘痘事件

app_buttons参数,游侠可见,需要配置哪个端口,低电平(false)还是高电平(ture)有效,管脚模式(上下拉等等),对应的回调函数

#ifndef BSP_SIMPLE
static const app_button_cfg_t app_buttons[BUTTONS_NUMBER] =
{
    #ifdef BSP_BUTTON_0
    {BSP_BUTTON_0, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_0

    #ifdef BSP_BUTTON_1
    {BSP_BUTTON_1, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_1

    #ifdef BSP_BUTTON_2
    {BSP_BUTTON_2, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_2

    #ifdef BSP_BUTTON_3
    {BSP_BUTTON_3, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_3

    #ifdef BSP_BUTTON_4
    {BSP_BUTTON_4, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_4
    #ifdef BSP_BUTTON_5
    {BSP_BUTTON_5, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_5

    #ifdef BSP_BUTTON_6
    {BSP_BUTTON_6, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_6

    #ifdef BSP_BUTTON_7
    {BSP_BUTTON_7, false, BUTTON_PULL, bsp_button_event_handler},
    #endif // BUTTON_7
};
#endif // BSP_SIMPLE
#endif // BUTTONS_NUMBER > 0

 //创建一个按键事件,用于长按的时候计数,请查看下面长按事件,

  err_code = app_timer_create(&m_button_timer_id,
                                        APP_TIMER_MODE_SINGLE_SHOT,
                                        button_timer_handler);



每次中断来了就会跳到:

  void GPIOTE_IRQHandler(void) 中断入口函数,然后跳到


#if (BUTTONS_NUMBER > 0) && !(defined BSP_SIMPLE)
/**@brief Function for handling button events.
 *
 * @param[in]   pin_no          The pin number of the button pressed.
 * @param[in]   button_action   Action button.
 */
static void bsp_button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    bsp_event_t        event  = BSP_EVENT_NOTHING;
    uint32_t           button = 0;
    uint32_t           err_code;
    static uint8_t     current_long_push_pin_no;              /**< Pin number of a currently pushed button, that could become a long push if held long enough. */
    static bsp_event_t release_event_at_push[BUTTONS_NUMBER]; /**< Array of what the release event of each button was last time it was pushed, so that no release event is sent if the event was bound after the push of the button. */

    while ((button < BUTTONS_NUMBER) && (m_buttons_list[button] != pin_no))
    {
        button++;
    }

    if (button < BUTTONS_NUMBER)
    {
        switch(button_action)
        {
            case APP_BUTTON_PUSH:
                event = m_events_list[button].push_event;
                if (m_events_list[button].long_push_event != BSP_EVENT_NOTHING)
                {
                    err_code = app_timer_start(m_button_timer_id, BSP_MS_TO_TICK(BSP_LONG_PUSH_TIMEOUT_MS), (void*)¤t_long_push_pin_no);
                    if (err_code == NRF_SUCCESS)
                    {
                        current_long_push_pin_no = pin_no;
                    }
                }                                                                                                                                                         release_event_at_push[button] = m_events_list[button].release_event;
                break;
            case APP_BUTTON_RELEASE:
                (void)app_timer_stop(m_button_timer_id);
                if (release_event_at_push[button] == m_events_list[button].release_event)
                {
                    event = m_events_list[button].release_event;
                }
                break;
            case BSP_BUTTON_ACTION_LONG_PUSH:
                event = m_events_list[button].long_push_event;
						
        }
    }

    if ((event != BSP_EVENT_NOTHING) && (m_registered_callback != NULL))
    {  
        m_registered_callback(event);
    }
}

这里来分析下如何实现长按和段按的。

 如果是长按, event = m_events_list[button].push_event; event是0(即BSP_EVENT_NOTHING),-->m_events_list[button].long_push_event不等于BSP_EVENT_NOTHING,然后会产生长按扫描事件m_button_timer_id-->赋值释放事件,如果长按没有到设定的时间则产生释放按键事件,停止app_timer_stop(m_button_timer_id);

注意:BSP_MS_TO_TICK(BSP_LONG_PUSH_TIMEOUT_MS)是长按的时间,设置这个参数可以改变长按的事件。

 如果是短接事件, event = m_events_list[button].push_event; 不为0为定义的事件,-->m_events_list[button].long_push_event 等于 BSP_EVENT_NOTHING-->赋值释放按键事件。

  m_registered_callback(event); 这个函数即:bsp_event_handler函数-,(请查阅上面 bsp_init()函数)


长按事件,时间到了,产生button_timer_handler事件

/**@brief Handle events from button timer.
 *
 * @param[in]   p_context   parameter registered in timer start function.
 */
static void button_timer_handler(void * p_context)
{
    bsp_button_event_handler(*(uint8_t *)p_context, BSP_BUTTON_ACTION_LONG_PUSH);
}

就是产生对应的长按事件。

注意:启动长按扫描的时候err_code = app_timer_start(m_button_timer_id, BSP_MS_TO_TICK(BSP_LONG_PUSH_TIMEOUT_MS), (void*)&current_long_push_pin_no);

current_long_push_pin_no 这个参数也传进去了,这个就是哪个管脚。方便产生长按超时的时候把哪个长按参数传进去,


看代码:bsp_init()函数

        for (num = 0; ((num < BUTTONS_NUMBER) && (err_code == NRF_SUCCESS)); num++)
        {
            err_code = bsp_event_to_button_action_assign(num, BSP_BUTTON_ACTION_PUSH, BSP_EVENT_DEFAULT);
        }

这里穿进去的参数是BSP_BUTTON_ACTION_PUSH 和 BSP_EVENT_DEFAULT

函数bsp_event_to_button_action_assign()里面

        if (event == BSP_EVENT_DEFAULT)
        {
            // Setting default action: BSP_EVENT_KEY_x for PUSH actions, BSP_EVENT_NOTHING for RELEASE and LONG_PUSH actions.
            event = (action == BSP_BUTTON_ACTION_PUSH) ? (bsp_event_t)(BSP_EVENT_KEY_0 + button) : BSP_EVENT_NOTHING;
					    
        }
可以看到只有当设置为BSP_BUTTON_ACTION_PUSH  和BSP_EVENT_DEFAULT的时候,event才有值不然为0.那假如要设置长按怎么办呢?











  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一、测试距离 0.软件为Keil5,不知道是否支持Keil4(如不行请手动新建Keil4工程) 1.单片机为STM32F103C8,采用硬件SPI 2.nRF24L01+采用3.3V供电,接线如下:       24L01+       STM32 CE   ——   PA3 CS   ——   PA4 SCK  ——   PA5 MISO ——   PA6 MOSI ——   PA7   IRQ未接(采用查询方式,如须用自加外部中断程序) 3.led灯为PC13控制,低电平亮(在User\led.c中修改GPIO);按键为PA0,按下后(接地)才开始发送,默认注释了,如需要可在程序中加上 4.My24L01_Tx为发送端程序,My24L01_Rx为接收端程序; 5.发送端约100ms发送一次,不要ACK;接收端每接收到一次led反转;将程序下载到单片机后可看到接收端led快速闪动,可将接收端的24L01在一定范围内走动,若led闪动变慢,则说明有丢包;led不闪,说明没有收到数据;因此大致可测得发送距离 6.24L01采用0频道,2Mbps, 0dBm, Address 3Bytes,实测距离大于10米(在不同的房间) 二、测试速率 0.软件为Keil5,不知道是否支持Keil4(如不行请手动新建Keil4工程) 
1.单片机为STM32F103C8,采用硬件SPI
 2.nRF24L01+采用3.3V供电,接线如下:
      24L01+       STM32
 CE   ——   PA3
 CS   ——   PA4
 SCK  ——   PA5
 MISO ——   PA6
 MOSI ——   PA7  
 IRQ未接(采用查询方式,如须用自加外部中断程序) 

3.led灯为PC13控制,低电平亮(在User\led.c中修改GPIO);按键为PA0,按下后(接地)才开始发送!!! 

4.My24L01_Tx为发送端程序,My24L01_Rx为接收端程序;

 5.接收端先上电,发送端上电后按下按键后才发送50KB(32B一帧 共32*50帧 32*32=1024=1K),发送端收到ACK后才发下一帧,发完后进入死循环,如须再发要先复位或重新上电;接收端每收到一次led反转;(如未反转说明未成功发送,发送端接收端重新复位后再试)时间可看在接收程序中tim3Count(单位ms 16进制,定时器1ms中断)在Watch1中

 6.发送端我用的是延时等待查询STATUS寄存器,用外部中断IRQ应该会更好(未测试) 

7.24L01采用0频道,2Mbps, 0dBm, Address 3Bytes,实测速率约为50KB/s
### 回答1: nRF52840-DK开发板是一款由Nordic Semiconductor推出的功能强大的开发板,适用于无线应用的开发和原型设计。该开发板基于nRF52840芯片,具有蓝牙5、蓝牙Mesh、Thread、Zigbee等多种无线通信协议的支持,是开发智能穿戴设备、物联网设备以及传感器网络等应用的理想选择。 nRF52840-DK开发板具有丰富的硬件接口,包括USB、UART、SPI、I2C、GPIO等,使得开发者可以方便地连接、控制各种外部设备。同时,开发板上还集成了按钮、LED指示灯、天线等常用的外设,便于用户进行基础的应用开发和调试。 nRF52840-DK开发板不仅提供了开发所需的硬件资源,还配备了丰富的软件开发工具和库函数。开发者可以使用Nordic Semiconductor提供的nRF5 SDK进行应用开发,还可以通过Keil、IAR和GCC等常用的集成开发环境进行编程。 除此之外,nRF52840-DK开发板还支持在线固件更新(OTA),提供了方便快捷的开发和调试环境。开发者可以使用Segger J-link等调试工具进行调试和烧录。 总之,nRF52840-DK开发板是一款强大而灵活的开发工具,适用于广泛的无线应用开发。无论是初学者还是有经验的开发者,都能够利用该开发板快速构建各种创新的无线应用,并实现功能丰富、稳定的产品。 ### 回答2: nrf52840-dk是一款常用的开发板,广泛用于物联网(IoT)和蓝牙应用的开发。以下是关于该开发板的一些重要信息: 1. 芯片:nrf52840是Nordic Semiconductor公司推出的一款高性能低功耗蓝牙LE SoC(System-on-a-Chip)芯片。这款芯片集成了一个Arm Cortex-M4F处理器和2.4GHz无线收发器,可支持蓝牙5.0和其他无线通信协议。 2. 开发板:nrf52840-dk是基于nrf52840芯片的开发板,具有丰富的外围设备和接口,方便开发者进行软件编程和硬件原型设计。该开发板包含了带有按钮、指示灯和天线的nRF52840 SoC模块,以及一系列GPIO引脚、USB接口、电源管理电路等。 3. 软件开发工具:使用nrf52840-dk进行开发时,可以使用Nordic Semiconductor提供的nRF5软件开发工具套件。这个工具套件包含了一系列用于编写和调试nRF52840软件的开发工具、库文件和示例代码。开发者可以使用该工具套件进行代码编辑、编译、下载和调试等操作。 4. 功能和应用:nrf52840-dk开发板支持蓝牙LE和其他无线通信协议,可以广泛应用于物联网设备、可穿戴设备、智能家居、健康监测、无线传感器网络等领域的开发。开发者可以利用该开发板进行传感器数据采集、通信协议开发和物联网应用的原型验证。 总之,nrf52840-dk开发板是一款功能强大、易于使用的开发工具,适用于物联网和蓝牙应用的开发。它提供了丰富的外围设备和接口,配合Nordic Semiconductor的软件开发工具套件,开发者可以快速进行硬件原型设计和软件编程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值