3、nRF52xx蓝牙学习(点亮第一个LED灯)

一、点灯代码:
led.h文件
#ifndef __LED_H
#define	__LED_H

#include "nrf52840.h"


#define LED_0          NRF_GPIO_PIN_MAP(0,13)
#define LED_1          NRF_GPIO_PIN_MAP(0,14)
#define LED_2          NRF_GPIO_PIN_MAP(0,15)
#define LED_3          NRF_GPIO_PIN_MAP(0,16)


void LED_Init(void);
void LED1_Open(void);
void LED1_Close(void);
void LED1_Toggle(void);
void LED2_Open(void);
void LED2_Close(void);
void LED2_Toggle(void);
void LED3_Open(void);
void LED3_Close(void);
void LED3_Toggle(void);


#endif /* __LED_H */

led.c文件

#include "nrf52840.h"
#include "nrf_gpio.h"
#include "led.h"
#include <stdio.h>
/*
#define ITM_PORT8(n)         (*(volatile unsigned char *)(0xe0000000 + 4*(n)))
#define ITM_PORT16(n)        (*(volatile unsigned short *)(0xe0000000 + 4*(n)))
#define ITM_PORT32(n)        (*(volatile unsigned long *)(0xe0000000 + 4*(n)))
#define DEMCR                (*(volatile unsigned long *)(0xE000EDFC))
#define TRCENA               0X01000000

int fputc(int ch, FILE *f)
{
    if(DEMCR & TRCENA)
    {
        while(ITM_PORT32(0) == 0);                                                                                                                                                                                                                                                                                      
        ITM_PORT8(0) = ch;
    }
    return ch;
}
*/
void LED_Init(void)
{
  // Configure LED-pins as outputs
  nrf_gpio_cfg_output(LED_0);
  nrf_gpio_cfg_output(LED_1);
	 nrf_gpio_cfg_output(LED_2);
}

void LED1_Open(void)
{
	nrf_gpio_pin_clear(LED_0);//设置低电平
	}

void LED1_Close(void)
{
	   nrf_gpio_pin_set(LED_0);//高电平
	
}
void LED1_Toggle(void)
{
  nrf_gpio_pin_toggle(LED_0);
}
void LED2_Open(void)
{
	nrf_gpio_pin_clear(LED_1);
	}

void LED2_Close(void)
{
	   nrf_gpio_pin_set(LED_1);
	
}
void LED2_Toggle(void)
{
  nrf_gpio_pin_toggle(LED_1);
}

void LED3_Open(void)
{
	nrf_gpio_pin_clear(LED_2);
	}

void LED3_Close(void)
{
	   nrf_gpio_pin_set(LED_2);
	
}
void LED3_Toggle(void)
{
  nrf_gpio_pin_toggle(LED_2);
}

二、代码解析(有些地方看文档不懂,是本人胡乱解析的,不对别喷,谢谢!)

1.NRF_GPIO_PIN_MAP是一个带参数的宏,在nrf_gpio.h中定义如下:

#define NRF_GPIO_PIN_MAP(port, pin) (((port) << 5) | ((pin) & 0x1F))

其用途是把端口号与引脚号映射成nrf_gpio函数能理解的值。在 Nordic 的 nRF 系列芯片里,
GPIO 引脚通常用一个 32 位整数来表示,这个整数融合了端口号和引脚号的信息。

