PyQt 开发采用串口通信的上位机软件(2)

本博客的所有原创文章采用创作公用版协议。要求署名、非商业用途和保持一致。要求署名必须包含我的网名(geokai)以及文章来源(选择博客首地址或者具体博文地址)。

商业性使用须预先征得本人同意(发送Email到 [email protected]).

3.2 串口的操作之Pyserial

  Python有自己的专用Serial的库,没有仔细阅读qt的代码不太清楚pyqt的串口库是否继承这个库。

  说说为什么又突然转到了这个库呢,因为上位机与下位机计划采用ModBus作为通信协议。本以为PyQt是完全继承C++ Qt的全部类,但是不行的是并不这样。所有的QModbus开头的类在pyqt文档中都没有找到。所以完全以PyQt做全部上位机功能看来不太容易实现。搜索了一番发现Python有一个Modbus_tk这个库,封装了Modbus的相关功能,包括rtu,ascii协议模式,master和slave节点模式。并且这个库直接继承了Pyserial库。OK既然这样那串口的操作直接使用pyserial库会更简单和高效。

  Pyserial的官方文档地址https://pythonhosted.org/pyserial/

  3.2.1 列出可用串口

    import serial.tools.list_ports #相应的函数在这个包下面,直接import serial是无法使用这个工具包的

    com_list = serial.tools.list_ports.comports() # 返回一个serial.tools.list_ports.ListPortInfo的列表对象,每个元素均含有串口的多个属性

    for port in com_list:

      print(port.device) #返回端口号如COM3

      print(port.describe) #返回设备名字

      print(port.location) #返回设备在计算机上的位置

    

    获得这些比较重要的信息后就可以打开端口了

  3.2.2 打开串口

    import serial

    ser = serial.Serial(com_list[0].device, baudrate=19200, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False) #采用直接给参数的方法配置创建一个Serial实例对象,执行完后会直接打开端口

    

    ser = serial.Serial()

    ser.baudrate=19200

    ser.port=com_list[0].device

    ser.open() #这种方式可以先配置后打开

    ser.close()

  

  3.2.3串口的异常状态判断

    之前采用QSerialPort()类时,有一个error()方法可以判断串口的状态。但是PySerial似乎没有这么完善的功能。我能力不足没办法把这个功能实现,这里介绍一个自己想的能有基本功能的方法。

    串口正常打开后,哪怕拔掉设备,使用ser.is_open判断串口状态始终是True。但是通过获取串口信息则得到一个空的对象。基于这样的想法重新写一下打开串口并通过串口发送数据的代码。

    

    ser =serial.Serial()
    ser.baudrate=19200
    ser.port='COM3'
    ser.timeout=1

    try:
      ser.open()
      print('Opened')
    except serial.SerialException:
      print('error')
 
    time.sleep(5) #暂停一下,拔掉串口设备
 
    if ser.is_open and len(list(lp.grep(ser.port))) !=0: #
      print('able to send')
      
      for i in range(3):
        try:
          ser.write(b'aaaaaaaaaaaaaaaaaaaa')
          print('send ok')
          break
        except serial.SerialException:
          print('re-try')
          ser.close()
          ser.open()
        if i==2:
          ser.close()
          print('port error')
    else:
      ser.close()
      print('port error')

    

4.通信部分

  这一部分主要实现上位机软件与下位机的通信实现。

  4.1上位机modbus master

    上位机的modbus实现采用python现有的modbus_tk库来实现,贴上基本代码

import modbus_tk
import modbus_tk.defines as cst
import serial
from modbus_tk import modbus_rtu
import time 

PORT = 'COM3'

def main(master):

  try:
    #Connect to the slave
     master.set_timeout(1)
    master.set_verbose(True)
    x = master.execute(1, cst.READ_INPUT_REGISTERS, 4,1)  #读取下位机的寄存器值,地址为4,,长度为1,采用cst.READ_INPUT_REGISTERS  与   cst.READ_HOLDING_REGISTERS方法效果是一致的
    if len(x)>0:
      print(x[0]) #此处是获取的值,注意符号,本例中下位机也使用16位无符号数

    
  except :
    print('unknown' error)

if __name__ == "__main__":
  master = modbus_rtu.RtuMaster(
    serial.Serial(port=PORT, baudrate=19200, bytesize=8, parity='N', stopbits=1, xonxoff=0))
  while True:
    main(master)
    tme.sleep(1)
 

  4.2下位机modbus部分基于Arduino

    采用arduino的modbus库,其中设置一个地址为4的寄存器。

    

/**
* Modbus slave example 2:
* The purpose of this example is to link the Arduino digital and analog
* pins to an external device.
*
* Recommended Modbus Master: QModbus
* http://qmodbus.sourceforge.net/
*
* Editado al español por LuxARTS
*/

//Incluye la librería del protocolo Modbus
#include <ModbusRtu.h>
#include <stdlib.h>
#define ID 1

