文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-03 12:18

关注

0、前言

网友提问如下:


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

两个pc之间 socket /unix

raw 套接字:

BSD socket unix -> bill joy bsd分支,

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

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

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

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

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

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

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

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

一、环境简介

1. 软硬件环境

下位机:CC2530 OS:vmware + ubuntu

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

2. 硬件连接图

硬件连接图如下:


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

3. pc下识别串口

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


windows下串口

4. ubuntu下识别串口

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


虚拟机抓取串口

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

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

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

 

ubuntu安装串口驱动

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

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

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

188, 0 :主次设备号

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


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

二、模块设计

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

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

1. 信令

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


含义如下:

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

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

  1. #define DEV_ID_LED_ON    0X1 
  2. #define DEV_ID_LED_OFF    0X2 
  3. #define DEV_ID_DELAY 0X3 
  4. #define DEV_ID_GAS  0X4 

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

2. 上传数据

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

  1. struct data{ 
  2.  unsigned char device; 
  3.  unsigned char crc;    
  4.  unsigned short data; 
  5. }; 

3. 功能模块

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

下位机


下位机流程图

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

上位机


上位机

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

三、 下位机功能函数

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

1. LED初始化

  1.  
  2. void InitLed(void) 
  3.     P1DIR |= 0x01;               //P1.0定义为输出口 
  4.     LED1 = 0;    

