切换到宽版
  • 7567阅读
  • 27回复

[ARM]nrf24l01无线音频传输 [复制链接]

上一主题 下一主题
离线yhj416606438
 

发帖
558
M币
8104
专家
14
粉丝
57
只看楼主 倒序阅读 我要置顶 楼主  发表于: 2016-10-14
NRF24L01现在价格很便宜只有1-2元左右,制作无线音频传输相当划算,2个模块也只要4元
我采用的mcu是arm7系列的lpc2138,大家可以移植到单片机使用,只不过单片机输出没有DA功能,可以外接DA芯片或者使用pwm输出,采样率为40kHz,8位,单声道,修改一下程序就可以变成双声道,理论模块传输速度为2Mbps/s,估计150Kbytes/s可以达到,立体声为80Kbytes/s所以双声道应该没问题。发送端有两个32字节缓存,轮流发送,一个发送时另一个采集,每秒发1250包数据,接收端采用环型缓存接收,这样播放时就不会有卡顿现象。
大家可以移植到单片机试一下,8位的音色也不错,不过在发送端最大把发送端音量开大最大,这样接收端音色才好,发送端io采用中继模式,也就是上拉和下拉电阻都是10k这样在没有输入声音的情况下是中点电压,输入端还需要加隔直流电容最好在10uf,这样低音比较好

发送代码
#include "config.h"
const uint32 CE   = (1 << 16);
const uint32 CSN  = (1 << 15);
const uint32 IRQ  = (1 << 7);
const uint8 TX_ADDRESS[TX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28}; //发送地址
const uint8 RX_ADDRESS[RX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28}; //发送地址
uint8 txbuf1[32],txbuf2[32];
uint16 ADC_Data;
volatile uint8 tt=0;
void delay(uint32 i)
{
  while(i--);
}
void DelayNS (uint32 dly)
{
    uint32 i;
    for ( ; dly>0; dly--)
        for (i=0; i<25; i++);
}
void  NRF24L01__Init(void)
{
    PINSEL1 = (PINSEL1 & (~(0xFF << 2))) | (0xAA << 2);
    IO0DIR|=CSN;
    IO0SET=CSN;
    IO0DIR|=CE;
    IO0CLR=CE;
    SSPCR0 = (0x01 << 8) |              // SCR  设置SPI时钟分频
             (0x00 << 7) |              // CPHA 时钟输出相位,仅SPI模式有效
             (0x00 << 6) |              // CPOL 时钟输出极性,仅SPI模式有效
             (0x00 << 4) |              // FRF  帧格式 00=SPI,01=SSI,10=Microwire,11=保留
             (0x07 << 0);               // DSS  数据长度,0000-0010=保留,0011=4位,0111=8位,1111=16位
    SSPCR1 = (0x00 << 3) |              // SOD  从机输出禁能,1=禁止,0=允许
             (0x00 << 2) |              // MS   主从选择,0=主机,1=从机
             (0x01 << 1) |              // SSE  SSP使能,1=允许SSP与其它设备通信
             (0x00 << 0);               // LBM  回写模式        
    SSPCPSR = 0x02;
}  
uint8  SPI0_communication(uint8 data)
{      
    while((SSPSR & 0x02) == 0 );          
    SSPDR = data;
    while((SSPSR & 0x04) == 0 );
    return(SSPDR);
}
uint8 NRF24L01_Write_Reg(uint8 reg,uint8 value)
{
    uint8 status;
    IO0CLR=CSN;
      status = SPI0_communication(reg);
    SPI0_communication(value);
    IO0SET=CSN;
    return status;
}
uint8 NRF24L01_Read_Reg(uint8 reg)
{
     uint8 value;
    IO0CLR=CSN;  
      SPI0_communication(reg);
    value = SPI0_communication(NOP);
    IO0SET=CSN;
    return value;
}
uint8 NRF24L01_Read_Buf(uint8 reg,uint8 *pBuf,uint8 len)
{
    uint8 status,u8_ctr;
    IO0CLR=CSN;    
      status=SPI0_communication(reg);        
     for(u8_ctr=0;u8_ctr<len;u8_ctr++)
    pBuf[u8_ctr]=SPI0_communication(0XFF);
    IO0SET=CSN;
      return status;        
}
uint8 NRF24L01_Write_Buf(uint8 reg, uint8 *pBuf, uint8 len)
{
    uint8 status,u8_ctr;
    IO0CLR=CSN;    
      status = SPI0_communication(reg);
      for(u8_ctr=0; u8_ctr<len; u8_ctr++)
    SPI0_communication(*pBuf++);
    IO0SET=CSN;
      return status;        
}
uint8 NRF24L01_Check(void)
{
    uint8 check_in_buf[5]={0x11,0x22,0x33,0x44,0x55};
    uint8 check_out_buf[5]={0x00};
    NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5);
    NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5);
    if((check_out_buf[0] == 0x11)&&
       (check_out_buf[1] == 0x22)&&
       (check_out_buf[2] == 0x33)&&
       (check_out_buf[3] == 0x44)&&
       (check_out_buf[4] == 0x55))return 0;
    else return 1;
}
void NRF24L01_RX_Mode(void)
{
    IO0CLR=CE;    
    NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0, (uint8*)RX_ADDRESS, RX_ADR_WIDTH);//写RX接收地址
      NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);    //开启通道0自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01);//通道0接收允许      
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);         //设置RF工作通道频率          
      NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
    NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启          
      NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
    NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
    IO0SET=CE;
}
void NRF24L01_TX_Mode(void)
{
    IO0CLR=CE;  
      NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(uint8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
      NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(uint8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK      
      NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
      NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);       //设置RF通道为40
      NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
      NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
    IO0SET=CE;
}
uint8 NRF24L01_RxPacket(uint8 *rxbuf)
{
    uint8 state;
    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值        
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&RX_OK)//接收到数据
    {
        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
        return 0;
    }      
    return 1;//没收到任何数据
}
uint8 NRF24L01_TxPacket(uint8 *txbuf)
{
    uint8 state;
    IO0CLR=CE;
      NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
     IO0SET=CE;      
    while((IO0PIN&IRQ)==IRQ);//等待发送完成
    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&MAX_TX)//达到最大重发次数
    {
        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
        return MAX_TX;
    }
    if(state&TX_OK)//发送完成
    {
        return TX_OK;
    }
    return 0xff;//发送失败
}    
void adc_init (void)
{
    PINSEL1 |= 1 << 28;        // P0.30连接到AD0.3
    AD0CR = (1 << 3)                        |    // SEL=8,选择通道3
            ((Fpclk / 2000000 - 1) << 8)    |    // CLKDIV=Fpclk/1000000-1,转换时钟为1MHz
            (0 << 16)                        |    // BURST=0,软件控制转换操作
            (2 << 17)                        |    // CLKS=0, 使用11clock转换
            (1 << 21)                        |      // PDN=1,正常工作模式
            (0 << 22)                        |      // TEST1:0=00,正常工作模式
            (0 << 24)                        |    // START=1,直接启动ADC转换
            (0 << 27);                             // 直接启动ADC转换时,此位无效
}

