前言
本篇博客会介绍基于STM32F405RGT6的NRF24L01遥控器,会主要从做遥控器所需模块的必备知识,原理图,以及代码部分做详细的分析。
模块学习部分
IIC通信协议
这里先简单介绍一下IIC通信原理:
一、什么是I2C
IIC也称I2C,是一个多主从的串行总线,由飞利浦公司发明的通讯总线,属于半双工同步传输类总线,仅由两条线就能完成多机通讯,一条SCL时钟线,另外一条双向数据线SDA,IIC总线要求每个设备SCL/SDA线都是漏极开路模式,因此必须带上拉电阻才能正常工作。I2C协议占用引脚少,硬件实现简单,可扩展性强,I2C数据传输速率有标准模式(100kbps)、快速模式(400kbps)和高速模式(3.4Mbps)。
注:本文目前只对常用方式(一主多从)进行讲解,多主机模式后续更新。
二、IIC协议简介
IIC总线的SDA和SCL两根总线需要上拉,使总线处于空闲状态。IIC总线一共有两种状态、四种信号。除此之外还需要了解IIC总线的数据有效性。
2.1 IIC总线物理接线
SDA 和SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电源电压。当总线空闲时,这两条线路都是高电平。连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能。
总线器件数目:由于每一个IIC器件在IIC总线上都有一个确切的7位地址码,这也意味着一条IIC总线上最多可链接127(0X00位地址不使用)个地址互不相同的IIC器件。但在单条IIC总线上链接不多与127个器件的同时,必须要满足总线电容不能超过400pF(协议规定),总线之所以规定电容大小是因为,IIC的OD要求外部有电阻上拉,电阻和总线电容产生了一个RC延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险。传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以RC乘积必须更小。实际设计中经验值大概是8个器件左右。
这里作者推荐去看B站的视频: https://www.bilibili.com/video/BV1cF411N7ei/?share_source=copy_web&vd_source=dd17c23c4316ed4d1993b41f903c8a67
OLED显示屏
OLED简介:(学习本模块之前建议先把IIC通信协议了解一下)
OLED,即有机发光二极管( Organic Light Emitting Diode )。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,内部SSD1306芯片(本芯片的介绍请看后边的附件)。
LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示OLED效果要来得好一些。以目前的技术,OLED的尺寸还难以大型化,但是分辨率确可以做到很高。在此我们使用的是中景园电子的0.96寸OLED显示屏,该屏有以下特点:
1、0.96 寸OLED有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上1/4部分为黄光,下3/4为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改
2、分辨率为128*64
3、多种接口方式; OLED裸屏总共种接口包括: 6800、8080 两种并行接口方式、3线或4线的串行SPI接口方式、IIC 接口方式(只需要2根线就可以控制OLED了!),这五种接口是通过屏上的BSO~BS2来配置的。
4、接口分别为七针的SPI/IIC 兼容模块,四针的IIC模块。
简单点说:可以把屏幕当作128×64的LED组成!
这里我是用的是中景园的OLED例程代码,因为STM32F4系列的芯片和STM32F1系列的芯片在初始化上并没有过多的差别,所以我这里给出我的STM32F103C8T6的例程代码的链接,请读者根据自己的实际情况更改代码,主要是注意引脚的选择和初始化。
链接:https://pan.baidu.com/s/1rsBXfJvrxDzbgosnWBQYOQ
提取码:apjn
TP4056充电管理
因为遥控器上需要用到1S锂电池,所以这里用TP4056做充电管理,接下来就介绍一下该芯片的使用方法:
1脚是电池温度检测引脚,当需要检测电池温度时需要外接热敏电阻,不检测电池温度时直接接地;
2脚为恒流充电电流设置引脚,通过接一个电阻到地设置恒流充电的电流,最大可设置充电电流为1A,这个是电阻和设置充电电流的关系大家可以看下:
3脚为GND引脚;
4脚为电源引脚,一般接5V电源;
5.脚接18650电池的正极,当电池充满时这个引脚电压为4.2V;
6脚为充电完成指示灯信号,当充电完成后,这个引脚为低电平,LED亮;
7脚为充电指示灯信号,当电池在充电时,这个引脚为低电平,LED亮;
8脚为芯片使能引脚,高电平时TP4065对电池充电;
这个电路就是TP4056给单节锂电池充电的完整电路了。
下面来看下TP4056的充电过程:
当芯片电源电压小于3.6V时。芯片处于一个关断模式,这时两个LED都是熄灭的;
当芯片电压大于3.6V,电池电压小于2.9V时,芯片处于一个预充电的模式,充电电流只有我们前面设置的充电电流的十分之一,这时充电LED灯是亮的;
当电池电压大于2.9V时,芯片处于一个恒流充电模式,充电电流就是我们前面通过PROG引脚电阻设置的电流,
如果从关断模式出来,电池电压大于2.9V,芯片会直接进入恒流充电模式
随着充电,电池电压慢慢升高,当电池电压等于浮充电压4.2V时,芯片会进入一个恒压充电模式,充电电压等于4.2V;
当充电电流小于前面设置的恒流充电电流的十分之一时,这时本次充电周期结束。充电完成指示灯亮,充电指示灯灭。
如果芯片检测到电池电压小于4.1V,芯片又会进入恒流模式给电池充电
2.4G无线通信模块NRF24L01
这里简单介绍一下NRF24L01模块,关于这个模块的使用方法,CSDN上面很多,读者可以自行搜索
1、什么是nRF24L01
nRF24L01是由NORDIC生产的工作在2.4GHz~2.5GHz的ISM 频段的单片无线收发器芯片。有着极低的电流消耗。
nRF24L01与5V单片机的连接通过SPI接口进行通讯,输出功率频道选择和协议的设置可以通过SPI 接口进行设置,几乎可以连接到各种单片机芯片,并完成无线数据传送工作。
2、接口电路
引脚说明
通过以下六个引脚,便可实现模块的所有功能:
(1)MOSI:主器件数据输出,从器件数据输入
(2)MISO:主器件数据输入,从器件数据输出
(3)SCLK:时钟信号,由主器件产生
(4) CSN :从器件使能信号(片选线)
(5)CE:芯片使能,使能器件的发送模式或者接收模式。高电平有效,在发送和接收过程中都要将这个引脚拉高,
(6)IRQ:中断信号线,中断输出。低电平有效,中断时变为低电平,在以下三种情况变低:Tx FIFO 发完并且收到ACK(使能ACK情况下)、Rx FIFO收到数据、达到最大重发次数。
(7)VCC:电压范围1.9V~3.6V,超过3.6V将会烧毁模块。一般电压3.3V左右。除电源VCC和接地端,其余脚都可以直接和普通的5V单片机IO口直接相连,无需电平转换。
通过 SPI 接口,可激活在数据寄存器 FIFO 中的数据,或者通过 SPI 命令访问寄存器。
在待机或掉电模式下,单片机通过 SPI 接口配置模块;
在发射或接收模式下,单片机通过 SPI 接口接收或发射数据。
在这里我放上我的NRF24L01使用的代码,这里是STM32F4的,要使用STM32F1的话也可以,因为代码逻辑是一样的,自行更改初始化就行
nrf24l01.c
#include "24l01.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
const unsigned char TX_ADDRESS[TX_ADR_WIDTH]={0x20,0x97,0x07,0x28,0x00}; //发送地址
const unsigned char RX_ADDRESS[RX_ADR_WIDTH]={0x20,0x97,0x07,0x28,0x00};
/*初始化24L01的IO口
使用的SPI通信协议
模拟SPI协议 分配的IO口:
NRF_SCK -> PA8
NRF_CS -> PA11
NRF_CE -> PA12
NRF_IRQ -> PB13 浮空输入
NRF_MISO -> PB14 浮空输入
NRF_MOSI -> PB15
*/
/**********************************************
** function : NRF24L01_Init
** parameter : NULL
**returned value: NULL
***********************************************/
void NRF24L01_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
///
//这个总线控制多个设备 外加一个片选引脚即可 CS
/问题: 没有设置IO口输出是 上下拉还是浮空 那么这时IO的初始后的状态应该是什么
//NRF_SCK 引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//NRF_CS 引脚初始化
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_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//NRF_CE 引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/
/
//NRF_IRQ 引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ; //输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//NRF_MISO 引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ; //输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//NRF_MOSI 引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ; //输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/
/
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource10);//PE2 连接到中断线2
/* 配置EXTI_Line2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line10;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能
EXTI_Init(&EXTI_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置
GPIO_SetBits(GPIOB,GPIO_Pin_14);//NRF_CS引脚拉高
NRF24L01_CE = 0; //CE引脚置低,方可配置NRF寄存器
NRF24L01_CS = 1; //禁止SPI传输
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
if(NRF24L01_IRQ==0)
{
LED0 = 0;
}
EXTI_ClearITPendingBit(EXTI_Line10);//清除LINE2上的中断标志位
}
//检测24L01是否存在
//返回值:1,成功;0,失败
unsigned char NRF24L01_Check(void)
{
unsigned char buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
unsigned char i;
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 0;//检测24L01错误
}
return 1; //检测到24L01
}
unsigned char NRF24L01_SPI_RW(unsigned char data)
{
unsigned char bit;
for(bit=0;bit<8;bit++)
{
NRF24L01_SCK = 0;
if(data & 0x80)
{
NRF24L01_MOSI = 1;
}
else
{
NRF24L01_MOSI = 0;
}
delay_us(1);
NRF24L01_SCK = 1;
data = data<<1;
data|= NRF24L01_MISO ;
delay_us(1);
NRF24L01_SCK = 0;
}
return data ;
}
//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
unsigned char NRF24L01_Write_Reg(unsigned char reg,unsigned char value)
{
unsigned char status;
NRF24L01_CS = 0;
status =NRF24L01_SPI_RW(reg);//发送寄存器号
NRF24L01_SPI_RW(value); //写入寄存器的值
NRF24L01_CS = 1; //关闭SPI传输
return(status); //返回状态值
}
//读取SPI寄存器值
//reg:要读的寄存器
unsigned char NRF24L01_Read_Reg(unsigned char reg)
{
unsigned char reg_val;
NRF24L01_CS = 0; //使能SPI传输
NRF24L01_SPI_RW(reg); //发送寄存器号
reg_val=NRF24L01_SPI_RW(0XFF); //读取寄存器内容
NRF24L01_CS = 1; //关闭SPI传输
return(reg_val); //返回状态值
}
//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
unsigned char NRF24L01_Read_Buf(unsigned char reg,unsigned char *pBuf,unsigned char len)
{
unsigned char status,u8_ctr;
NRF24L01_CS = 0; //使能SPI传输
status= NRF24L01_SPI_RW(reg);//发送寄存器值(位置),并读取状态值
for(u8_ctr=0;u8_ctr<len;u8_ctr++)
pBuf[u8_ctr]= NRF24L01_SPI_RW(0XFF);//读出数据
NRF24L01_CS = 1; //关闭SPI传输
return status; //返回读到的状态值
}
//在指定位置写指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
unsigned char NRF24L01_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char len)
{
unsigned char status,u8_ctr;
NRF24L01_CS = 0; //使能SPI传输
status = NRF24L01_SPI_RW(reg);//发送寄存器值(位置),并读取状态值
for(u8_ctr=0; u8_ctr<len; u8_ctr++)NRF24L01_SPI_RW(*pBuf++); //写入数据
NRF24L01_CS = 1; //关闭SPI传输
return status; //返回读到的状态值
}
//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
unsigned char NRF24L01_TxPacket(unsigned char *txbuf)
{
unsigned char sta;
NRF24L01_Write_Reg(FLUSH_TX,0xff); //清空发送FIFO指令 /* 清除上一次发送的FIFO寄存器 */
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,0x70); //清除所有中断标识
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); //读取状态寄存器的值
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,接收完成;其他,错误代码
unsigned char NRF24L01_RxPacket(unsigned char *rxbuf)
{
unsigned char sta;
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
if(sta&RX_OK)//接收到数据
{
NRF24L01_CE=0; //SPI使能
NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
NRF24L01_CE=1;
return 0;
}
return 1;//没收到任何数据
}
//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了
void NRF24L01_RX_Mode(void)
{
NRF24L01_CE=0;
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); //自动重发15次
NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
NRF24L01_Write_Reg(NRF_WRITE_REG + RF_CH,NRF_channel_RX); //设置RF通信频率
NRF24L01_Write_Reg(NRF_WRITE_REG + RF_SETUP,NRF_Speed+1); //设置发射速率功率,(发射模式下)无线速率 1Mbps-0x06 2Mbps-0x0E 接收模式要+1把LNA给打开
NRF24L01_Write_Reg(NRF_WRITE_REG + CONFIG,0x0f); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
NRF24L01_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0,(unsigned char*)RX_ADDRESS,RX_ADR_WIDTH);//写TX节点地址
NRF24L01_Write_Buf(NRF_WRITE_REG + TX_ADDR,(unsigned char*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
NRF24L01_CE = 1; //CE为高,进入接收模式
delay_ms(1);
}
//该函数初始化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,(unsigned char*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
NRF24L01_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0,(unsigned char*)RX_ADDRESS,RX_ADR_WIDTH);//写TX节点地址
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); //自动重发15次
NRF24L01_Write_Reg(NRF_WRITE_REG + RF_CH,NRF_channel_RX); //设置RF通信频率
NRF24L01_Write_Reg(NRF_WRITE_REG + RF_SETUP,NRF_Speed); //设置发射速率功率,(发射模式下)无线速率 1Mbps-0x06 2Mbps-0x0E 接收模式要+1把LNA给打开
NRF24L01_Write_Reg(NRF_WRITE_REG + CONFIG,0x0E); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
NRF24L01_CE = 1; //CE为高,进入接收模式
delay_ms(1);
}
nrf24l01.h
#ifndef __24L01_H
#define __24L01_H
#include "sys.h"
#include <stdio.h>
#include <string.h>
//NRF24L01寄存器操作命令
#define NRF_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址
#define NRF_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节
#define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节
#define FLUSH_TX 0xE1 //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送.
#define NOP 0xFF //空操作,可以用来读状态寄存器
//SPI(NRF24L01)寄存器地址
#define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
//bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5
#define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH 0x05 //RF通道,bit6:0,工作通道频率;
#define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
//bit5:数据发送完成中断;bit6:接收数据中断;
#define MAX_TX 0x10 //达到最大发送次数中断
#define TX_OK 0x20 //TX发送完成中断
#define RX_OK 0x40 //接收到数据中断
#define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
#define CD 0x09 //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
#define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
#define NRF_FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
//bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
//
//24L01操作线
/*
NRF_CS PA11
NRF_CE PA12
NRF_IRQ PB13
SCK PA8
MISO PB14
MOSI PB15
*/
/// 可以将几个设备搭载在同一个SPI总线上面
/// 直接设置SCK MISO MOSI几个引脚即可
#define NRF24L01_CE PBout(15)
#define NRF24L01_CS PBout(14)
#define NRF24L01_SCK PBout(13)
#define NRF24L01_IRQ PBin(10)
#define NRF24L01_MISO PBin(11)
#define NRF24L01_MOSI PBout(12)
//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字节的用户数据宽度
#define NRF_channel_RX 0x14 //无线信道取值0-127
#define NRF_channel_TX 0x14 //无线信道取值0-127
#define NRF_Speed 0x06 //(发射模式下)无线速率 1Mbps-0x06 2Mbps-0x0E 接收模式要+1把LNA给打开
unsigned char NRF24L01_SPI_RW(unsigned char data);
void NRF24L01_Init(void); //初始化
void NRF24L01_RX_Mode(void); //配置为接收模式
void NRF24L01_TX_Mode(void); //配置为发送模式
unsigned char NRF24L01_Write_Buf(unsigned char reg, unsigned char *pBuf, unsigned char u8s);//写数据区
unsigned char NRF24L01_Read_Buf(unsigned char reg, unsigned char *pBuf, unsigned char u8s); //读数据区
unsigned char NRF24L01_Read_Reg(unsigned char reg); //读寄存器
unsigned char NRF24L01_Write_Reg(unsigned char reg, unsigned char value); //写寄存器
unsigned char NRF24L01_Check(void); //检查24L01是否存在
unsigned char NRF24L01_TxPacket(unsigned char *txbuf); //发送一个包的数据
unsigned char NRF24L01_RxPacket(unsigned char *rxbuf); //接收一个包的数据
typedef struct
{
unsigned char nrf24l01_TxBuff[32];
unsigned char nrf24l01_RxBuff[32];
}nrf24l01TYPE;
typedef struct
{
//电位器采集的值
//发送前进后退 左右
short Send_L_LR;
short Send_L_UD;
short Send_R_LR;
short Send_R_UD;
//ICM20602采集的值
short sendPitch;
short sendRoll;
short sendYaw;
}Send_Get_Data;
extern nrf24l01TYPE nrf24l01;
extern Send_Get_Data Remote_Send;
#endif
摇杆电位器
这里简单介绍一下:
1、检测电压常态时2.5V,最大值5V,最小值0V,实际上就是两个x,y方向上的滑动变阻器,;
2、通过STM32内部的ADC采集,就可以读取到摇杆电位器的输出电压,从而根据采集值完成想要实现的功能
原理图讲解部分
原理图链接
链接:https://pan.baidu.com/s/1JMaMiW437nb55eizqtLK_Q
提取码:apjn
STM32F405RGT6最小系统
提一下需要注意的地方:
1.VCAP这个引脚需要接4.7uF的电容接地
2.BOOT0引脚需要接10K的电阻接地
3.给单片机供电的Vsys需要并联4个电容
充电系统
提一下需要注意的地方:
1.输入电压和输出电压最好并联10uF的电容接地,达到滤波的效果
2.在输出端口接了2个100k的电阻接地是为了采集电池电压,因为使用的是1S锂电池,充满电是4.2V,而单片机的最高采集电压是3.3V,所以需要串联分压,在最终读取采集值的时候我们讲采集到的电压值乘以2就行。
3.充电接口我采用的是6针脚的TYPE-C接口,需要注意的是CC1,CC2接5.1K的电阻接地就行。
降压系统
降压芯片我采用的是ME6211C33M5G芯片,管脚的功能大家可以顾名思义,比较简单。然后电源的供电最好是分开,这里我NRF24L01,OLED,系统的供电是分开的。
NRF24L01,OLED
特别注意,NRF24L01的供电一定要接滤波电容,因为有可能存在不接滤波电容导致NRF24L01工作不正常
OLED这里的时钟线和数据线接上拉电阻即可。
其他的部分就比较简单,大家看自行我的原理图即可,就不做介绍了
代码逻辑部分
整体的代码逻辑构思就是利用ADC多通道采集摇杆电位器的电压值,经过数据处理,转换成自己想要发送出去的值,让后将其通过NRF24L01发送,OLED可以用作显示电池电量,采集数据的值,发送出去的值,还可以显示自己想要的图形化界面。
这个工程如果有需要的话可以私信作者。。。
展示部分