引言

STM32F4 开发板带有一个无线模块(WIRELESS接口,采用 8 脚插针方式与开发板连接,可以用来连接 NRF24L01/RFID 等无线模块。本次实验将以 NRF24L01模块为例介绍 STM32F4 开发板上实现无线通信。在本次实验中,我们将使用两块探索者 STM32F4 开发板,一块用于发送收据,另外一块用于接收,从而实现无线数据传输。

1 NRF24L01 无线模块简介

NRF24L01 无线模块,采用的芯片是 NRF24L01,该芯片的主要特点如下:
1)2.4G 全球开放的 ISM 频段,免许可证使用。
2)最高工作速率 2Mbps,高校的 GFSK 调制,抗干扰能力强。
3)125 个可选的频道,满足多点通信和调频通信的需要。
4)内置 CRC 检错和点对多点的通信地址控制。
5)低工作电压(1.9~3.6V)。
6)可设置自动应答,确保数据可靠传输。
该芯片通过 SPI 与外部 MCU 通信,最大的 SPI 速度可以达到 10Mhz。该模块的外形和引脚图如图:

模块 VCC 脚的电压范围为 1.9~3.6V,建议不要超过 3.6V,否则可能烧坏模块,一般用 3.3V电压比较合适。除了 VCC 和 GND 脚,其他引脚都可以和 5V 单片机的 IO 口直连,正是因为其兼容 5V 单片机的 IO,故使用上具有很大优势。

2 硬件设计

本实验功能简介:开机的时候先检测 NRF24L01 模块是否存在,在检测到 NRF24L01模块之后,根据 KEY0 和 KEY1 的设置来决定模块的工作模式,在设定好工作模式之后,就会不停的发送/接收数据,同样用 DS0 来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯 DS0
2) KEY0 和 KEY1 按键
3) TFTLCD 模块
4) NRF24L01 模块
NRF24L01 模块属于外部模块,这里我们仅介绍开发板上 NRF24L01 模块接口和 STM32F4的连接情况,他们的连接关系如图 :

这里NRF24L01也是使用的SPI1,和W25Q128共用一个SPI接口,所以在使用的时候,他们分时复用SPI1。我们需要把W25Q128的片选信号置高,以防止这个器件对NRF24L01的通信造成干扰。

另外,NRF_IRQ和RS485_RE共用了PG8,所以,他们不能同时使用,不过我们一般用不到NRF_IRQ这个信号,因此,RS485和NRF一般也可以同时使用。由于无线通信实验是双向的,所以至少要有两个模块同时能工作,这里我们使用2套STM32F4开发板来演示。

3 软件设计

NRF24L01底层驱动函数:

const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址
void NRF24L01_SPI_Init(void)
{SPI_InitTypeDef SPI_InitStructure;
SPI_Cmd(SPI1, DISABLE); //失能 SPI 外设
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //工作模式:主 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;// 8 位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //空闲状态为低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//第 1 个跳变沿数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号软件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
//预分频值为 256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据传输从 MSB 位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //初始化外设 SPIx 寄存器
SPI_Cmd(SPI1, ENABLE); //使能 SPI 外设
}
//初始化 24L01 的 IO 口
void NRF24L01_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOG,
ENABLE);//使能 GPIOB,G 时钟//GPIOB14 初始化设置:推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 PB14
//GPIOG6,7 推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化 PG6,7
//GPIOG.8 上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化 PG8GPIO_SetBits(GPIOB,GPIO_Pin_14);//PB14 输出 1,防止 SPI FLASH 干扰 NRF 的通信SPI1_Init(); //初始化 SPI1
NRF24L01_SPI_Init();//针对 NRF 的特点修改 SPI 的设置
NRF24L01_CE=0; //使能 24L01
NRF24L01_CSN=1; //SPI 片选取消
}
//检测 24L01 是否存在
//返回值:0,成功;1,失败
u8 NRF24L01_Check(void)
{u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
u8 i;
SPI1_SetSpeed(SPI_BaudRatePrescaler_8);
NRF24L01_Write_Buf(NRF_WRITE_REG+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 1;//检测 24L01 错误
return 0; //检测到 24L01
}
//SPI 写寄存器
//reg:指定寄存器地址
//value:写入的值
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{u8 status;NRF24L01_CSN=0; //使能 SPI 传输status =SPI1_ReadWriteByte(reg);//发送寄存器号SPI1_ReadWriteByte(value); //写入寄存器的值NRF24L01_CSN=1; //禁止 SPI 传输 return(status); //返回状态值
}
//读取 SPI 寄存器值
//reg:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg)
{u8 reg_val; NRF24L01_CSN = 0; //使能 SPI 传输SPI1_ReadWriteByte(reg); //发送寄存器号reg_val=SPI1_ReadWriteByte(0XFF);//读取寄存器内容NRF24L01_CSN = 1; //禁止 SPI 传输 return(reg_val); //返回状态值
}
//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{u8 status,u8_ctr; NRF24L01_CSN = 0; //使能 SPI 传输status=SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI1_ReadWriteByte(0XFF);NRF24L01_CSN=1; //关闭 SPI 传输return status; //返回读到的状态值
}
//在指定位置写指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
{u8 status,u8_ctr; NRF24L01_CSN = 0; //使能 SPI 传输status = SPI1_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值for(u8_ctr=0; u8_ctr<len; u8_ctr++)SPI1_ReadWriteByte(*pBuf++); //写入数据NRF24L01_CSN = 1; //关闭 SPI 传输return status; //返回读到的状态值
}
//启动 NRF24L01 发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
u8 NRF24L01_TxPacket(u8 *txbuf)
{u8 sta;
SPI1_SetSpeed(SPI_BaudRatePrescaler_8);//s24L01 的最大 SPI 时钟为 10Mhz
NRF24L01_CE=0;NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);
//写数据到 TX BUF 32 个字节
NRF24L01_CE=1;//启动发送
while(NRF24L01_IRQ!=0);//等待发送完成
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除 TX_DS 或 MAX_RT 中断标志
if(sta&MAX_TX)//达到最大重发次数
{ NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除 TX FIFO 寄存器
return MAX_TX;
}
if(sta&TX_OK)//发送完成
{ return TX_OK;
}
return 0xff;//其他原因发送失败
}
//启动 NRF24L01 发送一次数据
//txbuf:待发送数据首地址
//返回值:0,接收完成;其他,错误代码
u8 NRF24L01_RxPacket(u8 *rxbuf)
{u8 sta;
SPI1_SetSpeed(SPI_BaudRatePrescaler_8); //24L01 的最大 SPI 时钟为 10Mhz
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除 TX_DS 或 MAX_RT 中断标志
if(sta&RX_OK)//接收到数据
{NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除 RX FIFO 寄存器
return 0;
}
return 1;//没收到任何数据
}
//该函数初始化 NRF24L01 到 RX 模式
//设置 RX 地址,写 RX 数据宽度,选择 RF 频道,波特率和 LNA HCURR
//当 CE 变高后,即进入 RX 模式,并可以接收数据了
void NRF24L01_RX_Mode(void)
{NRF24L01_CE=0; NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
//写 RX 节点地址NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道 0 的自动应答 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道 0 的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置 RF 通信频率 NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
//选择通道 0 的有效数据宽度 NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//设置 TX 发射参数,0db 增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);
//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式NRF24L01_CE = 1; //CE 为高,进入接收模式
}
//该函数初始化 NRF24L01 到 TX 模式
//设置 TX 地址,写 TX 数据宽度,设置 RX 自动应答的地址,填充 TX 发送数据,
//选择 RF 频道,波特率和 LNA HCURR
//PWR_UP,CRC 使能
//当 CE 变高后,即进入 RX 模式,并可以接收数据了
//CE 为高大于 10us,则启动发送.
void NRF24L01_TX_Mode(void)
{NRF24L01_CE=0; NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);
//写 TX 节点地址NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
//设置 TX 节点地址,主要为了使能 ACK NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道 0 的自动应答 NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道 0 的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);
//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10 次NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置 RF 通道为 40NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);
//设置 TX 发射参数,0db 增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);
//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
NRF24L01_CE=1;//CE 为高,10us 后启动发送
}

此部分代码我们不多介绍,在这里强调一个要注意的地方,在NRF24L01_Init 函数里面,我们调用了 SPI1_Init()函数,SCK空闲时为高,但是 NRF24L01 的 SPI 通信时序如图:

上图中 Cn 代表指令位,Sn 代表状态寄存器位,Dn 代表数据位。从图中可以看出,SCK 空闲的时候是低电平的,而数据在 SCK 的上升沿被读写。所以,我们需要设置 SPI 的 CPOL 和 CPHA均为 0,来满足NRF24L01 对 SPI 操作的要求。

所以,我们在 NRF24L01_Init 函数里面又单独添加了将 CPOL 和 CPHA 设置为 0 的函数NRF24L01_SPI_Init。这里主要是修改了下面两行代码;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //空闲状态为低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//第 1 个跳变沿数据被采样

接下来我们看看 24l01.h 头文件部分内容:

#ifndef __24L01_H
#define __24L01_H
#include "sys.h"
//NRF24L01 寄存器操作命令
#define READ_REG 0x00 //读配置寄存器,低 5 位为寄存器地址
......//省略部分定义
#define FIFO_STATUS 0x17 //FIFO 状态寄存器;bit0,RX FIFO 寄存器空标志;
//bit1,RX FIFO 满标志;bit2,3,保留 bit4,TX FIFO 空标志;bit5,TX FIFO 满标志;
//bit6,1, 循环发送上一数据包.0,不循环;
//24L01 操作线
#define NRF24L01_CE PGout(6) //24L01 片选信号
#define NRF24L01_CSN PGout(7) //SPI 片选信号
#define NRF24L01_IRQ PGin(8) //IRQ 主机数据输入
//24L01 发送接收数据宽度定义
#define TX_ADR_WIDTH 5 //5 字节的地址宽度
#define RX_ADR_WIDTH 5 //5 字节的地址宽度
#define TX_PLOAD_WIDTH 32 //32 字节的用户数据宽度
#define RX_PLOAD_WIDTH 32 //32 字节的用户数据宽度
void NRF24L01_Init(void); //初始化
......//省略部分函数申明
u8 NRF24L01_RxPacket(u8 *rxbuf); //接收一个包的数据
#endif

部分代码,主要定义了一些 24L01 的命令字(这里我们省略了一部分),以及函数声明,这里还通过 TX_PLOAD_WIDTH 和RX_PLOAD_WIDTH 决定了发射和接收的数据宽度,也就是我们每次发射和接受的有效字数。NRF24L01 每次最多传输 32 个字节,再多的字节传输则需要多次传
送。
最后我们看看主函数:

//要写入到 W25Q16 的字符串数组
const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
#define SIZE sizeof(TEXT_Buffer)
int main(void)
{
u8 key,mode, tmp_buf[33];
u16 t=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为 115200
LED_Init(); //初始化 LED
LCD_Init(); //LCD 初始化
KEY_Init(); //按键初始化
NRF24L01_Init(); //初始化 NRF24L01
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"NRF24L01 TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/5/9");
while(NRF24L01_Check())
{LCD_ShowString(30,130,200,16,16,"NRF24L01 Error");
delay_ms(200);
LCD_Fill(30,130,239,130+16,WHITE);
delay_ms(200);
}
LCD_ShowString(30,130,200,16,16,"NRF24L01 OK");
while(1)
{key=KEY_Scan(0);
if(key==KEY0_PRES)
{
mode=0; break;
}else if(key==KEY1_PRES)
{mode=1;break;
}
t++;
if(t==100)LCD_ShowString(10,150,230,16,16,
"KEY0:RX_Mode KEY1:TX_Mode"); //闪烁显示提示信息
if(t==200)
{LCD_Fill(10,150,230,150+16,WHITE); t=0;
}
delay_ms(5);
}
LCD_Fill(10,150,240,166,WHITE);//清空上面的显示
POINT_COLOR=BLUE;//设置字体为蓝色
if(mode==0)//RX 模式
{LCD_ShowString(30,150,200,16,16,"NRF24L01 RX_Mode");
LCD_ShowString(30,170,200,16,16,"Received DATA:");
NRF24L01_RX_Mode();
while(1)
{
if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来. {tmp_buf[32]=0;//加入字符串结束符
LCD_ShowString(0,190,lcddev.width-1,32,16,tmp_buf);
}else delay_us(100);
t++;
if(t==10000)//大约 1s 钟改变一次状态
{t=0;LED0=!LED0;
}
};
}else//TX 模式
{
LCD_ShowString(30,150,200,16,16,"NRF24L01 TX_Mode");
NRF24L01_TX_Mode();
mode=' ';//从空格键开始
while(1)
{
if(NRF24L01_TxPacket(tmp_buf)==TX_OK)
{LCD_ShowString(30,170,239,32,16,"Sended DATA:");
LCD_ShowString(0,190,lcddev.width-1,32,16,tmp_buf);
key=mode;
for(t=0;t<32;t++)
{key++;
if(key>('~'))key=' ';
tmp_buf[t]=key;
}
mode++;
if(mode>'~')mode=' ';
tmp_buf[32]=0;//加入结束符
}else
{
LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);//清空显示
LCD_ShowString(30,170,lcddev.width-1,32,16,"Send Failed ");
};
LED0=!LED0;delay_ms(1500);
};
}
}

程序运行时先通过 NRF24L01_Check 函 数检测 NRF24L01 是否存在,如果存在,则让用户选择发送模式(KEY1)还是接收模式(KEY0),在确定模式之后,设置 NRF24L01 的工作模式,然后执行相应的数据发送/接收处理。

至此,我们整个实验的软件设计就完成了。

4 下载验证

在代码编译成功之后,我们通过下载代码到 STM32F4 开发板上,可以看到 LCD 显示如图 所示的内容(假定 NRF24L01 模块已经接上开发板):

