[STM32F1] STM32_SPI驱动NRF24L01

[复制链接]
1647|5
 楼主| quray1985 发表于 2016-4-26 10:49 | 显示全部楼层 |阅读模式
其实自己摸索单片机已经有一年了,但没在网上写过笔记,终于觉得既然整天都在各种网站找资料,也看人家的博客,自己也写写笔记,一方面给自己看,一方面作为分享和供批评。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)步的代码:
  1. void SPI1_Config(void)
  2. {
  3.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);                //使能SPI1时钟
  4.    
  5.     /*  配置SPI1的复用GPIO  */
  6.       //  PA5--CLK PA7--MOSI  复用推挽输入
  7.     GPIO_InitTypeDef GPIO_InitStructure;
  8.       GPIO_InitStructure.GPIO_Pin =   GPIO_Pin_5|GPIO_Pin_7;
  9.       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  10.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  11.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  12.       //PA6--MISO  输入浮空
  13.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  14.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  15.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  16.     /*PA2--CE    PA1--NSS(本实验室驱动NRF24l01,用通用GPIOA1驱动CSN) */
  17.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1|GPIO_Pin_2;
  18.       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  19.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  20.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  21. /*PA3--IRQ      */
  22.     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_3;
  23.       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  24.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  25. SPI1_CE_LOW();
  26.    SPI1_NRF_CSN_HIGH();//拉高CSN 失能片选
  27.    
  28.     /*  配置SPI1模块 初始化*/
  29.     SPI_InitTypeDef SPI_InitStructure;                                  //声明用来初始化的结构体
  30.       SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工
  31.       SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                     //主模式
  32.       SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  //一次传输8位
  33.       SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                        //空闲电平低电平
  34.       SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                      //第一个上升沿采样
  35.       SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                         //NSS管理为软件件模式
  36.       SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_8; //波特率预分频8  9MHz
  37.       SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;       //数据传输低位在前
  38.       SPI_InitStructure.SPI_CRCPolynomial = 7;                           //CRC校验方式
  39.     SPI_Init(SPI1, &SPI_InitStructure);                                  //初始化
  40.    
  41.    // SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
  42.    

  43.     SPI_Cmd(SPI1, ENABLE); //使能SPI1
  44. }//SPI1_Config()
  45.    *操作NRF24L01寄存器的时候要在待机模式,CE=0的情况下,没发一个命令或数据,都会返回SPI一个字节,此字节是STAUS状态寄存器的内容,所以写命令的函数要发后,读回一字节,不然,发完命令,读数据的时候会出错。 下面就是SPI写1 BYTE的函数。
  46. /*************************************************************
  47. **函数名: SPI_RW_Byte(SPI_TypeDef* SPIx,unsigned char Byte);
  48. **描述  :   SPI写Byte
  49. **参数  :
  50. **返回值: u8 STATUS寄存器的内容
  51. **注意  :        
  52. ************************************************************/
  53. u8 SPI_RW_Byte(SPI_TypeDef* SPIx,unsigned char Byte)
  54. {
  55.       while( SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);  //查发送缓冲器是否为空,空即可以发送
  56.       SPI_I2S_SendData(SPIx, Byte);   //库函数:发送一个字节
  57.       //当SPI接收缓冲器为空时等待
  58.       while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
  59.    return SPI_I2S_ReceiveData(SPIx);
  60. }//SPI_RW_Byte()



 楼主| quray1985 发表于 2016-4-26 10:49 | 显示全部楼层
