ZigBee模块通信实现(电路介绍与程序分析)

产品名:ZigBee模块

型号:ZICM2410 PO-2

厂商:广州周立功公司代理(美国CEL公司的MeshConnect模块)

参数: 

*  103db链路预算;

*  接收灵敏度:-97dbm@ 1.5V

*  发送功率:+6dbm@1.5V

*  3000英尺无障碍传输距离

*  最低睡眠电流:<1μA

*  工作电压:2.1~3.3V

*  接收电流:35mA

*  发送电流:44mA

*  速率:ZigBee (250 kbps)、Turbo(500 kbps)、Premium (1 Mbps)

*  接口类型:SPI(主从)、UART(2路)、I2S/PCM 

支持语音传送:

语音编解码器支持u律、a律、ADPCM

16条射频通道

传送范围:3000英尺  = 914.4米

支持AES128位加密

ZICM2410 支持的数据通信接口:

UART0\UART1(1M)、I2S、SPI(2M)、GPIO、AD(4路)、IIS 

描述:Zigbee网络基于IEEE 802.15.4国际标准、上层协议为ZigBee协议栈,具有低功耗,低速率,高可靠性,网络路由功能强大,自恢复及冗余性能优异等特点,广泛应用低数据率监控的各个领域。

 ZigBee 物理连接:

ZigBee 模块是通过串口的形式与微处理器通讯,在LPC11C14 开发板这里是通过 SC16IS752IPW 芯片 转成 SPI 协议与 微处理器通讯。

ZigBee 模块(ZICM2410 PO-2) <------串口-----> SC16IS752IPW 芯片 <------SPI-----> LC11C14(ARM M0内核)

从原理图可以看出,ZigBee模块的RXD\TXD接在了SC16IS752IPW芯片的RXB\TXB上,而该芯片的CS\SI\SO\SCLK接在LPC11C14的PIO2_0\PIO2_1\PIO2_2\PIO2_3\PIO3_3上。

 
说明LPC11C14主控芯片是通过SPI间接与ZigBee模块通讯,所以我们只需要关注SC16IS752IPW芯片即可,往该芯片发送数据,ZigBee自然也能收到数据。
 
数据流向:应用程序 <---读写寄存器---> M0的SPI控制器  <---SPI---> SC16IS752IPW  <---串口---> ZigBee模块
 
程序流程:
 
 1、初始化LPC11C14的SSP1 的GPIO引脚,即 PIO2_0\PIO2_1\PIO2_2\PIO2_3
/* 初始化响应PIO引脚 ssp.c  */ 
 /* arg1:SSPI0或SSPI1  */
void SPI_IOConfig(uint8_t portNum)
{
  if(portNum == 0)
  {
       /* 此处为通道0的代码 未贴出 */
  }
  else/* port number 1 */
  {  // 主要设置 SSP1 使之能与 SC16IS752IPW 通信 间接与 ZigBee 通信
LPC_SYSCON->PRESETCTRL    |= (0x1<<2);  // bit2=1 SSP1复位取消 bit2=0 复位SSP1
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18);// 使能SSP1的时钟
LPC_SYSCON->SSP1CLKDIV     = 0x02;/* Divided by 2 设置 SSP1 时钟分频 48Mhz/2=24Mhz*/
LPC_IOCON->PIO2_2         &= ~0x07;/*  SSP I/O config */
LPC_IOCON->PIO2_2         |= 0x02;/* SSP MISO  将PIO2_2设置为 SSP MISO 模式*/
LPC_IOCON->PIO2_3         &= ~0x07;
LPC_IOCON->PIO2_3         |= 0x02;/* SSP MOSI  将PIO2_3设置为 SSP MOSI 模式*/
LPC_IOCON->PIO2_1         &= ~0x07;
LPC_IOCON->PIO2_1         |= 0x02;/* SSP CLK  将PIO2_1设置为 SSP 时钟 */
 
/* Enable AHB clock to the GPIO domain. */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);  // 使能GPIO时钟 使之产生时钟
 
LPC_IOCON->PIO2_0 &= ~0x07;/* SSP SSEL is a GPIO pin 设置PIO2_0 作为普通IO管脚功能 */
/* port2, bit 0 is set to GPIO output and high */
GPIOSetDir( PORT2, 0, 1 );   // 设置为输出
GPIOSetValue( PORT2, 0, 1 );   // 输出高电平 暂时不使能 该器件 是片选引脚
  }
}
 
