怎么用C语言在Linux下实现CC2530上位机

小编给大家分享一下怎么用C语言在Linux下实现CC2530上位机,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

一、环境简介

1. 软硬件环境

下位机:CC2530 OS:vmware + ubuntu

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

2. 硬件连接图

硬件连接图如下:

怎么用C语言在Linux下实现CC2530上位机

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

3. pc下识别串口

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

怎么用C语言在Linux下实现CC2530上位机

windows下串口

4. ubuntu下识别串口

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

怎么用C语言在Linux下实现CC2530上位机

虚拟机抓取串口

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

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

1 make  2 sudo make load 3 ls /dev/ttyUSB0

 怎么用C语言在Linux下实现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】回复 ch440 即可获得

怎么用C语言在Linux下实现CC2530上位机

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

二、模块设计

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

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

1. 信令

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

怎么用C语言在Linux下实现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

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

2. 上传数据

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

struct data{  unsigned char device;  unsigned char crc;     unsigned short data; };
  • device 设备

  • data 采集的数据

  • crc 校验码

3. 功能模块

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

下位机

怎么用C语言在Linux下实现CC2530上位机

下位机流程图

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

上位机

怎么用C语言在Linux下实现CC2530上位机

上位机

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

三、 下位机功能函数

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上位机

主菜单

2. 点亮led灯

点亮led1:

怎么用C语言在Linux下实现CC2530上位机

3. 灭灯

怎么用C语言在Linux下实现CC2530上位机

熄灭led1

4. 读取烟雾传感器数据

怎么用C语言在Linux下实现CC2530上位机

获取烟雾数据

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

以上是“怎么用C语言在Linux下实现CC2530上位机”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!