1-4 实验3 串口通信

串口通信

1、实验内容:PC端串口调试助手向板子发送数据,板子接受到数据后,再把数据发送回给PC端串口调试助手

2、串口发送接受数据的基本步骤:初始化串口(设置波特率、中断等)、向缓冲区发送数据或者从接受缓冲区读取数据。

       然而,上面的步骤都是以前不带操作系统单片机的步骤,而在OSAL中已经实现了串口的读取函数和写入函数。可以作为API一样使用。

       与串口相关的三个API函数:

      uint8    HalUARTOpen(uint8  port,halUARTCfg_t *    config);

      uint16  HalUARTRead(uint8 port,uint8 *buf,uint16 len);

      uint16  HalUARTWrite(uint8 port,uint8* buf,uint16  len);

3、在实验2基础上改动以实现串口的收发

//Coordinator.c
#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"
#include 

#include "Coordinator.h"

#include "DebugTrace.h"

#if !defined(WIN32) //????
#include "OnBoard.h"
#endif

#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_key.h"
#include "hal_uart.h"

const cId_t GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS]=
{
  GENERICAPP_CLUSTERID 
};

//简单设备描述符(描述一个ZigBee设备节点)
const SimpleDescriptionFormat_t GenericApp_SimpleDesc=
{
  GENERICAPP_ENDPOINT,
  GENERICAPP_PROFID,
  GENERICAPP_DEVICEID,
  GENERICAPP_DEVICE_VERSION,
  GENERICAPP_FLAGS,
  GENERICAPP_MAX_CLUSTERS,
 
 (cId_t*)GenericApp_ClusterList, //?????
  0,
  (cId_t *)NULL
};

endPointDesc_t GenericApp_epDesc;//节点描述符
byte GenericApp_TaskID;//任务优先级
byte GenericApp_TransID;//数据发送序列号。


unsigned char uartbuf[128];//串口接收发送数据缓冲单元



void GenericApp_MessageMSGCB(afIncomingMSGPacket_t *pckt);//消息处理函数
void GenericApp_SendTheMessage(void);//数据发送函数

static void rxCB(uint8 port,uint8 envent);//???????????

void GenericApp_Init(byte task_id)//任务初始化函数
{
  GenericApp_TaskID     =task_id;   //初始化任务优先级(任务优先级有协议栈的操作系统OSAL分配)
  GenericApp_TransID    =0;         //发送数据包的序号初始化为0
  
  //对节点描述符进行初始化
  GenericApp_epDesc.endPoint    =GENERICAPP_ENDPOINT;
  GenericApp_epDesc.task_id     =&GenericApp_TaskID;
  GenericApp_epDesc.simpleDesc   =(SimpleDescriptionFormat_t*)&GenericApp_SimpleDesc;
  GenericApp_epDesc.latencyReq  =noLatencyReqs;
  
  afRegister(&GenericApp_epDesc);//afRegister()对节点的描述符进行注册。注册后,才能使用OSAL提供的系统服务。
  
  
  halUARTCfg_t uartConfig;//该结构体变量是实现 串口的配置
  //串口的初始化
  uartConfig.configured   =TRUE;
  uartConfig.baudRate     =HAL_UART_BR_115200;//波特率
  uartConfig.flowControl  =FALSE;             //流控制
  uartConfig.callBackFunc =rxCB;             //填的是回调函数 ,数的指针(即函数的地址)作为参数传递给另一个函数,
  //其实callBackFunc是一个函数指针,它的定义为halUARTCBack_t callBackFunc;
  //而halUARTCBack_t的定义为 typed void (*halUARTCBack_t)(uint8 port,uint8 envent) 定义的是一个函数指针
  
  HalUARTOpen(0,&uartConfig);                 //串口是否打开
}

//下面这个是回调函数,回电函数就是一个通过函数指针(函数地址)调用的函数,如果把函数的指针(即函数的地址)作为参数传递给另一
//个函数,当通过该指正调用它锁指向的函数时,称为函数的回调。