//Crear instancia
Modbus slave(ID, 0, 0); //ID del nodo. 0 para el master, 1-247 para esclavo
//Puerto serie (0 = TX: 1 - RX: 0)
//Protocolo serie. 0 para RS-232 + USB (default), cualquier pin mayor a 1 para RS-485
boolean led;
int8_t state = 0;
unsigned long tempus;

uint16_t au16data[9]; //La tabla de registros que se desea compartir por la red

/*********************************************************
Configuración del programa
*********************************************************/
void setup() {
io_setup(); //configura las entradas y salidas

slave.begin(19200); //Abre la comunicación como esclavo
tempus = millis() + 100; //Guarda el tiempo actual + 100ms
digitalWrite(13, HIGH ); //Prende el led del pin 13 (el de la placa)
au16data[4] = 0x8000;
}

/*********************************************************
Inicio del programa
*********************************************************/
void loop() {
//Comprueba el buffer de entrada
state = slave.poll( au16data, 9 ); //Parámetros: Tabla de registros para el intercambio de info
// Tamaño de la tabla de registros
//Devuelve 0 si no hay pedido de datos
//Devuelve 1 al 4 si hubo error de comunicación
//Devuelve mas de 4 si se procesó correctamente el pedido

if (state > 4) { //Si es mayor a 4 = el pedido fué correcto
tempus = millis() + 50; //Tiempo actual + 50ms
digitalWrite(13, HIGH);//Prende el led
}
if (millis() > tempus) digitalWrite(13, LOW );//Apaga el led 50ms después

//Actualiza los pines de Arduino con la tabla de Modbus
io_poll();

}

/**
* pin maping:
* 2 - digital input
* 3 - digital input
* 4 - digital input
* 5 - digital input
* 6 - digital output
* 7 - digital output
* 8 - digital output
* 9 - digital output
* 10 - analog output
* 11 - analog output
* 14 - analog input
* 15 - analog input
*
* pin 13 reservado para ver el estado de la comunicación
*/
void io_setup() {
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(13, OUTPUT);

digitalWrite(6, LOW );
digitalWrite(7, LOW );
digitalWrite(8, LOW );
digitalWrite(9, LOW );
digitalWrite(13, HIGH ); //Led del pin 13 de la placa
analogWrite(10, 0 ); //PWM 0%
analogWrite(11, 0 ); //PWM 0%
}


/*********************************************************
Enlaza la tabla de registros con los pines
*********************************************************/
void io_poll() {
long temp;
// digital inputs -> au16data[0]
// Lee las entradas digitales y las guarda en bits de la primera variable del vector
// (es lo mismo que hacer una máscara)
bitWrite( au16data[0], 0, digitalRead( 2 )); //Lee el pin 2 de Arduino y lo guarda en el bit 0 de la variable au16data[0]
bitWrite( au16data[0], 1, digitalRead( 3 ));
bitWrite( au16data[0], 2, digitalRead( 4 ));
bitWrite( au16data[0], 3, digitalRead( 5 ));

// digital outputs -> au16data[1]
// Lee los bits de la segunda variable y los pone en las salidas digitales
digitalWrite( 6, bitRead( au16data[1], 0 )); //Lee el bit 0 de la variable au16data[1] y lo pone en el pin 6 de Arduino
digitalWrite( 7, bitRead( au16data[1], 1 ));
digitalWrite( 8, bitRead( au16data[1], 2 ));
digitalWrite( 9, bitRead( au16data[1], 3 ));

// Cambia el valor del PWM
analogWrite( 10, au16data[2] ); //El valor de au16data[2] se escribe en la salida de PWM del pin 10 de Arduino. (siendo 0=0% y 255=100%)
analogWrite( 11, au16data[3] );


temp=au16data[4];
// Serial.write(temp>>16);
// Serial.write(temp>>8);
// Serial.write(temp);

// Lee las entradas analógicas (ADC)

temp=(long)temp+(long)(random(-3,4));
//temp=-1;
// Serial.write(temp>>24);
// Serial.write(temp>>16);
// Serial.write(temp>>8);
// Serial.write(temp);


temp=temp<0?0:temp;
temp=temp>65535?65535:temp;


//temp=temp<0?0:temp;
//temp=temp>65535?65535:temp;
au16data[4] = temp;
//au16data[4] = byte(random(65530,65535));//analogRead( 0 ); //El valor anaalógico leido en el pin A0 se guarda en au16data[4]. (siendo 0=0v y 1023=5v)
au16data[5] = 0xffff;

// Diagnóstico de la comunicación (para debug)
au16data[6] = slave.getInCnt(); //Devuelve cuantos mensajes se recibieron
au16data[7] = slave.getOutCnt(); //Devuelve cuantos mensajes se transmitieron
au16data[8] = slave.getErrCnt(); //Devuelve cuantos errores hubieron
}

   

    

#至此基本的框架已经搭出来了,上位机与下位机的通讯也打通了。硬件上的问题已经基本解决

后面开始专注的GUI的编写。

  

      

作者:geokai

出处: https://www.cnblogs.com/geokai/

本文版权归作者geokai和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

如有问题, 可邮件([email protected])咨询.

    

    

  

猜你喜欢

转载自www.cnblogs.com/geokai/p/9568675.html