uint8 adc_read (void)    
{
    AD0CR |= 1 << 24;                    // 再次启动转换
    while ((AD0DR & 0x80000000) == 0);    // 等待转换结束                    // 读取ADC结果
    ADC_Data = (AD0DR >> 8) & 0xff;
    return ADC_Data;
}        
void __irq IRQ_Timer0 (void)//定时中断程序,40khz
{   static i=0;
    if(tt==0)
    {
        txbuf1=adc_read();
        i++;
        if(i==32){tt=1;i=0;}goto a;
    }
    if(tt==1)
    {
        txbuf2=adc_read();
        i++;
        if(i==32){tt=0;i=0;}
    }
a:    T0IR = 0x01;                /* 清除中断标志                                    */
    VICVectAddr = 0x00;            /* 通知VIC中断处理结束                            */
}
void Timer0_init (void)    
{
    IRQEnable();
    T0TC   = 0;            /* 定时器设置为0                                        */
    T0PR   = 0;            /* 时钟不分频                                            */
    T0MCR  = 0x03;        /* 设置T0MR0匹配后复位T0TC,并产生中断标志                */
    T0MR0  = Fpclk / 40000;    /* 40khz                                                   */
    T0TCR  = 0x01;        /* 启动定时器                                            */
    VICIntSelect = 0x00;                /* 所有中断通道设置为IRQ中断            */
    VICVectCntl0 = 0x20 | 0x04;            /* 设置定时器0中断通道分配最高优先级    */
    VICVectAddr0 = (uint32)IRQ_Timer0;    /* 设置中断服务程序地址                    */
    VICIntEnable = 1 << 0x04;            /* 使能定时器0中断*/
}    
int main (void)
{  
    adc_init();
    NRF24L01__Init();
    NRF24L01_Check();
    NRF24L01_TX_Mode();
    Timer0_init();
    while (1)
    {
      while(tt==0);
      UART0_SendByte(NRF24L01_TxPacket(txbuf1));
      while(tt==1);
      UART0_SendByte(NRF24L01_TxPacket(txbuf2));
    }
}
接收代码