*下面一个是发写命令+指定字节数的数据的函数
  1. /*************************************************************
  2. **函数名: SPI_NRF_Write(char CMD,char* WBuff,char ByteNUM);
  3. **描述  :   SPI1写NRF
  4. **参数  :
  5. **返回值: u8 STATUS
  6. **注意  :        
  7. ************************************************************/
  8. u8 SPI_NRF_Write(SPI_TypeDef* SPIx,char CMD,unsigned char* WBuff,unsigned char ByteNUM)
  9. {
  10. unsigned char i,status;
  11. SPI1_CE_LOW();
  12. SPI1_NRF_CSN_LOW();//使能片选

  13. status=SPI_RW_Byte( SPIx , CMD);
  14. for(i=0;i<ByteNUM;i++)
  15. {
  16.      SPI_RW_Byte( SPIx,*WBuff++);
  17.    // printf("写入第%d个数据\r\n",ByteNUM);   
  18. }
  19. SPI1_NRF_CSN_HIGH();//
  20. return status;
  21. }//SPI_NRF_Write()
  22.     *发 读命令+指定字节数的数据的函数
  23. /*************************************************************
  24. **函数名: SPI_NRF_Read(SPI_TypeDef* SPIx,char CMD,char* RBuff,char ByteNUM);
  25. **描述  :   SPI1写NRF
  26. **参数  :
  27. **返回值: u8 status
  28. **注意  :        
  29. ************************************************************/
  30. u8 SPI_NRF_Read(SPI_TypeDef* SPIx,char CMD,unsigned char* RBuff,unsigned char ByteNUM)
  31. {
  32. unsigned char i,status ;

  33. SPI1_CE_LOW();
  34. SPI1_NRF_CSN_LOW();
  35. status=SPI_RW_Byte( SPIx , CMD);
  36. for(i=0;i< ByteNUM ;i++)
  37. {   
  38.      RBuff[i]=SPI_RW_Byte(SPIx,NOP);          // 取接收缓冲器,一个字节
  39.     //printf("读出第%d个数据\r\n",ByteNUM);   
  40.     LCD_Num_6x12_O(100,20*(i+1),RBuff[i], WHITE);
  41. }


  42. SPI1_NRF_CSN_HIGH();
  43. return status;
  44. }//SPI_NRF_Read()

  45. 下面是NRF24L01两种模式TX RX的配置
  46. /*************************************************************
  47. **函数名: SPI_NRF_MOD_TX()
  48. **描述  :   配置NRF进入 发送模式
  49. **参数  :
  50. **返回值:
  51. **注意  :  仅用于测试 无参无返  可改成带配置参数的模块函数
  52.    Tx通道0 地址TX_ADDR=0x05B6B5B4B3
  53. ************************************************************/
  54. void SPI_NRF_MOD_TX(void)
  55. {
  56.     u8 TX_Array[5];
  57. u8 _TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05};
  58.   
  59. SPI1_CE_LOW();//CE=0 待机模式
  60.   TX_Array[0]=0x03;//设置地址宽度 11--5字节 10--4字节 01-3字节 00--不合法
  61. SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1);
  62.   TX_Array[0]=0xf3;//建立自动重发 间隔‘1111‘--等待4000+86us   15次
  63. SPI_NRF_Write(SPI1,W_REGISTER+SETUP_RETR,TX_Array,1);
  64.   TX_Array[0]=0x02;//射频通道 X000 0010
  65. SPI_NRF_Write(SPI1,W_REGISTER+RF_CH,TX_Array,1);
  66.   TX_Array[0]=0x0f;//射频参数寄存器 0000 1111 2Mbps 发射功率 00-18dBm 01-12dBm 10-6dBm 11-0dBm  1--低噪声放大器增益
  67. SPI_NRF_Write(SPI1,W_REGISTER+RF_SETUP,TX_Array,1);
  68.   TX_Array[0]=0x3f;//xx11 1111 0-5接收通道允许
  69. SPI_NRF_Write(SPI1,W_REGISTER+EN_RXADDR,TX_Array,1);
  70.   TX_Array[0]=0x3f;//xx11 1111 0-5通道允许自动应答
  71. SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
  72.   

  73. SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
  74. SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证


  75. TX_Array[0]=0x0e;//中断全开 发送模式 PRIM_RX=0 PWR_UP=1
  76. SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1);

  77. TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器 写‘1’清除所有标志
  78. SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);

  79. SPI1_CE_HIGH();//CE=1 使能发射模式
  80. Delay_us(100);//CE拉高需要一定的延时才能进行发送 延时之后 即可通过SPI接口发送TX_PLD
  81. }


评论