2. 初始化UART

  1.  
  2. void InitUart(void) 
  3. {  
  4.     PERCFG = 0x00;           //外设控制寄存器 USART 0的IO位置:0为P0口位置1  
  5.     P0SEL = 0x0c;            //P0_2,P0_3用作串口(外设功能) 
  6.     P2DIR &= ~0xC0;          //P0优先作为UART0 
  7.      
  8.     U0CSR |= 0x80;           //设置为UART方式 
  9.     U0GCR |= 11;            
  10.     U0BAUD |= 216;           //波特率设为115200 
  11.     UTX0IF = 0;              //UART0 TX中断标志初始置位0 
  12.     U0CSR |= 0x40;           //允许接收  
  13.     IEN0 |= 0x84;            //开总中断允许接收中断   

3. 串口发送函数

  1.  
  2. void UartSendString(char *Data, int len) 
  3.     uint i; 
  4.      
  5.     for(i=0; i
  6.     { 
  7.         U0DBUF = *Data++; 
  8.         while(UTX0IF == 0); 
  9.         UTX0IF = 0; 
  10.     } 

 4. 串口中断处理函数

  1.  
  2. #pragma vector = URX0_VECTOR  
  3. __interrupt void UART0_ISR(void)  
  4. {  
  5.     URX0IF = 0;       // 清中断标志  
  6.     RxBuf = U0DBUF;                            

5. 烟雾传感器数据读取

  1.  
  2. uint16 myApp_ReadGasLevel( void ) 
  3.   uint16 reading = 0; 
  4.    
  5.    
  6.   ADCCFG |= 0x80; 
  7.    
  8.    
  9.   ADCCON3 = 0x87; 
  10.    
  11.    
  12.   while (!(ADCCON1 & 0x80)); 
  13.    
  14.    
  15.   ADCCFG &= (0x80 ^ 0xFF); 
  16.    
  17.    
  18.   reading = ADCH; 
  19.   reading |= (int16) (ADCH << 8);  
  20.   reading >>= 8; 
  21.    
  22.   return (reading); 

6. LED灯控制函数

  1.  
  2. void led_opt(char RxData[],unsigned char flage) 
  3.  switch(RxData[1]) 
  4.  { 
  5.   case 1: 
  6.                   LED1 = (flage==DEV_ID_LED_ON)?ON:OFF
  7.    break; 
  8.    
  9.  
  10.    
  11.   default
  12.    break; 
  13.  } 
  14.  return

7. 主程序

  1.  
  2. void main(void) 
  3. {  
  4.  CLKCONCMD &= ~0x40;           //设置系统时钟源为32MHZ晶振 
  5.  while(CLKCONSTA & 0x40);      //等待晶振稳定为32M 
  6.  CLKCONCMD &= ~0x47;           //设置系统主时钟频率为32MHZ    
  7.  
  8.  InitLed();                    //设置LED灯相应的IO口 
  9.  InitUart();                   //串口初始化函数    
  10.  UartState = UART0_RX;         //串口0默认处于接收模式 
  11.  memset(RxData, 0, SIZE); 
  12.        
  13.  while(1) 
  14.  { 
  15.       //接收状态  
  16.   if(UartState == UART0_RX)              
  17.   { //读取数据,遇到字符'#'或者缓冲区字符数量超过4就设置UartState为CONTROL_DEV状态 
  18.    if(RxBuf != 0)  
  19.    {  
  20.     //以'#'为结束符,一次最多接收4个字符        
  21.     if((RxBuf != '#')&&(count < 4))      
  22.     {  
  23.      RxData[count++] = RxBuf;  
  24.     } 
  25.     else 
  26.     { 
  27.       //判断数据合法性,防止溢出 
  28.      if(count >= 4)             
  29.      {  
  30.       //计数清0 
  31.       count = 0;              
  32.       //清空接收缓冲区 
  33.       memset(RxData, 0, SIZE); 
  34.      } 
  35.      else
  36.       //进入发送状态  
  37.       UartState = CONTROL_DEV; 
  38.      } 
  39.     } 
  40.     RxBuf  = 0; 
  41.    } 
  42.   } 
  43.          //控制控制外设状态  
  44.          if(UartState == CONTROL_DEV)             
  45.          { 
  46.              //判断接收的数据合法性 
  47.    //RxData[]:  | device | data |crc | # | 
  48.    //check_crc:   crc = device ^ data 
  49.    //if(RxData[2] == (RxData[0]^RxData[1])) 
  50.    { 
  51.     switch(RxData[0]) 
  52.     { 
  53.      case DEV_ID_LED_ON : 
  54.       led_opt(RxData,DEV_ID_LED_ON); 
  55.       break; 
  56.      case DEV_ID_LED_OFF: 
  57.       led_opt(RxData,DEV_ID_LED_OFF); 
  58.       break; 
  59.      case DEV_ID_DELAY: 
  60.       break; 
  61.      case DEV_ID_GAS: 
  62.       send_gas(); 
  63.       break;    
  64.      default
  65.       break; 
  66.     }         
  67.    } 
  68.              UartState = UART0_RX; 
  69.              count = 0;      
  70.    //清空接收缓冲区 
  71.              memset(RxData, 0, SIZE);            
  72.   } 
  73.  } 

四、 上位机功能函数

结构体

  1. #define DEV_ID_LED_ON    0X1 
  2. #define DEV_ID_LED_OFF    0X2 
  3. #define DEV_ID_DELAY 0X3 
  4. #define DEV_ID_GAS  0X4 
  5. struct data{ 
  6.  unsigned char device; 
  7.  unsigned char crc;  
  8.  unsigned short data; 
  9. }; 

函数

  1. void uart_init(void ) 
  2.  int nset1,nset2; 
  3.  
  4.  serial_fd = open"/dev/ttyUSB0", O_RDWR); 
  5.  if(serial_fd == -1) 
  6.  { 
  7.   printf("open() error\n"); 
  8.   exit(1); 
  9.  } 
  10.  nset1 = set_opt(serial_fd, 115200, 8, 'N', 1); 
  11.  if(nset2 == -1) 
  12.  { 
  13.   printf("set_opt() error\n"); 
  14.   exit(1); 
  15.  } 
  16. int Menu()  
  17.  int option
  18.   
  19.  system("clear"); 
  20.  
  21.  printf("\n\t\t************************************************\n"); 
  22.  printf("\n\t\t**               ALARM SYSTERM                **\n"); 
  23.  printf("\n\t\t**               1----LED                     **\n"); 
  24.  printf("\n\t\t**               2----GAS                   **\n"); 
  25.  printf("\n\t\t**               0----EXIT                    **\n"); 
  26.  printf("\n\t\t************************************************\n");  
  27.  while(1) 
  28.  {  
  29.   printf("Please choose what you want: "); 
  30.   scanf("%d",&option);  
  31.   if(option<0||option>2) 
  32.    printf("\t\t    choose error!\n"); 
  33.   else  
  34.    break; 
  35.  } 
  36.  return option;  
  37. // RxData[]:  | device | data |crc | # | 
  38. void led() 
  39.  int lednum = 0; 
  40.  int onoff; 
  41.  
  42.  char cmd[4]; 
  43.  //选择led灯 
  44.  while(1) 
  45.  { 
  46.   printf("input led number :[1 2]\n#"); 
  47.  
  48.   scanf("%d",&lednum); 
  49.   //check   
  50.   if(lednum<1 || lednum >2) 
  51.   { 
  52.    printf("invalid led number\n"); 
  53.    system("clear"); 
  54.    continue
  55.   }else
  56.    break; 
  57.   } 
  58.  } 
  59.  printf("operation: 1 on , 0  off\n"); 
  60.  scanf("%d",&onoff);  
  61.  
  62.  if(onoff == 1) 
  63.  { 
  64.   cmd[0] = DEV_ID_LED_ON; 
  65.  }else if(onoff == 0) 
  66.  { 
  67.   cmd[0] = DEV_ID_LED_OFF; 
  68.  }else
  69.   printf("invalid led number\n"); 
  70.   return
  71.  } 
  72.   
  73.  cmd[1] = lednum; 
  74.  //fulfill crc  area 
  75.  cmd[2] = cmd[0]^cmd[1];   
  76.  cmd[3] = '#';//表示结束符 
  77.   
  78.  tcflush(serial_fd, TCIOFLUSH); 
  79.  
  80.  int i = 0; 
  81.  
  82.  for(i=0;i<4;i++) 
  83.  { 
  84.   printf("%d ",cmd[i]); 
  85.  } 
  86.  printf("\n"); 
  87.   
  88.  write(serial_fd,&cmd,sizeof(cmd));   
  89.   
  90.  sleep(1); 
  91.   
  92. // RxData[]:  | device | data |crc | # | 
  93. void gas() 
  94.  int len ; 
  95.  unsigned short  GasLevel; 
  96.  struct data msg; 
  97.  char gas[4]={0}; 
  98.  char cmd[4]; 
  99.   
  100.  cmd[0] = DEV_ID_GAS; 
  101.  cmd[3] = '#';//表示结束符 
  102.  write(serial_fd,&cmd,sizeof(cmd)); 
  103.  sleep(1); 
  104.   
  105.  len = read(serial_fd,&msg,sizeof(struct data)); 
  106.  //转换读取的gas数据格式 
  107.  GasLevel = msg.data; 
  108.  gas[0] = GasLevel / 100 + '0'
  109.  gas[1] = GasLevel / 10%10 + '0'
  110.  gas[2] = GasLevel % 10 + '0'
  111.  
  112.  printf("%s\n",gas); 
  113.  getchar(); 
  114. void run() 
  115.  int x; 
  116.   
  117.  while(1) 
  118.  {   
  119.   x=Menu();  
  120.   switch(x)  
  121.   {  
  122.    case 1: 
  123.     led(); 
  124.     break;   
  125.    case 2: 
  126.     gas(); 
  127.     break;  
  128.    case 0: 
  129.     printf("\n\t\t     exit!\n\n"); 
  130.     close(serial_fd); 
  131.     exit(0); 
  132.    default
  133.     fg=1; 
  134.     break; 
  135.    } 
  136.    if(fg) 
  137.     break; 
  138.   } 
  139.  
  140. int main()  
  141.  uart_init(); 
  142.  run(); 
  143.  return 0; 

五、 运行结果

1. 上位机运行界面


主菜单

2. 点亮led灯

点亮led1:


3. 灭灯


熄灭led1

4. 读取烟雾传感器数据


获取烟雾数据

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

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

 

来源:一口Linux内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