STM32驱动NRF24L01 C语言程序
- 格式:pdf
- 大小:233.00 KB
- 文档页数:8
奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验NRF24L01+转 USB 虚拟串口实验实验平台:奋斗版STM32开发板Tiny 实验内容:板子通过USB加电后,先向串口1输出一串测试数据,然后USB被PC识 别出来,虚拟出一个串口号给这个USB设备,此时可以通过在PC端的串口助手类 软件选择该串口号。
进入串口软件界面,可以通过软件无线收发一帧长度最长 为32字节的数据。
该例程可以和V3及MINI板的NRF24L01 UCGUI例程配合使用。
预先需要掌握的知识 2.4G通信模块NRF24L01 1. 产品特性2.4GHz 全球开放ISM 频段,最大0dBm 发射功率,免许可证使用 支持六路通道的数据接收 低工作电压:1.9 1.9~3.6V 低电压工作 高速率:2Mbps,由于空中传输时间很短,极大的降低了无线传输中的碰撞现象(软件设置1Mbps或者2Mbps的空中传输速率) 多频点:125 频点,满足多点通信和跳频通信需要 超小型:内置2.4GHz天线,体积小巧,15x29mm(包括天线) 低功耗:当工作在应答模式通信时,快速的空中传输及启动时间,极大的降低了电流消耗。
低应用成本:NRF24L01 集成了所有与RF协议相关的高速信号处理部分,比如:自动重发丢失数据包和自动产生应答信号等, NRF24L01的SPI接口可以利用单片机的硬件SPI口连接或用单片机I/O口进行模拟,内部有FIFO可以与各种高低速微处理器接口, 便于使用低成本单片机。
便于开发:由于链路层完全集成在模块上,非常便于开发。
自动重发功能,自动检测和重发丢失的数据包,重发时间及重发次数可软件控制 自动存储未收到应答信号的数据包 自动应答功能,在收到有效数据后,模块自动发送应答信号,无须另行编程 载波检测—固定频率检测 内置硬件CRC 检错和点对多点通信地址控制 数据包传输错误计数器及载波检测功能可用于跳频设置 可同时设置六路接收通道地址,可有选择性的打开接收通道 标准插针Dip2.54MM 间距接口,便于嵌入式应用2.基本电气特性淘宝店铺:1奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验3. 引脚定义:4.工作方式NRF2401有工作模式有四种: 收发模式 配置模式 空闲模式 关机模式 工作模式由CE 和寄存器内部PWR_UP、PRIM_RX 共同控制,见下表:淘宝店铺:2奋斗版 STM32 开发板例程手册———NRF24L01+转 USB 虚拟串口实验4.1 收发模式收发模式有Enhanced ShockBurstTM收发模式、ShockBurstTM收发模式和直接收发模式三种,收发模式由器件配置字决定,具体 配置将在器件配置部分详细介绍。
关于使用STM32硬件SPI驱动NRF24L01+ 今天是大年初一总算有时间做点想做很久的事了,说到NRF2401可能很多电子爱好者都有用过或是软件模拟驱动又或是用单片机自带的硬件SPI来驱动,但不管是用哪种方法来驱动我想都在调试方面耗费了不少的时间(可能那些所谓的电子工程师不会出现这种情况吧!)网上的资料确实很多,但大多数都并没有经过发贴人认真测试过,有的只是理论上可以行的通但上机测试只能说是拿回来给他修改。
本文作者也是经过无助的多少天才算是调试成功了(基于STM32硬件SPI,软件模拟的以前用51单片机已经调通了今天就不准备再拿来讲了,当然如果以后有朋友有需要也可以告诉我,我也可以重新写一篇关于51的驱动的只要有时间是没有问题的。
)因为我用的是STM32F103C8T6的系统而且是刚接触不知道别的系统和我用的这个系统有多大的差别所以我不会整个代码全贴上来就完事了,我将就着重思路配合代码写出来,这样对于刚接触单片机的朋友会有很好的作用,但是还有一点请大家要原谅,可能会存在一些说的不好的地方,毕竟我没有经过正规渠道系统地学习过电子知识,对于前辈来说存在这样那样的问题不可避免的,在此也希望大家指教!贴个图先:NRF2401+的资料大家上网查一下,我输字的速度有点不好说!下面我来说一下整个调试流程吧1.先把STM32串口一调通(因为我不知道STM32 I/O口不知可不可以像51那样并口输出数据,如果可以那就更方便啰)。
2.与NRF2401建立起通信(这个才是问题的关键);3.利用读NRF2401的一个状态寄存器(STATUS)的值并通过串口发送到PC后通过51下载软件的串口助手显示出来(如果你用液晶来调试那你太有才了,你液晶和NRF2401存在牵连可能就会给寻找不成功的原因造成困难,而且还有不少硬件工作要做)在这说一下本文只调试发送程序,致于接收只改一个程序参数就行了。
我们先来调试STM32F103C8T6的串口1吧(也就是USART1)!它是STM32F103C8T6的片上外设之一,使用它时相对来说简单了不少。
STM32驱动NRF24L01
1. 简介
NRF24L01 是nordic 的无线通信芯片,它具有以下特点:
1)2.4G 全球开放的ISM 频段(2.400 - 2.4835GHz),免许可证使用;
2)最高工作速率2Mbps,高校的GFSK 调制,抗干扰能力强;
3)125 个可选的频道,满足多点通信和调频通信的需要;
4)内置CRC 检错和点对多点的通信地址控制;
5)低工作电压(1.9~3.6V),待机模式下状态为26uA;掉电模式下为
900nA;
6)可设置自动应答,确保数据可靠传输;
7)工作于EnhancedShockBurst 具有Automatic packet handling,Auto packet transaction handling ,可以实现点对点或是 1 对 6 的无线通信,速度可以达
到2M(bps),具有可选的内置包应答机制,极大的降低丢包率。
8)通过SPI 总线与单片机进行交互,最大通信速率为10Mbps;
1.1 结构框图
如图右侧为六个控制和数据信号,分别为CSN、SCK、MISO、MOSI、IRQ、CE。
信号线功能
CSN 芯片的片选线,CSN 为低电平芯片工作
SCK 芯片控制的时钟线(SPI 时钟)
MISO 芯片控制数据线(Master input slave output)
MOSI 芯片控制数据线(Master output slave input)
IRQ 中断信号。
无线通信过程中MCU 主要是通过IRQ 与NRF24L01 进。
#include <reg52.h>#include <intrins.h>/********************************************************** *****************************//* NRF24L01 地管脚定义,以及在本程序中地应用,VCC接3.3V 电源,可以通过5V用电压转换芯片/*得到,NC 管脚可以接可以不接,暂时没用途.本程序应用于51或者52单片机,是两个模块进行通讯/*成功地简单指示,现象是:模块1地 KEY1 对应模块1地LED1 和模块2地LED3 ,模块1地 KEY2 对应模/*块1地LED2 和模块2地LED4,发过来也对应./********************************************************** *****************************/typedef unsigned char uchar;typedef unsigned char uint;/************************************NRF24L01端口定义***********************************/sbit NC =P2^0; //没用,不接也可sbit MISO =P2^5; //数字输出(从 SPI 数据输出脚)sbit MOSI =P2^4; //数字输入(从 SPI 数据输入脚)sbit SCK =P1^7; //数字输入(SPI 时钟)sbit CE =P2^1; //数字输入(RX 或 TX 模式选择)sbit CSN =P2^2; //数字输入(SPI片选信号)sbit IRQ =P2^6; //数字输入(可屏蔽中断)/************************************按键***********************************************/sbit KEY1=P3^3;//按键S1sbit KEY2=P3^2;//按键S2/************************************数码管位选******************************************/sbit led1=P1^0; //LED0sbit led2=P1^1; //LED1sbit led3 =P1^2; //LED2sbit led4 =P1^3; //LED3sbit led5 =P1^4; //LED4/*********************************************NRF24L01***********************************/#define TX_ADR_WIDTH 5 // 5 uints TX address width 发送地址宽度#define RX_ADR_WIDTH 5 // 5 uints RX address width 接收地址宽度#define TX_PLOAD_WIDTH 20 // 20 uints TX payload 有效载荷装载货物#define RX_PLOAD_WIDTH 20 // 20 uints TX payloaduint const TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //本地地址uint const RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //接收地址/***************************************NRF24L01寄存器指令*******************************/#define READ_REG 0x00 // 读寄存器指令#define WRITE_REG 0x20 // 写寄存器指令#define RD_RX_PLOAD 0x61 // 读取接收数据指令#define WR_TX_PLOAD 0xA0 // 写待发数据指令#define FLUSH_TX 0xE1 // 冲洗发送 FIFO指令#define FLUSH_RX 0xE2 // 冲洗接收 FIFO指令#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令#define NOP 0xFF // 保留/*************************************SPI(nRF24L01)寄存器地址***********************/#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式#define EN_AA 0x01 // 自动应答功能设置#define EN_RXADDR 0x02 // 可用信道设置#define SETUP_AW 0x03 // 收发地址宽度设置#define SETUP_RETR 0x04 // 自动重发功能设置#define RF_CH 0x05 // 工作频率设置#define RF_SETUP 0x06 // 发射速率.功耗功能设置#define STATUS 0x07 // 状态寄存器#define OBSERVE_TX 0x08 // 发送监测功能#define CD 0x09 // 地址检测#define RX_ADDR_P0 0x0A // 频道0接收数据地址#define RX_ADDR_P1 0x0B // 频道1接收数据地址#define RX_ADDR_P2 0x0C // 频道2接收数据地址#define RX_ADDR_P3 0x0D // 频道3接收数据地址#define RX_ADDR_P4 0x0E // 频道4接收数据地址#define RX_ADDR_P5 0x0F // 频道5接收数据地址#define TX_ADDR 0x10 // 发送地址寄存器#define RX_PW_P0 0x11 // 接收频道0接收数据长度#define RX_PW_P1 0x12 // 接收频道0接收数据长度#define RX_PW_P2 0x13 // 接收频道0接收数据长度#define RX_PW_P3 0x14 // 接收频道0接收数据长度#define RX_PW_P4 0x15 // 接收频道0接收数据长度#define RX_PW_P5 0x16 // 接收频道0接收数据长度#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置/*************************************函数声明****************************************/void Delay(unsigned int s); //大延时void inerDelay_us(unsigned char n); //小延时void init_NRF24L01(void); //NRF24L01 初始化uint SPI_RW(uint dat); //根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01读出一字节uchar SPI_Read(uchar reg); //从reg寄存器读一字节void SetRX_Mode(void); //数据接收配置uint SPI_RW_Reg(uchar reg, uchar value); //写数据value到reg寄存器uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars); //从reg寄存器读出bytes个字节,通常用来读取接收通道数据或接收/发送地址uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars); //把pBuf缓存中地数据写入到nRF24L01,通常用来写入发射通道数据或接收/发送地址unsigned char nRF24L01_RxPacket(unsigned char* rx_buf);//数据读取后放入rx_buf接收缓冲区中void nRF24L01_TxPacket(unsigned char * tx_buf);//发送 tx_buf中数据/*****************************************长延时*****************************************/void Delay(unsigned int s){unsigned int i;for(i=0; i<s; i++);for(i=0; i<s; i++);}/********************************************************** ********************************/uint bdata sta; //状态标志sbit RX_DR =sta^6; //RX_DR 为 sta 地第六位sbit TX_DS =sta^5; //TX_DS 为 sta 地第五位sbit MAX_RT =sta^4; //MAX_RT 为 sta 地第四位/********************************************************** ********************************//*延时函数/********************************************************** ********************************/void inerDelay_us(unsigned char n) //延时,us 级{for(;n>0;n--)_nop_();}/********************************************************** ******************************//*NRF24L01初始化/********************************************************** *****************************/void init_NRF24L01(void){inerDelay_us(100);CE=0; // 芯片使能CSN=1; // 禁止 SPISCK=0; // SPI时钟置低SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS,TX_ADR_WIDTH); // 写本地地址SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS,RX_ADR_WIDTH); // 写接收端地址SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 频道0自动ACK应答允许SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 允许接收地址只有频道0,如果需要多频道可以参考Page21SPI_RW_Reg(WRITE_REG + RF_CH, 0); // 设置信道工作为2.4GHZ,收发必须一致SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); //设置发射速率为1MHZ,发射功率为最大值0dB}/********************************************************** ******************************************//*函数:uint SPI_RW(uint uchar)/*功能:NRF24L01地SPI写时序-----根据SPI协议,写一字节数据到nRF24L01,同时从nRF24L01 读出一字节/********************************************************** ******************************************/uint SPI_RW(uint dat){uint i;for(i=0;i<8;i++) // 循环8次{MOSI = (dat & 0x80); // dat地最高位输出到MOSI MSB to MOSIdat = (dat << 1); // 从右向左进一位shift next bit into MSB..SCK = 1; // 拉高SCK,nRF24L01从MOSI读入1位数据,同时从MISO输出1位数据Set SCK high..dat |= MISO; //读MISO到 dat 最低位 capture current MISO bitSCK = 0; // SCK置低..then set SCK low again}return(dat); //返回读出地一字节 return read dat}/********************************************************** ******************************************/*函数:uchar SPI_Read(uchar reg)/*功能:NRF24L01地SPI时序-----------从reg寄存器读一字节/********************************************************** ******************************************/uchar SPI_Read(uchar reg){uchar reg_val;CSN = 0; //CSN置低,开始传输数据CSN low, initialize SPI communication...SPI_RW(reg); //选择寄存器 Select register to read from..reg_val = SPI_RW(0); //然后从该寄存器读数据 ..then read registervalueCSN = 1; //CSN拉高,结束数据传输CSN high, terminate SPI communicationreturn(reg_val); //返回寄存器数据 return register value}/********************************************************** ******************************************//*功能:NRF24L01读写寄存器函数/*描述:写数据value到reg寄存器/********************************************************** ******************************************/uint SPI_RW_Reg(uchar reg, uchar value){uchar status;CSN = 0; // CSN置低,开始传输数据CSN low, init SPI transactionstatus = SPI_RW(reg); // 选择寄存器,同时返回状态字select registerSPI_RW(value); // 然后写数据到该寄存器 ..and write value to it..CSN = 1; // CSN拉高,结束数据传输CSN high againreturn(status); // 返回状态寄存器 return nRF24L01 status uchar}/********************************************************** ******************************************//*函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) /*功能: 用于读数据,reg:为寄存器地址,pBuf:为待读出数据地址,uchars:读出数据地个数/*描述: 从reg寄存器读出bytes个字节,通常用来读取接收通道数据或接收/发送地址/********************************************************** ******************************************/uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars){uint status,i;CSN = 0; //CSN置低,开始传输数据 Set CSN low, init SPI tranactionstatus = SPI_RW(reg); //选择寄存器,同时返回状态字Select register to write to and read status uchar for(i=0;i<uchars;i++)pBuf[i] = SPI_RW(0); //逐个字节从nRF24L01读出CSN = 1; //CSN拉高,结束数据传输return(status); //返回状态寄存器return nRF24L01 status uchar}/********************************************************** ***********************************************/*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)/*功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据地个数/*描述:把pBuf缓存中地数据写入到nRF24L01,通常用来写入发射通道数据或接收/发送地址/********************************************************** ***********************************************/uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars){uint status,i;CSN = 0; //CSN置低,开始传输数据status = SPI_RW(reg); //选择寄存器,同时返回状态字inerDelay_us(10);for(i=0; i<uchars; i++)SPI_RW(*pBuf++); //逐个字节写入nRF24L01CSN = 1; //CSN拉高,结束数据传输return(status); //返回状态寄存器}/********************************************************** ******************************************//*函数:void SetRX_Mode(void)/*功能:数据接收配置/********************************************************** ******************************************/void SetRX_Mode(void){CE=0;SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f);//CRC使能,16位CRC 校验,上电,接收模式CE = 1; // 拉高CE启动接收设备inerDelay_us(130);}/********************************************************** ********************************************//*函数:unsigned char nRF24L01_RxPacket(unsigned char* rx_buf) /*功能:数据读取后放入rx_buf接收缓冲区中/********************************************************** ********************************************/unsigned char nRF24L01_RxPacket(unsigned char* rx_buf){unsigned char revale=0;sta=SPI_Read(STATUS); // 读取状态寄存其来判断数据接收状况if(RX_DR) // 判断是否接收到数据{CE = 0; //SPI使能SPI_Read_Buf(RD_RX_PLOAD,rx_buf,TX_PLOAD_WIDTH);// read receive payload from RX_FIFO bufferrevale =1; //读取数据完成标志}SPI_RW_Reg(WRITE_REG+STATUS,sta); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标志return revale;}/********************************************************** *************************************************/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf)/*功能:发送 tx_buf中数据/********************************************************** ************************************************/void nRF24L01_TxPacket(unsigned char * tx_buf){CE=0; //StandBy I模式SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS,TX_ADR_WIDTH); // 装载接收端地址SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);// 装载数据SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // IRQ 收发完成中断响应,16位CRC,主发送CE=1; //置高CE,激发数据发送}/************************************主函数*********************************************************** */void main(void){unsigned char tf =0;unsigned char TxBuf[20]={0}; // 要发送地数组unsigned char RxBuf[20]={0}; // 接收地数据数组init_NRF24L01() ; //模块初始化led1=1;led2=1;led3 =1;led4 =1; //led 灯关闭Delay(1000);while(1){if(KEY1 ==0 ) //按键 1 按下{TxBuf[1] = 1 ; //赋值tf = 1 ;led1=0; //本地led 灯闪烁led1=1;Delay(200);}if(KEY2 ==0 ) //按键 2 按下{TxBuf[2] =1 ; //赋值tf = 1 ;led2=0; //本地led 灯闪烁Delay(200);led2=1;Delay(200);}if (tf==1) //有键按下{nRF24L01_TxPacket(TxBuf); //发送数据 Transmit Tx buffer dataTxBuf[1] = 0x00; //清零TxBuf[2] = 0x00;tf=0;Delay(1000);}SetRX_Mode(); //设置成接受模式RxBuf[1] = 0x00; //接收地数组相应位清零RxBuf[2] = 0x00;Delay(1000);nRF24L01_RxPacket(RxBuf); //接收数据if(RxBuf[1]|RxBuf[2]){if( RxBuf[1]==1){led3=RxBuf[0];}if( RxBuf[2]==1){led4=RxBuf[4];}Delay(3000); //old is '1000'}RxBuf[1] = 0x00; //清零RxBuf[2] = 0x00;led3=1; //关灯led4=1;}}本程序存在地问题:反应不够灵敏,当在按键1和按键2之间切换地时候,对方地灯闪烁会有一定地延时,另外本程序没有消除按键地抖动.对部分函数地解释:uint SPI_RW(uint dat)最基本地函数,完成 GPIO模拟 SPI 地功能.将输出字节(MOSI)从 MSB 循环输出,同时将输入字节(MISO)从 LSB 循环移入.上升沿读入,下降沿输出. (从 SCK被初始化为低电平可以判断出)uchar SPI_Read(uchar reg); //从reg寄存器读一字节读取寄存器值地函数:基本思路就是通过 READ_REG命令(也就是 0x00+寄存器地址) ,把寄存器中地值读出来.对于函数来说也就是把 reg 寄存器地值读到reg_val 中去.uint SPI_RW_Reg(uchar reg, uchar value); //写数据value到reg寄存器寄存器访问函数:用来设置 24L01 地寄存器地值.基本思路就是通过 WRITE_REG命令(也就是 0x20+寄存器地址)把要设定地值写到相应地寄存器地址里面去,并读取返回值.对于函数来说也就是把 value值写到 reg 寄存器中.需要注意地是,访问 NRF24L01 之前首先要 enable 芯片(CSN=0;) ,访问完了以后再 disable芯片(CSN=1;).uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars); //从reg寄存器读出bytes个字节,通常用来//读取接收通道数据或接收/发送地址接收缓冲区访问函数:主要用来在接收时读取 FIFO 缓冲区中地值.基本思路就是通过READ_REG命令把数据从接收 FIFO(RD_RX_PLOAD)中读出并存到数组里面去.uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars); //把pBuf缓存中地数据写入到nRF24L01,通常//用来写入发发射缓冲区访问函数:主要用来把数组里地数放到发射 FIFO缓冲区中.基本思路就是通过WRITE_REG命令把数据存到发射 FIFO(WR_TX_PLOAD)中去.Tx 模式初始化过程1)写 Tx 节点地地址 TX_ADDR2)写 Rx 节点地地址(主要是为了使能 Auto Ack) RX_ADDR_P0 3)使能 AUTO ACK EN_AA4)使能 PIPE 0 EN_RXADDR5)配置自动重发次数 SETUP_RETR6)选择通信频率 RF_CH7)配置发射参数(低噪放大器增益.发射功率.无线速率) RF_SETUP 8 ) 选择通道0 有效数据宽度 Rx_Pw_P09)配置 24L01 地基本参数以及切换工作模式 CONFIG.Rx 模式初始化过程:初始化步骤 24L01 相关寄存器1)写 Rx 节点地地址 RX_ADDR_P02)使能 AUTO ACK EN_AA3)使能 PIPE 0 EN_RXADDR4)选择通信频率 RF_CH5) 选择通道0 有效数据宽度 Rx_Pw_P06)配置发射参数(低噪放大器增益.发射功率.无线速率) RF_SETUP 7)配置 24L01 地基本参数以及切换工作模式 CONFIG.。
NRF24L01无线模块C语言法式之巴公井开创作24MHz晶振#include#include#include#include#include#include#define U8 unsigned char#define U16 unsigned int#define TX_ADDR_WITDH 5 //发送地址宽度设置为5个字节#define RX_ADDR_WITDH 5 //接收地址宽度设置为5个字节#define TX_DATA_WITDH 1//发送数据宽度1个字节#define RX_DATA_WITDH 1//接收数据宽度1个字节#define R_REGISTER 0x00//读取配置寄存器#define W_REGISTER 0x20//写配置寄存器#define R_RX_PAYLOAD 0x61//读取RX有效数据#define W_TX_PAYLOAD 0xa0//写TX有效数据#define FLUSH_TX 0xe1//清除TXFIFO寄存器#define FLUSH_RX 0xe2//清除RXFIFO寄存器#define REUSE_TX_PL 0xe3//重新使用上一包有效数据#define NOP 0xff//空把持#define CONFIG 0x00//配置寄存器#define EN_AA 0x01//使能自动应答#define EN_RXADDR 0x02//接收通道使能0-5个通道#define SETUP_AW 0x03//设置数据通道地址宽度3-5#define SETUP_RETR 0x04//建立自动重发#define RF_CH 0x05//射频通道设置#define RF_SETUP 0x06//射频寄存器#define STATUS 0x07//状态寄存器#define OBSERVE_TX 0x08//发送检测寄存器#define CD 0x09//载波#define RX_ADDR_P0 0x0a//数据通道0接收地址#define RX_ADDR_P1 0x0b//数据通道1接收地址#define RX_ADDR_P2 0x0c//数据通道2接收地址#define RX_ADDR_P3 0x0d//数据通道3接收地址#define RX_ADDR_P4 0x0e//数据通道4接收地址#define RX_ADDR_P5 0x0f//数据通道5接收地址#define TX_ADDR 0x10//发送地址#define RX_PW_P0 0x11//P0通道数据宽度设置#define RX_PW_P1 0x12//P1通道数据宽度设置#define RX_PW_P2 0x13//P2通道数据宽度设置#define RX_PW_P3 0x14//P3通道数据宽度设置#define RX_PW_P4 0x15//P4通道数据宽度设置#define RX_PW_P5 0x16//P5通道数据宽度设置#define FIFO_STATUS 0x17//FIFO状态寄存器//NRF24L01U8 NRFACK();U8 NRFSPI(U8 date);U8 NRFReadReg(U8 RegAddr);U8 NRFWriteReg(U8 RegAddr,U8 date);U8 NRFReadRxDate(U8 RegAddr,U8 *RxDate,U8 DateLen);U8 NRFWriteTxDate(U8 RegAddr,U8 *TxDate,U8 DateLen);U8 NRFRevDate(U8 *RevDate);void NRFSetTxMode(U8 *TxDate);void NRF24L01Int();void NRFSetRXMode();U8 CheckACK();void Delay(U16 t);U8 bdata sta;//mainvoid Delay_10ms(U16 del);bit CE=P1^4; //RX/TX模式选择端sbit IRQ=P1^1; //可屏蔽中断端sbit CSN=P1^6; //SPI片选端//就是SSsbit MOSI=P1^0; //SPI主机输出从机输入端1sbit MISO=P1^2; //SPI主机输入从机输出端sbit SCLK=P1^3; //SPI时钟端U8 code TxAddr[]={0x34,0x43,0x10,0x10,0x01};//发送地址U8 bdata sta; //状态标识表记标帜sbit RX_DR=sta^6;sbit TX_DS=sta^5;sbit MAX_RT=sta^4;void Delay(U16 t){ U16 x,y; for(x=t;x>0;x--) for(y=110;y>0;y--);}U8 NRFSPI(U8 date){U8 i; for(i=0;i<8;i++) // 循环8次{ if(date&0x80) MOSI=1; else MOSI=0; // byte最高位输出到MOSI date<<=1; // 低一位移位到最高位 SCLK=1; if(MISO) // 拉高SCK,nRF24L01从MOSI读入1位数据, 同时从MISO输出1位数据 date|=0x01; // 读MISO 到byte最低位 SCLK=0; // SCK置低} return(date); // 返回读出的一字节}void NRF24L01Int(){ Delay(2);//让系统什么都不干CE=0; //待机模式1 CSN=1; SCLK=0; IRQ=1; }U8 NRFReadReg(U8 RegAddr){ U8 BackDate; CSN=0;//启动时序NRFSPI(RegAddr);//写寄存器地址BackDate=NRFSPI(0x00);//写入读寄存器指令CSN=1; return(BackDate); //返回状态}U8NRFWriteReg(U8 RegAddr,U8 date){ U8 BackDate; CSN=0;//启动时序BackDate=NRFSPI(RegAddr);//写入地址NRFSPI(date);//写入值CSN=1; return(BackDate);}U8 NRFReadRxDate(U8 RegAddr,U8 *RxDate,U8 DateLen){ //寄存器地址//读取数据寄存变量//读取数据长度//用于接收U8 BackDate,i; CSN=0;//启动时序BackDate=NRFSPI(RegAddr);//写入要读取的寄存器地址for(i=0;i { RxDate[i]=NRFSPI(0); } CSN=1; return(BackDate);}U8 NRFWriteTxDate(U8 RegAddr,U8 *TxDate,U8 DateLen){ //寄存器地址//写入数据寄存变量//读取数据长度//用于发送U8 BackDate,i; CSN=0; BackDate=NRFSPI(RegAddr);//写入要写入寄存器的地址 for(i=0;i { NRFSPI(*TxDate++); }CSN=1; return(BackDate);}void NRFSetTxMode(U8 *TxDate){//发送模式 CE=0; NRFWriteTxDate(W_REGISTER+TX_ADDR,Tx Addr,TX_ADDR_WITDH);//写寄存器指令+接收地址使能指令+接收地址+地址宽度NRFWriteTxDate(W_REGISTER+RX_ADDR_P0,TxAddr,TX_ADDR_WITDH);//为了应答接收设备,接收通道0地址和发送地址相同NRFWriteTxDate(W_TX_PAYLOAD,TxDate,TX_DATA_WITDH);//写入数据NRFWriteReg(W_REGISTER+EN_AA,0x01);// 使能接收通道0自动应答NRFWriteReg(W_REGISTER+EN_RXADDR,0x01); // 使能接收通道0 NRFWriteReg(W_REGISTER+SETUP_RETR,0x0a); // 自动重发延时等候250us+86us,自动重发10次NRFWriteReg(W_REGISTER+RF_CH,0x40);// 选择射频通道0x40 NRFWriteReg(W_REGISTER+RF_SETUP,0x07); // 数据传输率1Mbps,发射功率0dBm,低噪声放年夜器增益NRFWriteReg(W_REGISTER+CONFIG,0x0e); // CRC 使能,16位CRC校验,上电CE=1; Delay(5);//坚持10us秒以上}//主要接收模式void NRFSetRXMode(){ CE=0; NRFWriteTxDate(W_REGISTER+RX _ADDR_P0,TxAddr,TX_ADDR_WITDH); // 接收设备接收通道0使用和发送设备相同的发送地址NRFWriteReg(W_REGISTER+EN_AA,0x01);// 使能接收通道0自动应答NRFWriteReg(W_REGISTER+EN_RXADDR,0x01);// 使能接收通道0 NRFWriteReg(W_REGISTER+RF_CH,0x40);// 选择射频通道0x40 NRFWriteReg(W_REGISTER+RX_PW_P0,TX_DATA_WITDH);// 接收通道0选择和发送通道相同有效数据宽度NRFWriteReg(W_REGISTER+RF_SETUP,0x07);// 数据传输率1Mbps,发射功率0dBm,低噪声放年夜器增益*/ NRFWriteReg(W_REGISTER+CONFIG,0x0f);// CRC使能,16位CRC校验,上电,接收模式CE = 1; Delay(5);//坚持10us秒以上}U8 CheckACK(){ //用于发射sta=NRFReadReg(R_REGISTER+STATUS);// 返回状态寄存器if(TX_DS||MAX_RT) //发送完毕中断{ NRFWriteReg(W_REGISTER+STATUS,0xff); // 清除TX_DS或MAX_RT中断标识表记标帜CSN=0; NRFSPI(FLUSH_TX);//用于清空FIFO !!关键!!否则会呈现意想不到的后果!!!年夜家记住!! CSN=1; return(0); } else return(1);}//用于接收模式U8 NRFRevDate(U8 *RevDate){ U8RevFlags=0; sta=NRFReadReg(R_REGISTER+STATUS);//发送数据后读取状态寄存器 if(RX_DR) // 判断是否接收到数据{ CE=0; //SPI使能NRFReadRxDate(R_RX_PAYLOAD,RevDate,RX_DATA_WITDH);/ / 从RXFIFO读取数据RevFlags=1; //读取数据完成标识表记标帜} NRFWriteReg(W_REGISTER+STATUS,0xff); //接收到数据后RX_DR,TX_DS,MAX_PT都置高为1,通过写1来清楚中断标 return(RevFlags);}void Delay_10ms(U16 del){ U16 i,j; for(i=0; i<del; i++) for(j=0; j<1827; j++) //这个是通过软件仿真得出的数;}// 做发射main(){ U8 TxDate[1]={0}; NRF24L01Int(); Delay(6000); while(1) { if(KEY==0) { if(KEY==0) { Delay_1 0ms(2); TxDate[0]=0x08;//发射按键标识值NRFSetTxMode(TxDate); while(CheckACK());TxDate[0]=0; } } }}//// 做接收:一直处于接受模式查询所获得的值是不是0x08main(){ U8 x; U8 RxBuf[1]={0}; NRF24L01Int(); Delay(6000);while(1) { NRFSetRXMode(); NRFRevDate(RxBuf); x=RxBuf[0]; if(x==0x08)//0x08为发射部份所发送的按键标识{ // //加入所要执行的功能函数 //固然了这里用到单片机io口的时候需要在之前界说//还有就是发送法式中的KEY也需要之前界说好 RxBuf[0]=0; } }}。
STM32F0无法使NRF2401工作,求达人指点迷津!(amoBBS阿莫电子论坛)首先,说一下情况:参考STM32F0开发板的例程中的推挽输出(参照GPIO),上拉输入(参照EXTI),复用输出(参照UART),然后照着正点原子的ALIENTEK MINISTM32 实验无线通信实验完成了连线以及配置,但是,无法使这两个板子通讯,经过检测,发现F0的板子的NRF24L01_Check()一直无法过去,while在那里,现在传上代码,请达人们给予指点,谢谢了程序很简单,就是初始化24L01及检测它的存在主函数部分int main(void){NRF24L01_Init(); //初始化NRF24L01while(NRF24L01_Check());//检测24L01,一直死在这里,很不解while (1){}}24L01函数部分const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址//初始化24L01的IO口void NRF24L01_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOC, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;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_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;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_NOPULL;GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_SetBits(GPIOC,GPIO_Pin_4);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ; //上拉输入GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOC, &GPIO_InitStructure);SPIx_Init(); //初始化SPIClr_NRF24L01_CE; //使能24L01 NRF24L01_CESet_NRF24L01_CSN; //SPI片选取消NRF24L01_CSN}void SPIx_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE );GPIO_DeInit(GPIOA);/* Connect pin to Periph */GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0);GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0);GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);/* Configure pins as AF pushpull */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 ;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA, &GPIO_InitStructure);SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //选择了串行时钟的稳态:时钟悬空低电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第一个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); //使能SPI外设SPIx_ReadWriteByte(0xff);//启动传输}//检测24L01是否存在//返回值:0,成功;1,失败u8 NRF24L01_Check(void){u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};u8 i;SPIx_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz (24L01的最大SPI时钟为10Mhz)NRF24L01_Write_Buf(NRF24L01_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}//SPIx 读写一个字节//TxData:要写入的字节//返回值:读取到的字节u8 SPIx_ReadWriteByte(u8 TxData){u8 retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位{retry++;if(retry>200)return 0;}SPI_I2S_SendData16(SPI1, TxData); //通过外设SPIx发送一个数据retry=0;while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //检查指定的SPI标志位设置与否:接受缓存非空标志位{retry++;if(retry>200)return 0;}return SPI_I2S_ReceiveData16(SPI1); //返回通过SPIx最近接收的数据}红色部分和原子的不一样,因为F0的SPI部分有void SPI_SendData8(SPI_TypeDef* SPIx, uint8_t Data)uint8_t SPI_ReceiveData8(SPI_TypeDef* SPIx)。
如何通过STM32驱动nRF24L01发送数据最近在一个项目中用到了nRF24L01这个无线2.4G收发芯片,项目中有主机和分机,默认都是使用数据通道0,主机通过nRF24L01发送数据后,对应地址的分机在收到数据后会返回一个确认数据包给主机(注意:这个确认数据包并不是nRF24L01自动应答时的数据包,而是自定义的一个数据包,说明了就是双方都能进行收发),在经过长时间的摸索之后,终于将接收和发送都调通了,基本的SPI驱动我使用的是正点原子的教程,我是使用的中断法来处理相应的收发工作。
我的软件硬件环境大致如下:服务端:STM32F103VET6+u/COS-III从机端:STM32F103RBT6+u/COS-III库函数是使用的3.5版本的。
在这里我只是列出主机部分的代码,从机上的都是差不多的。
下面是nRF24L01的驱动部分头文件部分(24l01.h):#ifndef__24L01_H#define__24L01_H#include“config.h”#ifnRF24L01_EN》0u#include“sys_temp.h”/*---------------------------------------------------------------------------------------------NRF24L01寄存器操作命令及寄存器地址-----------------------------------------------------------------------------------------------*/#defineSPI_READ_REG0x00//读配置寄存器,低5位为寄存器地址#defineSPI_WRITE_REG0x20//写配置寄存器,低5位为寄存器地址#defineRD_RX_PLOAD0x61//读RX有效数据,1~32字节#defineWR_TX_PLOAD0xA0//写TX有效数据,1~32字节。
基于STM32和nRF24l01的智能车无线监控平台设计作者:赫玉莹来源:《科技创新导报》 2014年第13期赫玉莹(中南大学信息科学与工程学院湖南长沙 410083)摘要:在飞思卡尔智能车竞赛的基础上,设计了基于STM32和nrf24l01的智能车监控平台,可实现无线数据接收、远程遥控,触屏控制,具备可靠性、灵活性、便捷性,方便智能车的调试。
该文针对其主要功能模块详细介绍了其实现方法和软件设计流程。
关键词:STM32 nRF24l01 无线通信监控中图分类号:TN92文献标识码:A 文章编号:1674-098X(2014)05(a)-0074-01在进行智能车的开发和调试过程中,需要不断地验证程序和算法的有效性,从而调节智能车的各项参数。
大多数参赛队员都是采用和电脑相连接的方法进行调试,这种方法一是无法显示车辆高速行驶过程中的实时状况,二是调试监控十分不便,因此,开发一种上位机进行无线监视和控制成为一种必然趋势和选择。
目前比较成熟的短距离无线传输网络的技术有:蓝牙、wifi、超带宽技术、Zigbee技术等,但是上述技术开发起来较为复杂,开发周期较长、成本较高[1]。
因此,本设计选用stm32和nRF24l01进行模块开发,并能很好地实现相应功能。
1 系统的结构设计本设计采用nRF24l01以无线通信方式与附近的其他模块或系统进行数据交换,其基本原理如图1所示。
XS128单片机采集智能车参数,如传感器数据信息、舵机打角、行驶速度等。
然后将信息传输到车载nrf24l01中,以人为设置的固定匹配波特率发送出去,在这一端,将以相同的波特率进行接收。
同时,采用stm32上的配套触摸液晶屏可进行实时显示。
通过对传输的数据进行分析,可根据预想情况进行控制,通过触摸屏和机械按键的结合,可更改设置参数并控制小车的启动和运行状况。
2 系统实现分析STM32通过SPI与外部MCU通信,最大SPI速度可以达到10MHz。
NRF24L01详细教程近来课程的项目需要用到NRF24L01,用来做基本的收发,虽然资料拿到不少,但是,很多资料并不是很清晰、所带的例程并不够简洁或有不少冗余的部分,再加上对应的中文数据手册部分没翻译出来,翻译出来的不够有条理,很多地方模糊,甚至关键的地方看一两次还看不出来,导致了在学NRF24L01时花费了较多时间,所以,学完NRF24L01后,萌生了写个尽量清晰的教程的想法。
教程中的例程虽然是库开发方式,但基本都是最底层的操作才用到库函数譬如发一字节数据、GPIO置位等,虽然用的STM32,但我在看其他板子的例程时,发觉内容与流程都是差不多的,只是不同板引脚不同所导致的引脚配置的不同,不管用什么方式开发,用什么芯片,了解清楚NRF24L01如何配置,了解清楚其收发流程,基本上就会开发了,所以此文档虽然写的是以STM32为例,但看完此文档用NRF24L01基本也没什么大问题了。
教程说明:这教程是基于STMF103ZET6的,是野火的板子,例程也是从野火提供修改例程得来,用的是库开发的方式。
学习NRF24L01的步骤:1.学习SPI,SPI就是NRF24L01传送数据到单片机的一种协议,类似于USB,当然USB还是比较有难度的。
2.了解NRF24L01相关寄存器,结合中文数据手册了解NRF24L01的基本配置,收发数据前后的操作(如何启动发送接收、寄存器清空、标志位重置等)。
3.分析具体代码SPI的简介:具体的SPI教程,大家可以去野火的教程进行学习,在此只是简略介绍一下,SPI是一种一对多协议:一个主机(MCU)对应对多个从机,可以分时与多个从机通讯SPI 总线包含4 条总线,分别为SS、SCK、MOSI、MISO,其含义分别为SS:Slave Select,片选信号线,主机借此信号线选择一个从机,低电平有效。
MOSI:Master Output,Slave Input,主机数据从此线输出到从机,数据方向从主机到从机。
(相关人员如觉得本人水平低下,还请见谅)Nrf24L01的使用程序和使用方法和简单操作:功能:无线对发程序。
两个模块a,b,实现按下一个按键,会在对方的数码管上显示3或4,在本机上显示1,2。
当一个模块,比如a模块。
当两个按键按下其中一个,则会在另一个模块b上显示数字3,4(具体根据按下哪个按键)。
以上功能描述,B模块按键按下,如同a模块一样的功能,不做系统性描述了。
下面给出程序中几个地方的解释:#define READ_REG 0x00 // 读寄存器指令#define WRITE_REG 0x20 // 写寄存器指令#define RD_RX_PLOAD 0x61 // 读取接收数据指令#define WR_TX_PLOAD 0xA0 // 写待发数据指令#define FLUSH_TX 0xE1 // 冲洗发送FIFO指令#define FLUSH_RX 0xE2 // 冲洗接收FIFO指令#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令#define NOP 0xFF // 保留类似这种的描述,可以等同于READ_REG =0x00;这个是经过实际程序测试出来的,比如以下程序:#include<reg51.h>#define k 0xfevoid main(){P1=k;}则会出现此类结果:MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSIuchar = (uchar << 1); // shift next bit into MSB..SCK = 1; // Set SCK high..uchar |= MISO; // capture current MISO bitSCK = 0;此处为spi的核心,是spi协议的编程,其中uchar |= MISO; 表示uchar |= MISO | uchar; MOSI = (uchar & 0x80);其中0x80是1000 0000,与上uchar,这种&,是按位与,故可以从uchar提取出一个电平给mosi。
NRF24L01详细教程近来课程的项目需要用到NRF24L01,用来做基本的收发,虽然资料拿到不少,但是,很多资料并不是很清晰、所带的例程并不够简洁或有不少冗余的部分,再加上对应的中文数据手册部分没翻译出来,翻译出来的不够有条理,很多地方模糊,甚至关键的地方看一两次还看不出来,导致了在学NRF24L01时花费了较多时间,所以,学完NRF24L01后,萌生了写个尽量清晰的教程的想法。
教程中的例程虽然是库开发方式,但基本都是最底层的操作才用到库函数譬如发一字节数据、GPIO置位等,虽然用的STM32,但我在看其他板子的例程时,发觉内容与流程都是差不多的,只是不同板引脚不同所导致的引脚配置的不同,不管用什么方式开发,用什么芯片,了解清楚NRF24L01如何配置,了解清楚其收发流程,基本上就会开发了,所以此文档虽然写的是以STM32为例,但看完此文档用NRF24L01基本也没什么大问题了。
教程说明:这教程是基于STMF103ZET6的,是野火的板子,例程也是从野火提供修改例程得来,用的是库开发的方式。
学习NRF24L01的步骤:1.学习SPI,SPI就是NRF24L01传送数据到单片机的一种协议,类似于USB,当然USB还是比较有难度的。
2.了解NRF24L01相关寄存器,结合中文数据手册了解NRF24L01的基本配置,收发数据前后的操作(如何启动发送接收、寄存器清空、标志位重置等)。
3.分析具体代码SPI的简介:具体的SPI教程,大家可以去野火的教程进行学习,在此只是简略介绍一下,SPI是一种一对多协议:一个主机(MCU)对应对多个从机,可以分时与多个从机通讯SPI 总线包含4 条总线,分别为SS、SCK、MOSI、MISO,其含义分别为SS:Slave Select,片选信号线,主机借此信号线选择一个从机,低电平有效。
MOSI:Master Output,Slave Input,主机数据从此线输出到从机,数据方向从主机到从机。