厉害  发表于 2019-3-20 13:51
 楼主| quray1985 发表于 2016-4-26 10:51 | 显示全部楼层
  1. /*************************************************************
  2. **函数名: SPI_NRF_MOD_RX()
  3. **描述  :   配置NRF进入 发送模式
  4. **参数  :
  5. **返回值:
  6. **注意  :  仅用于测试 无参无返  可改成带配置参数的模块函数
  7.    RX通道0 地址TX_ADDR=0xB3B4B5B605
  8. ************************************************************/
  9. void SPI_NRF_MOD_RX(void)
  10. {
  11.     u8 TX_Array[5];
  12. u8 _TX_RX_ADDR_[5]={0xB3,0xB4,0xB5,0xB6,0x05};

  13. SPI1_CE_LOW();//CE=0 待机模式
  14.   TX_Array[0]=0x03;//允许接收通道0000 0011
  15. SPI_NRF_Write(SPI1,W_REGISTER+EN_RXADDR,TX_Array,1);
  16.   TX_Array[0]=0x03;//设置地址宽度 11--5字节 10--4字节 01-3字节 00--不合法
  17. SPI_NRF_Write(SPI1,W_REGISTER+SETUP_AW,TX_Array,1);
  18.   TX_Array[0]=0x20;//射频通道 X000 0010
  19. SPI_NRF_Write(SPI1,W_REGISTER+RF_CH,TX_Array,1);
  20.   TX_Array[0]=0x0f;//射频参数寄存器 0000 1111 2Mbps 发射功率 00-18dBm 01-12dBm 10-6dBm 11-0dBm  1--低噪声放大器增益
  21. SPI_NRF_Write(SPI1,W_REGISTER+RF_SETUP,TX_Array,1);
  22.   TX_Array[0]=0x3f;//xx11 1111 0-5通道允许自动应答
  23. SPI_NRF_Write(SPI1,W_REGISTER+EN_AA,TX_Array,1);
  24.   TX_Array[0]=0x04;//xx11 1111 数据通道0 有效数据宽度 (1-32)字节
  25. SPI_NRF_Write(SPI1,W_REGISTER+RX_PW_P0,TX_Array,1);
  26.   TX_Array[0]=0xfe;//1111 xxxx STATUS寄存器 写‘1’清除所有标志
  27. SPI_NRF_Write(SPI1,W_REGISTER+STATUS,TX_Array,1);

  28. SPI_NRF_Write(SPI1,W_REGISTER+TX_ADDR,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证
  29. SPI_NRF_Write(SPI1,W_REGISTER+RX_ADDR_P0,_TX_RX_ADDR_,5);//写入接收发送数据的地址,这个地址是接收端收件的凭证



  30. TX_Array[0]=0x0f;//接收模式 PRIM_RX=1 PWR_UP=1 允许接收终端
  31. SPI_NRF_Write(SPI1,W_REGISTER+CONFIG,TX_Array,1);

  32. SPI1_CE_HIGH();//CE=1 使能发射模式
  33. Delay_us(100);//CE拉高需要一定的延时才能进行发送 延时之后 即可通过SPI接口发送TX_PLD
  34. //轮询中断24L01中断的到来 NRF_Read_IRQ()
  35. }
下面是两种模式的测试
  1. /*************************************************************
  2. **函数名: SPI_NRF_TX_DATAS()
  3. **描述  :   配置NRF发送 数据
  4. **参数  : TBuff[ByteNUM] ByteNUM
  5. **返回值: ErrorStatus
  6. **注意  :  
  7. ************************************************************/
  8. ErrorStatus SPI_NRF_TX_DATAS(u8* TBuff,u8 ByteNUM)
  9. {
  10.    u8 Status[1];
  11. do{
  12.   SPI1_CE_LOW();//拉低待机
  13.    SPI_NRF_Write(SPI1,W_TX_PAYLOAD,TBuff,ByteNUM);//发送TBuff数组
  14.   SPI1_CE_HIGH();//拉低待机
  15.    }while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平
  16. SPI_NRF_Write(SPI1, FLUSH_TX,TBuff,0);
  17. SPI1_CE_LOW();//拉低待机
  18. Delay_us(100);
  19. SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1);//读取Status
  20. LCD_Num_6x12_O(200,20,Status[0], WHITE);
  21. if(Status[0]&0x10)
  22. {
  23.   Status[0]&=0x10;
  24.   SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//
  25.   LCD_Str_6x12_O_P(220 , 10 ,"Tx Error!", WHITE);//重发超时 发送失败
  26.   return ERROR;
  27. }
  28. else
  29. {
  30.   Status[0]&=0x20;
  31.   SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//
  32.   LCD_Str_6x12_O_P(240 , 10 ,"Tx Success", WHITE);//发送成功
  33.   return SUCCESS;
  34. }
  35. }
  36. /*************************************************************
  37. **函数名: SPI_NRF_RX_DATAS()
  38. **描述  :   配置NRF发送 数据
  39. **参数  : RBuff[ByteNUM] ByteNUM
  40. **返回值: ErrorStatus
  41. **注意  :  
  42. ************************************************************/
  43. ErrorStatus SPI_NRF_RX_DATAS(u8* RBuff)
  44. {
  45.    ErrorStatus RX_Status=SUCCESS;
  46.     u8 Status[1];
  47. while(NRF_Read_IRQ()!=0);//中断产生时,IRQ引脚低电平
  48. SPI1_CE_LOW();//拉低待机,才能操作寄存器
  49. Delay_us(100);
  50. SPI_NRF_Read(SPI1,R_REGISTER+STATUS,Status,1);//读取Status
  51. switch(Status[0]&0x0e)
  52. {
  53. case 0x0e: RX_Status=ERROR; break; //RX_FIFO 空
  54. default :LCD_Str_6x12_O_P(200 , 10 ,"Rx Success!", WHITE);//RX_FIFO非空
  55.     break;
  56. /*  
  57. case 0x00: break; //通道0
  58. case 0x02: break; //通道1
  59. case 0x04: break; //通道2
  60. case 0x06: break; //通道3
  61. case 0x08: break; //通道4
  62. case 0x0A: break; //通道5*/
  63. }

  64. SPI_NRF_Read(SPI1,R_RX_PAYLOAD,RBuff,4);//读RX_FIFO
  65. SPI_NRF_Write(SPI1,W_REGISTER+STATUS,Status,1);//处理状态寄存器标志
  66. return RX_Status;
  67. }
特别要注意的是在发射模式的时候,应该先拉低CE,先在TX FIFO里写入要发射的数据,再拉高CE真正发射。
huzi2099 发表于 2019-3-20 16:16 | 显示全部楼层
不错,你还可以做得更好
小明的同学 发表于 2019-3-21 22:12 | 显示全部楼层
多谢分享资料。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

156

主题

1488

帖子

5

粉丝
快速回复 在线客服 返回列表 返回顶部