//回调函数不是有该函数的实现方直接调用的,而是在特定的事件或条件时,由另一方调用的额,用于对该事件或条件进行响应。
//回调函数机制提供了系统对异步事件的处理能力。
static void rxCB(uint8 port,uint8 envent)
{
       HalLedBlink(HAL_LED_2,0,50,500);    //LED1 闪烁
     HalUARTRead(0,uartbuf,10);   //从串口读取数据放在uartbuf缓冲区中
 if(osal_memcmp(uartbuf,"abcdefghij",10))//判断接受到的数据是否是www.wlwmaker.com,如果是,函数返回TURE
 {
   
       HalLedBlink(HAL_LED_1,0,50,500);    //LED2 闪烁
       HalUARTWrite(0,uartbuf,10); //将接收到的数字输出到串口
 }
}


//消息处理函数    
UINT16 GenericApp_ProcessEvent(byte task_id,UINT16 events)
{
 /* afIncomingMSGPacket_t* MSGpkt;//MSGpkt用于指向接收消息结构体的指针
  if(events&SYS_EVENT_MSG)
  {
     MSGpkt=(afIncomingMSGPacket_t*)osal_msg_receive(GenericApp_TaskID);//osal_msg_receive()从消息队列上接收消息
   while(MSGpkt)
   {
     switch(MSGpkt->hdr.event)
    {
    case AF_INCOMING_MSG_CMD:          //接受到新数据的消息的ID是AF_INCOMING_MSG_CMD,这个宏是在协议栈中定义好的值为0x1A
                                       //接受到的是无线数据包
      GenericApp_MessageMSGCB(MSGpkt);//功能是完成对接受数据的处理
      break;
    default:
      break;
    }
  
    osal_msg_deallocate((uint8 *)MSGpkt);//接收到的消息处理完后,释放消息所占的存储空间
    
     MSGpkt=(afIncomingMSGPacket_t*)osal_msg_receive(GenericApp_TaskID);
    //处理完一个消息后,再从消息队列里接受消息,然后对其进行相应处理,直到所有消息处理完
   }
   return (events ^ SYS_EVENT_MSG);
  }*/
  //  return 0;
}


void GenericApp_MessageMSGCB(afIncomingMSGPacket_t *pkt)
{
 unsigned char buffer[4]= "    ";
 
 switch(pkt->clusterId)
 {
 case GENERICAPP_CLUSTERID:
   osal_memcpy(buffer,pkt->cmd.Data,3);
   
   if((buffer[0]=='L')&&(buffer[1]=='E')&&(buffer[2]=='D'))
   {
      HalLedBlink(HAL_LED_1,0,50,500);    //LED2 闪烁
       HalLedBlink(HAL_LED_2,0,50,500);    //LED2 闪烁
      HalLedBlink(HAL_LED_3,0,50,500);    //LED2 闪烁
      HalLedBlink(HAL_LED_4,0,50,500);    //LED2 闪烁
   }
   else
   {
     HalLedSet(HAL_LED_2,HAL_LED_MODE_ON);//其他内容 LED2 亮    这几个函数在哪里定义了呢????
     
   }
   
   break;
 }
}
4、编译、下载、仿真调试

    编译过程中别忘了设置下面一步(这种叫做条件编译,用来控制不同的模块是否参与编译,以达到最大程度地节省存储资源资源)

1-4 实验3 串口通信_第1张图片

       PC端调试助手设置如下:

1-4 实验3 串口通信_第2张图片


结果:(发送的abcdefghij,接受到的是abcdefghij的ASCII码值(十六进制的):61 62 63 64 65 66 67 68 69 6A )

1-4 实验3 串口通信_第3张图片

仿真调试真是个调试,第一次在一个板子上搞了一个下午都没整出来,换了块板子一下子就搞好了,估计那块板的硬件有点问题。当自己重新下次进行调试要不行啦。当然,要学会IAR的Debug仿真调试,单步、查看变量的等方式去测试问题出在哪了,这个过程得都知识点的熟悉且斗志斗勇。重要的是耐心。得好好提高。