#include "config.h"
const uint32 CE   = (1 << 16);
const uint32 CSN  = (1 << 15);
const uint32 IRQ  = (1 << 7);
const uint8 TX_ADDRESS[TX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28}; //发送地址
const uint8 RX_ADDRESS[RX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28}; //发送地址
#define NMAX 256
uint8 iput = 0; /* 环形缓冲区的当前放入位置 */
uint8 iget = 0; /* 缓冲区的当前取出位置 */
uint8 n = 0; /* 环形缓冲区中的元素总数量 */
uint8 buf[NMAX];
uint8 rxbuf[32];
void delay(uint32 i)
{
  while(i--);
}
void DelayNS (uint32 dly)
{
    uint32 i;
    for ( ; dly>0; dly--)
        for (i=0; i<25; i++);
}
void  NRF24L01__Init(void)
{
    PINSEL1 = (PINSEL1 & (~(0xFF << 2))) | (0xAA << 2);
    IO0DIR|=CSN;
    IO0SET=CSN;
    IO0DIR|=CE;
    IO0CLR=CE;
    SSPCR0 = (0x01 << 8) |              // SCR  设置SPI时钟分频
             (0x00 << 7) |              // CPHA 时钟输出相位,仅SPI模式有效
             (0x00 << 6) |              // CPOL 时钟输出极性,仅SPI模式有效
             (0x00 << 4) |              // FRF  帧格式 00=SPI,01=SSI,10=Microwire,11=保留
             (0x07 << 0);               // DSS  数据长度,0000-0010=保留,0011=4位,0111=8位,1111=16位
    SSPCR1 = (0x00 << 3) |              // SOD  从机输出禁能,1=禁止,0=允许
             (0x00 << 2) |              // MS   主从选择,0=主机,1=从机
             (0x01 << 1) |              // SSE  SSP使能,1=允许SSP与其它设备通信
             (0x00 << 0);               // LBM  回写模式        
    SSPCPSR = 0x02;
}  
uint8  SPI0_communication(uint8 data)
{      
    while((SSPSR & 0x02) == 0 );          
    SSPDR = data;
    while((SSPSR & 0x04) == 0 );
    return(SSPDR);
}
uint8 NRF24L01_Write_Reg(uint8 reg,uint8 value)
{
    uint8 status;
    IO0CLR=CSN;
      status = SPI0_communication(reg);
    SPI0_communication(value);
    IO0SET=CSN;
    return status;
}
uint8 NRF24L01_Read_Reg(uint8 reg)
{
     uint8 value;
    IO0CLR=CSN;  
      SPI0_communication(reg);
    value = SPI0_communication(NOP);
    IO0SET=CSN;
    return value;
}
uint8 NRF24L01_Read_Buf(uint8 reg,uint8 *pBuf,uint8 len)
{
    uint8 status,u8_ctr;
    IO0CLR=CSN;    
      status=SPI0_communication(reg);        
     for(u8_ctr=0;u8_ctr<len;u8_ctr++)
    pBuf[u8_ctr]=SPI0_communication(0XFF);
    IO0SET=CSN;
      return status;        
}
uint8 NRF24L01_Write_Buf(uint8 reg, uint8 *pBuf, uint8 len)
{
    uint8 status,u8_ctr;
    IO0CLR=CSN;    
      status = SPI0_communication(reg);
      for(u8_ctr=0; u8_ctr<len; u8_ctr++)
    SPI0_communication(*pBuf++);
    IO0SET=CSN;
      return status;        
}
uint8 NRF24L01_Check(void)
{
    uint8 check_in_buf[5]={0x11,0x22,0x33,0x44,0x55};
    uint8 check_out_buf[5]={0x00};
    NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5);
    NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5);
    if((check_out_buf[0] == 0x11)&&
       (check_out_buf[1] == 0x22)&&
       (check_out_buf[2] == 0x33)&&
       (check_out_buf[3] == 0x44)&&
       (check_out_buf[4] == 0x55))return 0;
    else return 1;
}
void NRF24L01_RX_Mode(void)
{
    IO0CLR=CE;    
    NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0, (uint8*)RX_ADDRESS, RX_ADR_WIDTH);//写RX接收地址
      NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);    //开启通道0自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01);//通道0接收允许      
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);         //设置RF工作通道频率          
      NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
    NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启          
      NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
    NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
    IO0SET=CE;
}
void NRF24L01_TX_Mode(void)
{
    IO0CLR=CE;  
      NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(uint8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址
      NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(uint8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK      
      NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
      NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
      NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
      NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);       //设置RF通道为40
      NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启  
      NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
    IO0SET=CE;
}

uint8 NRF24L01_RxPacket(uint8 *rxbuf)
{
    uint8 state;
    while((IO0PIN&IRQ)==IRQ);//等待发送完成  
    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&RX_OK)//接收到数据
    {
        NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
        NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
        return 0;
    }      
    return 1;//没收到任何数据
}
uint8 NRF24L01_TxPacket(uint8 *txbuf)
{
    uint8 state;
    IO0CLR=CE;
      NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
     IO0SET=CE;      
    while((IO0PIN&IRQ)==IRQ);//等待发送完成
    state=NRF24L01_Read_Reg(STATUS);  //读取状态寄存器的值      
    NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
    if(state&MAX_TX)//达到最大重发次数
    {
        NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
        return MAX_TX;
    }
    if(state&TX_OK)//发送完成
    {
        return TX_OK;
    }
    return 0xff;//发送失败
}
uint8 addring (uint8 i)
{
       return(i+1)==NMAX?0:i+1;
}
uint8 get (void)//取出1个
{
    uint8 Pos;
    if(n>0)
    {
          Pos = iget;
       iget = addring(iget);
       n--;
       return buf[Pos];
    }
    else
    {
       return buf[iget];
    }      
}
void put(void)//放入32个
{   uint8 i;
    if(n<(NMAX-32))
    {
         for(i=0;i<32;i++)
         {
             buf[iput]=rxbuf;
             iput = addring(iput);
         }
         n+=32;
    }
}
void __irq IRQ_Timer0 (void)
{  
  if((tt==1)&&(cf==0))
    {
        DACR=rxbuf1<<8;
        i++;
        goto a;
    }
    if((tt==2)&&(cf==0))
    {
        DACR=rxbuf2<<8;
        i++;
    }
a:  if(i==32){i=0;cf=1;}*/
    DACR=get()<<8;
    T0IR = 0x01;
    VICVectAddr = 0x00;    
}                        
void Timer0_init (void)    
{
    IRQEnable();
    T0TC   = 0;            /* 定时器设置为0                                        */
    T0PR   = 0;            /* 时钟不分频                                            */
    T0MCR  = 0x03;        /* 设置T0MR0匹配后复位T0TC,并产生中断标志                */
    T0MR0  = Fpclk / 40000;    /* 40khz                                            */
    T0TCR  = 0x01;        /* 启动定时器                                            */
    VICIntSelect = 0x00;                /* 所有中断通道设置为IRQ中断            */
    VICVectCntl0 = 0x20 | 0x04;            /* 设置定时器0中断通道分配最高优先级    */
    VICVectAddr0 = (uint32)IRQ_Timer0;    /* 设置中断服务程序地址                    */
    VICIntEnable = 1 << 0x04;            /* 使能定时器0中断*/
}
int main (void)
{  
    NRF24L01__Init();
    PINSEL1|=1<<19;
    NRF24L01_Check();
    NRF24L01_RX_Mode();
    NRF24L01_RxPacket(rxbuf);put();
    NRF24L01_RxPacket(rxbuf);put();
    NRF24L01_RxPacket(rxbuf);put();
    Timer0_init();
    while (1)
    {  
       NRF24L01_RxPacket(rxbuf);
       put();            
    }    
}







