本博客的所有原创文章采用创作公用版协议。要求署名、非商业用途和保持一致。要求署名必须包含我的网名(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。但是通过获取串口信息则得到一个空的对象。基于这样的想法重新写一下打开串口并通过串口发送数据的代码。
4.通信部分
这一部分主要实现上位机软件与下位机的通信实现。
4.1上位机modbus master
上位机的modbus实现采用python现有的modbus_tk库来实现,贴上基本代码
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])咨询.