(port << 5):把端口号向左移 5 位。这是因为每个端口有 32 个引脚(0-31也就是 2^5 个),所以左移 5 位就能给引脚号留出足够的位宽。
(port) << 5) | ((pin) & 0x1F):使用按位或操作符将移位后的端口号和引脚号组合起来,从而得到一个唯一的整数值,该值可被nrf_gpio函数使用。为了以后看的时候省一点时间,下面用图表说明一下,比如端口号port=0xb(二进制为1011),存储形式如下:
左移5位后变成下面的形式:
后面5位空出来写引脚号,比如引脚号是是pin=21=0x15=10101,存储形式如下:
为了排除引脚号5--31位的干扰,通过(pin) & 0x1F(0x1f=0001 1111)这一操作,将低5位以外的都置0,保留低5位引脚号。
通过上面的步骤后,再通过按位或连接端口号与引脚号:
这个就是这个宏的作用,把端口号与引脚号映射成nrf_gpio函数能理解的值。
2.nrf_gpio_cfg_output(LED_0);用途是把指定编号的 GPIO(通用输入输出)引脚配置成输出模式,这里指把LED_0对应引脚设为输出模式。
       nrf_gpio_cfg_output是nrf_gpio.h中定义的一个函数,其原型如下:
__STATIC_INLINE void nrf_gpio_cfg_output(uint32_t pin_number)
{
    nrf_gpio_cfg(
        pin_number,
        NRF_GPIO_PIN_DIR_OUTPUT,
        NRF_GPIO_PIN_INPUT_DISCONNECT,
        NRF_GPIO_PIN_NOPULL,
        NRF_GPIO_PIN_S0S1,
        NRF_GPIO_PIN_NOSENSE);
}

这个函数传入要设置的引脚编号,再通过nrf_gpio_cfg函数对该引脚进行设置。

__STATIC_INLINE:这是一个预处理宏,其作用是将函数定义为静态内联函数。静态函数意味着该函数的作用域仅限于当前文件,内联函数则是建议编译器在调用该函数的地方直接嵌入函数体代码,从而避免函数调用带来的开销。
 

  nrf_gpio_cfg(
        pin_number,
        NRF_GPIO_PIN_DIR_OUTPUT,
        NRF_GPIO_PIN_INPUT_DISCONNECT,
        NRF_GPIO_PIN_NOPULL,
        NRF_GPIO_PIN_S0S1,
        NRF_GPIO_PIN_NOSENSE)
参数含义是:nrf_gpio_cfg(引脚,方向,输入模式,上下拉,驱动能力,电平检测功能),从6个参数对此引脚进行设置。

3.nrf_gpio_cfg函数是nrf_gpio.h中定义的一个函数,其代码如下 :

__STATIC_INLINE void nrf_gpio_cfg(//引脚,方向,输入模式,上下拉,驱动能力,电平检测功能
    uint32_t  pin_number,
    nrf_gpio_pin_dir_t   dir,
    nrf_gpio_pin_input_t input,
    nrf_gpio_pin_pull_t  pull,
    nrf_gpio_pin_drive_t drive,
    nrf_gpio_pin_sense_t sense)
{
    NRF_GPIO_Type * reg = nrf_gpio_pin_port_decode(&pin_number);//得到pin_number所属的端口

    reg->PIN_CNF[pin_number] = ((uint32_t)dir << GPIO_PIN_CNF_DIR_Pos)//第0位开始,(0)
                               | ((uint32_t)input << GPIO_PIN_CNF_INPUT_Pos)//第1位开始(1)
                               | ((uint32_t)pull << GPIO_PIN_CNF_PULL_Pos)//第2位开始 (3..2)
                               | ((uint32_t)drive << GPIO_PIN_CNF_DRIVE_Pos)//第8位开始(10..8)
                               | ((uint32_t)sense << GPIO_PIN_CNF_SENSE_Pos);//第16位开始(17..16)
}

其主要功能是对指定 GPIO(通用输入输出)引脚的各项配置参数进行设置。通过这个函数,
可以灵活地配置 GPIO 引脚的方向、输入模式、上拉 / 下拉电阻、驱动能力以及电平检测功能等。