[ 此帖被yhj416606438在2016-10-15 10:18重新编辑 ]
本帖最近打赏记录:共14条打赏M币+43专家+1
12
离线yhj416606438

发帖
558
M币
8104
专家
14
粉丝
57
只看该作者 1楼 发表于: 2016-10-14
本帖最近打赏记录:共1条打赏M币+2
离线2545889167

发帖
11765
M币
61350
专家
273
粉丝
4580
只看该作者 2楼 发表于: 2016-10-14
有意思,有空也尝试下
离线yanxue11

发帖
513
M币
4810
专家
9
粉丝
71
只看该作者 3楼 发表于: 2016-10-15
有意思,有空用stc的单片机实验一下。
在线keye

发帖
1482
M币
4832
专家
2
粉丝
44
只看该作者 4楼 发表于: 2016-10-15
謝謝分享,代码挺长的
离线zhengve

发帖
44
M币
2066
专家
1
粉丝
0
只看该作者 5楼 发表于: 2016-10-15
利害,调电压可以调音量
离线独行者

发帖
1049
M币
6272
专家
2
粉丝
23
只看该作者 6楼 发表于: 2016-10-15
楼主辛苦,谢谢分享。不知stc行 否!
离线llcc
发帖
154
M币
2642
专家
8
粉丝
9
只看该作者 7楼 发表于: 2016-10-15
谢谢分享,有空试试做成对讲机。

内容来自Android手机客户端

离线yhj416606438

发帖
558
M币
8104
专家
14
粉丝
57
只看该作者 8楼 发表于: 2016-10-15
回 llcc 的帖子
llcc:谢谢分享,有空试试做成对讲机。
 (2016-10-15 09:47) 回 llcc 的帖子

完全可以做的只不过距离太近还不如直接对话方便,如果用nrf24l01+pa就可以的

发帖
56
M币
150
专家
0
粉丝
1
只看该作者 9楼 发表于: 2016-10-16
例程呢,例程呢 有没有例程 好像很好玩

内容来自Android手机客户端

快速回复
限80 字节
温馨提示:所有技术区严禁灌水,“沙发”“顶”字样;禁止广告贴;以免被删除
 
上一个 下一个