5、代码分析:

      重要的代码莫非刚才提到的三个API函数

      uint8    HalUARTOpen(uint8  port,halUARTCfg_t *    config);

      uint16  HalUARTRead(uint8 port,uint8 *buf,uint16 len);

      uint16  HalUARTWrite(uint8 port,uint8* buf,uint16  len);

     还有一个重要的数据结构halUARTCfg_t  ,它用于定义了一个halUARTCfg_t uartConfig;变量,该结构体变量是用于实现 串口的配置

     //串口的初始化
    uartConfig.configured   =TRUE;
    uartConfig.baudRate     =HAL_UART_BR_115200;//波特率
    uartConfig.flowControl  =FALSE;             //流控制
    uartConfig.callBackFunc =rxCB;              //这是一个回调函数。(函数体见下面)


   static void rxCB(uint8 port,uint8 envent)
{
       HalLedBlink(HAL_LED_2,0,50,500);    //LED1 闪烁
     
     HalUARTRead(0,uartbuf,3);   //从串口读取数据放在uartbuf缓冲区中
 if(osal_memcmp(uartbuf,"abc",3))//判断接受到的数据是否是abc,如果是,函数返回TURE
 {
       HalLedBlink(HAL_LED_1,0,50,500);    //LED2 闪烁
       HalUARTWrite(0,uartbuf,3); //将接收到的数字输出到串口
 }
}

        回调函数,回电函数就是一个通过函数指针(函数地址)调用的函数,如果把函数的指针(即函数的地址)作为参数传递给另一个函数,当通过该指正调用它锁指向的函数时,称为函数的回调。

        回调函数不是有该函数的实现方直接调用的,而是在特定的事件或条件时,由另一方调用的额,用于对该事件或条件进行响应。回调函数机制提供了系统对异步事件的处理能力。


6、串口工作原理

      串口的初始化:用到了HalUARTOpen( )

  Filename:       _hal_uart.c
/******************************************************************************
 * @fn      HalUARTOpen
 *
 * @brief   Open a port according tp the configuration specified by parameter.
 *
 * @param   port   - UART port
 *          config - contains configuration information
 *
 * @return  Status of the function call
 *****************************************************************************/
uint8 HalUARTOpen(uint8 port, halUARTCfg_t *config)
{
  (void)port;
  (void)config;

#if (HAL_UART_DMA == 1)
  if (port == HAL_UART_PORT_0)  HalUARTOpenDMA(config);//本实验只调用个这个函数,因此只要看这个函数即可,见下面
#endif
#if (HAL_UART_DMA == 2)
  if (port == HAL_UART_PORT_1)  HalUARTOpenDMA(config);
#endif
#if (HAL_UART_ISR == 1)
  if (port == HAL_UART_PORT_0)  HalUARTOpenISR(config);
#endif
#if (HAL_UART_ISR == 2)
  if (port == HAL_UART_PORT_1)  HalUARTOpenISR(config);
#endif
#if (HAL_UART_USB)
  HalUARTOpenUSB(config);
#endif
  
  return HAL_UART_SUCCESS;
}

HalUARTOpenDMA(config);

  Filename:       _hal_uart_dma.c
/******************************************************************************
 * @fn      HalUARTOpenDMA
 *
 * @brief   Open a port according tp the configuration specified by parameter.
 *
 * @param   config - contains configuration information
 *
 * @return  none
 *****************************************************************************/
