找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4100|回復(fù): 1
收起左側(cè)

STM32_SPI驅(qū)動NRF24L01+

[復(fù)制鏈接]
ID:262 發(fā)表于 2014-7-7 15:31 | 顯示全部樓層 |閱讀模式
                        其實自己摸索單片機已經(jīng)有一年了,但沒在網(wǎng)上寫過筆記,終于覺得既然整天都在各種網(wǎng)站找資料,也看人家的博客,自己也寫寫筆記,一方面給自己看,一方面作為分享和供批評。
STM32這塊片子接觸也有半年了,還沒真正做自己的作品,這陣子和一個朋友想做一下一個基于STM32的小作品,要用到無線控制,決定用NRF24L01+這片芯片,主要也是這片常用,簡單,也便宜。
   一個小插曲是在淘寶上淘模塊的時候發(fā)現(xiàn),相當(dāng)多的店子都寫明了是Si24R91(其實是應(yīng)該是Si24R1,那個好像是不存在的)這一代替芯片,其實看了手冊,單單從功能上,Si24R1是提升了一些,屬于向下兼容NRF24L01,但是不解的是淘寶發(fā)回來的芯片上印的卻是NRF24L01,通過比對調(diào)試寄存器的內(nèi)容確實是NRF24L01(這才讓我不解,商家搞什么名堂)。
   STM32F103VET6有倆SPI接口,選擇用SPI1來驅(qū)動NRF24L01.
    1.配置STM32的SPI1接口:
      1)開啟GPIOA,AFIO時鐘
      2)配置GPIOA對應(yīng)Pin為SPI1的MISO,MOSI,CLK,NSS(SPI1的NSS已經(jīng)為他用,故用PA1軟件控制NRF24L01的CSN),CE(NRF24L01使能端口),IRQ--NRF24L01的中斷信號
     3)配置SPI模式
    2.仔細(xì)讀手冊(重點是時序和寄存器)
    3.編寫用SPI1控制NRF的函數(shù)數(shù)個
   4.用以上的函數(shù)編寫功能函數(shù)
  OK!但這是說的。真正做起來的時候飽受打擊。串口轉(zhuǎn)USB線壞了,無奈只能LCD屏來顯示調(diào)試信息,增大了代碼負(fù)擔(dān)。后來J-Link有壞了,差點放棄,好在重寫了固件還能復(fù)活。
   1)2)步的代碼:
void SPI1_Config(void)
{
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);               //使能SPI1時鐘
   
   
     //  PA5--CLK PA7--MOSI 復(fù)用推挽輸入
   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;                                 //聲明用來初始化的結(jié)構(gòu)體
     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;//波特率預(yù)分頻8  9MHz
     SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB;      //數(shù)據(jù)傳輸?shù)臀辉谇?br />      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的情況下,沒發(fā)一個命令或數(shù)據(jù),都會返回SPI一個字節(jié),此字節(jié)是STAUS狀態(tài)寄存器的內(nèi)容,所以寫命令的函數(shù)要發(fā)后,讀回一字節(jié),不然,發(fā)完命令,讀數(shù)據(jù)的時候會出錯。下面就是SPI寫1 BYTE的函數(shù)。

u8 SPI_RW_Byte(SPI_TypeDef* SPIx,unsigned char Byte)
{
     while( SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET); //查發(fā)送緩沖器是否為空,空即可以發(fā)送
     SPI_I2S_SendData(SPIx,Byte);   //庫函數(shù):發(fā)送一個字節(jié)
     //當(dāng)SPI接收緩沖器為空時等待
      while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) ==RESET);
   returnSPI_I2S_ReceiveData(SPIx);
}//SPI_RW_Byte()

  
   *下面一個是發(fā)寫命令+指定字節(jié)數(shù)的數(shù)據(jù)的函數(shù)

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個數(shù)據(jù)\r\n",ByteNUM);   
}
SPI1_NRF_CSN_HIGH();//
return status;
}//SPI_NRF_Write()

    *發(fā)讀命令+指定字節(jié)數(shù)的數(shù)據(jù)的函數(shù)

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);         // 取接收緩沖器,一個字節(jié)
   //printf("讀出第%d個數(shù)據(jù)\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;//設(shè)置地址寬度11--5字節(jié) 10--4字節(jié) 01-3字節(jié) 00--不合法
SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1);
  TX_Array[0]=0xf3;//建立自動重發(fā)間隔‘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;//射頻參數(shù)寄存器 00001111 2Mbps 發(fā)射功率 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通道允許自動應(yīng)答
SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
   

SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//寫入接收發(fā)送數(shù)據(jù)的地址,這個地址是接收端收件的憑證
SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//寫入接收發(fā)送數(shù)據(jù)的地址,這個地址是接收端收件的憑證


TX_Array[0]=0x0e;//中斷全開 發(fā)送模式 PRIM_RX=0PWR_UP=1
SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1);

TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器寫‘1’清除所有標(biāo)志
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);

SPI1_CE_HIGH();//CE=1使能發(fā)射模式
Delay_us(100);//CE拉高需要一定的延時才能進行發(fā)送 延時之后即可通過SPI接口發(fā)送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;//設(shè)置地址寬度11--5字節(jié) 10--4字節(jié) 01-3字節(jié) 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;//射頻參數(shù)寄存器 00001111 2Mbps 發(fā)射功率 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通道允許自動應(yīng)答
SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
  TX_Array[0]=0x04;//xx11 1111數(shù)據(jù)通道0 有效數(shù)據(jù)寬度 (1-32)字節(jié)
SPI_NRF_Write(SPI1,W_REGISTER+RX_PW_P0,TX_Array,1);
  TX_Array[0]=0xfe;//1111 xxxxSTATUS寄存器 寫‘1’清除所有標(biāo)志
SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);

SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//寫入接收發(fā)送數(shù)據(jù)的地址,這個地址是接收端收件的憑證
SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//寫入接收發(fā)送數(shù)據(jù)的地址,這個地址是接收端收件的憑證



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使能發(fā)射模式
Delay_us(100);//CE拉高需要一定的延時才能進行發(fā)送 延時之后即可通過SPI接口發(fā)送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);//發(fā)送TBuff數(shù)組
  SPI1_CE_HIGH();//拉低待機
  }while(NRF_Read_IRQ()!=0);//中斷產(chǎn)生時,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);//重發(fā)超時 發(fā)送失敗
  return ERROR;
}
else
{
  Status[0]&=0x20;
  SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//
  LCD_Str_6x12_O_P(240 , 10 ,"TxSuccess", WHITE);//發(fā)送成功
  return SUCCESS;
}
}


ErrorStatus SPI_NRF_RX_DATAS(u8* RBuff)
{
   ErrorStatusRX_Status=SUCCESS;
    u8Status[1];
while(NRF_Read_IRQ()!=0);//中斷產(chǎn)生時,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);//處理狀態(tài)寄存器標(biāo)志
return RX_Status;
}


特別要注意的是在發(fā)射模式的時候,應(yīng)該先拉低CE,先在TX FIFO里寫入要發(fā)射的數(shù)據(jù),再拉高CE真正發(fā)射。
                                                                       

回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復(fù) 返回頂部 返回列表