《RT-Thread BLE5.0和ANT+应用开发实战指南》的文档已经写了前面19章,目前已经177页,还有15章左右没有写。欢迎各位转载,转载说明出处https://blog.csdn.net/qwea123456/article/details/83247005
论坛:bbs.codertown.cn 公众号:Bluetooth-BLE QQ群:177341833
GPIO(General Purpose Input Output)通用输入输出,常用的作为输出控制LED,输入作为KEY。对于nordic的GPIO有两种,一种就是普通GPIO还有一种为GPIOTE(GPIO tasks and events),下面先简单介绍一些nRF52840的GPIOTE。
先看一下GPIO的内部结构图19-1所示。
图19-1 GPIO内部结构示意图
从图19-1 GPIO内部结构,其实很容易了解GPIO的使用,但是对于GPIO的中断和事件有点容易混淆。所以主要看看中断和事件,图中A部分的内部结构由B和C两部分组成,也就是由IN、OUT和CNF寄存器控制的,重点看A部分,这个部分影响到GPIO的中断和事件。GPIO的事件是由引脚的DETECT上产生一个上升沿触发,而DETECT的上升沿由设置在每个GPIO的PIN_CNF寄存器中的SENSE位决定的,但是SENSE只能是检测高电平或者低电平两种极性,所以如果SENSE设置检测高电平,而引脚产生了一个上升沿,硬件会使DETECT产生一个上升沿,从而产生EVENT,EVENT又能产生中断,而有分为EVENT中断和PORT中断。
nRF2840共有8组事件和任务,也就是说同时只能有8个不同的GPIO产生事件或者任务,当然事件也能触发中断,但是这8组中断基本上是不用的,因为会带来很大的功耗,当调用SDK中的NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(hi_accu)接口时,如果hi_accu=true,就使用的这8组中的一组,也就是所谓的高精度,这样就会带来功耗,如果hi_accu=false就会只使用中断,也就是后面要讲的PORT中断,这时功耗就会降下来。那么GPIO的event和中断有什么关系呢?如果某个引脚使用了GPIOTE,那么它的中断就只能是由event产生。这样来看是不是意味着nRF52840只能同时产生8个GPIO中断呢?肯定不是的,就是下面讲的PORT中断。
PORT中断模式意思就是所有的GPIO都能产生中断,但是中断服务函数只有一个,进入中断后再根据一些条件判断出是哪个产生了中断,同一个引脚只能是EVENT中断或者PORT中断产生,不能同时支持。它是一个低精度中断模式,PORT中断虽然能带来功耗上的优化,但是有可能会漏掉中断,所以特别注意的地方是,因为SENSE只能感知高电平还是低电平,所以在每次设置希望的SENSE之前,先将GPIO设置为相反的状态,另一个注意的当在中断服务函数中进行SENSE变换时,需要程序轮询一次引脚状态,以免漏掉中断,因为这时即使SENSE信号发生改变也不会再触发中断,这个一点在后文驱动中的中断服务函数会有体现。
在RT-Thread的GPIO设备管理框架是源码/components/drivers/misc 中的pin.c和pin.h文件,这个misc文件夹是“Miscellaneous”的前4个字母,即GPIO为混杂设备。要使用pin的杂项文件需要在rtconfig.h中打开RT_USING_PIN宏。那么底层驱动需要在注册pin设备时传入哪些接口函数呢?这里看一下pin.h文件就知道了。
在分析pin.h之前先将misc文件夹从RT-Thread源码中拷贝到ZJ-SDK中的RT_THREAD/components/drivers/下面。将008.finsh_shell复制粘贴为009.gpio_driver,在ZJ-SDK/RT_THREAD_NRF_HAL/src文件夹下新建文件空文件rt_nrf_hal_gpio.c,在include文件新建rt_nrf_hal_gpio.h文件。将文件以及头文件添加到009.gpio_driver工程,记得打开宏,就可以进行成功编译了。
头文件一般就是一些宏、结构体及一些函数的声明。pin.h源码如代码清单19-1所示。
代码清单19-1 pin.h源码
#ifndef PIN_H__
#define PIN_H__
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
/* pin device and operations for RT-Thread */
struct rt_device_pin
{
struct rt_device parent;
const struct rt_pin_ops *ops;
};
#define PIN_LOW 0x00 (1)
#define PIN_HIGH 0x01
#define PIN_MODE_OUTPUT 0x00 (2)
#define PIN_MODE_INPUT 0x01
#define PIN_MODE_INPUT_PULLUP 0x02
#define PIN_MODE_INPUT_PULLDOWN 0x03
#define PIN_MODE_OUTPUT_OD 0x04
#define PIN_IRQ_MODE_RISING 0x00 (3)
#define PIN_IRQ_MODE_FALLING 0x01
#define PIN_IRQ_MODE_RISING_FALLING 0x02
#define PIN_IRQ_MODE_HIGH_LEVEL 0x03
#define PIN_IRQ_MODE_LOW_LEVEL 0x04
#define PIN_IRQ_DISABLE 0x00 (4)
#define PIN_IRQ_ENABLE 0x01
#define PIN_IRQ_PIN_NONE -1
struct rt_device_pin_mode
{
rt_uint16_t pin;
rt_uint16_t mode;
};
struct rt_device_pin_status
{
rt_uint16_t pin;
rt_uint16_t status;
};
struct rt_pin_irq_hdr (5)
{
rt_int16_t pin;
rt_uint16_t mode;
void (*hdr)(void *args);
void *args;
};
struct rt_pin_ops (6)
{
void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode); (6)-1
void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value); (6)-2
int (*pin_read)(struct rt_device *device, rt_base_t pin); (6)-3
/* TODO: add GPIO interrupt */
rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args); (6)-4
rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin); (6)-5
rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled); (6)-6
};
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data); (7)
void rt_pin_mode(rt_base_t pin, rt_base_t mode); (8)
void rt_pin_write(rt_base_t pin, rt_base_t value);
int rt_pin_read(rt_base_t pin);
rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
void (*hdr)(void *args), void *args);
rt_err_t rt_pin_detach_irq(rt_int32_t pin);
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled);
#ifdef __cplusplus
}
#endif
#endif
代码清单19-1(1):引脚状态宏。
代码清单19-1(2):引脚方向和驱动能力设置宏。
代码清单19-1(3):中断极性宏。
代码清单19-1(4):中断使能控制宏。
代码清单19-1(5):中断引脚以及对应的中断回调函数结构体。
代码清单19-1(6):底层需实现的接口。
代码清单19-1(6)-1:底层实现的设置引脚输入输出,上下来等模式接口。
代码清单19-1(6)-2:写某个引脚。
代码清单19-1(6)-3:读某个引脚。
代码清单19-1(6)-4:中断回调附着接口,这个接口是某个引脚的引脚号、中断模式、中断回调函数以及回调函数的参数存放位置,这样做的目的是,将上层应用和中断服务函数彻底分开,应用层不用管中断服务函数中的事情,中断服务函数只需根据中断引脚调用这个引脚的回调函数即可。
代码清单19-1(6)-5:关闭中断时,将附着的中断引脚信息清空。
代码清单19-1(7)-6:使能中断控制接口。
代码清单19-1(7):pin设备注册函数,底层通过调用这个函数将设备名称、结构体const struct rt_pin_ops以及用户数据指针传给内核进行设备管理。
代码清单19-1(8):应用层调用接口。
从上面来看,底层应用要实现的就是const struct rt_pin_ops结构体中的6个函数接口。
这个rt_nrf_hal_gpio.c和.h文件需要根据nRF52840的数据手册以及NORDIC官方SDK进行调试和编写,下面直接分析我调试出来的驱动源码。
rt_nrf_hal_gpio.h源码分析,它的源码如代码清单19-2所示。
代码清单19-2 rt_nrf_hal_gpio.h源码
#ifndef _RT_nRF_HAL_GPIO_H_
#define _RT_nRF_HAL_GPIO_H_
#define RT_GPIO_SENSE_Pos (0UL) /*!< Position of SENSE field. */
#define RT_GPIO_SENSE_Msk (0x3UL << RT_GPIO_DRI_SENSE_Pos) /*!< Bit mask of SENSE field. */
#define RT_GPIO_POLARITY_Pos (2UL) /*!< Position of SENSE field. */
#define RT_GPIO_POLARITY_Msk (0x3UL << RT_GPIO_DRI_POLARITY_Pos) /*!< Bit mask of SENSE field. */
#define RT_GPIO_MODE_SET(polarity,sense) (((polarity << RT_GPIO_POLARITY_Pos) & RT_GPIO_POLARITY_Msk) | ((sense << RT_GPIO_SENSE_Pos) & RT_GPIO_SENSE_Msk)) /*!< Bit mask of SENSE field. */ (1)
#define ZJ_LED1 NRF_GPIO_PIN_MAP(0,13) (2)
#define ZJ_LED2 NRF_GPIO_PIN_MAP(0,14)
#define ZJ_LED3 NRF_GPIO_PIN_MAP(0,15)
#define ZJ_LED4 NRF_GPIO_PIN_MAP(0,16)
#define ZJ_KEY1 NRF_GPIO_PIN_MAP(0,11) (3)
#define ZJ_KEY2 NRF_GPIO_PIN_MAP(0,12)
#define ZJ_KEY3 NRF_GPIO_PIN_MAP(0,13)
#define ZJ_KEY4 NRF_GPIO_PIN_MAP(0,14)
#endif
代码清单19-2(1):这个宏非常重要,前面已经调到,NORDIC的gpio中断不是直接由引脚的上升下降沿产生,换句话说就是在代码清单19-1(5)结构体中的mode需要用传入NORDIC GPIO设置的极性和SENSCE两个状态才能确定是哪个GPIO产生的中断。所以在调用代码清单19-1(6)-4接口时需要调用这个宏,以及在中断服务函数中,确定是哪个引脚产生的中断时需要使用上面的宏。
代码清单19-2(2):开发板的4个灯。
代码清单19-2(3):开发板的4个按键。
rt_nrf_hal_gpio.c源码分析,目前这个只支持PORT中断,对于高精度的EVENT_IN没有在中断函数中处理。它的源码如代码清单19-3所示。
代码清单19-3 rt_nrf_hal_gpio.c源码
#include "board.h"
#include "rt_nrf_hal_gpio.h"
#include "nrf_drv_common.h"
#include "app_util_platform.h"
#include "nrfx_gpiote.h"
#include "nrf_gpio.h"
#include
#include
#include
#include "nrf52840.h"
#include "core_cm4.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_bitmask.h"
static void _nrf_pin_mode(struct rt_device *device, rt_base_t pin, rt_base_t mode) (1)
{
switch(mode)
{
case PIN_MODE_OUTPUT:
{
/* output setting */
nrf_gpio_cfg_output(pin);
}
break;
case PIN_MODE_INPUT:
{
/* input setting: not pull. */
nrf_gpio_cfg_input(pin,NRF_GPIO_PIN_NOPULL);
}
break;
case PIN_MODE_INPUT_PULLUP:
{
/* input setting: pull up. */
nrf_gpio_cfg_input(pin,NRF_GPIO_PIN_PULLUP);
}
break;
case PIN_MODE_INPUT_PULLDOWN:
{
/* input setting: pull up. */
nrf_gpio_cfg_input(pin,NRF_GPIO_PIN_PULLDOWN);
}
break;
case PIN_MODE_OUTPUT_OD:
{
/* output setting: od. */
}
break;
default:
break;
}
}
static void _nrf_pin_write(struct rt_device *device, rt_base_t pin, rt_base_t value) (2)
{
nrf_gpio_pin_write(pin,value);
}
static int _nrf_pin_read(struct rt_device *device, rt_base_t pin) (3)
{
return nrf_gpio_pin_read(pin);
}
struct rt_pin_irq_hdr pin_irq_hdr_tab[P0_PIN_NUM+P1_PIN_NUM] = (4)
{
/*P0*/
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
/*P1*/
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
{ -1, 0, RT_NULL, RT_NULL},
};
static rt_err_t _nrf_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args) (5)
{
rt_base_t level;
rt_int32_t pinindex = pin;
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[pinindex].pin == pin &&
pin_irq_hdr_tab[pinindex].hdr == hdr &&
pin_irq_hdr_tab[pinindex].mode == mode &&
pin_irq_hdr_tab[pinindex].args == args)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
if (pin_irq_hdr_tab[pinindex].pin != -1)
{
rt_hw_interrupt_enable(level);
return RT_EBUSY;
}
pin_irq_hdr_tab[pinindex].pin = pin;
pin_irq_hdr_tab[pinindex].hdr = hdr;
pin_irq_hdr_tab[pinindex].mode = mode;
pin_irq_hdr_tab[pinindex].args = args;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
static rt_err_t _nrf_pin_detach_irq(struct rt_device *device, rt_int32_t pin) (6)
{
rt_base_t level;
rt_int32_t pinindex = pin;
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[pinindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
pin_irq_hdr_tab[pinindex].pin = -1;
pin_irq_hdr_tab[pinindex].hdr = RT_NULL;
pin_irq_hdr_tab[pinindex].mode = 0;
pin_irq_hdr_tab[pinindex].args = RT_NULL;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
static rt_err_t _nrf_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled) (7)
{
rt_base_t level;
rt_int32_t pinindex = pin;
nrf_gpio_pin_sense_t sense;
nrf_gpio_pin_pull_t config;
if (enabled == PIN_IRQ_ENABLE)
{
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[pinindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_ENOSYS;
}
switch ((pin_irq_hdr_tab[pinindex].mode >> RT_GPIO_POLARITY_Pos) & 0x03)
{
case NRF_GPIOTE_POLARITY_HITOLO:
sense = NRF_GPIO_PIN_SENSE_LOW;
config = NRF_GPIO_PIN_PULLUP;
break;
case NRF_GPIOTE_POLARITY_LOTOHI:
sense = NRF_GPIO_PIN_SENSE_HIGH;
config = NRF_GPIO_PIN_PULLDOWN;
break;
case NRF_GPIOTE_POLARITY_TOGGLE: (7)-1
/* read current pin state and set for next sense to oposit */
sense = (nrf_gpio_pin_read(pin)) ? NRF_GPIO_PIN_SENSE_LOW : NRF_GPIO_PIN_SENSE_HIGH;
config = (sense == NRF_GPIO_PIN_SENSE_LOW)?NRF_GPIO_PIN_PULLUP:NRF_GPIO_PIN_PULLDOWN;
break;
default:
break;
}
nrf_gpio_cfg_sense_input(pin,config,sense);
NVIC_SetPriority(GPIOTE_IRQn, 5);
NVIC_EnableIRQ(GPIOTE_IRQn);
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_enable(GPIOTE_INTENSET_PORT_Msk);
rt_hw_interrupt_enable(level);
}
else if (enabled == PIN_IRQ_DISABLE)
{
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT);
nrf_gpiote_int_disable(NRF_GPIOTE_INT_PORT_MASK);
NVIC_DisableIRQ(GPIOTE_IRQn);
}
else
{
return RT_ENOSYS;
}
return RT_EOK;
}
const static struct rt_pin_ops _nrf_pin_ops =
{
_nrf_pin_mode,
_nrf_pin_write,
_nrf_pin_read,
_nrf_pin_attach_irq,
_nrf_pin_detach_irq,
_nrf_pin_irq_enable
};
int rt_hw_pin_init(void) (8)
{
int ret = RT_EOK;
ret = rt_device_pin_register("nrfpin", &_nrf_pin_ops, RT_NULL);
return ret;
}
INIT_BOARD_EXPORT(rt_hw_pin_init);
typedef void (*rt_evt_handler_t)(void *args);
void nrfx_gpiote_irq_handler(void) (9)
{
uint8_t i;
uint32_t status = 0;
uint32_t input[GPIO_COUNT] = {0};
/* collect PORT status event, if event is set read pins state. Processing is postponed to the
* end of interrupt. */
if (nrf_gpiote_event_is_set(NRF_GPIOTE_EVENTS_PORT)) (9)-1
{
nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT); (9)-2
status |= (uint32_t)NRF_GPIOTE_INT_PORT_MASK;
nrf_gpio_ports_read(0, GPIO_COUNT, input); (9)-3
}
if (status & (uint32_t)NRF_GPIOTE_INT_PORT_MASK) (9)-4
{
/* Process port event. */
uint32_t port_idx;
uint8_t repeat = 0;
uint32_t toggle_mask[GPIO_COUNT] = {0}; (9)-5
uint32_t pins_to_check[GPIO_COUNT];
// Faster way of doing memset because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++) (9)-6
{
pins_to_check[port_idx] = 0xFFFFFFFF;
}
do
{
repeat = 0;
for (i = 0; i < P0_PIN_NUM+P1_PIN_NUM; i++)
{
uint8_t polarity_and_sense = (uint8_t)pin_irq_hdr_tab[i].mode; (9)-7
nrfx_gpiote_pin_t pin = pin_irq_hdr_tab[i].pin; (9)-8
if (pin != -1 && nrf_bitmask_bit_is_set(pin, pins_to_check)) (9)-9
{
nrf_gpiote_polarity_t polarity = (nrf_gpiote_polarity_t)((polarity_and_sense >> RT_GPIO_POLARITY_Pos) & 0x03); (9)-10
rt_evt_handler_t handler = pin_irq_hdr_tab[i].hdr; (9)-11
if (handler || (polarity == NRF_GPIOTE_POLARITY_TOGGLE)) (9)-12
{
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE) (9)-13
{
nrf_bitmask_bit_set(pin, toggle_mask); (9)-14
}
nrf_gpio_pin_sense_t sense = nrf_gpio_pin_sense_get(pin); (9)-15
uint32_t pin_state = nrf_bitmask_bit_is_set(pin, input); (9)-16
if ((pin_state && (sense == NRF_GPIO_PIN_SENSE_HIGH)) || (9)-17
(!pin_state && (sense == NRF_GPIO_PIN_SENSE_LOW))) (9)-18
{
//NRF_LOG_INFO("PORT event for pin: %d, polarity: %d.", pin, polarity);
if (polarity == NRF_GPIOTE_POLARITY_TOGGLE) (9)-19
{
nrf_gpio_pin_sense_t next_sense =
(sense == NRF_GPIO_PIN_SENSE_HIGH) ?
NRF_GPIO_PIN_SENSE_LOW :
NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(pin, next_sense); (9)-20
++repeat;
}
if (handler)
{
uint32_t args = polarity<<6|pin; (9)-21
handler((void*)args); (9)-22
}
}
}
}
}
if (repeat) (9)-23
{
// When one of the pins in low-accuracy and toggle mode becomes active,
// it's sense mode is inverted to clear the internal SENSE signal.
// State of any other enabled low-accuracy input in toggle mode must be checked
// explicitly, because it does not trigger the interrput when SENSE signal is active.
// For more information about SENSE functionality, refer to Product Specification. (9)-24
uint32_t new_input[GPIO_COUNT];
bool input_unchanged = true;
nrf_gpio_ports_read(0, GPIO_COUNT, new_input);
// Faster way of doing memcmp because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
if (new_input[port_idx] != input[port_idx]) (9)-25
{
input_unchanged = false;
break;
}
}
if (input_unchanged)
{
// No change.
repeat = 0; (9)-26
}
else
{
// Faster way of doing memcpy because in interrupt context.
for (port_idx = 0; port_idx < GPIO_COUNT; port_idx++)
{
input[port_idx] = new_input[port_idx];
pins_to_check[port_idx] = toggle_mask[port_idx]; (9)-27
}
}
}
}
while (repeat);
}
}
代码清单19-3(1):GPIO模式设置。
代码清单19-3(2):GPIO写。
代码清单19-3(3):GPIO读。
代码清单19-3(4):GPIO的中断引脚,模式以及回调函数存储数组,下面就叫这个GPIO回调数组。这里共有32+16个元素,因为共有P0端口引脚32个,P1端口引脚16个,这里其实可以通过宏进行大小控制,使用了几个中断口就开启设置几个元素,以节省内存。
代码清单19-3(5):附着引脚到GPIO回调数组,
代码清单19-3(6):从GPIO回调数组中脱离引脚。
代码清单19-3(7):中断使能接口。
代码清单19-3(7)-1:双边沿触发中断时,由于GPIO的SENSE只支持高电平和低电平,也就是上升沿和下降沿触发,所以在双边沿时,需要代码乒乓处理。
代码清单19-3(8):pin设备注册。
代码清单19-3(9):中断服务函数。
代码清单19-3(9)-1:判断PORT中断。
代码清单19-3(9)-2:清PORT中断。
代码清单19-3(9)-3:读取32+16个引脚的状态,后文需要判断是哪个引脚发送了中断。
代码清单19-3(9)-4:处理PORT中断。
代码清单19-3(9)-5:引脚是不是双边沿触发,因为需要手动将SENSE状态切换。
代码清单19-3(9)-6:这变量是为了双边沿触发用的,这里先认为所有的引脚都是双边沿触发。
代码清单19-3(9)-7:从GPIO回调数组中读到引脚的极性和SENSE。
代码清单19-3(9)-8:从GPIO回调数组得到引脚。
代码清单19-3(9)-9:引脚是不是已经被用了并判断引脚是不是需要检查。
代码清单19-3(9)-10:得到引脚极性。
代码清单19-3(9)-11:得到引脚的回调函数。
代码清单19-3(9)-12:判断回调函数是否有效,或者是否是双边沿触发。
代码清单19-3(9)-13\14:如果是双边沿触发,就为下一次进来做准备。
代码清单19-3(9)-15:得到SENSE。
代码清单19-3(9)-16:读取引脚状态。
代码清单19-3(9)-17\18:根据SENSE和引脚状态判断是否这个引脚产生的中断。
代码清单19-3(9)-19\20:判断是不是双边沿触发,如果是就要进行手动切换SENCE以及极性。
代码清单19-3(9)-21:这里注意,因为回调函数中需要引脚的极性和引脚号,但是参数只有是一个指针,所以这里把极性左移6位(极性2bits,1:LoToHi,2:HiToLove,3:Toggle),然后与上引脚号,这里引脚号中的6bits的最低5bits是引脚号,而第5bit为0时表示P0端口,为1表示P1端口。这里直接将这个求与之后的值强转为一个指针,只有只需在应用层回调函数中,把这个指针强转为32位的数据即可。
代码清单19-3(9)-22:调用应用层的回调。
代码清单19-3(9)-23:是不是双边沿触发,如果是就需要再次读取GPIO状态,并再中断中循环一次。
代码清单19-3(9)-24:这个注释说明了必须轮询一次,因为SENSE不会再产生中断。
代码清单19-3(9)-25:判断引脚有没有变化,这里判断的是所有引脚是不是有变化,如果有变化,就在执行以便中断服务函数。
代码清单19-3(9)-26:如果没有变化,说明这次中断处理完成,跳出中断服务函数。
代码清单19-3(9)-26:这里得到哪个引脚发送了变化,循环中断服务函数时就只需判断这一个引脚了。
上面两个文件写完之后,只需要在写一些应用层代码就能进行验证了,这里修改app_init.c文件,同时将之前在app_init.h文件中定义的LED灯的宏删掉,因为已经在rt_nrf_hal_gpio.h中定义了。app_init.c修改如代码清单19-4。
代码清单19-4 app_init.c源码
#include
#include
#include "nordic_common.h"
#include "nrf.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include
#include "board.h"
#include "app_init.h"
#include "app_uart.h"
#include "rt_nrf_hal_gpio.h"
#include
#include "nrfx_gpiote.h"
void key_handle(void *args) (1)
{
static bool pin_toggle=false;
nrf_gpiote_polarity_t polarity = (uint32_t)args >>6; (1)-1
nrfx_gpiote_pin_t pin = (uint32_t)args & 0x3F; (1)-2
if(pin == ZJ_KEY1 && polarity == NRF_GPIOTE_POLARITY_HITOLO) (1)-3
{
rt_pin_write(ZJ_LED2, pin_toggle); (1)-4
pin_toggle = !pin_toggle;
// rt_kprintf("ZJ_KEY1 INT");
}
}
//FINSH_FUNCTION_EXPORT(key_handle, key_handle(139) led2 will toggle)
FINSH_FUNCTION_EXPORT_ALIAS(key_handle, key, key_handle(139) led2 will toggle) (1)-5
int led_cortrol(uint8_t ctl ) (2)
{
if(ctl == 31)
{
rt_pin_write(ZJ_LED3, 0);
}
else if(ctl == 30)
{
rt_pin_write(ZJ_LED3, 1);
}
else if(ctl == 41)
{
rt_pin_write(ZJ_LED4, 0);
}
else if(ctl == 40)
{
rt_pin_write(ZJ_LED4, 1);
}
}
//FINSH_FUNCTION_EXPORT(led_cortrol, led_cortrol led3(31/30) and led4(41/40))
FINSH_FUNCTION_EXPORT_ALIAS(led_cortrol, led, led_cortrol led3(31/30) and led4(41/40)) (2)-1
void nrf_log_thread_init();
int main(void)
{
// Initialize.
uint32_t cnt=0;
bool pin_toggle=false;
nrf_log_thread_init();
NRF_LOG_INFO("RT-Thread for nrf52840 started.");
rt_pin_mode(ZJ_LED1,PIN_MODE_OUTPUT); (3)
rt_pin_mode(ZJ_LED2,PIN_MODE_OUTPUT);
rt_pin_mode(ZJ_LED3,PIN_MODE_OUTPUT);
rt_pin_mode(ZJ_LED4,PIN_MODE_OUTPUT);
rt_pin_write(ZJ_LED1,PIN_HIGH); (4)
rt_pin_write(ZJ_LED2,PIN_HIGH);
rt_pin_write(ZJ_LED3,PIN_HIGH);
rt_pin_write(ZJ_LED4,PIN_HIGH);
rt_pin_attach_irq(ZJ_KEY1, RT_GPIO_MODE_SET(NRF_GPIOTE_POLARITY_HITOLO,NRF_GPIO_PIN_SENSE_LOW),key_handle, RT_NULL); (5)
rt_pin_irq_enable(ZJ_KEY1, PIN_IRQ_ENABLE); (6)
NRF_LOG_INFO("RT-Thread for nrf52840 started");
while(1)
{
rt_pin_write(ZJ_LED1, pin_toggle);
rt_thread_mdelay(500);
// NRF_LOG_INFO("This is nrf_log test %d.",cnt);
// rt_kprintf("This is rt_kprintf test %d.",cnt);
// cnt++;
pin_toggle = !pin_toggle;
}
return RT_TRUE;
}
代码清单19-4(1):按键中断回调函数。
代码清单19-4(1)-1:得到中断pin的极性。
代码清单19-4(1)-2:得到中断pin。
代码清单19-4(1)-3:判断是不是KEY1中断。
代码清单19-4(1)-4:翻转LED2。
代码清单19-4(1)-5:将key_handle导入finsh,并重命名为key。可以使用finsh进行调试,调用key_handle(139) 就相当于KEY1中断,139=1<<6|11 =10001011,这个就能看到LED2进行翻转。通过按键KEY1也能控制LED2翻转。
代码清单19-4(2):使用finsh控制LED3和LED4亮灭。
代码清单19-4(2)-1:将led_cortrol导入finsh。
代码清单19-4(3):配置LED灯。
代码清单19-4(4):关闭LED灯。
代码清单19-4(4):附着KEY1到GPIO中断数组。
代码清单19-4(5):使能KEY1中断。
将工程编译下载到开发板,能看到LED1在闪烁。按键能控制LED2亮灭,但是因为没有消抖,所以不稳定,下面使用finsh进行调试。
在下载后打开终端,然后输入tab键就能看到key和led导入到 了finsh。输入list_device()回车就能看到已挂载的设备信息。如图19-2所示。key和led调试信息如图19-3所示。
图19-2 nrfpin设备挂载
图19-3 finsh调试GPIO设备驱动
论坛 公众号 QQ群