其实自己摸索单片机已经有一年了,但没在网上写过笔记,终于觉得既然整天都在各种网站找资料,也看人家的博客,自己也写写笔记,一方面给自己看,一方面作为分享和供批评。
STM32这块片子接触也有半年了,还没真正做自己的作品,这阵子和一个朋友想做一下一个基于STM32的小作品,要用到无线控制,决定用NRF24L01+这片芯片,主要也是这片常用,简单,也便宜。
一个小插曲是在淘宝上淘模块的时候发现,相当多的店子都写明了是Si24R91(其实是应该是Si24R1,那个好像是不存在的)这一代替芯片,其实看了手册,单单从功能上,Si24R1是提升了一些,属于向下兼容NRF24L01,但是不解的是淘宝发回来的芯片上印的却是NRF24L01,通过比对调试寄存器的内容确实是NRF24L01(这才让我不解,商家搞什么名堂)。
STM32F103VET6有俩SPI接口,选择用SPI1来驱动NRF24L01.
1.配置STM32的SPI1接口:
1)开启GPIOA,AFIO时钟
2)配置GPIOA对应Pin为SPI1的MISO,MOSI,CLK,NSS(SPI1的NSS已经为他用,故用PA1软件控制NRF24L01的CSN),CE(NRF24L01使能端口),IRQ--NRF24L01的中断信号
3)配置SPI模式
2.仔细读手册(重点是时序和寄存器)
3.编写用SPI1控制NRF的函数数个
4.用以上的函数编写功能函数
OK!但这是说的。真正做起来的时候饱受打击。串口转USB线坏了,无奈只能LCD屏来显示调试信息,增大了代码负担。后来J-Link有坏了,差点放弃,好在重写了固件还能复活。
1)2)步的代码:
void SPI1_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); //使能SPI1时钟
// PA5--CLK PA7--MOSI 复用推挽输入
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PA6--MISO 输入浮空
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI1_CE_LOW();
SPI1_NRF_CSN_HIGH();//拉高CSN失能片选
SPI_InitTypeDefSPI_InitStructure; //声明用来初始化的结构体
SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;//全双工
SPI_InitStructure.SPI_Mode =SPI_Mode_Master; //主模式
SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b; //一次传输8位
SPI_InitStructure.SPI_CPOL =SPI_CPOL_Low; //空闲电平低电平
SPI_InitStructure.SPI_CPHA =SPI_CPHA_1Edge; //第一个上升沿采样
SPI_InitStructure.SPI_NSS =SPI_NSS_Soft; //NSS管理为软件件模式
SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_8;//波特率预分频8 9MHz
SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB; //数据传输低位在前
SPI_InitStructure.SPI_CRCPolynomial =7; //CRC校验方式
SPI_Init(SPI1,&SPI_InitStructure); //初始化
//SPI_NSSInternalSoftwareConfig(SPI1,SPI_NSSInternalSoft_Set);
SPI_Cmd(SPI1, ENABLE); //使能SPI1
}//SPI1_Config()
*操作NRF24L01寄存器的时候要在待机模式,CE=0的情况下,没发一个命令或数据,都会返回SPI一个字节,此字节是STAUS状态寄存器的内容,所以写命令的函数要发后,读回一字节,不然,发完命令,读数据的时候会出错。下面就是SPI写1 BYTE的函数。
u8 SPI_RW_Byte(SPI_TypeDef* SPIx,unsigned char Byte)
{
while( SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET); //查发送缓冲器是否为空,空即可以发送
SPI_I2S_SendData(SPIx,Byte); //库函数:发送一个字节
//当SPI接收缓冲器为空时等待
while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) ==RESET);
returnSPI_I2S_ReceiveData(SPIx);
}//SPI_RW_Byte()
*下面一个是发写命令+指定字节数的数据的函数
u8 SPI_NRF_Write(SPI_TypeDef* SPIx,char CMD,unsigned char*WBuff,unsigned char ByteNUM)
{
unsigned chari,status;
SPI1_CE_LOW();
SPI1_NRF_CSN_LOW();//使能片选
status=SPI_RW_Byte( SPIx , CMD);
for(i=0;i
{
SPI_RW_Byte( SPIx,*WBuff++);
//printf("写入第%d个数据\r\n",ByteNUM);
}
SPI1_NRF_CSN_HIGH();//
return status;
}//SPI_NRF_Write()
*发读命令+指定字节数的数据的函数
u8 SPI_NRF_Read(SPI_TypeDef* SPIx,char CMD,unsigned char*RBuff,unsigned char ByteNUM)
{
unsigned char i,status ;
SPI1_CE_LOW();
SPI1_NRF_CSN_LOW();
status=SPI_RW_Byte( SPIx , CMD);
for(i=0;i< ByteNUM ;i++)
{
RBuff=SPI_RW_Byte(SPIx,NOP); // 取接收缓冲器,一个字节
//printf("读出第%d个数据\r\n",ByteNUM);
LCD_Num_6x12_O(100,20*(i+1),RBuff,WHITE);
}
SPI1_NRF_CSN_HIGH();
return status;
}//SPI_NRF_Read()
下面是NRF24L01两种模式TX RX的配置
void SPI_NRF_MOD_TX(void)
{
u8TX_Array[5];
u8_TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05};
SPI1_CE_LOW();//CE=0待机模式
TX_Array[0]=0x03;//设置地址宽度11--5字节 10--4字节 01-3字节 00--不合法
SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1);
TX_Array[0]=0xf3;//建立自动重发间隔‘1111‘--等待4000+86us 15次
SPI_NRF_Write(SPI1,W_REGISTER+SETUP_RETR,TX_Array,1);
TX_Array[0]=0x02;//射频通道 X0000010
SPI_NRF_Write(SPI1,W_REGISTER+RF_CH,TX_Array,1);
TX_Array[0]=0x0f;//射频参数寄存器 00001111 2Mbps 发射功率 00-18dBm 01-12dBm 10-6dBm 11-0dBm 1--低噪声放大器增益
SPI_NRF_Write(SPI1,W_REGISTER+RF_SETUP,TX_Array,1);
TX_Array[0]=0x3f;//xx11 11110-5接收通道允许
SPI_NRF_Write(SPI1,W_REGISTER+EN_RXADDR,TX_Array,1);
TX_Array[0]=0x3f;//xx11 11110-5通道允许自动应答
SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
TX_Array[0]=0x0e;//中断全开 发送模式 PRIM_RX=0PWR_UP=1
SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1);
TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器写‘1’清除所有标志
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);
SPI1_CE_HIGH();//CE=1使能发射模式
Delay_us(100);//CE拉高需要一定的延时才能进行发送 延时之后即可通过SPI接口发送TX_PLD
}
void SPI_NRF_MOD_RX(void)
{
u8TX_Array[5];
u8_TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05};
SPI1_CE_LOW();//CE=0待机模式
TX_Array[0]=0x03;//允许接收通道00000011
SPI_NRF_Write(SPI1,W_REGISTER+EN_RXADDR,TX_Array,1);
TX_Array[0]=0x03;//设置地址宽度11--5字节 10--4字节 01-3字节 00--不合法
SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1);
TX_Array[0]=0x20;//射频通道 X0000010
SPI_NRF_Write(SPI1,W_REGISTER+RF_CH,TX_Array,1);
TX_Array[0]=0x0f;//射频参数寄存器 00001111 2Mbps 发射功率 00-18dBm 01-12dBm 10-6dBm 11-0dBm 1--低噪声放大器增益
SPI_NRF_Write(SPI1,W_REGISTER+RF_SETUP,TX_Array,1);
TX_Array[0]=0x3f;//xx11 11110-5通道允许自动应答
SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
TX_Array[0]=0x04;//xx11 1111数据通道0 有效数据宽度 (1-32)字节
SPI_NRF_Write(SPI1,W_REGISTER+RX_PW_P0,TX_Array,1);
TX_Array[0]=0xfe;//1111 xxxxSTATUS寄存器 写‘1’清除所有标志
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);
SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
TX_Array[0]=0x0f;//接收模式 PRIM_RX=1 PWR_UP=1允许接收终端
SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1);
SPI1_CE_HIGH();//CE=1使能发射模式
Delay_us(100);//CE拉高需要一定的延时才能进行发送 延时之后即可通过SPI接口发送TX_PLD
//轮询中断24L01中断的到来 NRF_Read_IRQ()
}
下面是两种模式的测试
ErrorStatus SPI_NRF_TX_DATAS(u8* TBuff,u8 ByteNUM)
{
u8 Status[1];
do{
SPI1_CE_LOW();//拉低待机
SPI_NRF_Write(SPI1,W_TX_PAYLOAD,TBuff,ByteNUM);//发送TBuff数组
SPI1_CE_HIGH();//拉低待机
}while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平
SPI_NRF_Write(SPI1, FLUSH_TX,TBuff,0);
SPI1_CE_LOW();//拉低待机
Delay_us(100);
SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1);//读取Status
LCD_Num_6x12_O(200,20,Status[0],WHITE);
if(Status[0]&0x10)
{
Status[0]&=0x10;
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//
LCD_Str_6x12_O_P(220 , 10 ,"TxError!", WHITE);//重发超时 发送失败
return ERROR;
}
else
{
Status[0]&=0x20;
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//
LCD_Str_6x12_O_P(240 , 10 ,"TxSuccess", WHITE);//发送成功
return SUCCESS;
}
}
ErrorStatus SPI_NRF_RX_DATAS(u8* RBuff)
{
ErrorStatusRX_Status=SUCCESS;
u8Status[1];
while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平
SPI1_CE_LOW();//拉低待机,才能操作寄存器
Delay_us(100);
SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1);//读取Status
switch(Status[0]&0x0e)
{
case 0x0e: RX_Status=ERROR;break; //RX_FIFO 空
default :LCD_Str_6x12_O_P(200 , 10 ,"RxSuccess!", WHITE);//RX_FIFO非空
break;
}
SPI_NRF_Read(SPI1,R_RX_PAYLOAD,RBuff,4);//读RX_FIFO
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//处理状态寄存器标志
return RX_Status;
}
特别要注意的是在发射模式的时候,应该先拉低CE,先在TX FIFO里写入要发射的数据,再拉高CE真正发射。
|