static void HalUARTOpenDMA(halUARTCfg_t *config)//这个函数是对串口的波特率(关注UxBAUD和UxGCR两个寄存器的值)进行初始化并对DMA接收缓冲区进行初始化。
{
  dmaCfg.uartCB = config->callBackFunc;
  // Only supporting subset of baudrate for code size - other is possible.
  HAL_UART_ASSERT((config->baudRate == HAL_UART_BR_9600) ||
                  (config->baudRate == HAL_UART_BR_19200) ||
                  (config->baudRate == HAL_UART_BR_38400) ||
                  (config->baudRate == HAL_UART_BR_57600) ||
                  (config->baudRate == HAL_UART_BR_115200));
  
  if (config->baudRate == HAL_UART_BR_57600 ||
      config->baudRate == HAL_UART_BR_115200)           //UxBAUD寄存器的值的设置

  {
    UxBAUD = 216;
  }
  else
  {
    UxBAUD = 59;
  }
  
  switch (config->baudRate) //UxGCR寄存器的值的设置
  {
    case HAL_UART_BR_9600:
      UxGCR = 8;
      dmaCfg.txTick = 35; // (32768Hz / (9600bps / 10 bits))
                          // 10 bits include start and stop bits.
      break;
    case HAL_UART_BR_19200:
      UxGCR = 9;
      dmaCfg.txTick = 18;
      break;
    case HAL_UART_BR_38400:
      UxGCR = 10;
      dmaCfg.txTick = 9;
      break;
    case HAL_UART_BR_57600:
      UxGCR = 10;
      dmaCfg.txTick = 6;
      break;
    default:
      // HAL_UART_BR_115200
      UxGCR = 11;
      dmaCfg.txTick = 3;
      break;
  }

  // 8 bits/char; no parity; 1 stop bit; stop bit hi.
  if (config->flowControl)
  {
    UxUCR = UCR_FLOW | UCR_STOP;
    PxSEL |= HAL_UART_Px_CTS;
    // DMA Rx is always on (self-resetting). So flow must be controlled by the S/W polling the Rx
    // buffer level. Start by allowing flow.
    PxOUT &= ~HAL_UART_Px_RTS;
    PxDIR |=  HAL_UART_Px_RTS;
  }
  else
  {
    UxUCR = UCR_STOP;
  }

  //DMA 的初始化
  dmaCfg.rxBuf[0] = *(volatile uint8 *)DMA_UDBUF;  // Clear the DMA Rx trigger.
  HAL_DMA_CLEAR_IRQ(HAL_DMA_CH_RX);
  HAL_DMA_ARM_CH(HAL_DMA_CH_RX);
  osal_memset(dmaCfg.rxBuf, (DMA_PAD ^ 0xFF), HAL_UART_DMA_RX_MAX*2);

  UxCSR |= CSR_RE;
  
  // Initialize that TX DMA is not pending
  dmaCfg.txDMAPending = FALSE;
  dmaCfg.txShdwValid = FALSE;
}
在ZigBee协议栈中,串口是和DMA结合起来使用的,这样能降低CPU的负担。

调用  HalUARTOpen(0,&uartConfig); 这个函数,重要的参数是uartConfig,他是halUARTCfg_t型的参数

halUARTCfg结构体如下:

Filename:       hal_uart.h
typedef struct
{
  bool                configured;
  uint8               baudRate;
  bool                flowControl;
  uint16              flowControlThreshold;
  uint8               idleTimeout;
  halUARTBufControl_t rx;
  halUARTBufControl_t tx;
  bool                intEnable;
  uint32              rxChRvdTime;
  halUARTCBack_t      callBackFunc;//回调函数
}halUARTCfg_t;

ZigBee协议栈中,开辟了DMA发送缓冲区和接收缓冲区,向板子发送数据时,数据首先存放在DMA接收缓冲区,然后是调用HalUARTRead()函数读取,实际读取的是DMA的缓冲区的内容。

  Filename:       _hal_uart.c
/*****************************************************************************
 * @fn      HalUARTRead
 *
 * @brief   Read a buffer from the UART
 *
 * @param   port - USART module designation
 *          buf  - valid data buffer at least 'len' bytes in size
 *          len  - max length number of bytes to copy to 'buf'
 *
 * @return  length of buffer that was read
 *****************************************************************************/
uint16 HalUARTRead(uint8 port, uint8 *buf, uint16 len)
{
  (void)port;
  (void)buf;
  (void)len;

#if (HAL_UART_DMA == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTReadDMA(buf, len);//本实验仅需要关注这个函数  见下面那个函数
#endif
#if (HAL_UART_DMA == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTReadDMA(buf, len);
#endif
#if (HAL_UART_ISR == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTReadISR(buf, len);
#endif
#if (HAL_UART_ISR == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTReadISR(buf, len);
#endif

#if HAL_UART_USB
  return HalUARTRx(buf, len);
#else
  return 0;
#endif
}

 Filename:       _hal_uart_dma.c
/*****************************************************************************
 * @fn      HalUARTReadDMA
 *
 * @brief   Read a buffer from the UART
 *
 * @param   buf  - valid data buffer at least 'len' bytes in size
 *          len  - max length number of bytes to copy to 'buf'
 *
 * @return  length of buffer that was read
 *****************************************************************************/
static uint16 HalUARTReadDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;

  for (cnt = 0; cnt < len; cnt++)
  {
    if (!HAL_UART_DMA_NEW_RX_BYTE(dmaCfg.rxHead))
    {
      break;
    }
    *buf++ = HAL_UART_DMA_GET_RX_BYTE(dmaCfg.rxHead);
    HAL_UART_DMA_CLR_RX_BYTE(dmaCfg.rxHead);
#if HAL_UART_DMA_RX_MAX == 256
    (dmaCfg.rxHead)++;
#else
    if (++(dmaCfg.rxHead) >= HAL_UART_DMA_RX_MAX)
    {
      dmaCfg.rxHead = 0;
    }
#endif
  }
  PxOUT &= ~HAL_UART_Px_RTS;  // Re-enable the flow on any read.

  return cnt;
}

