爱收集资源网

(技术分析)如何给下位机编写一个简单的上位机

网络整理 2024-01-22 10:36

0、前言

网友提问如下:

粉丝提问

项目框架

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

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

下位机要才能接收上位机下发的命令c语言开发上位机,并解析这种命令;

下位机才能依据这种命令配置对应的外设、读取对应的传感的数据上传到上位机;

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

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

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

本篇只讨论怎样给下位机编撰一个简单的上位机。

一、环境简介1.软硬件环境

下位机:CC2530OS: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 make 2 sudo make load3 ls /dev/ttyUSB0

ubuntu安装并口驱动

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

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

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

188,0:主次设备号

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

驱动

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

二、模块设计

上位机和下位机的通讯常常都是通过并口,linux下常常生成字符设备ttyUSB0【有的是ttyS0】c语言开发上位机,操作并口设备就只须要操作该字符设备即可。

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

1.鉴权

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

鉴权格式

涵义如下:

鉴权格式可以按照须要扩充或则精简。

其中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;
};

3.功能模块

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

下位机

下位机流程图

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

上位机

上位机

上位机主要任务是复印菜单,由用户针对菜单作出选择,之后根据指令格式封装命令,并通过并口将该命令下发给下位机。

三、下位机功能函数

c语言开发上位机_上位机开发语言有哪些_上位机软件开发用什么语言

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

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.上位机运行界面

主菜单

2.照亮led灯

照亮led1:

照亮led1

3.灭灯

熄灭led1

4.读取烟雾传感数据

获取烟雾数据

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

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

c语言开发上位机
相关文章