2、初始化 SSP1 寄存器 ssp.c 
             /* arg1:SSPI0或SSPI1       arg2:传送位数          arg3:时钟分配 */
        void SPI_Init(uint8_t portNum, uint8_t Bit, uint8_t EvenDiv)
        {
              uint8_ti, Dummy=Dummy;
              if(portNum == 0)
              {
                     /* 此处为通道0的代码 未贴出 */
              }
              else
             {
LPC_SSP1->CR0 = 0x0700 | (Bit-1); // (Bit-1)=0b0111 8位传输 串行时钟速率、总线类型、数据长度
LPC_SSP1->CPSR = EvenDiv;  // 时钟预分频寄存器 设置为2分频  -> 48Mhz/2=24Mhz
for ( i = 0; i < SSP_FIFOSIZE; i++ )
{
  Dummy = LPC_SSP1->DR;/* clear the RxFIFO 数据寄存器,读空则接受FIFO 写满则发送 FIFO */
}
NVIC_EnableIRQ( SSP1_IRQn );// 使能中断
LPC_SSP1->CR1 = SSPCR1_SSE; // SSP1以正常模式与串行总线上的其它设备相互通信。
LPC_SSP1->IMSC = SSPIMSC_RORIM | SSPIMSC_RTIM; // 中断触发条件:接受上溢 接受超时触发中断
              }
        }
 
3、SSP1读写 SC16IS752IPW 芯片
3.0:SC16IS752IPW  时序图
写寄存器时序图: 
 
                 
读寄存器时序图
 
                   
数据格式:
 
                  
3.1:SSP1 读写寄存器
                /* 操作*/
                 /* arg1:SSPI0或SSPI1       arg2:数据 */