当用户调用的HalUARTWrite( )函数发送数据是,实际上是将数据写入DMA发送缓冲区,然后DMA自动将发送缓冲区中的数据通过串口发送给PC。

//  Filename:       _hal_uart.c
/******************************************************************************
 * @fn      HalUARTWrite
 *
 * @brief   Write a buffer to the UART.
 *
 * @param   port - UART port
 *          buf  - pointer to the buffer that will be written, not freed
 *          len  - length of
 *
 * @return  length of the buffer that was sent
 *****************************************************************************/
uint16 HalUARTWrite(uint8 port, uint8 *buf, uint16 len)
{
  (void)port;
  (void)buf;
  (void)len;

#if (HAL_UART_DMA == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTWriteDMA(buf, len);//本实验仅需要关注这个函数  见下面那个函数
#endif
#if (HAL_UART_DMA == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTWriteDMA(buf, len);
#endif
#if (HAL_UART_ISR == 1)
  if (port == HAL_UART_PORT_0)  return HalUARTWriteISR(buf, len);
#endif
#if (HAL_UART_ISR == 2)
  if (port == HAL_UART_PORT_1)  return HalUARTWriteISR(buf, len);
#endif

#if HAL_UART_USB
  HalUARTTx(buf, len);
  return len;
#else
  return 0;
#endif
}

//  Filename:       _hal_uart_dma.c
/******************************************************************************
 * @fn      HalUARTWriteDMA
 *
 * @brief   Write a buffer to the UART.
 *
 * @param   buf - pointer to the buffer that will be written, not freed
 *          len - length of
 *
 * @return  length of the buffer that was sent
 *****************************************************************************/
static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len)
{
  uint16 cnt;
  halIntState_t his;
  uint8 txSel;
  txIdx_t txIdx;

  // Enforce all or none.
  if ((len + dmaCfg.txIdx[dmaCfg.txSel]) > HAL_UART_DMA_TX_MAX)
  {
    return 0;
  }

  HAL_ENTER_CRITICAL_SECTION(his);
  txSel = dmaCfg.txSel;
  txIdx = dmaCfg.txIdx[txSel];
  HAL_EXIT_CRITICAL_SECTION(his);

  for (cnt = 0; cnt < len; cnt++)
  {
    dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
  }

  HAL_ENTER_CRITICAL_SECTION(his);
  if (txSel != dmaCfg.txSel)
  {
    HAL_EXIT_CRITICAL_SECTION(his);
    txSel = dmaCfg.txSel;
    txIdx = dmaCfg.txIdx[txSel];

    for (cnt = 0; cnt < len; cnt++)
    {
      dmaCfg.txBuf[txSel][txIdx++] = buf[cnt];
    }
    HAL_ENTER_CRITICAL_SECTION(his);
  }

  dmaCfg.txIdx[txSel] = txIdx;

  if (dmaCfg.txIdx[(txSel ^ 1)] == 0)
  {
    // TX DMA is expected to be fired
    dmaCfg.txDMAPending = TRUE;
  }
  HAL_EXIT_CRITICAL_SECTION(his);
  return cnt;
}


得学会调试程序才行。还有我不清的是什么时候什么原因才能触发去调用下面的回调函数,然后进行读写串口???

static void rxCB(uint8 port,uint8 envent)
{    
     HalUARTRead(0,uartbuf,10);   //从串口读取数据放在uartbuf缓冲区中
 if(osal_memcmp(uartbuf,"abcdefghij",10))//判断接受到的数据是否是www.wlwmaker.com,如果是,函数返回TURE
 {   
       HalLedBlink(HAL_LED_1,0,50,500);    //LED2 闪烁
       HalUARTWrite(0,uartbuf,10); //将接收到的数字输出到串口
 }
}

本文参考自:《ZigBee无线传感器网络设计与实现》      王小强等人编著化学工业出版社


你可能感兴趣的:(ZigBee之旅)