通过 KEY0 和 KEY1 来选择 NRF24L01 模块所要进入的工作模式,我们两个开发板一个选择发送,一个选择接收就可以了。
开发板 A 发送数据:

开发板 B 接收数据:

A发送,B 接收。可以看到收发数据是一致的,说明实验成功。

基于STM32F407无线通信实验(有代码)相关推荐

  1. 基于STM32F407的俄罗斯方块游戏代码分析

    这里只给了关键代码进行分析,并非全部代码. 项目概述和测试见文章 基于STM32F407的俄罗斯方块小游戏的设计_钻仰弥坚的博客-CSDN博客 一.方块编码的方式 首先需要知道俄罗斯方块本质上为4个小 ...

  2. 基于STM32F407摄像头实验(有代码)

    1.OV2640 简介 OV2640 是 OV(OmniVision)公司生产的一颗 1/4 寸的 CMOS UXGA(16321232)图像 传感器.该传感器体积小.工作电压低,提供单片 UXGA ...

  3. 基于Stm32f407 的贪吃蛇小游戏【正点原子-探索者开发板】

    基于单片机stm32f407的单机小游戏----贪吃蛇小游戏 1.介绍 这是我花一个星期完成的一个简单地单机贪吃蛇小游戏项目,芯片是stm32f407,项目是基于正点原子-探索者开发板完成的,有需要的 ...

  4. 【正点原子STM32连载】第四十一章 无线通信实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  5. 基于STM32F407 ADC双通道 PS2游戏机摇杆ADC采集

    基于STM32F407 ADC双通道 PS2游戏机摇杆ADC采集 一.PS2游戏机摇杆概述 1.1PS2游戏机摇杆概述 1.2PS2游戏机摇杆图解 二.硬件连接分析 三.代码实现 3.1 ADC代码 ...

  6. 【STM32】基于STM32F407中断方式实现串口通信

    目录 一.中断介绍 二.中断方式实现串口通信 1. 新建工程 2. 工程设置 3.代码编写 4. 烧录验证 三.总结 四.参考 一.中断介绍 具体介绍参考文章: [STM32]基于STM32F407的 ...

  7. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  8. java框架ssh实验报告_基于SSH的实验报告提交系统

    [实例简介] 基于SSH的实验报告提交的开发系统,可以用于毕业设计和平时的学习中,适合学习javaweb三大框架的同学学习. [实例截图] [核心代码] 364e55cd-edbc-4bfc-a29b ...

  9. 基于STM32F407的人脸追踪

    整体概述 本项目采用两个舵机构成的二自由度的电动云台作为执行机构,控制摄像头在水平和垂直方向的运动.舵机带动摄像头进行二维平面的运动的同时,摄像头进行实时人脸检测,一旦检测到人脸,则进行人脸跟踪. 摄 ...

最新文章

  1. Vue单文件组件基础模板
  2. oracle接收输入参数,Oracle带输入输出参数存储过程(包括sql分页功能)
  3. python写界面进度条程序_Python中如何写控制台进度条的整理
  4. OpenCV定制和调试检测系统
  5. nfs+inotify
  6. 在线网摘收藏?让Google来吧!
  7. go vs python 对接外部web api_python--web--让python提供api服务--aiohttp-Go语言中文社区
  8. Android 调用12306接口,GitHub - AndroidyxChen/loading-12306: 仿PC端12306的刷新loading的自定义view...
  9. [十二省联考 2019] 异或粽子(可持久化字典树 + 二叉堆)
  10. java yaml dump方法_yamlyaml.load与yaml.dump方法
  11. GitHub 多次宕机的罪魁祸首竟是 MySQL?
  12. Kotlin从入门到放弃(三)——协程
  13. 军用软件开发周期和文档
  14. matlab 输出两列,Matlab绘制两列正弦波的叠加
  15. 当冬日阳光照耀我孤苦的心怀
  16. 笔记本突然不能连接无线网解决办法
  17. 什么是甘特图?怎么做甘特图?
  18. 【Linux】服务器部署:阿里云服务器购买配置与报价参考
  19. 前端面试知识梳理(高级前端开发工程师)
  20. vue中自定义select

热门文章

  1. 2013完美世界校招笔试题及答案
  2. Java多线程编程详解
  3. 软考中级软件评测师,你真的了解它吗?
  4. C语言算法之:猜字游戏
  5. 服务器主板BIOS位置,服务器主板bios设置
  6. python入门实践 奶茶馆售卖奶茶
  7. 港中文商汤提出SMCA:用于DETR快速收敛的空间调制协同注意力
  8. OpenLayers学习笔记中级篇(一、各种地图的加载)
  9. 手淘、微博一直钟情的_Netty框架是个什么鬼?_参与互动可获《Netty实战》新书
  10. Wi-Fi 链路层 - MAC帧格式