四轴飞行器项目记录
第三章 无线通信模块
文章目录
前言
NRF24L01 是一个非常经典的2.4G无线通信模块,他的资料丰富,操作简单功耗低,很适合近距离点对点小数据量的通信。所以用于控制四轴遥控器上采用的NRF24L01 模块+AP 天线这样通信距离就大大增加,如果两个带 AP 天线的NRF24L01 模块通信距离能够达到 500~1000m。考虑到小四轴的续航和重量,所以四轴上采用的是一款功耗更小,功能更强并且还完全兼容 NRF24L01 的芯片 SI24R1,并且还进行了板载天线。所以本章主要讲解 SI24R1的配置与使用。
一、SI24R1简介
Si24R1 操作方式非常方便,只需要微控制器(MCU)通过 SPI 接口对芯片少数几个寄存器配置即可以实现数据的收发通信。嵌入式 ARQ 基带引擎基于包通信原理,支持多种通信模式,可以手动或全自动 ARQ 协议操作。内部集成收发 FIFO,可以保证芯片与 MCU 数据连续传输,增强型 ARQ基带协议引擎能处理所有高速操作,因此大大降低了 MCU 的系统消耗。
需要注意的是该芯片支持的电压范围:1.9-3.6v
二、SI24R1的SPI通信协议
1.SI24R1与MCU引脚的连接
STM32 与 SI24R1 是通过 SPI总线进行数据通信的,MCU 作为 SPI通信的主机,SI24R1为从机。
图中,绿色圈中四个引脚是SPI总线的四个信号线,不再过多解释。CE引脚是SI24R1的使能引脚,当使用SI24R1时需要将此引脚电平置高;IRQ引脚是SI24R1的中断输出引脚,当配置打开SI24R1 的中断时,SI24R1接收完数据或发送完数据都将产生中断相应的 IRQ 引脚电平的变化,这样就可以及时通知 MCU 到 SI24R1中把数据读出或写入,省去了MCU 轮询查看寄存器的过程。SI24R1的中断类型有很多,详细请自行查看手册。
2.SI24R1的工作模式和数据包协议
1)工作模式转换
SI24R1可配置为关断、待机、发送空闲、发送和接受五种工作模式。
模式 | PWR_UP | CE | PRIM_RX | FIFO寄存器状态 |
---|---|---|---|---|
关断 | 0 | - | - | - |
待机 | 1 | 0 | - | - |
接收 | 1 | 1 | 1 | 接收数据写入RX_FIFO |
空闲 | 1 | -1 | 0 | TX_FIFO无数据 |
发送 | 1 | 1 | 0 | TX_FIFO有数据 |
利用单片机把它配置成接受模式或者发送模式,还可以配置频道、地址、每次发送的字节数、是否带CRC校验、功率等。
配置成发送模式以后,用单片机把要发送的数据写进去,他就会自动把数据发出去;配置成接收模式以后。单片机通过观察它的IRQ引脚,就可以知道是否接收到数据,IRQ为低电平,说明接受到数据,单片机就可以通过SPI把接收到的数据读取出来。
2)数据包协议(可以略做了解)
Si24R1 基于包通信,支持停等式 ARQ协议。芯片内部 ARQ 协议基带处理引擎,可以不需要外部微控制器干预下,自动实现 ACK 和 NO_ACK 数据包的处理。ARQ 协议基带处理单元支持1到 32字节动态数据长度,数据长度在数据包内。也可以采用固定数据长度,通过寄存器指定基带处理单元完成数据的自动解包、打包、自动回复 ACK 确认信号以及自动重发。该处理单元内部有6个通信管道,可以直接支持1:6星型网络。
ARQ包格式
一个完整的 ARQ 数据包包括前导码、地址、包控制字、负载数据以及 CRC。如下图显示为一个完整的包。
前导码字段主要用于接收数据同步,发射时芯片自动附上,接收时芯片自动去掉,对用户不可见。
地址字段为接收数据方地址,只有当该地址与芯片的地址寄存器中地址相同时才会接收。地址长度可以通过配置寄存器 AW 配置为 3、或 4、或 5 字节。
包控制字段长度为 9bit, 结构如下图。
数据包长度子字段指定数据包 32 字节。
PID 子字段告知接收端这个包是一个新的包还是一个重发的包,可以防止接收端多次接收同一个包。发射方通过 SPI写 FIFO,PID 的值自动累加。
NO ACK子字段为1时,则表明发射方告知接收端不需要回 ACK 确认信号。对于发射方,使 NO ACK 位为1需要先配置 FEATURE 寄存器中的 EN DYN ACK位为 1,且使用W_TX_PAYLOAD_NOACK 命令写 FIF0。当收到一个这样的包后,接收端不会发送 ACK 确认信号给发射方。(即使接收端工作在 ACK 接收模式)
负载数据字段为发射数据内容,可以最长32字节。
CRC 字段为包的 CRC 值,CRC 支持 8bit 和 16bit 两种,CRC 的长度通过 CONFIG 寄存器中的 CRCO 位配置。
3)ARQ通信模式
在 TX 模式下,发送端自动将前导码、地址、包控制字、负载数据、 CRC 打包。通过射频模块将信号调制通过天线发射。
在 RX 模式下,接收端在接收到的解调信号中不断侦测有效地址,一旦侦测到地址与接收地址相同,开始接收数据,如果接收到的数据有效,则将负载数据部分存放入 RX_FIFO 中,并产生中断通知 MCU。 MCU 通过 SPI 接口可随时访问 RX_FIFO 寄存器,进行数据读取。
SI24R1一共有六种通信模式,下面只讲解了ACK通信模式。
ACK模式
当用 W_TX_PAYLOAD 命令对发送端 TX FIFO 写数据时,将数据打包后,数据包中包控制字段 NO_ACK 标志位复位。接收端接收到一帧有效数据后, 产生 RX_DR 中断后,会自动发送一帧 ACK 信号,发送端接收到 ACK 信号,则自动清除 TX FIFO 数据并产生 TX_DS 发射中断,表明此次通信成功。
接收端在发送 ACK 信号时,取接收管道地址作为目标地址来发送 ACK 信号,所以发送端需要设置接收管道 0 地址与自身发送地址相同,以便接收 ACK 信号。
如果发送端在 ARD 时间内没有接收到 ACK 信号,则重新发送上一帧数据。当重发次数达到最大,仍没有收到确认信号时,发送端产生 MAX_RT 中断。 MAX_RT 中断在清除之前不能进行下一步的数据发送。所有中断通过对状态寄存器进行写操作来清除。PLOS_CNT 寄存器在每产生一个 MAX_RT 中断后加 1,用来记录当前频段下,丢失的数据包的数量。 ARC_CNT 寄存器记录当前数据重发的次数,在发送一包新数据时使其复位。最大重发次数与 ARD 时间通过 SETUP_RETR 寄存器来进行配置。接收端开启自动回复 ACK 信号由 EN_AA 寄存器来控制。下图为 ACK 模式下的一次通信完成。
三、SI24R1的SPI接口和寄存器配置
芯片采用标准的四线 SPI 接口,实测最高读写速度大于 10Mb/S。外部微控制器可
以通过 SPI 接口对芯片进行配置,包括读写功能寄存器、读写 FIFO、读芯片状态、清
除中断等。
1.SPI时序
SPI 操作包括基本的读写操作以及其他的命令操作。
注:只能在 Shutdown、 Standby 和 Idle-TX 模式下才能对寄存器进行配置。
CSN 从高电平翻转为低电平, SPI 接口开始工作。每一次 SPI操作, MISO 输出的第一字节为状态寄存器的值,之后通过命令来确定是否输出值(不输出为高阻态)。命令格式中命令字按从 MSBit 到 LSBit 的顺序输入,数据格式中按从LSByte 到 MSByte 的顺序,每字节中按从 MSBit 到 LSBit 的顺序输入。详细请参考 SPI时序。
SI24R1 一般作为SPI通信的从机,因此SPI主机的通信时序应以从机为准。通过SI24R1读写时序可以得出,SI24R1的时钟空闲状态为低电平,也就是说“CPOL=0”,SI24R1 在第一个时钟沿进行数据采集,也就是说“CPHA=0”。还可以得出寄存器读写都是先发相应的控制命令,然后进行数据的读或者写交互。
2.寄存器操作命令
寄存器操作命令共有八个。
3.寄存器数量
寄存器共有24个。
四、SI24R1与NRF24L01的半双工通信
1.通信条件
两个芯片之间通信,收发双方需要满足4个条件:
1)发射接收频道相同(设置频道寄存器RF_CH,0-125);
2)发射接收地址相同(设置TX_ADDR和RX_ADDR_P0相同,五个8位地址);
3)发射接收数据宽度相同(n <=32);
4)发射接收速率相同(250k,1M,2M);
2.Tx模式初始化过程
3.Rx模式初始化过程
4.中断函数处理
SI24R1 的中断是根据初始化时配置的中断,然后满足条件后就会在IRQ引脚上产生一个低电平,来通知 MCU 进行相应的反应。由于实时性的要求,所以采用外部中断来捕获 IRQ 引脚的下降沿,如果捕获到下降沿就进入中断服务函数中进行处理。
例如:配置 SI24R1 为发送中断时,一旦 SI24R1 收到接收方返回的 ACK 时,IRQ引脚就会产生一个低电平,这时 EXIT 外部中断捕获到了这个下降沿,就会产生一个中断。
5.读入数据
代码如下(示例):
#include "nrf2401.h"
#include "spi.h"
#include "delay.h"
#include "stdio.h"
#include "led.h"
#include "remotedata.h"
#include "structconfig.h"
#include "stdlib.h"
#include "paramsave.h"
/************************************************************************
*代码移植修改区
*只需要根据原理图修改对应的端口时钟 端口 引脚
************************************************************************/
#define RCC_NRF_SCN RCC_AHB1Periph_GPIOB //端口时钟
#define NRF_SCN_PORT GPIOB //端口
#define NRF_SCN GPIO_Pin_12 //引脚
#define RCC_NRF_CE RCC_AHB1Periph_GPIOA //端口时钟
#define NRF_CE_PORT GPIOA //端口
#define NRF_CE GPIO_Pin_8 //引脚
#define RCC_NRF_IRQ RCC_AHB1Periph_GPIOB //端口时钟
#define NRF_IRQ_PORT GPIOB //端口
#define NRF_IRQ GPIO_Pin_2 //引脚
/**************************************************************************/
//设置引脚电平
#define NRF_SCN_LOW NRF_SCN_PORT->BSRRH |= NRF_SCN
#define NRF_SCN_HIGH NRF_SCN_PORT->BSRRL |= NRF_SCN
#define NRF_CE_LOW NRF_CE_PORT->BSRRH |= NRF_CE
#define NRF_CE_HIGH NRF_CE_PORT->BSRRL |= NRF_CE
//读取引脚电平
//#define NRF_IRQ_READ (NRF_IRQ_PORT->IDR & NRF_IRQ)
#define NRFAddrMax 50 //NRF最后一个字节地址最大为50
uint8_t NRFaddr = 0xFF; //初始化NRF最后一字节地址
uint8_t NRF_TX_DATA[TX_PAYLO_WIDTH];//NRF发送缓冲区
uint8_t NRF_RX_DATA[RX_PAYLO_WIDTH];//NRF接收缓冲区
uint8_t TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0xFF}; //发送地址
uint8_t RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0xFF}; //接收地址
void NRF24L01_Config(void);
void NRF_GetAddr(void);
/*****************************************************************************
*函 数:void NRF24l01_Init(void)
*功 能:NRF引脚GPIO初始化
*参 数:无
*返回值:无
*备 注:无
*****************************************************************************/
void NRF24l01_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = NRF_SCN; //NRF的片选信号SCN
GPIO_Init(NRF_SCN_PORT, &GPIO_InitStructure);
GPIO_ResetBits(NRF_SCN_PORT,NRF_SCN);
GPIO_InitStructure.GPIO_Pin = NRF_CE; //NRF的片选信号SCN
GPIO_Init(NRF_CE_PORT, &GPIO_InitStructure);
GPIO_ResetBits(NRF_CE_PORT,NRF_CE);
SPI2_Init(); //SPI2初始化
NRF24L01_Check(); //检查NRF24L01是否与MCU通信
NRF_SCN_HIGH; //失能NRF
NRF_CE_LOW; //待机模式
// NRF24L01_Config(); //配置NRF并初始化为接收模式
}
/*****************************************************************************
*函 数:uint8_t NRF24l01_write_reg(uint8_t reg,uint8_t value)
*功 能:写一字节数据到寄存器
*参 数:reg: 寄存器地址
* val: 要写入的数据
*返回值:status
*备 注:NRF2401代码移植只需把SPI驱动修改成自己的即可
*****************************************************************************/
uint8_t NRF24l01_write_reg(uint8_t reg,uint8_t value)
{
uint8_t status;
NRF_SCN_LOW;
status=SPI2_WriteReadByte(reg);
SPI2_WriteReadByte(value);
NRF_SCN_HIGH;
return status;
}
/*****************************************************************************
*函 数:uint8_t NRF24l01_read_reg(uint8_t reg)
*功 能:读一字节数据到寄存器
*参 数:reg: 寄存器地址
*返回值:reg_val
*备 注:NRF2401代码移植只需把SPI驱动修改成自己的即可
*****************************************************************************/
uint8_t NRF24l01_read_reg(uint8_t reg)
{
uint8_t reg_val;
NRF_SCN_LOW;
SPI2_WriteReadByte(reg);
reg_val = SPI2_WriteReadByte(0xff);
NRF_SCN_HIGH;
return reg_val;
}
/*****************************************************************************
*函 数:uint8_t NRF24L01_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len)
*功 能:写一组数据到寄存器
*参 数:reg: 寄存器地址
* pBuf: 要写入数据的地址
* len: 要写入的数据长度
*返回值:status
*备 注:NRF2401代码移植只需把SPI驱动修改成自己的即可
*****************************************************************************/
uint8_t NRF24L01_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{
uint8_t status;
int i;
NRF_SCN_LOW;
status = SPI2_WriteReadByte(reg);
for( i=0;i<len;i++)
{
SPI2_WriteReadByte(*pBuf);
pBuf++;
}
NRF_SCN_HIGH;
return status;
}
/*****************************************************************************
*函 数:uint8_t NRF24L01_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len)
*功 能:读一组数据到寄存器
*参 数:reg: 寄存器地址
* pBuf: 要读取数据的地址
* len: 要读取的数据长度
*返回值:status
*备 注:NRF2401代码移植只需把SPI驱动修改成自己的即可
*****************************************************************************/
uint8_t NRF24L01_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{
uint8_t status;
int i;
NRF_SCN_LOW;
status = SPI2_WriteReadByte(reg);
for(i = 0;i < len ;i++)
{
*pBuf = SPI2_WriteReadByte(0xff);
pBuf++;
}
NRF_SCN_HIGH;
return status;
}
/*****************************************************************************
*函 数:void NRFset_Mode(uint8_t mode)
*功 能:切换NRF2401的工作模式模式
*参 数:无
*返回值:无
*备 注:无
*****************************************************************************/
void NRFset_Mode(uint8_t mode)
{
if(mode == IT_TX)
{
NRF_CE_LOW;
NRF24l01_write_reg(W_REGISTER+CONFIG,IT_TX);
NRF24l01_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去发送模式就触发中断
NRF_CE_HIGH;
// delay_us(15);
}
else
{
NRF_CE_LOW;
NRF24l01_write_reg(W_REGISTER+CONFIG,IT_RX);//配置为接收模式
NRF24l01_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去接收模式就触发中断
NRF_CE_HIGH;
delay_us(200);
}
}
/*****************************************************************************
*函 数:void NRF24L01_Config(void)
*功 能:NRF基本参数配置,并初始化为接收模式
*参 数:无
*返回值:无
*备 注:无
*****************************************************************************/
void NRF24L01_Config(void)
{
NRF_CE_LOW;
NRF24l01_write_reg(W_REGISTER+SETUP_AW, 0x03); //配置通信地址的长度,默认值时0x03,即地址长度为5字节
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,(uint8_t*)TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址
NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)TX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK
NRF24l01_write_reg(W_REGISTER+SETUP_RETR,0x1A); //设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次 0x1A
NRF24l01_write_reg(W_REGISTER+EN_RXADDR,0x01);//使能通道0的接收地址
NRF24l01_write_reg(W_REGISTER+EN_AA,0x01); //使能通道0自动应答
NRF24l01_write_reg(W_REGISTER+RX_PW_P0,RX_PAYLO_WIDTH);//选择通道0的有效数据宽度
NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)RX_ADDRESS,RX_ADR_WIDTH); //写RX节点地址
NRF24l01_write_reg(W_REGISTER+RF_CH,30); //设置RF通道为40hz(1-64Hz都可以)
NRF24l01_write_reg(W_REGISTER+RF_SETUP,0x27); //设置TX发射参数,0db增益,2Mbps,低噪声增益关闭 (注意:低噪声增益关闭/开启直接影响通信,要开启都开启,要关闭都关闭0x0f)0x07
NRFset_Mode(IT_RX); //默认为接收模式
NRF_CE_HIGH;
}
/*****************************************************************************
*函 数:uint8_t NRF24L01_TxPacket(uint8_t *txbuf)
*功 能:NRF发送一包数据
*参 数:txbuf:要发送数据地址
*返回值:无
*备 注:无
*****************************************************************************/
void NRF24L01_TxPacket(uint8_t *txbuf)
{
NRF_CE_LOW;
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,(uint8_t*)TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址
NRF24L01_Write_Buf(W_REGISTER+RX_ADDR_P0,(uint8_t*)TX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK
NRF24L01_Write_Buf(W_RX_PAYLOAD,txbuf,TX_PAYLO_WIDTH); //写数据到TX_BUFF
NRF24l01_write_reg(W_REGISTER+CONFIG,0x0e); //设置为发送模式,开启所有中断
NRF24l01_write_reg(W_REGISTER+STATUS,0X7E); //清除所有中断,防止一进去发送模式就触发中断
NRF_CE_HIGH;
delay_us(10); //CE持续高电平10us
}
/*****************************************************************************
*函 数:uint8_t NRF24L01_RxPacket(uint8_t *rxbuf)
*功 能:NRF接收一包数据
*参 数:rxbuf:接收数据存储地址
*返回值:无
*备 注:无
*****************************************************************************/
void NRF24L01_RxPacket(uint8_t *rxbuf)
{
NRF_CE_LOW;
NRF24L01_Read_Buf(R_RX_PAYLOAD,rxbuf,TX_PAYLO_WIDTH);//读取RX的有效数据
NRF24l01_write_reg(FLUSH_RX,0xff); //清除RX FIFO(注意:这句话很必要)
NRF_CE_HIGH;
}
/*****************************************************************************
*函 数:uint8_t NRF24L01_testConnection(void)
*功 能:检查NRF2401与MCU的SPI总线是否通信正常
*参 数:无
*返回值:1已连接 0未连接
*备 注:无
*****************************************************************************/
uint8_t NRF24L01_testConnection(void)
{
uint8_t buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
uint8_t i;
NRF24L01_Write_Buf(W_REGISTER+TX_ADDR,buf,5); //写入5个字节的地址.
NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址
for(i=0;i<5;i++)
if(buf[i]!=0XA5)break;
if(i!=5)return 0; //检测24L01错误
return 1; //检测到24L01
}
void NRF24L01_Check(void)
{
while(!NRF24L01_testConnection())
{
printf("\rNRF2401 no connect...\r\n");
RGB_LED_Red();//红灯常亮
}
}
/*****************************************************************************
*函 数:void EXTI2_IRQHandler(void)
*功 能:NRF(全双工)的外中断处理函数
*参 数:无
*返回值:无
*备 注: NRF的所有中断事件都在次函数中给予相应的处理
*****************************************************************************/
void EXTI2_IRQHandler(void)
{
uint8_t sta;
if(EXTI_GetITStatus(EXTI_Line2) != RESET )
{
RunTimer_Test();
NRF_CE_LOW;//拉低CE,以便读取NRF中STATUS中的数据
sta = NRF24l01_read_reg(R_REGISTER+STATUS); //读取STATUS中的数据,以便判断是由什么中断源触发的IRQ中断
/* 发送完成中断 TX_OK */
if(sta & TX_OK)
{
NRFset_Mode(IT_RX);
NRF24l01_write_reg(W_REGISTER+STATUS,TX_OK); //清除发送完成标志·
NRF24l01_write_reg(FLUSH_TX,0xff); //清除TX_FIFO
//printf("Sent OK!!!!\r\n");
}
/* 接收完成中断 RX_OK */
if(sta & RX_OK)
{
NRF24L01_RxPacket(NRF_RX_DATA);
Remote_Data_ReceiveAnalysis();
NRF24l01_write_reg(W_REGISTER+STATUS,RX_OK); //清除发送完成标志·
//printf("Receive OK!!!!\r\n");
}
/* 达到最大重发次数中断 MAX_TX */
if(sta & MAX_TX)
{
NRFset_Mode(IT_RX);
NRF24l01_write_reg(W_REGISTER+STATUS,MAX_TX);//清除接达到最大重发标志
NRF24l01_write_reg(FLUSH_TX,0xff); //清除TX_FIFO
//printf("Sent Max Data!!!\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
/*****************************************************************************
*函 数:void NRF_GetAddr(void)
*功 能:给飞机获取上的NRF获取一个地址
*参 数:无
*返回值:无
*备 注:此函数需要与遥控器的对频函数联合使用否者NRF通信不成功,
如果自己做的的遥控器可直接用固定地址
*****************************************************************************/
void NRF_GetAddr(void)
{
if(NRFaddr > NRFAddrMax)//当 NRFaddr大于NRFAddrMax,就说明次时NRF还未初始化完成
{
srand(SysTick->VAL);//给随机数种子
//printf("SysTick->VAL:%d\r\n",SysTick->VAL);
NRFaddr = rand()%NRFAddrMax;//随机获取NRF最后一字节地址(地址:0~50)
PID_WriteFlash();//保存此地址Flash
}else if(NRFaddr != TX_ADDRESS[TX_ADR_WIDTH-1])
{
TX_ADDRESS[TX_ADR_WIDTH-1] = NRFaddr;
RX_ADDRESS[TX_ADR_WIDTH-1] = NRFaddr;
NRF24L01_Config();
//printf("NRFAddr:%d\r\n",NRFaddr);
}
}
/*****************************************************************************
*函 数:void NRF_Test(void)
*功 能:MRF通信测试函数
*参 数:无
*返回值:无
*备 注:测试时用
*****************************************************************************/
void NRF_Test(void)
{
uint8_t t=0;
static uint8_t mode,key;
mode = ' ';
key=mode;
for(t=0;t<32;t++)
{
key++;
if(key>('~'))key=' ';
NRF_TX_DATA[t]=key;
}
mode++;
if(mode>'~')mode=' ';
NRF24L01_TxPacket(NRF_TX_DATA);
}
总结
以上就是本次学习以及记录内容。