uint16_t SPI_PutGet(uint8_t portNum, uint16_t SendData)
{
  if(portNum == 0)
  {
 /* 此处为通道0的代码 未贴出 */
  }
  else
  {
/* Move on only if NOT busy and TX FIFO not full. */
// SSP1控制器不忙 发送FIFO未满、发送FIFO不为空 就跳出循环
while((LPC_SSP1->SR & (SSPSR_TNF|SSPSR_BSY)) != SSPSR_TNF);
LPC_SSP1->DR = SendData;//写入数据
/* Wait until the Busy bit is cleared. */
while(LPC_SSP1->SR & SSPSR_BSY); // 等待总线不忙
 
/* Wait until the Busy bit is cleared */
// 等到接受FIFO不为空后跳出循环
while((LPC_SSP1->SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE);
return LPC_SSP1->DR;// 将数据读出
  }
}
 
3.2:写寄存器时序流程(主要涉及 SCLK、MIOS)
1、片选选中 SC16IS752IPW
2、往SPI数据寄存器写数据
2.1 MIOS 写 SC16IS752IPW THR 寄存器地址
    写位(1bit) + SC16IS752IPW THR 寄存器地址(4bit) + 通道选择(2bit) + X
2.2 写 8位数据
3、取消片选
    /* 往指定的 SC16IS752IPW 寄存器中写数据 */
       /* arg1:SSPI0或SSPI1       arg2:SC16IS752IPW 寄存器     arg3:写入的数据*/
void SPI752_RegWrite(uint8_t Channel, uint8_t Reg, uint8_t Data)
{
  SPI_UART_CS(0);// 片选选中 SC16IS752IPW 
  SPI_PutGet(1, SPI752_WRITE | (Reg<<3) | (Channel<<1));// 写寄存器
  SPI_PutGet(1, Data);// 写数据
  SPI_UART_CS(1);// 取消片选
}
 
3.3:读寄存器时序(主要涉及 SCLK、MIOS、MOIS)
1、片选选中 SC16IS752IPW
2、往SPI数据寄存器写数据
2.1 MIOS 写 SC16IS752IPW RHR 寄存器地址
    读位(1bit) + SC16IS752IPW  RHR 寄存器地址(4bit) + 通道选择(2bit) + X
2.2 MOSI 读出 8位数据
3、取消片选
        /* 读数据 */
        /* arg1:SSPI0或SSPI1       arg2:SC16IS752IPW 寄存器 */
uint8_t SPI752_RegRead(uint8_t Channel, uint8_t Reg)
{
  uint8_trd;
 
  SPI_UART_CS(0);// 片选选中 SC16IS752IPW
  SPI_PutGet(1, SPI752_READ | (Reg<<3) | (Channel<<1)); // 写寄存器地址
  rd = SPI_PutGet(1, 0);// 取出数据
  SPI_UART_CS(1);// 取消片选
  return rd;
}
 
4、通过SSP1 初始化  SC16IS752IPW
 
完成SSP1初始化后,就能通过SSP1与 SC16IS752IPW 通讯,,从而初始化该芯片
/*******************************************************************************
* Function Name  : SPI752_Init
* Description    : Set channel 0 & 1 baud rate. The range is 300-230400 Baud.
*                  The crystal input frequency is 14745600Hz.
*   The default value of prescaler after reset is divide-by-1.
*                  The format is: 8N1
* Input          : - Channel : 0 & 1.
*                  - Baud : 300-230400 Baud.
* Output         : None
* Return         : None
* file           :ssp.c 
*******************************************************************************/
/* arg1:SSPI0或SSPI1       arg2:波特率 */
void SPI752_Init(uint8_t Channel, uint32_t Baud)
{
  uint16_trd;
 
  // Disable sleep
  // 设置 数据通信的格式 1 0 1 1 1 1 11   奇葩的设置。。。
  // 除数锁存使能 没有TX间隔条件 奇偶位强制为0 偶数格式 奇偶位 1个停止位 8位
  SPI752_RegWrite(Channel, SPI752_LCR_RW, 0xBF);
 
  // 使能增强型功能
  rd = SPI752_RegRead(Channel, SPI752_EFR_RW);
  SPI752_RegWrite(Channel, SPI752_EFR_RW, rd | 0x10);
 
  // 设置通信格式为 8位数据传送   奇葩的设置
  SPI752_RegWrite(Channel, SPI752_LCR_RW, 0x03);
 
  // 使能RHR、THR中断、使能接收器线状态中断、使能modem状态寄存器中断 
  rd = SPI752_RegRead(Channel, SPI752_IER_RW);
  SPI752_RegWrite(Channel, SPI752_IER_RW, rd & (~0x10));
 
  // DTR、RTS输出有效、TCR\TLR使能、使能局部环回模式、使能Xon Any功能  1分频  奇葩why?
  rd = SPI752_RegRead(Channel, SPI752_MCR_RW);
  SPI752_RegWrite(Channel, SPI752_MCR_RW, rd & (~0x80));
 
  // Set baud rate & 8N1 format
  // 设置为 8位 除数所存使能估计为设置波特率准备
  SPI752_RegWrite(Channel, SPI752_LCR_RW, 0x83);
 
  // 设置波特率
  rd = (14745600/16) / Baud;
  SPI752_RegWrite(Channel, SPI752_DLL_RW, rd); // 写入除数最低字节
  SPI752_RegWrite(Channel, SPI752_DLH_RW, rd>>8);  // 写入除数最高字节
  // 8位  又来设置通讯格式。。 奇葩。。。
  SPI752_RegWrite(Channel, SPI752_LCR_RW, 0x03);
 
  SPI752_RegRead(Channel, SPI752_RHR_R);
 
  // use port3_3 as input event, ZigBee interrupt. 
  // 设置为输入模式
  GPIOSetDir(PORT3, 3, 0);
  // port3_3 interrupt. edge, single trigger, falling edges.
  // 设置为中断功能
  GPIOSetInterrupt(PORT3, 3, 0, 0, 0);
  // 使能该中断 当Zig有数据接受时,该中断被触发
  GPIOIntEnable(PORT3, 3);
 
  // Set SPI752 RXDx interrupt Enable.
  //rd = SPI752_RegRead(1, SPI752_IER_RW);  
  //SPI752_RegWrite(Channel, SPI752_IER_RW, rd | 0x01);
  // 使能RHR中断又设置了一次 RHR中断  还能再奇葩点吗?
  SPI752_RegWrite(Channel, SPI752_IER_RW, 0x01);
  // 读取中断Why? 
  SPI752_RegRead(Channel, SPI752_RHR_R);
}
 
5、发送1字节数据 (实现了发送1字节就可以发送多字节数据了)
            /* 操作M0的SPI控制器 */
        /* arg1:SSPI0或SSPI1      arg2:数据 */
uint16_t SPI_PutGet(uint8_t portNum, uint16_t SendData)
{
  if(portNum == 0)
  {
      /* 此处为通道0的代码 未贴出 */
  }
 
  else
  {
    /* Move on only if NOT busy and TX FIFO not full. */
    // SSP1控制器不忙 发送FIFO未满、发送FIFO不为空 就跳出循环
    while((LPC_SSP1->SR & (SSPSR_TNF|SSPSR_BSY)) != SSPSR_TNF);
    LPC_SSP1->DR = SendData;//写入数据
    /* Wait until the Busy bit is cleared. */
    while(LPC_SSP1->SR & SSPSR_BSY); // 等待总线不忙
 
    /* Wait until the Busy bit is cleared */
// 等到接受FIFO不为空后跳出循环
    while((LPC_SSP1->SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE);
    return LPC_SSP1->DR;
  }
}
 
/*******************************************************************************
* Function Name  : SPI752_PutChar
* Description    : Use SPI572 channel 0 & 1 send a byte.
* Input          : - Channel : 0 & 1.
*                  - Ch : 8bit data.
* Output         : None
* Return         : None
* file           :spi_uart.c 
*******************************************************************************/
/* arg1:SSPI0或SSPI1       arg2:8位数据 */
void SPI752_PutChar(uint8_t Channel, uint8_t Ch)
{
  // 等待发送保存寄存器为空
  while(!(SPI752_RegRead(Channel, SPI752_LSR_R)&0x20));
  // 往发送保存寄存器写入数据
  SPI752_RegWrite(Channel, SPI752_THR_W, Ch);
}
 
6、接受数据 需要借助中断
6.1:中断读取、保存数据
      /*  当PIO3 组引脚产生中断时 此中断函数被执行 gpio.c */
void PIOINT3_IRQHandler(void)
{
    /*  此处为其他引脚需要的代码  未贴出*/
 
  // 判断是不是 PORT3_3 引脚产生的
  if(GPIOIntStatus(PORT3, 3))
  {
        ZigBee_IRQ_Process();            // 是则说明ZigBee触发的
GPIOIntClear(PORT3, 3);        // 清中断
  }
  return;
}
           /* ZigBee 中断服务程序 读取数据  spi_uart.c  */
void ZigBee_IRQ_Process(void)
{
  uint8_trd;
 /*  此处为0通道的读取数据的代码  未贴出*/
  rd = SPI752_RegRead(1, SPI752_IIR_R);
  rd &= 0x3f;  
  if(rd == 0x04)   // 判断是否为RHR中断  是则读取数据
  {
        // 读取RHR寄存器值存入数组缓冲区
        SPI752_rbuf_1[SPI752_rbuf_1_ip] = SPI752_RegRead(1, SPI752_RHR_R);
        SPI752_rbuf_1_ip ++;
if(SPI752_rbuf_1_ip >= SPI752_RBUF_1_NUMB)
  SPI752_rbuf_1_ip = 0;
  }
  else
  {
        rd = SPI752_RegRead(1, SPI752_RHR_R);   // 从接受保存寄存器读空数据 估计是清空该寄存器
  }
}
6.2:应用程序读取数据
/* 当程序读取数据时,从缓冲区里取数据   spi_uart.c */
/* arg1:SSPI0或SSPI1       arg2:执行可写的1字节的空间的指针*/
uint8_t SPI752_GetChar(uint8_t Channel, uint8_t *Ch)
{
   /*  此处为0通道的读取数据的代码  未贴出*/
   // SPI752_rbuf_1_ip 是在中断里面记录的读取的字节数
    if(SPI752_rbuf_1_op != SPI752_rbuf_1_ip)
{
  *Ch = SPI752_rbuf_1[SPI752_rbuf_1_op];
  SPI752_rbuf_1_op ++;
  if(SPI752_rbuf_1_op >= SPI752_RBUF_1_NUMB)
    SPI752_rbuf_1_op = 0;
  return 1;
}
  return 0;
}
永不止步步 发表于12-12 11:35 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67417个|学分:363741个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号