参数说明:
uint32_t pin_number:要配置的 GPIO 引脚编号,是一个无符号 32 位整数。
nrf_gpio_pin_dir_t dir:指定 GPIO 引脚的方向,例如输入或输出模式。nrf_gpio_pin_dir_t 是一个自定义的枚举类型,用于表示不同的引脚方向。(枚举值为NRF_GPIO_PIN_DIR_INPUT,NRF_GPIO_PIN_DIR_OUTPUT)
nrf_gpio_pin_input_t input:设置引脚的输入模式,比如是否断开输入等。nrf_gpio_pin_input_t 是一个自定义的枚举类型。(枚举值为NRF_GPIO_PIN_INPUT_CONNECT ,NRF_GPIO_PIN_INPUT_DISCONNECT )
nrf_gpio_pin_pull_t pull:配置引脚的上拉或下拉电阻,例如是否启用上拉、下拉或不使用上拉 / 下拉电阻。nrf_gpio_pin_pull_t 是一个自定义的枚举类型。(枚举值为NRF_GPIO_PIN_NOPULL,NRF_GPIO_PIN_PULLDOWN,NRF_GPIO_PIN_PULLUP)
nrf_gpio_pin_drive_t drive:设置引脚的驱动能力,例如强驱动、弱驱动等。nrf_gpio_pin_drive_t 是一个自定义的枚举类型。枚举值如下:
NRF_GPIO_PIN_S0S1 = GPIO_PIN_CNF_DRIVE_S0S1 ,
NRF_GPIO_PIN_H0S1 = GPIO_PIN_CNF_DRIVE_H0S1 ,
NRF_GPIO_PIN_S0H1 = GPIO_PIN_CNF_DRIVE_S0H1 ,
NRF_GPIO_PIN_H0H1 = GPIO_PIN_CNF_DRIVE_H0H1 ,
NRF_GPIO_PIN_D0S1 = GPIO_PIN_CNF_DRIVE_D0S1 ,
NRF_GPIO_PIN_D0H1 = GPIO_PIN_CNF_DRIVE_D0H1 ,
NRF_GPIO_PIN_S0D1 = GPIO_PIN_CNF_DRIVE_S0D1 ,
NRF_GPIO_PIN_H0D1 = GPIO_PIN_CNF_DRIVE_H0D1 ,
NRF_GPIO_PIN_E0E1 = GPIO_PIN_CNF_DRIVE_E0E1
nrf_gpio_pin_sense_t sense:配置引脚的电平检测功能,例如是否检测高电平、低电平或不进行检测。nrf_gpio_pin_sense_t 是一个自定义的枚举类型。
枚举值如下:
NRF_GPIO_PIN_NOSENSE = GPIO_PIN_CNF_SENSE_Disabled ,
NRF_GPIO_PIN_SENSE_LOW = GPIO_PIN_CNF_SENSE_Low ,
NRF_GPIO_PIN_SENSE_HIGH = GPIO_PIN_CNF_SENSE_High

nrf_gpio_pin_port_decode 是一个函数,其功能是对 pin_number 所属的端口进行解码,返回引脚对应的端口。

reg 是一个指向 NRF_GPIO_Type 类型结构体的指针,该结构体代表 GPIO 端口的寄存器映射。
reg->PIN_CNF[pin_number]:这是 GPIO 端口的引脚配置寄存器,用于对指定引脚的各项配置进行设置。
((uint32_t)dir << GPIO_PIN_CNF_DIR_Pos):把 dir 的值左移 GPIO_PIN_CNF_DIR_Pos 位,从而将其放置到寄存器里对应的位置。
|:按位或运算符,用于把各个配置项组合到一起。
同样的操作也会对 input、pull、drive 和 sense 进行,最终将它们组合后写入到 PIN_CNF 寄存器。此函数的作用是依据传入的参数对指定 GPIO 引脚的配置寄存器进行设置,
从而实现对引脚的方向、输入模式、上下拉电阻、驱动能力以及电平检测功能等的配置。

下面对nrf_gpio_pin_port_decode这个函数进行说明,它 是nrf_gpio.h中定义的一个函数,其代码如下:

__STATIC_INLINE NRF_GPIO_Type * nrf_gpio_pin_port_decode(uint32_t * p_pin)//decode:解码,uint32_t * p_pin是一个指向无符号 32 位整数的指针。该指针指向要解析的 GPIO 引脚编号。
{             																														//此函数用于解码 GPIO 引脚所属的端口,引脚编号个人理解:00000000 00000000 00000000 00000000~00000000 00000000 00000000 00011111(0-31)
    NRFX_ASSERT(nrf_gpio_pin_present_check(*p_pin));//断言
#if (GPIO_COUNT == 1)
    return NRF_P0;
#else
    if (*p_pin < P0_PIN_NUM)
    {
        return NRF_P0;
    }
    else
    {
        *p_pin = *p_pin & 0x1F;//0001 1111,比如:pin为34号引脚,已经超出P0拥有的引脚数0-31了,32,33,34,根当于P1口的0,1,2号引脚。
        return NRF_P1;
    }
#endif
}

其主要功能是根据传入的 GPIO 引脚编号,解析出该引脚所属的 GPIO 端口寄存器,并返回对应的寄存器指针。

NRFX_ASSERT:这是一个断言宏,用于在调试阶段进行错误检查。如果断言条件不满足,程序会触发断言失败,通常会终止程序运行并给出错误信息。

nrf_gpio_pin_present_check(*p_pin):调用 nrf_gpio_pin_present_check 函数,传入 *p_pin(即指针所指向的引脚编号)作为参数。该函数的作用是检查传入的引脚编号是否有效,如果无效则会触发断言失败。

#if (GPIO_COUNT == 1):这是一个条件编译指令,GPIO_COUNT 是一个宏定义,表示系统中 GPIO 端口的数量。如果 GPIO_COUNT 等于 1,说明系统中只有一个 GPIO 端口。
return NRF_P0;:如果只有一个 GPIO 端口,那么所有的引脚都属于该端口,直接返回 NRF_P0 指针,NRF_P0 通常是指向第一个 GPIO 端口寄存器的指针。
else:如果系统中有多个 GPIO 端口。
if (*p_pin < P0_PIN_NUM):检查传入的引脚编号是否小于 P0_PIN_NUM。P0_PIN_NUM 是一个宏定义,表示第一个 GPIO 端口(通常是 P0)的引脚数量。
return NRF_P0;:如果引脚编号小于 P0_PIN_NUM,说明该引脚属于第一个 GPIO 端口(P0),返回 NRF_P0 指针。
else:如果引脚编号大于等于 P0_PIN_NUM,说明该引脚属于第二个 GPIO 端口(通常是 P1)。
*p_pin = *p_pin & 0x1F;:将引脚编号与 0x1F(二进制 0001 1111)进行按位与操作。这一步的作用是将引脚编号映射到第二个 GPIO 端口的有效范围内,因为第二个 GPIO 端口的引脚编号通常是从 0 开始重新编号的。比如:pin为34号引脚,已经超出P0拥有的引脚数0-31了,32,33,34,相当于P1口的0,1,2号引脚。
return NRF_P1;:返回 NRF_P1 指针,NRF_P1 通常是指向第二个 GPIO 端口寄存器的指针。
总结
该函数通过检查传入的 GPIO 引脚编号,结合系统中 GPIO 端口的数量,判断该引脚所属的 GPIO 端口,并返回对应的寄存器指针。同时,在调试阶段会对引脚编号的有效性进行检查,确保程序的健壮性。

  现在,再回到上一函数nrf_gpio_cfg,继续说明:

 NRF_GPIO_Type * reg = nrf_gpio_pin_port_decode(&pin_number);//得到pin_number所属的端口寄存器指针。

NRF_GPIO_Type结构体定义如下 :

