用C语言在Linux下实现CC2530上位机-1

时间:2022-01-04 19:33:57

0、前言

网友提问如下:

用C语言在Linux下实现CC2530上位机-1

用C语言在Linux下实现CC2530上位机-1

本地进程之间 pipe shm msg 消息队列, sem

两个pc之间 socket /unix

raw 套接字:

BSD socket unix -> bill joy bsd分支,

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

下位机,通过串口与上位机相连;

下位机要能够接收上位机下发的命令,并解析这些命令;

下位机能够根据这些命令配置对应的外设、读取对应的传感器的数据上传到上位机;

主程序串口操作模块:通过串口下发命令或者读取下位机上传的数据信息;

主程序网络通信模块:接收远程服务器下发的命令,并将下位机采集的数据上传到服务器。

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

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

一、环境简介

1. 软硬件环境

下位机:CC2530 OS:vmware + ubuntu

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

2. 硬件连接图

硬件连接图如下:

用C语言在Linux下实现CC2530上位机-1

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

3. pc下识别串口

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

用C语言在Linux下实现CC2530上位机-1

windows下串口

4. ubuntu下识别串口

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

用C语言在Linux下实现CC2530上位机-1

虚拟机抓取串口

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

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

 make  

 sudo make load 

ls /dev/ttyUSB0 

 用C语言在Linux下实现CC2530上位机-1

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 即可获得

用C语言在Linux下实现CC2530上位机-1

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

二、模块设计

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

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

1. 信令

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

用C语言在Linux下实现CC2530上位机-1

含义如下:

  • 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 

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

2. 上传数据

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

struct data{ 

 unsigned char device; 

 unsigned char crc;    

 unsigned short data; 

}; 

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

3. 功能模块

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

下位机

用C语言在Linux下实现CC2530上位机-1

下位机流程图

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

上位机

用C语言在Linux下实现CC2530上位机-1

上位机

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

三、 下位机功能函数

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

1. LED初始化

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

* 名    称: InitLed() 

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

* 入口参数: 无 

* 出口参数: 无 

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

void InitLed(void) 

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

    LED1 = 0;    

2. 初始化UART

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

* 名    称: 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;            //开总中断允许接收中断   

3. 串口发送函数

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

* 名    称: UartSendString() 

* 功    能: 串口发送函数 

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

* 出口参数: 无 

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

void UartSendString(char *Data, int len) 

    uint i; 

     

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

    { 

        U0DBUF = *Data++; 

        while(UTX0IF == 0); 

        UTX0IF = 0; 

    } 

4. 串口中断处理函数

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

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

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

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

#pragma vector = URX0_VECTOR  

__interrupt void UART0_ISR(void)  

{  

    URX0IF = 0;       // 清中断标志  

    RxBuf = U0DBUF;                            

5. 烟雾传感器数据读取

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

* 名    称: 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); 

6. LED灯控制函数

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

* 名    称: 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

7. 主程序

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

* 主程序入口函数 

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

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\n"); 

  exit(1); 

 } 

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

 if(nset2 == -1) 

 { 

  printf("set_opt() error\n"); 

  exit(1); 

 } 

int Menu()  

 int option

  

 system("clear"); 

 

 printf("\n\t\t************************************************\n"); 

 printf("\n\t\t**               ALARM SYSTERM                **\n"); 

 printf("\n\t\t**               1----LED                     **\n"); 

 printf("\n\t\t**               2----GAS                   **\n"); 

 printf("\n\t\t**               0----EXIT                    **\n"); 

 printf("\n\t\t************************************************\n");  

 while(1) 

 {  

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

  scanf("%d",&option);  

  if(option<0||option>2) 

   printf("\t\t    choose error!\n"); 

  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]\n#"); 

 

  scanf("%d",&lednum); 

  //check   

  if(lednum<1 || lednum >2) 

  { 

   printf("invalid led number\n"); 

   system("clear"); 

   continue

  }else

   break; 

  } 

 } 

 printf("operation: 1 on , 0  off\n"); 

 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\n"); 

  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("\n"); 

  

 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\n",gas); 

 getchar(); 

void run() 

 int x; 

  

 while(1) 

 {   

  x=Menu();  

  switch(x)  

  {  

   case 1: 

    led(); 

    break;   

   case 2: 

    gas(); 

    break;  

   case 0: 

    printf("\n\t\t     exit!\n\n"); 

    close(serial_fd); 

    exit(0); 

   default

    fg=1; 

    break; 

   } 

   if(fg) 

    break; 

  } 

 

int main()  

 uart_init(); 

 run(); 

 return 0; 

五、 运行结果

1. 上位机运行界面

用C语言在Linux下实现CC2530上位机-1

主菜单

2. 点亮led灯

点亮led1:

用C语言在Linux下实现CC2530上位机-1

3. 灭灯

用C语言在Linux下实现CC2530上位机-1

熄灭led1

4. 读取烟雾传感器数据

用C语言在Linux下实现CC2530上位机-1

获取烟雾数据

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

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

原文地址:https://mp.weixin.qq.com/s/4abx3fahrHW3d264paP3mA