分享

如何在Linux下给zigbee CC2530实现上位机

 新用户0175WbuX 2022-02-12

  网友提问如下:

  如何在Linux下给zigbee CC2530实现上位机

  粉丝提问

  如何在Linux下给zigbee CC2530实现上位机

  项目框架

  汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:

  下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命令配置对应的外设、读取对应的传感器的数据上传到上位机;主程序串口操作模块:通过串口下发命令或者读取下位机上传的数据信息;主程序网络通信模块:接收远程服务器下发的命令,并将下位机采集的数据上传到服务器。

  整体看来,这个相当于是一个小的项目了,内容难度都比较大,下面我们会分为几篇独立的文章来讲解。

  本篇只讨论如何给下位机编写一个简单的上位机。

  下位机:CC2530 OS:vmware + ubuntu

  在这里彭老师采用的是CC2530,读者也可以采用其他的板子,我们只需要该板子有串口,可以和PC通信,同时板子上有可设置的led灯、继电器以及可以采集数据的传感器即可。

  硬件连接图如下:

  如何在Linux下给zigbee CC2530实现上位机

  物理连接图

  该款CC2530已经集成了CH340芯片,usb线连接电脑,即可被识别。

  如果该串口被PC获取,名字为COMn【n为某整数】。

  如何在Linux下给zigbee CC2530实现上位机

  windows下串口

  首先需要vmware抓取串口【串口在同一时刻要么被windows抓取要么被vmware抓取】,按下图所示,点击连接即可:

  如何在Linux下给zigbee CC2530实现上位机

  虚拟机抓取串口

  但是往往ubuntu中没有ch340的驱动,经过实际测试,ubuntu14及之前的版本都没有这个驱动,ubuntu16以上的版本有这个驱动。

  如果没有ch340驱动可以用以下方法安装对应的驱动:

  1 make

  2 sudo make load

  3 ls /dev/ttyUSB0

  如何在Linux下给zigbee CC2530实现上位机

  ubuntu安装串口驱动

  按照上述步骤,会生成设备文件**/dev/ttyUSB0**。

  ls /dev/ttyUSB0 -l

  crw-rw---- 1 root dialout 188, 0 Jan 15 05:45 /dev/ttyUSB0

  c : 字符设备 rw-rw---- :文件操作权限

  188, 0 : 主次设备号

  3、4节提到的usb转串口驱动和linux下驱动源码后台【GH】回复 ch340 即可获得

  如何在Linux下给zigbee CC2530实现上位机

  驱动

  【注意】 如果是其他开发板,自行安装其他的串口驱动。

  上位机和下位机的通信往往都是通过串口,linux下往往生成字符设备ttyUSB0【有的是ttyS0】,操作串口设备就只需要操作该字符设备即可。

  下面我们设计上下位机的软件模块。

  设计上位机,首先需要设计上位机下发给下位机的指令格式,上位机按照该指令格式发送命令给下位机,下位需严格按照该指令格式进行解析指令。

  如何在Linux下给zigbee CC2530实现上位机

  信令格式

  含义如下:

  device:要操作的设备data :对应的设备及其额外的数据CRC :校验码# :信令终止符

  信令格式可以根据需要扩展或者精简。

  其中device定义如下【可以根据实际情况进行扩展】:

  #define DEV_ID_LED_ON 0X1

  #define DEV_ID_LED_OFF 0X2

  #define DEV_ID_DELAY 0X3

  #define DEV_ID_GAS 0X4

  【注意】 为便于理解,我们暂不考虑效率问题。

  下位机需要采集传感器的数据并通过串口上传,数据结构定义如下:

  struct data{

  unsigned char device;

  unsigned char crc;

  unsigned short data;

  };

  device 设备data 采集的数据crc 校验码

  现在就可以开始设计软件的各个功能模块了。

  如何在Linux下给zigbee CC2530实现上位机

  下位机流程图

  下位主要任务就是循环接收上位机通过串口下发的数据,然后解析该指令内容,操作对应的硬件。

  如何在Linux下给zigbee CC2530实现上位机

  上位机

  上位机主要任务是打印菜单,由用户针对菜单做出选择,然后按照指令格式封装命令,并通过串口将该命令下发给下位机。

  cc2530的操作原理,本文不讨论,如果是其他开发板,只需要修改串口操作函数。

  /****************************************************************************

  * 名 称: InitLed()

  * 功 能: 设置LED灯相应的IO口

  * 入口参数: 无

  * 出口参数: 无

  ****************************************************************************/

  void InitLed(void)

  {

  P1DIR |= 0x01; //P1.0定义为输出口

  LED1 = 0;

  }

  /****************************************************************

  * 名 称: InitUart()

  * 功 能: 串口初始化函数

  * 入口参数: 无

  * 出口参数: 无

  *****************************************************************/

  void InitUart(void)

  {

  PERCFG = 0x00; //外设控制寄存器 USART 0的IO位置:0为P0口位置1

  P0SEL = 0x0c; //P0_2,P0_3用作串口(外设功能)

  P2DIR &= ~0xC0; //P0优先作为UART0

  U0CSR |= 0x80; //设置为UART方式

  U0GCR |= 11;

  U0BAUD |= 216; //波特率设为115200

  UTX0IF = 0; //UART0 TX中断标志初始置位0

  U0CSR |= 0x40; //富贵论坛允许接收

  IEN0 |= 0x84; //开总中断允许接收中断

  }

  /**********************************************************************

  * 名 称: UartSendString()

  * 功 能: 串口发送函数

  * 入口参数: Data:发送缓冲区 len:发送长度

  * 出口参数: 无

  ***********************************************************************/

  void UartSendString(char *Data, int len)

  {

  uint i;

  for(i=0; i<len; i++)

  {

  U0DBUF = *Data++;

  while(UTX0IF == 0);

  UTX0IF = 0;

  }

  }

  /**********************************************************************

  * 名 称: UART0_ISR(void) 串口中断处理函数

  * 描 述: 当串口0产生接收中断,将收到的数据保存在RxBuf中

  **********************************************************************/

  #pragma vector = URX0_VECTOR

  __interrupt void UART0_ISR(void)

  {

  URX0IF = 0; // 清中断标志

  RxBuf = U0DBUF;

  }

  /****************************************************************

  * 名 称: myApp_ReadGasLevel()

  * 功 能: 烟雾传感器数据读取

  * 入口参数: 无

  * 出口参数: 无

  *****************************************************************/

  uint16 myApp_ReadGasLevel( void )

  {

  uint16 reading = 0;

  /* Enable channel */

  ADCCFG |= 0x80;

  /* writing to this register starts the extra conversion */

  ADCCON3 = 0x87;

  /* Wait for the conversion to be done */

  while (!(ADCCON1 & 0x80));

  /* Disable channel after done conversion */

  ADCCFG &= (0x80 ^ 0xFF);

  /* Read the result */

  reading = ADCH;

  reading |= (int16) (ADCH << 8);

  reading >>= 8;

  return (reading);

  }

  /****************************************************************

  * 名 称: led_opt()

  * 功 能: LED灯控制函数

  * 入口参数: RxData:接收到的指令 flage:led的操作,点亮或者关闭

  * 出口参数: 无

  *****************************************************************/

  void led_opt(char RxData[],unsigned char flage)

  {

  switch(RxData[1])

  {

  case 1:

  LED1 = (flage==DEV_ID_LED_ON)?ON:OFF;

  break;

  /* TBD for led2 led3*/

  default:

  break;

  }

  return;

  }

  /****************************************************************************

  * 主程序入口函数

  ****************************************************************************/

  void main(void)

  {

  CLKCONCMD &= ~0x40; //设置系统时钟源为32MHZ晶振

  while(CLKCONSTA & 0x40); //等待晶振稳定为32M

  CLKCONCMD &= ~0x47; //设置系统主时钟频率为32MHZ

  InitLed(); //设置LED灯相应的IO口

  InitUart(); //串口初始化函数

  UartState = UART0_RX; //串口0默认处于接收模式

  memset(RxData, 0, SIZE);

  while(1)

  {

  //接收状态

  if(UartState == UART0_RX)

  { //读取数据,遇到字符'#'或者缓冲区字符数量超过4就设置UartState为CONTROL_DEV状态

  if(RxBuf != 0)

  {

  //以'#'为结束符,一次最多接收4个字符

  if((RxBuf != '#')&&(count < 4))

  {

  RxData[count++] = RxBuf;

  }

  else

  {

  //判断数据合法性,防止溢出

  if(count >= 4)

  {

  //计数清0

  count = 0;

  //清空接收缓冲区

  memset(RxData, 0, SIZE);

  }

  else{

  //进入发送状态

  UartState = CONTROL_DEV;

  }

  }

  RxBuf = 0;

  }

  }

  //控制控制外设状态

  if(UartState == CONTROL_DEV)

  {

  //判断接收的数据合法性

  //RxData[]: | device | data |crc | # |

  //check_crc: crc = device ^ data

  //if(RxData[2] == (RxData[0]^RxData[1]))

  {

  switch(RxData[0])

  {

  case DEV_ID_LED_ON :

  led_opt(RxData,DEV_ID_LED_ON);

  break;

  case DEV_ID_LED_OFF:

  led_opt(RxData,DEV_ID_LED_OFF);

  break;

  case DEV_ID_DELAY:

  break;

  case DEV_ID_GAS:

  send_gas();

  break;

  default:

  break;

  }

  }

  UartState = UART0_RX;

  count = 0;

  //清空接收缓冲区

  memset(RxData, 0, SIZE);

  }

  }

  }

  结构体

  #define DEV_ID_LED_ON 0X1

  #define DEV_ID_LED_OFF 0X2

  #define DEV_ID_DELAY 0X3

  #define DEV_ID_GAS 0X4

  struct data{

  unsigned char device;

  unsigned char crc;

  unsigned short data;

  };

  函数

  void uart_init(void )

  {

  int nset1,nset2;

  serial_fd = open( "/dev/ttyUSB0", O_RDWR);

  if(serial_fd == -1)

  {

  printf("open() error

  ");

  exit(1);

  }

  nset1 = set_opt(serial_fd, 115200, 8, 'N', 1);

  if(nset2 == -1)

  {

  printf("set_opt() error

  ");

  exit(1);

  }

  }

  int Menu()

  {

  int option;

  system("clear");

  printf("

  ************************************************

  ");

  printf("

  ** ALARM SYSTERM **

  ");

  printf("

  ** 1----LED **

  ");

  printf("

  ** 2----GAS **

  ");

  printf("

  ** 0----EXIT **

  ");

  printf("

  ************************************************

  ");

  while(1)

  {

  printf("Please choose what you want: ");

  scanf("%d",&option);

  if(option<0||option>2)

  printf(" choose error!

  ");

  else

  break;

  }

  return option;

  }

  // RxData[]: | device | data |crc | # |

  void led()

  {

  int lednum = 0;

  int onoff;

  char cmd[4];

  //选择led灯

  while(1)

  {

  printf("input led number :[1 2]

  #");

  scanf("%d",&lednum);

  //check

  if(lednum<1 || lednum >2)

  {

  printf("invalid led number

  ");

  system("clear");

  continue;

  }else{

  break;

  }

  }

  printf("operation: 1 on , 0 off

  ");

  scanf("%d",&onoff);

  if(onoff == 1)

  {

  cmd[0] = DEV_ID_LED_ON;

  }else if(onoff == 0)

  {

  cmd[0] = DEV_ID_LED_OFF;

  }else{

  printf("invalid led number

  ");

  return;

  }

  cmd[1] = lednum;

  //fulfill crc area

  cmd[2] = cmd[0]^cmd[1];

  cmd[3] = '#';//表示结束符

  tcflush(serial_fd, TCIOFLUSH);

  int i = 0;

  for(i=0;i<4;i++)

  {

  printf("%d ",cmd[i]);

  }

  printf("

  ");

  write(serial_fd,&cmd,sizeof(cmd));

  sleep(1);

  }

  // RxData[]: | device | data |crc | # |

  void gas()

  {

  int len ;

  unsigned short GasLevel;

  struct data msg;

  char gas[4]={0};

  char cmd[4];

  cmd[0] = DEV_ID_GAS;

  cmd[3] = '#';//表示结束符

  write(serial_fd,&cmd,sizeof(cmd));

  sleep(1);

  len = read(serial_fd,&msg,sizeof(struct data));

  //转换读取的gas数据格式

  GasLevel = msg.data;

  gas[0] = GasLevel / 100 + '0';

  gas[1] = GasLevel / 10%10 + '0';

  gas[2] = GasLevel % 10 + '0';

  printf("%s

  ",gas);

  getchar();

  }

  void run()

  {

  int x;

  while(1)

  {

  x=Menu();

  switch(x)

  {

  case 1:

  led();

  break;

  case 2:

  gas();

  break;

  case 0:

  printf("

  exit!

  ");

  close(serial_fd);

  exit(0);

  default:

  fg=1;

  break;

  }

  if(fg)

  break;

  }

  }

  int main()

  {

  uart_init();

  run();

  return 0;

  }

  如何在Linux下给zigbee CC2530实现上位机

  主菜单

  点亮led1:

  如何在Linux下给zigbee CC2530实现上位机

  点亮led1

  如何在Linux下给zigbee CC2530实现上位机

  熄灭led1

  如何在Linux下给zigbee CC2530实现上位机

  获取烟雾数据

  烟雾的数据是079,可以点根华子,你会发现每次读取的值都是在变化。

  OK! 至此为止,一个简易的CC2530上位机我们就编写完毕,如果想将从串口获取的数据的值发送到远端服务器,后续文章我们将继续讨论。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多