精华内容
下载资源
问答
  • 上位机与下位机通信
    千次阅读
    2021-03-20 08:29:34

    一、概念

    **上位机:**是指人可以直接发出操作命令的计算机,一般指PC、人机界面等。发出的命令首先给下位机,下位机再根据命令解释成相应的时序信号,直接控制相应设备。
    **下位机:**直接控制设备获取设备状况的计算机,一般指PLC、智能仪表、智能模块等。不时读取设备状态数据,转换成数字信号反馈给上位机。
    下位机要和和上位机通讯,可以把下位机分为前端通讯部分后台管理部分。下位机工作在这样一个场景,上位机发一条指令,下位机执行,然后反馈结果。前端通讯代表了下位机与上位机之间通讯的部分,而后台管理代表了动作的执行或者反馈给上位机需要的数据。
    前端通信可以分为四个部分:接收、解析、处理、返回

    1)接收:需要保证接收数据的完整性
    2)解析:对接收到的数据进行解析,解析方式按照协议规则,得到指令字与数据,为接下来的处理提供依据。
    3)处理:得到了需要执行的指令和数据,根据不同指令做出不同的处理,这就需要后台管理部分进行处理。
    4)返回:处理以后,将数据返回,可以是指令也可以是上位机所请求的数据。

    **后台管理:**一般处于长期运行的状态,下位机往往会接很多传感器,那么后台的主要职能为:数据的采集、加工、更新、存储以及动作的执行。往往它会处于数据准备阶段,当上位机需要反馈数据时它将数据反馈,当上位机需要执行某个动作时,它会执行某个动作。

    二、上位机和下位机的通讯

    上位机和下位机的通讯方式,大多取决于下位机
    通常上位机和下位机通讯可以采用不同的通讯协议,如RS232串口通讯、RS485串行通讯和CAN总线通信。
    我们现在的底盘上的说明书上写出了底盘的通讯控制协议:RS232串口通信和CAN总线通信。
    1.可以用无线串口通信,比如蓝牙。通过串口把指令传给PC端蓝牙,PC端蓝牙把数据传送给底盘上的蓝牙,蓝牙通过串口把指令传送给底盘,进行底盘的控制。
    2.还有一种控制方式就是仍然通过遥控器发送信号给底盘,而PC端通过串口连接遥控器即可。
    现在需要做的工作是:
    1.看懂底盘通信协议的说明书,
    2.学习PC端的通讯程序编写

    更多相关内容
  • 上位机与下位机通信

    千次阅读 2021-06-26 22:50:30
    本文链接地址:上位机与下位机通信 Content: STM32串口发送接收 ROS串口发送接收 一般情况,上位机由ROS框架运行在Ubuntu的树莓派构成,下位机由STM32F103VET6芯片板载电机,舵机,陀螺仪,里程计等构成。...

    本文链接地址: 上位机与下位机通信

    Content:

    1. STM32串口发送与接收
    2. ROS串口发送与接收


    一般情况,上位机由ROS框架运行在Ubuntu的树莓派构成,下位机由STM32F103VET6芯片板载电机,舵机,陀螺仪,里程计等构成。里程计提供ROS需要的速度信息,陀螺仪提供加速度方向等信息给ROS,再加上连接到树莓派上的激光雷达,ROS就可以进行SLAM制图和导航了。下位机接收到ROS下发的速度信息后,转换成电机的PWM信号和舵机的PWM信号进行方向和速度控制。本篇就介绍上位机和下位机进行通信的一种方式:串口通信。

    STM32串口发送与接收

    首先进行串口3的初始化,其对应的GPIO口为C10发送,C11接收。

    void uart3_init(u32 bound)
    {  	 
            GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
     
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);    //Enable the AFIO clock  //使能AFIO时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //Enable the gpio clock  //使能GPIO时钟
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //Enable the Usart clock //使能USART时钟
    	GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE); //Pin remapping          //引脚重映射
     
    	//USART_TX  发送端口配置
            GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //C10
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//Reuse push-pull output	//复用推挽输出
            GPIO_Init(GPIOC, &GPIO_InitStructure);   
     
           //USART_RX  接收端口配置
           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PC11
           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //Pull up input//上拉输入
           GPIO_Init(GPIOC, &GPIO_InitStructure);
     
           //UsartNVIC configuration //UsartNVIC配置 中断优先级
           NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    	//Preempt priority //抢占优先级
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
    	//Preempt priority //抢占优先级
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		
    	//Enable the IRQ channel //IRQ通道使能	
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	
            //Initialize the VIC register with the specified parameters 
    	//根据指定的参数初始化VIC寄存器		
    	NVIC_Init(&NVIC_InitStructure);
     
            //USART Initialization Settings 串口初始化设置
    	USART_InitStructure.USART_BaudRate = bound; //Port rate //串口波特率
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b; //The word length is 8 bit data format //字长为8位数据格式
    	USART_InitStructure.USART_StopBits = USART_StopBits_1; //A stop bit //一个停止
    	USART_InitStructure.USART_Parity = USART_Parity_No; //Prosaic parity bits //无奇偶校验位
    	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //No hardware data flow control //无硬件数据流控制
    	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//Sending and receiving mode //收发模式
            USART_Init(USART3, &USART_InitStructure);      //Initialize serial port 3 //初始化串口3
    	//开启中断,其对应的中断函数为USART3_IRQHandler,这是启动函数里面hard code的
            USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //Open the serial port to accept interrupts //开启串口接受中断
            USART_Cmd(USART3, ENABLE);                     //Enable serial port 3 //使能串口3 
    }

    中断后,从串口中读取上位机发送的数据到Receive_Data变量中。并从Receive_Data中解析出3轴的速度:Move_X,Move_Y和Move_Z。

    int USART3_IRQHandler(void)
    {	
    	static u8 Count=0;
    	u8 Usart_Receive;
     
    	if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //Check if data is received //判断是否接收到数据
    	{
    		Usart_Receive = USART_ReceiveData(USART3);//Read the data //读取数据
    		if(Time_count0) 
    			Count++; 
    		else 
    			Count=0;
     
    		if (Count == 11) //Verify the length of the packet //验证数据包的长度
    		{   
    				Count=0; //Prepare for the serial port data to be refill into the array //为串口数据重新填入数组做准备
    				if(Receive_Data.buffer[10] == FRAME_TAIL) //Verify the frame tail of the packet //验证数据包的帧尾
    				{
    					//Data exclusionary or bit check calculation, mode 0 is sent data check
    					//数据异或位校验计算,模式0是发送数据校验
    					if(Receive_Data.buffer[9] ==Check_Sum(9,0))	 
    				        {		
    						//All modes flag position 0, USART3 control mode
                                                    //所有模式标志位置0,为Usart3控制模式						
    						PS2_ON_Flag=0;
    						Remote_ON_Flag=0;
    						APP_ON_Flag=0;
    						CAN_ON_Flag=0;
    						Usart_ON_Flag=0;
     
    						//Calculate the target speed of three axis from serial data, unit m/s
    						//从串口数据求三轴目标速度, 单位m/s
    						Move_X=XYZ_Target_Speed_transition(Receive_Data.buffer[3],Receive_Data.buffer[4]);
    						Move_Y=XYZ_Target_Speed_transition(Receive_Data.buffer[5],Receive_Data.buffer[6]);
    						Move_Z=XYZ_Target_Speed_transition(Receive_Data.buffer[7],Receive_Data.buffer[8]);
    				       }
    			         }
    		}
    	} 
      return 0;	
    }

    下位机定时给上位机发送当前小车的速度,加速度,角速度等信息。

    void data_transition(void)
    {
    	Send_Data.Sensor_Str.Frame_Header = FRAME_HEADER; //Frame_header //帧头
    	Send_Data.Sensor_Str.Frame_Tail = FRAME_TAIL;     //Frame_tail //帧尾
     
    	//According to different vehicle types, different kinematics algorithms were selected to carry out the forward kinematics solution, 
    	//and the three-axis velocity was obtained from each wheel velocity
    	//根据不同车型选择不同运动学算法进行运动学正解,从各车轮速度求出三轴速度
    	switch(Car_Mode)
    	{	    
    		case Akm_Car:  
    			Send_Data.Sensor_Str.X_speed = ((MOTOR_A.Encoder+MOTOR_B.Encoder)/2)*1000; 
    			Send_Data.Sensor_Str.Y_speed = 0;
    			Send_Data.Sensor_Str.Z_speed = ((MOTOR_B.Encoder-MOTOR_A.Encoder)/Wheel_spacing)*1000;
    		  break; 
    	}
     
    	//The acceleration of the triaxial acceleration //加速度计三轴加速度
    	Send_Data.Sensor_Str.Accelerometer.X_data= accel[1]; //The accelerometer Y-axis is converted to the ros coordinate X axis //加速度计Y轴转换到ROS坐标X轴
    	Send_Data.Sensor_Str.Accelerometer.Y_data=-accel[0]; //The accelerometer X-axis is converted to the ros coordinate y axis //加速度计X轴转换到ROS坐标Y轴
    	Send_Data.Sensor_Str.Accelerometer.Z_data= accel[2]; //The accelerometer Z-axis is converted to the ros coordinate Z axis //加速度计Z轴转换到ROS坐标Z轴
     
    	//The Angle velocity of the triaxial velocity //角速度计三轴角速度
    	Send_Data.Sensor_Str.Gyroscope.X_data= gyro[1]; //The Y-axis is converted to the ros coordinate X axis //角速度计Y轴转换到ROS坐标X轴
    	Send_Data.Sensor_Str.Gyroscope.Y_data=-gyro[0]; //The X-axis is converted to the ros coordinate y axis //角速度计X轴转换到ROS坐标Y轴
    	if(Flag_Stop==0) 
    		//If the motor control bit makes energy state, the z-axis velocity is sent normall
    	  //如果电机控制位使能状态,那么正常发送Z轴角速度
    		Send_Data.Sensor_Str.Gyroscope.Z_data=gyro[2];  
    	else  
    		//If the robot is static (motor control dislocation), the z-axis is 0
        //如果机器人是静止的(电机控制位失能),那么发送的Z轴角速度为0		
    		Send_Data.Sensor_Str.Gyroscope.Z_data=0;        
     
    	//Battery voltage (this is a thousand times larger floating point number, which will be reduced by a thousand times as well as receiving the data).
    	//电池电压(这里将浮点数放大一千倍传输,相应的在接收端在接收到数据后也会缩小一千倍)
    	Send_Data.Sensor_Str.Power_Voltage = Voltage*1000; 
     
    	Send_Data.buffer[0]=Send_Data.Sensor_Str.Frame_Header; //Frame_heade //帧头
      Send_Data.buffer[1]=Flag_Stop; //Car software loss marker //小车软件失能标志位
     
    	//The three-axis speed of / / car is split into two eight digit Numbers
    	//小车三轴速度,各轴都拆分为两个8位数据再发送
    	Send_Data.buffer[2]=Send_Data.Sensor_Str.X_speed >>8; 
    	Send_Data.buffer[3]=Send_Data.Sensor_Str.X_speed ;    
    	Send_Data.buffer[4]=Send_Data.Sensor_Str.Y_speed>>8;  
    	Send_Data.buffer[5]=Send_Data.Sensor_Str.Y_speed;     
    	Send_Data.buffer[6]=Send_Data.Sensor_Str.Z_speed >>8; 
    	Send_Data.buffer[7]=Send_Data.Sensor_Str.Z_speed ;    
     
    	//The acceleration of the triaxial axis of / / imu accelerometer is divided into two eight digit reams
    	//IMU加速度计三轴加速度,各轴都拆分为两个8位数据再发送
    	Send_Data.buffer[8]=Send_Data.Sensor_Str.Accelerometer.X_data>>8; 
    	Send_Data.buffer[9]=Send_Data.Sensor_Str.Accelerometer.X_data;   
    	Send_Data.buffer[10]=Send_Data.Sensor_Str.Accelerometer.Y_data>>8;
    	Send_Data.buffer[11]=Send_Data.Sensor_Str.Accelerometer.Y_data;
    	Send_Data.buffer[12]=Send_Data.Sensor_Str.Accelerometer.Z_data>>8;
    	Send_Data.buffer[13]=Send_Data.Sensor_Str.Accelerometer.Z_data;
     
    	//The axis of the triaxial velocity of the / /imu is divided into two eight digits
    	//IMU角速度计三轴角速度,各轴都拆分为两个8位数据再发送
    	Send_Data.buffer[14]=Send_Data.Sensor_Str.Gyroscope.X_data>>8;
    	Send_Data.buffer[15]=Send_Data.Sensor_Str.Gyroscope.X_data;
    	Send_Data.buffer[16]=Send_Data.Sensor_Str.Gyroscope.Y_data>>8;
    	Send_Data.buffer[17]=Send_Data.Sensor_Str.Gyroscope.Y_data;
    	Send_Data.buffer[18]=Send_Data.Sensor_Str.Gyroscope.Z_data>>8;
    	Send_Data.buffer[19]=Send_Data.Sensor_Str.Gyroscope.Z_data;
     
    	//Battery voltage, split into two 8 digit Numbers
    	//电池电压,拆分为两个8位数据发送
    	Send_Data.buffer[20]=Send_Data.Sensor_Str.Power_Voltage >>8; 
    	Send_Data.buffer[21]=Send_Data.Sensor_Str.Power_Voltage; 
     
      //Data check digit calculation, Pattern 1 is a data check
      //数据校验位计算,模式1是发送数据校验
    	Send_Data.buffer[22]=Check_Sum(22,1); 
     
    	Send_Data.buffer[23]=Send_Data.Sensor_Str.Frame_Tail; //Frame_tail //帧尾
    }
     
    void USART3_SEND(void)
    {
      unsigned char i = 0;	
    	for(i=0; i<24; i++)
    	{
    		usart3_send(Send_Data.buffer[i]);
    	}	 
    }
     
    void usart3_send(u8 data)
    {
    	USART3->DR = data;
    	while((USART3->SR&0x40)==0);	
    }

    ROS串口发送与接收

    在ROS中注册小车节点,并利用节点的回调函数Cmd_Vel_Callback组装数据,通过库serial完成串口数据发送。

    void turn_on_robot::Cmd_Vel_Callback(const geometry_msgs::Twist &twist_aux)
    {
      short  transition;  //intermediate variable //中间变量
     
      Send_Data.tx[0]=FRAME_HEADER; //frame head 0x7B //帧头0X7B
      Send_Data.tx[1] = 0; //set aside //预留位
      Send_Data.tx[2] = 0; //set aside //预留位
     
      //The target velocity of the X-axis of the robot
      //机器人x轴的目标线速度
      transition=0;
      transition = twist_aux.linear.x*1000; //将浮点数放大一千倍,简化传输
      Send_Data.tx[4] = transition;     //取数据的低8位
      Send_Data.tx[3] = transition>>8;  //取数据的高8位
     
      //The target velocity of the Y-axis of the robot
      //机器人y轴的目标线速度
      transition=0;
      transition = twist_aux.linear.y*1000;
      Send_Data.tx[6] = transition;
      Send_Data.tx[5] = transition>>8;
     
      //The target angular velocity of the robot's Z axis
      //机器人z轴的目标角速度
      transition=0;
      transition = twist_aux.angular.z*1000;
      Send_Data.tx[8] = transition;
      Send_Data.tx[7] = transition>>8;
     
      Send_Data.tx[9]=Check_Sum(9,SEND_DATA_CHECK); //For the BBC check bits, see the Check_Sum function //BBC校验位,规则参见Check_Sum函数
      Send_Data.tx[10]=FRAME_TAIL; //frame tail 0x7D //帧尾0X7D
      try
      {
        if(akm_cmd_vel=="none")
        Stm32_Serial.write(Send_Data.tx,sizeof (Send_Data.tx)); //Sends data to the downloader via serial port //通过串口向下位机发送数据 
        //else ROS_INFO("akm mode, not subcribe cmd_vel");
      }
      catch (serial::IOException& e)   
      {
        ROS_ERROR_STREAM("Unable to send data through serial port"); //If sending data fails, an error message is printed //如果发送数据失败,打印错误信息
      }
    }

    同时,在ROS的节点循环中,从串口读Stm32_Serial.read下位机发送过来的数据。

    bool turn_on_robot::Get_Sensor_Data()
    { 
      short transition_16=0, j=0, Header_Pos=0, Tail_Pos=0; //Intermediate variable //中间变量
      uint8_t Receive_Data_Pr[RECEIVE_DATA_SIZE]={0}; //Temporary variable to save the data of the lower machine //临时变量,保存下位机数据
      Stm32_Serial.read(Receive_Data_Pr,sizeof (Receive_Data_Pr)); //Read the data sent by the lower computer through the serial port //通过串口读取下位机发送过来的数据
     
      /*//View the received raw data directly and debug it for use//直接查看接收到的原始数据,调试使用
      ROS_INFO("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x",
      Receive_Data_Pr[0],Receive_Data_Pr[1],Receive_Data_Pr[2],Receive_Data_Pr[3],Receive_Data_Pr[4],Receive_Data_Pr[5],Receive_Data_Pr[6],Receive_Data_Pr[7],
      Receive_Data_Pr[8],Receive_Data_Pr[9],Receive_Data_Pr[10],Receive_Data_Pr[11],Receive_Data_Pr[12],Receive_Data_Pr[13],Receive_Data_Pr[14],Receive_Data_Pr[15],
      Receive_Data_Pr[16],Receive_Data_Pr[17],Receive_Data_Pr[18],Receive_Data_Pr[19],Receive_Data_Pr[20],Receive_Data_Pr[21],Receive_Data_Pr[22],Receive_Data_Pr[23]);
      */  
     
      //Record the position of the head and tail of the frame //记录帧头帧尾位置
      for(j=0;j<24;j++)
      {
        if(Receive_Data_Pr[j]==FRAME_HEADER)
        Header_Pos=j;
        else if(Receive_Data_Pr[j]==FRAME_TAIL)
        Tail_Pos=j;    
      }
     
      if(Tail_Pos==(Header_Pos+23))
      {
        //If the end of the frame is the last bit of the packet, copy the packet directly to receive_data.rx
        //如果帧尾在数据包最后一位,直接复制数据包到Receive_Data.rx
        // ROS_INFO("1----");
        memcpy(Receive_Data.rx, Receive_Data_Pr, sizeof(Receive_Data_Pr));
      }
      else if(Header_Pos==(1+Tail_Pos))
      {
        //如果帧头在帧尾后面,纠正数据位置后复制数据包到Receive_Data.rx
        // If the header is behind the end of the frame, copy the packet to receive_data.rx after correcting the data location
        // ROS_INFO("2----");
        for(j=0;j<24;j++)
        Receive_Data.rx[j]=Receive_Data_Pr[(j+Header_Pos)%24];
      }
      else 
      {
        //其它情况则认为数据包有错误
        // In other cases, the packet is considered to be faulty
        // ROS_INFO("3----");
        return false;
      }    
      /* //Check receive_data.rx for debugging use //查看Receive_Data.rx,调试使用
      ROS_INFO("%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x",
      Receive_Data.rx[0],Receive_Data.rx[1],Receive_Data.rx[2],Receive_Data.rx[3],Receive_Data.rx[4],Receive_Data.rx[5],Receive_Data.rx[6],Receive_Data.rx[7],
      Receive_Data.rx[8],Receive_Data.rx[9],Receive_Data.rx[10],Receive_Data.rx[11],Receive_Data.rx[12],Receive_Data.rx[13],Receive_Data.rx[14],Receive_Data.rx[15],
      Receive_Data.rx[16],Receive_Data.rx[17],Receive_Data.rx[18],Receive_Data.rx[19],Receive_Data.rx[20],Receive_Data.rx[21],Receive_Data.rx[22],Receive_Data.rx[23]); 
      */
      Receive_Data.Frame_Header= Receive_Data.rx[0]; //The first part of the data is the frame header 0X7B //数据的第一位是帧头0X7B
      Receive_Data.Frame_Tail= Receive_Data.rx[23];  //The last bit of data is frame tail 0X7D //数据的最后一位是帧尾0X7D
     
      if (Receive_Data.Frame_Header == FRAME_HEADER ) //Judge the frame header //判断帧头
      {
        if (Receive_Data.Frame_Tail == FRAME_TAIL) //Judge the end of the frame //判断帧尾
        { 
          if (Receive_Data.rx[22] == Check_Sum(22,READ_DATA_CHECK)||(Header_Pos==(1+Tail_Pos))) //BBC check passes or two packets are interlaced //BBC校验通过或者两组数据包交错
          {
            Receive_Data.Flag_Stop=Receive_Data.rx[1]; //set aside //预留位
            Robot_Vel.X = Odom_Trans(Receive_Data.rx[2],Receive_Data.rx[3]); //Get the speed of the moving chassis in the X direction //获取运动底盘X方向速度
            Robot_Vel.Y = Odom_Trans(Receive_Data.rx[4],Receive_Data.rx[5]); //Get the speed of the moving chassis in the Y direction, The Y speed is only valid in the omnidirectional mobile robot chassis
                                                                             //获取运动底盘Y方向速度,Y速度仅在全向移动机器人底盘有效
            Robot_Vel.Z = Odom_Trans(Receive_Data.rx[6],Receive_Data.rx[7]); //Get the speed of the moving chassis in the Z direction //获取运动底盘Z方向速度   
     
            //MPU6050 stands for IMU only and does not refer to a specific model. It can be either MPU6050 or MPU9250
            //Mpu6050仅代表IMU,不指代特定型号,既可以是MPU6050也可以是MPU9250
            Mpu6050_Data.accele_x_data = IMU_Trans(Receive_Data.rx[8],Receive_Data.rx[9]);   //Get the X-axis acceleration of the IMU     //获取IMU的X轴加速度  
            Mpu6050_Data.accele_y_data = IMU_Trans(Receive_Data.rx[10],Receive_Data.rx[11]); //Get the Y-axis acceleration of the IMU     //获取IMU的Y轴加速度
            Mpu6050_Data.accele_z_data = IMU_Trans(Receive_Data.rx[12],Receive_Data.rx[13]); //Get the Z-axis acceleration of the IMU     //获取IMU的Z轴加速度
            Mpu6050_Data.gyros_x_data = IMU_Trans(Receive_Data.rx[14],Receive_Data.rx[15]);  //Get the X-axis angular velocity of the IMU //获取IMU的X轴角速度  
            Mpu6050_Data.gyros_y_data = IMU_Trans(Receive_Data.rx[16],Receive_Data.rx[17]);  //Get the Y-axis angular velocity of the IMU //获取IMU的Y轴角速度  
            Mpu6050_Data.gyros_z_data = IMU_Trans(Receive_Data.rx[18],Receive_Data.rx[19]);  //Get the Z-axis angular velocity of the IMU //获取IMU的Z轴角速度  
            //Linear acceleration unit conversion is related to the range of IMU initialization of STM32, where the range is ±2g=19.6m/s^2
            //线性加速度单位转化,和STM32的IMU初始化的时候的量程有关,这里量程±2g=19.6m/s^2
            Mpu6050.linear_acceleration.x = Mpu6050_Data.accele_x_data / ACCEl_RATIO;
            Mpu6050.linear_acceleration.y = Mpu6050_Data.accele_y_data / ACCEl_RATIO;
            Mpu6050.linear_acceleration.z = Mpu6050_Data.accele_z_data / ACCEl_RATIO;
            //The gyroscope unit conversion is related to the range of STM32's IMU when initialized. Here, the range of IMU's gyroscope is ±500°/s
            //Because the robot generally has a slow Z-axis speed, reducing the range can improve the accuracy
            //陀螺仪单位转化,和STM32的IMU初始化的时候的量程有关,这里IMU的陀螺仪的量程是±500°/s
            //因为机器人一般Z轴速度不快,降低量程可以提高精度
            Mpu6050.angular_velocity.x =  Mpu6050_Data.gyros_x_data * GYROSCOPE_RATIO;
            Mpu6050.angular_velocity.y =  Mpu6050_Data.gyros_y_data * GYROSCOPE_RATIO;
            Mpu6050.angular_velocity.z =  Mpu6050_Data.gyros_z_data * GYROSCOPE_RATIO;
     
            //Get the battery voltage
            //获取电池电压
            transition_16 = 0;
            transition_16 |=  Receive_Data.rx[20]<<8;
            transition_16 |=  Receive_Data.rx[21];  
            Power_voltage = transition_16/1000+(transition_16 % 1000)*0.001; //Unit conversion millivolt(mv)->volt(v) //单位转换毫伏(mv)->伏(v)
     
            return true;
         }
        }
      } 
      return false;
    }

    展开全文
  • 功能包括: 1.读线圈、批量读线圈、读int保持寄存器、读float保持寄存器、读INT输入寄存器、读float输入寄存器. 2.写单个线圈、写单个保持寄存器、写多个线圈保存寄存器、写多个int保持寄存器、写多个float保持...
  • 把基于Delphi的上位机下位机单片机结合起来,对上位机与下位机作了深入研究,为单片机在工业控制中的应用提供了一些优化方案。
  • 电信设备-上位机与下位机通信的方法、装置及系统.zip
  • 上位机与下位机之间穿行接口通信协议格式说明
  • 基于stm32的上位机与下位机通信.rar
  • 承接上文WIFI上位机部分,现阶段实现了STM32单片机ESP8266WIFI模块作为下位机与WIFI上位机通信。 一、ESP8266模块STM32 ATK-ESP8266 是 ALIENTEK 推出的一款高性能的 UART-WiFi(串口-无线)模块,此处使用的是...

    前言

    承接上文WIFI上位机部分:【上位机】通过WIFI上位机与网络调试助手通信绘制曲线,现阶段实现了STM32单片机与ESP8266WIFI模块作为下位机与WIFI上位机通信。

    一、ESP8266模块与STM32连接

    ATK-ESP8266 是 ALIENTEK 推出的一款高性能的 UART-WiFi(串口-无线)模块,此处使用的是正点原子团队自主开发的ATK-ESP-01模块。该模块通过串口与单片机(此处使用启明欣欣的STM32F407ZGT6)进行通信,只需连接四个引脚,使用十分方便。
    在与单片机使用前,建议先连接电脑,使用串口调试助手与网络调试助手进行互相通信的测试,确保WIFI模块的正常及网络的连通。参考教程:ESP8266模块使用教程
    测试完毕后,可与单片机串口连接,如下所示:
    在这里插入图片描述

    二、单片机代码

    对于ESP8266的使用,其本质是使用串口发送AT指令操纵模块,可参考:AT指令集,故只需熟悉基本指令,便可编写相关代码。以下是部分操作指令:

    #include "esp8266.hpp"
    using namespace ESP8266_Space;
    
    extern "C" void MineMain(void)
    {
    	char buf[339];
    
    	wifi.init();
    	HAL_Delay(100);
    
    	//wifi.sendCmd("AT+RESTORE", "ODDK", 60000);
    	//wifi.sendCmd("AT+CWMODE=1", "OK" , 100);  //切换到STA模式
    	//wifi.sendCmd("AT+RST", "OK" , 100);  //切换到STA模式
    	//HAL_Delay(60000);
    	
    	while(wifi.sendCmd("AT", "OK", 100) == Timeout){
    		wifi.quitTrans(); //退出透传
    	}
    
    	wifi.sendCmd("ATE0"    , "OK" , 100);  //关闭回显
    	//wifi.sendCmd("AT+CWLAP", "OK" , 5000); //显示可加入的AP
    
    	/* 加入wifi */
    	sprintf(buf,"AT+CWJAP=\"%s\",\"%s\"","dxxy16-403-1","1234567890");
    	wifi.sendCmd(buf, "WIFI GOT IP", 15000); //加入AP
    	wifi.send((char*)"", "OK"         , 2000); //加入AP
    
    	/* 与PC机建立连接 */
    	sprintf(buf,"AT+CIPSTART=\"%s\",\"%s\",%s", "TCP","192.168.0.171","8086");
    	wifi.sendCmd(buf, "OK", 15000);
    
    	/* 进入透传模式 */
    	//wifi.sendCmd("AT+CIPMODE=1", "OK", 200); //开启透传模式
    	//wifi.sendCmd("AT+CIPSEND"  , ">", 100); //进入透传
    
    	for(;;){
    		static char messa[339] = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40|"\
    			"1,1,1,0.994,0.993,0.991,0.889,0.875,0.866,0.850,0.838,0.816,0.806,0.789,0.775,0.759,0.745,0.739,0.724,0.715,0.710,0.689,0.667,"\
    		"0.658,0.645,0.635,0.626,0.598,0.578,0.568,0.559,0.496,0.481,0.472,0.452,0.435,0.418,0.406,0.389,0.377";
    		sprintf(buf,"%s\r\n",messa);
    		HAL_Delay(500);
    		//非透传
    		wifi.sendCmd("AT+CIPSEND=676",">",100); //将要发送676个数据
    		wifi.send(buf, "SEND OK", 100);
    		
    		//透传模式
    		//wifi.sendNoAck((uint8_t *)buf);
    	}
    }
    

    这里使用了模块的STA模式,即WIFI模块作为一个客户端(Client)与WIFI上位机(Server)通信,原理与上一篇文章相似。只需要更改相应的指令,就可切换至AT或AT/STA模式,但AT模式与STA模式的使用方法稍有不同(AT模式时模块作为服务端)。我们使用一个char型数组将所要发送的消息存储起来,注意格式与上位机所规定的协议保持一致。
    使用另外一个串口将单片机与电脑相连,好处是能够观察WIFI模块是否已连接且正常工作:

    #include "esp8266.hpp"
    #include "string.h"
    
    extern DMA_HandleTypeDef hdma_usart6_rx;//使用了串口的DMA,usart6与电脑相连
    
    CPP_UART test_uart = CPP_UART(&huart3);
    ESP8266 wifi = ESP8266(&huart6, &hdma_usart6_rx, &htim6);
    
    /**
     * @brief  ESP8266 类 构造函数
     * @param  huart  : 连接ESP8266所用到的串口
     * @param  dma_rx : 串口接收对应的DMA
     * @param  htim   : 用于计数是否超时的定时器
     * @note  huart 对应的串口配置要求正确,波特率用默认的115200(也可自己更改,
     * @            但要注意ESP8266本身接收串口的速率),并且需要打开中断
     * @     dma_rx 串口设置dma的接收,一次一个字节,循环模式(circle)
     * @       htim 不用开启定时器中断,不用关心预分频系数和自动重装载值和OnePulse模式
     * @retval None
     **/
    ESP8266::ESP8266(UART_HandleTypeDef* huart, DMA_HandleTypeDef *dma_rx, TIM_HandleTypeDef *htim)
    	:uart(huart)
    {
    	this->dma_rx     = dma_rx;
    	this->tim        = htim;
    	this->uart_state = Idle;
    	this->mode       = NOINIT;
    }
    

    完整源码:ESP8266模块使用代码(基于C++)
    这里可能出现的问题是可能会连接不上或者出现busy提示,此时重启一下模块并重新连接。程序烧录后执行效果如下:
    在这里插入图片描述

    三、总结

    单片机数据通过串口发送给WIFI模块,WIFI模块通过无线通信方式发送至电脑上位机,实现了基本的物联网功能,此处展示的仅为一个简单的测试。

    展开全文
  • 上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用。在说个之前先来简单解释一下上位机与下位机的概念。上位机与下位机通常上位机指的是PC,下位机指的是单片机或者带微...

    串口通信是在工程应用中很常见。在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用。在说个之前先来简单解释一下上位机与下位机的概念。

    上位机与下位机

    通常上位机指的是PC,下位机指的是单片机或者带微处理器的系统。下位机一般是将模拟信号经过AD采集将模拟量转换为数字量,下位机再经过数字信号处理以后将数字信号通过串口发送到上位机,相反上位机可以给下位机发送一些指令或者信息。常见的通信串口包括RS232、RS485、RS422等。这些串口只是在电平特性有所不同,在上位机与下位机进行数据通信时可以不考虑电平特性,而且现在在硬件上有各种转接接口,使用起来也很方便。

    当然在通常做简单的串口UART实验时我们可以使用各种各样的串口助手小软件,但是这些串口小工具有时候并不能很好满足需求,那就尝试着自己写一套属于自己的串口助手?接下来说说如何使用java实现上位机与下位机之间的RS485串口通信。

    step 1: 下载支持java串口通信的jar包,这里给出下载地址:

    对以上的版本解释一下,因为本人在这里踩了一个坑,32位或者64位是与ecplise/myecplise一致,要是版本弄错了会报错。

    step 2:下载了那个jar包解压后会出现一下内容:

    96255068_1

    这个文件夹里面需要注意两点:jar包RXTXcomm需要导入到java工程里面去。另外就是需要将rxtxParallel.dll与rxtxSerial.dll复制在安转JDK的bin文件下和jre的bin文件夹下面,这样才能保证能够正常使用这个jar包。以下是将两个dll文件复制的位置:

    C:\Program Files (x86)\Java\jdk1.8.0_25\binC:\Program Files (x86)\Java\jdk1.8.0_25\jre\bin\

    step 3:RXTXComm Api如何使用

    接下来就是使用该导入jar包进行编码实现串口通信的功能了。在编码之前先来理一理串口通信的主要环节,本人总结主要分为以下几点:

    1)计算机首先需要进行硬件check,查找是否有可用的COM端口,并对该对端口进行简要判断,包括这些端口是否是串口,是否正在使用。以下是部分主要代码:

    /*类方法 不可改变 不接受继承

    * 扫描获取可用的串口

    * 将可用串口添加至list并保存至list

    */

    public static final ArrayList uartPortUseAblefind()

    {

    //获取当前所有可用串口

    //由CommPortIdentifier类提供方法

    Enumeration portList=CommPortIdentifier.getPortIdentifiers();

    ArrayList portNameList=new ArrayList();

    //添加并返回ArrayList

    while(portList.hasMoreElements())

    {

    String portName=portList.nextElement().getName();

    portNameList.add(portName);

    }

    return portNameList;

    }

    以下是测试类的测试实例:

    ArrayList arraylist=UARTParameterSetup.uartPortUseAblefind();

    int useAbleLen=arraylist.size();

    if(useAbleLen==0)

    {

    System.out.println("没有找到可用的串口端口,请check设备!");

    }

    else

    {

    System.out.println("已查询到该计算机上有以下端口可以使用:");

    for(int index=0;index

    {

    System.out.println("该COM端口名称:"+arraylist.get(index));

    //测试串口配置的相关方法

    }

    }

    2)通过计算机对串口的自检后,可以对串口参数进行简单的配置。常见的配置可以从常见的串口助手中得到启发。以下是一个串口助手的人机交换界面:

    96255068_2

    以下是对串口设置主要代码:

    /*

    * 串口常见设置

    * 1)打开串口

    * 2)设置波特率 根据单板机的需求可以设置为57600 ...

    * 3)判断端口设备是否为串口设备

    * 4)端口是否占用

    * 5)对以上条件进行check以后返回一个串口设置对象new UARTParameterSetup()

    * 6)return:返回一个SerialPort一个实例对象,若判定该com口是串口则进行参数配置

    * 若不是则返回SerialPort对象为null

    */

    public static final SerialPort portParameterOpen(String portName,int baudrate)

    {

    SerialPort serialPort=null;

    try

    { //通过端口名识别串口

    CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);

    //打开端口并设置端口名字 serialPort和超时时间 2000ms

    CommPort commPort=portIdentifier.open(portName,1000);

    //进一步判断comm端口是否是串口 instanceof

    if(commPort instanceof SerialPort)

    {

    System.out.println("该COM端口是串口!");

    //进一步强制类型转换

    serialPort=(SerialPort)commPort;

    //设置baudrate 此处需要注意:波特率只能允许是int型 对于57600足够

    //8位数据位

    //1位停止位

    //无奇偶校验

    serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8,SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

    //串口配制完成 log

    System.out.println("串口参数设置已完成,波特率为"+baudrate+",数据位8bits,停止位1位,无奇偶校验");

    }

    //不是串口

    else

    {

    System.out.println("该com端口不是串口,请检查设备!");

    //将com端口设置为null 默认是null不需要操作

    }

    }

    catch (NoSuchPortException e)

    {

    e.printStackTrace();

    }

    catch (PortInUseException e)

    {

    e.printStackTrace();

    }

    catch (UnsupportedCommOperationException e)

    {

    e.printStackTrace();

    }

    return serialPort;

    }

    以上代码就是返回一个对象,同时也返回了对象属性,因为对象在java里面是属于传值引用。对以上需要说明的是:在实验时需要连接串口才能让计算机检测到才能让程序工作,这里使用的是RS485转接线:

    96255068_3

    3)通过以上两个步骤后基本对串口的设置也完成了,对于串口类型的确认例如:RS232/RS485/RS422等,可以作为进一步确认的条件。RS485可以在gnu.io中找到。

    96255068_4

    接下来就是上位机与下位机之间的双向通信的功能实现了。该部分主要是利用java的输入输出流来实现。以下是主要代码:

    /*

    * 串口数据发送以及数据传输作为一个类

    * 该类做主要实现对数据包的传输至下单板机

    */

    class DataTransimit

    {

    /*

    * 上位机往单板机通过串口发送数据

    * 串口对象 seriesPort

    * 数据帧:dataPackage

    * 发送的标志:数据未发送成功抛出一个异常

    */

    public static void uartSendDatatoSerialPort(SerialPort serialPort,byte[] dataPackage)

    {

    OutputStream out=null;

    try

    {

    out=serialPort.getOutputStream();

    out.write(dataPackage);

    out.flush();

    } catch (IOException e)

    {

    e.printStackTrace();

    }finally

    {

    //关闭输出流

    if(out!=null)

    {

    try

    {

    out.close();

    out=null;

    System.out.println("数据已发送完毕!");

    } catch (IOException e)

    {

    e.printStackTrace();

    }

    }

    }

    }

    /*

    * 上位机接收数据

    * 串口对象seriesPort

    * 接收数据buffer

    * 返回一个byte数组

    */

    public static byte[] uartReceiveDatafromSingleChipMachine(SerialPort serialPort)

    {

    byte[] receiveDataPackage=null;

    InputStream in=null;

    try

    {

    in=serialPort.getInputStream();

    //获取data buffer数据长度

    int bufferLength=in.available();

    while(bufferLength!=0)

    {

    receiveDataPackage=new byte[bufferLength];

    in.read(receiveDataPackage);

    bufferLength=in.available();

    }

    }

    catch (IOException e)

    {

    e.printStackTrace();

    }

    return receiveDataPackage;

    }

    通过以上关于Uart两个基本类实现对底层Uart的功能封装,其中一个类主要负责Uart串口自检和基本设置,另外一个类主要has数据传输的两个方法。接下来以一个实例说一说通过RS485串口通信将系统当前时间发送至单板机系统。

    step 4:实现实时系统时间的数据包传输至下位机

    这一步可以分为以下两个步骤:首先实现获取系统时间,将时间进行封装成帧;另外就是通过RS485串口将时间数据包发送至单板机系统进行解析。

    1) 系统时间的获取

    根据java面对对象设计思想,这里将有关系统时间的方法归为一类。

    以下是获取当前系统时间代码:

    public static String getCurrentDateTime()

    {

    //单例模式

    Calendar calendar=Calendar.getInstance();

    int year = calendar.get(Calendar.YEAR);//获取年份

    int month=calendar.get(Calendar.MONTH);//获取月份

    int day=calendar.get(Calendar.DATE);//获取日期

    int minute=calendar.get(Calendar.MINUTE);//分

    int hour=calendar.get(Calendar.HOUR);//小时

    int second=calendar.get(Calendar.SECOND);//秒

    String curerentDateTime = year + " " + (month + 1 )+ " " + day + " "+ (hour+12) + " " + minute + " " + second + " ";

    timeCheckSum=year+(month+1)+day+(hour+12)+minute+second;

    return curerentDateTime;

    }

    java 提供了calender类,该类提供了一些与时间有关方法。至于Calendar.getInstance()使用单例模式获取一个Calendar实例对象,单例模式就是一个类在任何时候只允许有一个实例化对象。获取系统时间除了使用Calendar还可以使用Date类,通过创建对象也可以实现系统当前时间的获取。timeCheckSum作为时间数据的校验和发送至单板机作为自定义协议的一部分。

    由于发送的数据包通常是以字节(byte)为单位进行发送和传输的,因此需要将int型的时间转换为byte使用byte[]进行存储,作为一个数据包发送。

    /*

    * 将以上时间字符串进行隔开用byte[]保存

    */

    public static byte[] dateTimeBytesGet(String currenDateTime)

    {

    //对当前时间参数进行格式判断

    //对格式进行判断

    int rawDataSize=6;

    byte[] dateTimeBytes=new byte[rawDataSize+1];

    String[] currentDateTimeSplit=currenDateTime.split(" ");

    if(currentDateTimeSplit.length==rawDataSize)

    {

    //时间数据格式正确

    //eg 2016 12 23 22 18 26

    //使用byte[]进行存储时需要 -128~+127

    //对于年份使用两个byte存储

    for(int dataIndex=0;dataIndex

    {

    int dateTemp=Integer.parseInt(currentDateTimeSplit[dataIndex]);

    if(dataIndex==0)

    {

    byte H8bits=(byte)((dateTemp)>>8);

    byte L8bits=(byte)((dateTemp)&0xff);

    dateTimeBytes[dataIndex]= H8bits;

    dateTimeBytes[dataIndex+1]= L8bits;

    }

    dateTimeBytes[dataIndex+1]=(byte)dateTemp;

    }

    }else

    {

    System.out.println("当前时间获取出现异常数据");

    System.exit(-1);

    dateTimeBytes=null;

    }

    return dateTimeBytes;

    }

    以上数据可以使用7个byte对时间数据进行存储,因为年份需要使用两个字节来存储,格式为高字节在前,低字节在后,之后依次存放。

    将时间数据存放在byte[]数组以后接下来就是添加自己的协议部分了。该部分具有较大的随意性,因为该协议可以根据不同的风格有不同的形式。为了简单起见,只需要在时间数据byte[]之前添加head、CMD、时间数据长度length这三个字节进行补充,时间数据byte[]后面依次添加校验和的高低字节以及tail指令即可。以上基本实现了一个简单的时间数据package。以下是本模块的代码:

    /*

    * 将数组封装成帧

    * 每一个数据帧由以下几个部分组成

    * 1)数据包头部 head 0X2F

    * 2)数据包命令 CMD 0X5A

    * 3)数据个数 length of data 7

    * 4)校验和 H8/L8 byte of check sum(高字节在前 低字节在后)

    * 5)数据结尾标志 tail OX30

    * 6)可采用线程进行获取当前时间

    */

    public static byte[] makeCurrentDateTimefromStringtoFramePackage(byte[] dateTimeBytes)

    {

    //在时间byte[]前后添加一些package校验信息

    int dataLength=13;

    byte[] terimalTimePackage=new byte[dataLength];

    //装填信息

    //时间数据包之前的信息

    terimalTimePackage[0]=0x2F;

    terimalTimePackage[1]=0X5A;

    terimalTimePackage[2]=7;

    //计算校验和

    //转化为无符号进行校验

    for(int dataIndex=0;dataIndex

    {

    terimalTimePackage[dataIndex+3]=dateTimeBytes[dataIndex];

    }

    //将校验和分为高低字节

    byte sumH8bits=(byte)((timeCheckSum)>>8);

    byte sumL8bits=(byte)((timeCheckSum)&0xff);

    terimalTimePackage[10]=sumH8bits;//高字节在前

    terimalTimePackage[11]=sumL8bits;//低字节在后

    //数据包结尾

    terimalTimePackage[12]=0X30;

    return terimalTimePackage;

    }

    下面给出了将时间数据byte数组进行解析的debug代码,一方面是确定上位机本部分模块的程序可靠性,另外也可以直接移植到下位机对数据包的解析之中。在下位机解析过程中需要注意一点:因为在java中8大基本类型都是带符号,年份时间和时间校验和拆分为高低字节时,低字节是二进制无符号的,但是计算机却是按照有符号数(补码方式)进行读取,例如在2016年转换为二进制数为:11111100000,那么高字节为00000111,低字节为11100000。计算机读取为:高字节为7,低字节为-32。其实由两个byte真实还原的过程应为:7<<8+(低字节二进制数字)=7*256+224=2016,因此在debug解析时间数据包时需要将有符号数字转换为无符号数字。

    /*

    * 对时间格式进行解析并还原原来的时间格式

    * 对数据进行还原

    * 仅限于debug使用

    */

    public static String dateTimeBytesfromTostring(byte[] currentDateTime)

    {

    String string="";

    if(currentDateTime.length==7)

    {

    string=((currentDateTime[0]<<8)+bytetoUnsigendInt(currentDateTime[1]))+" "+currentDateTime[2]+" "+

    currentDateTime[3]+" "+currentDateTime[4]+" "+currentDateTime[5]+" "+

    currentDateTime[6];

    }

    return string;

    }

    /*

    * 将byte转化为字符串

    * 将有符号byte转化为无符号数字

    * debug使用

    */

    public static int bytetoUnsigendInt(byte aByte)

    {

    String s=String.valueOf(aByte);

    //System.out.println(s);

    int bytetoUnsigendInt=0;

    for(int i=0;i

    {

    if(s.charAt(i)!='0')

    {

    bytetoUnsigendInt+=1<

    }

    }

    return bytetoUnsigendInt;

    }

    2)将最后的时间数据包通过RS485串口发送至下位机

    结合前面的串口程序就可以使用串口发送程序了。在程序debug的前期可以在程序的关键位置输出日志就是打印log的方法可以提高程序调试的效率。以下是主类的测试代码:

    //取出第一个COM端口进行测试

    SerialPort serialPort=UARTParameterSetup.portParameterOpen(arraylist.get(0), 57600);

    //退出程序 后续不需要监测 因为transimit一直需要保证连接状态

    //System.exit(0);

    DataTransimit.uartSendDatatoSerialPort(serialPort, dataFrame);

    String currentDateTime=SystemDateTimeGet.getCurrentDateTime();

    System.out.println(currentDateTime);

    byte[] bytes=SystemDateTimeGet.dateTimeBytesGet(currentDateTime);

    //System.out.println(Arrays.toString(bytes));

    String str=SystemDateTimeGet.dateTimeBytesfromTostring(bytes);

    System.out.println(str);

    //System.out.println(SystemDateTimeGet.bytetoUnsigendInt((byte) -32));

    byte[] terimalTimeByte=SystemDateTimeGet.makeCurrentDateTimefromStringtoFramePackage(bytes);

    System.out.println(Arrays.toString(terimalTimeByte));

    DataTransimit.uartSendDatatoSerialPort(serialPort, terimalTimeByte);

    以下是测试结果:

    当没有串口设备接入计算机时控制台打印一条信息:

    没有找到可用的串口端口,请check设备!

    当RS485设备接入计算机时,控制台打印消息如下:

    96255068_5

    通过以上几个步骤基本实现了上位机与下位机串口通信的功能,接下来还可以对程序进行改进:

    1)添加界面,可以类比串口助手界面根据自身需要设计独具风格的人机交互界面。

    2) 在程序中添加线程,在以上程序中对于系统时间的获取可以通过线程的方式进行获取,这样上位机就可以一直往下位机发送数据包,而不是仅仅发一次。

    3)对于上位机数据接收,除了以上最基本的接收功能外,还可以使用JDBC与mysql等数据进行存储,并绘画数据曲线实现特性分析。

    展开全文
  • QT实现串口通信步骤以及问题记录,小白文,大佬轻锤,欢迎指错。
  • 上位机下位机通信

    2017-12-08 17:34:42
    上下位机通信程序,使用socket套接字,c++控制台程序
  • 介绍了PC机与8051单片机的串口通信的初步知识,比较适合通信的初学者。
  • 一、测试框架所使用的示例命令定义 本框架的示例代码所使用的命令协议规定如下: CRC校验码 = [命令类型码 + 命令码 + 命令参数] 一个完整封包 = [包头 + 包长 + 命令类型码 + ...0x01:上位机下位机设置的命...
  • 一、前言 在上一章中已经介绍了协议设计和封包设计。那么怎样把这些设计优美的落实为代码的形式呢。使用几个函数就可以实现,但是不够优美和实用,因此本章主要介绍一个...二、通信构架的需求分析(框架具有的功能...
  • 基于stm32的嵌入式开发程序,可用于数据读取存储,同时上位机与下位机通信,可以修改通信协议,关于停止位,奇偶校验位等均可通过程序设置
  • 上位机与下位机通信的设计初步 讲述c8051单片机的上位机与下位机通信的设计 了解其中的方法步骤
  • 上位机下位机串口通信大连海事大学课程设计报告课程名称:计算机微机原理课程设计成 员:成 员1:2220133293 范凯锋成 员2:2220132642 唐绍波成 员3:2220130079 曹晓露设计时间:2016年3月7日至3月18日考核记录及...
  • 实现上位机与下位机通信做出控制方案,通过VB开发控制界面;
  • QT5实现串口收发数据(上位机与下位机通信

    万次阅读 多人点赞 2019-07-18 17:27:09
    最近帮老师做一个应用程序,通过上位机与下位机进行串口通信,最后实现实时绘图,通过几天努力,成功实现蓝牙串口通信。 参考博客1 注意:代码中一些串口无关代码,可以忽略掉 一、QT5串口基础知识 1. pro文件...
  • C#的串口通信上位机下位机源程序,适合新手入门操作
  • 摘要:VB源码,系统相关,串口通信 VB编写的串口通信程序,主要用于上位机与下位机间的通信,VB串口通讯程序源码下载,截图如上图所示。
  • 本实例为完整的上位机下位机通信代码,按照规定上位机传送协议将BMP图片装维十六进制数据发送到指定端口,下位机程序接收完成后存储。内含spcomm通信全套设置、Bmp图片十进制转十六进制数据,按照每帧字节数限制、...
  • 项目需求:需要通过使用USB完成上位机与下位机(STM32)的数据通信,使用C#怎么编写?解决方案15上下位机通讯是个大课题也是很多人用C#做工业项目的课题首先上下位机两个有线通信方式一个是串口一个是以太网你用usb那...
  • 主要介绍了C# WPF上位机实现和下位机TCP通讯的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 基于C#的串口通信上位机下位机源程序文件.pdf基于C#的串口通信上位机下位机源程序文件.pdf基于C#的串口通信上位机下位机源程序文件.pdf基于C#的串口通信上位机下位机源程序文件.pdf基于C#的串口通信上位机和...
  • #include #define SYSCLK 22118400 // 系统时钟频率22.1184MHz#define BAUDRATE 115200 // BAUDRATE#define TIMER2_RELOAD 0x0000sfr16 RCAP2 = 0xCA; ...
  • 在 IDE 中,通过这种关联,在特定节点以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件“源文件”筛选器关联。 MFCTCP2.h 这是应用程序的主头文件。 其中包括其他项目特定的标头(包括 Resource.h)...
  • c8051f320通过usb实现上位机与下位机通信,包括上位机与下位机的程序

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,847
精华内容 2,738
关键字:

上位机与下位机通信

友情链接: LiveFolderInfo.rar