Bonjour,
Sur une plateforme Compute Module 3 avec un transceiver RS232=>RS485, je dois interroger un équipement en Modbus.
Entre l'écriture et la lecture je dois piloter une sortie (GPIO 12) pour activer la lecture au l’écriture du transceiver.
Avec le code ci dessous j'obtiens une réponse incomplète et très aléatoire. Soit pour la réponse réelle 01 03 04 17 12 03 21 9F 6A j'obtiens :
03 04 17 12 03 21 9F 6A
ou
17 12 03 21 9F 6A
et quelques fois rien
On dirait que la lecture ne se déclenche pas assez rapidement après l'écriture et le pilotage du transceiver.
Est-ce un problème de configuration ou peut-être la façon de faire ?
D'avance merci
gcc -Wall -o serial serial.c -lwiringPi
Voici le code :
#include <sys/resource.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h>/* POSIX Terminal Control Definitions*/
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <wiringPi.h> /*pilotage du GPIO */
int main(int argc, char **argv)
{
//Test Priorité de l'application :
int which = PRIO_PROCESS;
id_t pid;
int ret;
int priority = -20;
pid = getpid();
ret = getpriority(which, pid);
ret = setpriority(which, pid, priority);
ret = getpriority(which, pid);
printf("Priorité = %i\n",ret);
//Ouverture et configuration du port
int fd;
fd = open("/dev/ttyAMA0",O_RDWR | O_NOCTTY);
//printf("fd = %i\n",fd);
if (fd == -1) printf("Pb d'ouverture du périphérique\n");
else {
printf("Ouverture du port OK\n");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B9600); /* Set Read Speed as 9600 */
cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */
/* 8N1 Mode */
SerialPortSettings.c_cflag |= PARENB; /*EVEN.... Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 0; /* Read at least 10 characters */
SerialPortSettings.c_cc[VTIME] = 5; /* Wait 500ms */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("Configuration OK\n");
/*------------------------------- Write data to serial port -----------------------------*/
//Activation de l'ecriture sur le transceivers RS485 => PIN 12 :ON
wiringPiSetup ();
pinMode (12, OUTPUT);
digitalWrite (12, 1);
char write_buffer [] = "\x01\x03\x40\x00\x00\x02\xD1\xCB";
//char write_buffer [] = "\x01\x03\x30\x20\x00\x02\xCA\xC1"; /* Buffer ForwActiv */
//char write_buffer [] = "\x01\x03\x30\x40\x00\x02\xCA\xDF"; /* Buffer RevActiv */
//char write_buffer [] = "\x01\x03\x20\x00\x00\x02\xCF\xCB"; /* Buffer Voltage */
int bytes_written = 0; /* Value for storing the number of bytes written to the port */
bytes_written = write(fd,write_buffer,sizeof(write_buffer));/* use write() to send data to port */
/* "fd" - file descriptor pointing to the opened serial port */
/* "write_buffer" - address of the buffer containing data */
/* "sizeof(write_buffer)" - No of bytes to write */
//printf("\n %s ecrit sur le périphérique",write_buffer);
printf("\n %d octets ecrit sur le périphérique\n", bytes_written);
//Activation de la lecture sur le transceiver RS485 => PIN 12 :OFF
//delay (90); /test delai
if (tcdrain(fd) != 0){
perror("tcdrain() error");
}
digitalWrite (12, 0);
/*------------------------------- Read data from serial port -----------------------------*/
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
char read_buffer[32]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int i = 0;
bytes_read = read(fd,&read_buffer,8); /* Read the data */
printf("Bytes Rxed -%d\n", bytes_read); /* Print the number of bytes read */
for(i=0;i<bytes_read;i++) /*printing only the received characters*/
printf("%x",read_buffer[i]);
close(fd);
printf("\nfermeture du port\n");
}
return 0;
}
# tcflush
Posté par goeb . Évalué à 3.
C'est probablement la ligne tcflush qui fait perdre les premiers octets.
Tu devrais la supprimer.
Rappel de l'extrait de la man page :
# Complèments
Posté par JLELONG82 . Évalué à 1.
Bonjour,
J'ai essayé sans le tcflush…
Mais il n'y a aucun changement.
Pour info : avec un convertisseur USB/RS485 sur Windows et Easymodbus la com fonctionne très bien…
[^] # Re: Complèments
Posté par plr . Évalué à 2.
En plus du flush à faire non pas après mais avant l'envoi de la trame (conseillé pour éviter que ton buffer soit pollué par d'anciennes trames ou autre), la question qui se pose est le temps de retournement de ton interface de communication:
Ne connaissant pas ces fonctions, je ne peux pas te répondre sur le type/temps d'exécution de ces fonctions. Sont-elles synchrones/asynchrones?
Quel est le temps de retournement du transceiver?
avec un analyseur logique (ou oscillo), tu verrais les signaux et ce serait plus facile à débuggeur: tu verrais peut-être que ton signal de commande arrive au transceiver alors que ton équipement a déjà commencé à répondre…
[^] # Re: Complèments
Posté par JLELONG82 . Évalué à 2. Dernière modification le 10 juillet 2018 à 15:56.
Avec le flush avant l'envoi idem.
Concernant le temps d'application du "digitalWrite", j'ai vérifié à l'oscillo et il est fluctuant (0/+10ms) mais toujours appliqué avant la trame de réponse de l'équipement.
Concernant le temps de retournement du transceiver (ISL83085E), c'est de l'analogique donc très inférieur à la ms.
Voici un graphe l'oscillo (mes diff sur modbus) de mon appli et celui du easymodub (com fonctionnelle à chaque fois). Il n'y a pas de grandes différences à part la fin de la trame d'envoi qui repasse à l'état initial un peu plus tard car lié au pilotage du transceiver (0/+10ms).
Je penche plus pour une mauvaise configuration soft mais peut-être que le hard est en cause.
De mon appli :
De Easy modbus :
[^] # Re: Complèments
Posté par plr . Évalué à 2.
Oui, c'est normal, ce n'est pas ça qui changera quelque chose, c'est juste histoire que le buffer soit propre.
tes oscillogrammes, sont bien pris entre le transceiver et la raspberry? ou sur la 485?… et oui, peut-être faut-il aller gratter du côté du sw…
dans ta config, j'ai l'impression que tu actives la parity (PARENB) alors que le commentaire dit le contraire…
[^] # Re: Complèments
Posté par JLELONG82 . Évalué à 1.
Voici la mesure coté RS485 (jaune) et UART RX CM3 (violet) :
Pour moi CM3 reçoit bien la bonne trame.
Effectivement les commentaires sont faux (copier/coller dsl) mais la config de communication(EVEN) parait bonne car l'équipement répond. Si je le modifie il ne répond plus.
Est-ce un problème d’interprétation de bit de start de la trame de réponse ?
Si c'est un problème de timing soft à la lecture du buffer, comment le visualiser ?
[^] # Re: Complèments
Posté par plr . Évalué à 2. Dernière modification le 11 juillet 2018 à 13:10.
La trame de retour est nickel, on voit bien les premiers octets 0x01, 0x03, 0x04 (héhé, l'habitude du décodage en live de traffic uart!…)
En cherchant un peu sur le net, j'ai vu que tu n'étais pas le premier à qui celà est arrivé:
C language program reading UART loses characters
il faut "disabler" getty dans inittab… lis l'ensemble du post, ça devrait t'éclairer et peut-être résoudre le problème.
[^] # Re: Complèments
Posté par JLELONG82 . Évalué à 1.
Effectivement j'ai "agetty" qui tourne par défaut.
lorsque que je le désactive :
Et que je lance mon programme, la trame est bien envoyé, l'équipement répond mais l'application se met en attente lors du "read". J'ai essayé plusieurs configurations des VMIN et VTIME sans succès.
EDIT :
Pour que mon appli fonctionne il faut masquer "aggety" :
Et miracle… Les trames sont lus à chaque coup !
Un grand MERCI à a2g pour m'avoir mis sur la voie.
Bonne continuation
[^] # Re: Complèments
Posté par JLELONG82 . Évalué à 1.
Par contre comment l'appliquer (le mask) par défaut au démarrage du système ?
[^] # Re: Complèments
Posté par plr . Évalué à 1.
Super que tu tu aies trouvé une solution!
pour le démarrage, on dirait que l'init manager est systemd.
Comme je n'ai pas encore utilisé du systemd sur mes plateformes, je ne peux que t'aiguiller sur la recherche: créer un service dans /etc/systemd/system qui lancera un script où tu mettra tes commandes… Il y a des exemples un peu partout sur le net, et avec un peu de chance il y a déjà ce genre de questions sur le forum! ;-)
ou bien, de lancer la commande juste avant le lancement de ton application.
[^] # Re: Complèments
Posté par claudex . Évalué à 4.
La commande
mask
est permanente. Il n'y a donc pas besoin de l'appliquer au démarrage du système.« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Complèments
Posté par JLELONG82 . Évalué à 2.
Effectivement elle est permanente…
Merci a tous
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.