typedef struct {                                /*!< (@ 0x50000000) P0 Structure                                               */
  __IM  uint32_t  RESERVED[321];
  __IOM uint32_t  OUT;                          /*!< (@ 0x00000504) Write GPIO port                                            */
  __IOM uint32_t  OUTSET;                       /*!< (@ 0x00000508) Set individual bits in GPIO port                           */
  __IOM uint32_t  OUTCLR;                       /*!< (@ 0x0000050C) Clear individual bits in GPIO port                         */
  __IM  uint32_t  IN;                           /*!< (@ 0x00000510) Read GPIO port                                             */
  __IOM uint32_t  DIR;                          /*!< (@ 0x00000514) Direction of GPIO pins                                     */
  __IOM uint32_t  DIRSET;                       /*!< (@ 0x00000518) DIR set register                                           */
  __IOM uint32_t  DIRCLR;                       /*!< (@ 0x0000051C) DIR clear register                                         */
  __IOM uint32_t  LATCH;                        /*!< (@ 0x00000520) Latch register indicating what GPIO pins that
                                                                    have met the criteria set in the PIN_CNF[n].SENSE
                                                                    registers                                                  */
  __IOM uint32_t  DETECTMODE;                   /*!< (@ 0x00000524) Select between default DETECT signal behavior
                                                                    and LDETECT mode                                           */
  __IM  uint32_t  RESERVED1[118];
  __IOM uint32_t  PIN_CNF[32];                  /*!< (@ 0x00000700) Description collection: Configuration of GPIO
                                                                    pins                                                       */
} NRF_GPIO_Type;   

这个结构体的成员就是GPIO端口的寄存器,通过访问成员,可以设置相应寄存器。

其中PIN_CNF寄存器有32个,分别对应32个引脚,比如PIN_CNF[0]是个32位寄存器,用来配置第0位引脚,其内容如下:

所以GPIO_PIN_CNF_DIR_Pos=0,GPIO_PIN_CNF_INPUT_Pos=1,

 GPIO_PIN_CNF_PULL_Pos=2,GPIO_PIN_CNF_DRIVE_Pos=8,

GPIO_PIN_CNF_SENSE_Pos=16

通过下面的移位,按位或运算,将各数据连接成到一个32位的整型数,写入PIN_CNF寄存器。

    reg->PIN_CNF[pin_number] = ((uint32_t)dir << GPIO_PIN_CNF_DIR_Pos)//第0位开始,(0)
                               | ((uint32_t)input << GPIO_PIN_CNF_INPUT_Pos)//第1位开始(1)
                               | ((uint32_t)pull << GPIO_PIN_CNF_PULL_Pos)//第2位开始 (3..2)
                               | ((uint32_t)drive << GPIO_PIN_CNF_DRIVE_Pos)//第8位开始(10..8)
                               | ((uint32_t)sense << GPIO_PIN_CNF_SENSE_Pos);//第16位开始(17..16)

这样就完成了对相应引脚的各种状态的设置。

4. nrf_gpio_pin_set函数

这是nrf_gpio.h中定义的一个函数,其代码如下 :

__STATIC_INLINE void nrf_gpio_pin_set(uint32_t pin_number)
{
    NRF_GPIO_Type * reg = nrf_gpio_pin_port_decode(&pin_number);

    nrf_gpio_port_out_set(reg, 1UL << pin_number);
}

其中nrf_gpio_port_out_set也是nrf_gpio.h中定义的一个函数,其代码如下:

__STATIC_INLINE void nrf_gpio_port_out_set(NRF_GPIO_Type * p_reg, uint32_t set_mask)
{
    p_reg->OUTSET = set_mask;
}

将OUTSET相应引脚位设为1.(高电平)

5.nrf_gpio_pin_clear函数

这是nrf_gpio.h中定义的一个函数,其代码如下:

__STATIC_INLINE void nrf_gpio_pin_clear(uint32_t pin_number)
{
    NRF_GPIO_Type * reg = nrf_gpio_pin_port_decode(&pin_number);//解码引脚

    nrf_gpio_port_out_clear(reg, 1UL << pin_number);
}

nrf_gpio_port_out_clear也是nrf_gpio.h中定义的一个函数,其代码如下:

__STATIC_INLINE void nrf_gpio_port_out_clear(NRF_GPIO_Type * p_reg, uint32_t clr_mask)
{
    p_reg->OUTCLR = clr_mask;//OUTCLR::32位寄存器
}

将相应引脚位设为1,(低电平)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值