当前位置:首页 > 未命名 > 正文

0、前言

网友提问如下:

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

用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(用c语言在linux下实现cc2530上位机-1)

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

3. pc下识别串口

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

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

windows下串口

4. ubuntu下识别串口

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

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

虚拟机抓取串口

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

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

make

sudomakeload

ls/dev/ttyUSB0

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

ubuntu安装串口驱动

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

ls/dev/ttyUSB0-l

crw-rw----1rootdialout188,0Jan1505:45/dev/ttyUSB0

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

188, 0 :主次设备号

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

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

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

二、模块设计

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

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

1. 信令

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

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

含义如下:

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

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

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

#defineDEV_ID_LED_ON0X1

#defineDEV_ID_LED_OFF0X2

#defineDEV_ID_DELAY0X3

#defineDEV_ID_GAS0X4

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

2. 上传数据

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

structdata{

unsignedchardevice;

unsignedcharcrc;

unsignedshortdata;

};

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

3. 功能模块

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

下位机

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

下位机流程图

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

上位机

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

上位机

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

三、 下位机功能函数

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

1. LED初始化

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

*名称:InitLed()

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

*入口参数:无

*出口参数:无

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

voidInitLed(void)

{

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

LED1=0;

}

2. 初始化UART

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

*名称:InitUart()

*功能:串口初始化函数

*入口参数:无

*出口参数:无

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

voidInitUart(void)

{

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

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

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

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

U0GCR|=11;

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

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

U0CSR|=0x40;//允许接收

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

}

3. 串口发送函数

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

*名称:UartSendString()

*功能:串口发送函数

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

*出口参数:无

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

voidUartSendString(char*Data,intlen)

{

uinti;

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

{

U0DBUF=*Data++;

while(UTX0IF==0);

UTX0IF=0;

}

}

4. 串口中断处理函数

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

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

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

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

#pragmavector=URX0_VECTOR

__interruptvoidUART0_ISR(void)

{

URX0IF=0;//清中断标志

RxBuf=U0DBUF;

}

5. 烟雾传感器数据读取

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

*名称:myApp_ReadGasLevel()

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

*入口参数:无

*出口参数:无

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

uint16myApp_ReadGasLevel(void)

{

uint16reading=0;

/*Enablechannel*/

ADCCFG|=0x80;

/*writingtothisregisterstartstheextraconversion*/

ADCCON3=0x87;

/*Waitfortheconversiontobedone*/

while(!(ADCCON1&0x80));

/*Disablechannelafterdoneconversion*/

ADCCFG&=(0x80^0xFF);

/*Readtheresult*/

reading=ADCH;

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

reading>>=8;

return(reading);

}

6. LED灯控制函数

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

*名称:led_opt()

*功能:LED灯控制函数

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

*出口参数:无

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

voidled_opt(charRxData[],unsignedcharflage)

{

switch(RxData[1])

{

case1:

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

break;

/*TBDforled2led3*/

default:

break;

}

return;

}

7. 主程序

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

*主程序入口函数

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

voidmain(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])

{

caseDEV_ID_LED_ON:

led_opt(RxData,DEV_ID_LED_ON);

break;

caseDEV_ID_LED_OFF:

led_opt(RxData,DEV_ID_LED_OFF);

break;

caseDEV_ID_DELAY:

break;

caseDEV_ID_GAS:

send_gas();

break;

default:

break;

}

}

UartState=UART0_RX;

count=0;

//清空接收缓冲区

memset(RxData,0,SIZE);

}

}

}

四、 上位机功能函数

结构体

#defineDEV_ID_LED_ON0X1

#defineDEV_ID_LED_OFF0X2

#defineDEV_ID_DELAY0X3

#defineDEV_ID_GAS0X4

structdata{

unsignedchardevice;

unsignedcharcrc;

unsignedshortdata;

};

函数

voiduart_init(void)

{

intnset1,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);

}

}

intMenu()

{

intoption;

system("clear");

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

printf("\n\t\t**ALARMSYSTERM**\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("Pleasechoosewhatyouwant:");

scanf("%d",&option);

if(option<0||option>2)

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

else

break;

}

returnoption;

}

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

voidled()

{

intlednum=0;

intonoff;

charcmd[4];

//选择led灯

while(1)

{

printf("inputlednumber:[12]\n#");

scanf("%d",&lednum);

//check

if(lednum<1||lednum>2)

{

printf("invalidlednumber\n");

system("clear");

continue;

}else{

break;

}

}

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

scanf("%d",&onoff);

if(onoff==1)

{

cmd[0]=DEV_ID_LED_ON;

}elseif(onoff==0)

{

cmd[0]=DEV_ID_LED_OFF;

}else{

printf("invalidlednumber\n");

return;

}

cmd[1]=lednum;

//fulfillcrcarea

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

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

tcflush(serial_fd,TCIOFLUSH);

inti=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|#|

voidgas()

{

intlen;

unsignedshortGasLevel;

structdatamsg;

chargas[4]={0};

charcmd[4];

cmd[0]=DEV_ID_GAS;

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

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

sleep(1);

len=read(serial_fd,&msg,sizeof(structdata));

//转换读取的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();

}

voidrun()

{

intx;

while(1)

{

x=Menu();

switch(x)

{

case1:

led();

break;

case2:

gas();

break;

case0:

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

close(serial_fd);

exit(0);

default:

fg=1;

break;

}

if(fg)

break;

}

}

intmain()

{

uart_init();

run();

return0;

}

五、 运行结果

1. 上位机运行界面

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

主菜单

2. 点亮led灯

点亮led1:

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

3. 灭灯

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

熄灭led1

4. 读取烟雾传感器数据

用c语言在linux下实现cc2530上位机-1(用c语言在linux下实现cc2530上位机-1)

获取烟雾数据

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

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

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

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。