|
Just an update on things I found using my oscilloscope and looking at what is being sent back and forth between the PC and PIC.
I wish I could upload a screen shot from the scope.
The PC is sending out three (sometimes two) data bursts. The first is obviously the
"UPLOAD NEW MAP\n" command to the PIC. What the other two data burst are, I don't know yet.
The PIC is replying twice, as would be expected if the data bursts contain the '\n' char.
So looks like my first objective is to find out why
COMPORT << "UPLOAD NEW MAP" << endl;
is sending data out more than once.
Any ideas would be appreciated.
OdlG
|
|
|
|
|
I did a little debugging on the PIC side and see that the PC app is echoing back the data it gets from the PIC.
Any ideas why and how to stop that?
Thanks?
OdlG
|
|
|
|
|
That is probably because the serial device has echoing enabled. Such is used with serial terminals. When a character is received, it is send back to be displayed in the terminal (which usually does not display the character locally before sending it but waits for the echoed character to display that).
To avoid that you have to disable echoing by configuring the serial device initially:
stty -F /dev/ttyACM0 raw pass8 -echo -hup -clocal 9600 Note that it might be necessary to specify also other settings, remove some used above, and change the baud rate. See stty(1): change/print terminal line settings - Linux man page[^] for the available options. Before changing the options I suggest to execute
stty -F /dev/ttyACM0 to check the current settings.
As already mentioned by others it is rather uncommon to use C++ streams with serial ports. The common method is using termios and the IO functions from the standard C library, or a library like Serial Ports - 1.65.0[^] from Boost.Asio - 1.66.0[^] .
Related reads:
Serial HOWTO[^]
Serial Programming/Serial Linux - Wikibooks, open books for an open world[^]
|
|
|
|
|
Jochen Arndt wrote: The common method is using termios and the IO functions from the standard C library
Thank you for the reply. It is ironic that I ran across this yesterday and was able to write something that used this successfully. I will use this in my final code I am sure. My issue now is the write command, it seems to be char based, so I'm working on sending a char buffer.
Once again, thanks to you and the forum, I really appreciate it.
OdlG
|
|
|
|
|
Thanks for the help so far. I have written a small PC App program to try termios out.
I am having two issues.
First is the first time I send data to the PIC, the return response is junk.
Second is the response is out of sync with the sent command, it takes sending the command a second time for the response string to be correct. Would appreciate a second pair of eyes to see what is the problem.
PC app code ..
int main(int argc, char** argv)
{
ComPort MyComPort;
char TXBuf[255];
char RXBuf[255];
char c;
while(1)
{
cout << "(U)load New Map, (N)ext Map, (Q)uit .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'Q') return 0;
if (c == 'U')
{
strcpy(TXBuf, "UPLOAD NEW MAP\n");
MyComPort.WriteComPortDataMsg(TXBuf, strlen(TXBuf));
MyComPort.ReadComPortDataMsg(RXBuf);
cout << "RXBuf = " << RXBuf << endl;
}
else if (c == 'N')
{
strcpy(TXBuf, "NEXT MAP\n");
MyComPort.WriteComPortDataMsg(TXBuf, strlen(TXBuf));
MyComPort.ReadComPortDataMsg(RXBuf);
cout << "RXBuf = " << RXBuf << endl;
}
cout << endl;
}
return 0;
}
class defs ..
class ComPort
{
public:
ComPort();
~ComPort();
void WriteComPortDataMsg(char *data, char num);
void ReadComPortDataMsg(char *data, char dlimc);
void ReadComPortDataMsg(char *data);
void SetDlimiter(char dlimc);
private:
char dlim;
struct termios config;
int hComPort;
bool Error;
};
void ComPort::WriteComPortDataMsg(char *data, char num)
{
for (int cnt = 0; cnt < num; cnt++)
{
write(hComPort, &data[cnt], 1);
}
tcdrain(hComPort);
}
void ComPort::ReadComPortDataMsg(char *data)
{
int cnt = 0;
char c;
data[0] = 0x00;
do
{
read(hComPort, &c, 1);
if ((c >= 0x20) && (c <= 0x7E)) data[cnt++] = c;
} while ((c != dlim) && (cnt < 255));
data[cnt++] = '\0';
}
responses ..
(U)load New Map, (N)ext Map, (Q)uit ..
u
RXBuf = MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
(U)load New Map, (N)ext Map, (Q)uit ..
u
RXBuf = Processing: UPLOAD NEW MAP
(U)load New Map, (N)ext Map, (Q)uit ..
u
RXBuf = Processing: UPLOAD NEW MAP
(U)load New Map, (N)ext Map, (Q)uit ..
n
RXBuf = Processing: UPLOAD NEW MAP
(U)load New Map, (N)ext Map, (Q)uit ..
n
RXBuf = Processing: NEXT MAP
(U)load New Map, (N)ext Map, (Q)uit ..
n
RXBuf = Processing: NEXT MAP
(U)load New Map, (N)ext Map, (Q)uit ..
q
RUN FINISHED; exit value 0; real time: 13s; user: 0ms; system: 0ms
As you can see the first command response gets junk in RXBuf. The second time response is correct.
Changing the command to "Next Map" gets incorrect response the first time but OK response the second.
Any ideas would be appreciated.
Thanks
OdlG
|
|
|
|
|
I did not see any code that initialises the serial port. So it will still use the current settings as shown by the stty command line tool. You have to use tcsetattr to select the same basic settings as on the PIC (probably 8N1; baud rate seems to mach already), disable all kinds of flow control (software and hardware), and all additional features like echoing.
Example:
memset(&config, 0, sizeof(config));
config.c_cflag = CS8 | CREAD | CLOCAL; cfsetospeed(&config, B9600); cfsetispeed(&config, B9600);
tcsetattr(hComPort, TCSANOW, &config); With new line terminated data transfers, you may also use the canonical mode (ICANON ).
|
|
|
|
|
Jochen Arndt wrote: I did not see any code that initialises the serial port.
There is, I was having trouble with the forum editor, thought maybe my code was too long.
The first part is here ..
class ComPort
{
public:
ComPort();
~ComPort();
void WriteComPortDataMsg(char *data, char num);
void ReadComPortDataMsg(char *data, char dlimc);
void ReadComPortDataMsg(char *data);
void SetDlimiter(char dlimc);
private:
char dlim;
struct termios config;
int hComPort;
bool Error;
};
ComPort::ComPort()
{
dlim = '\n';
char c;
Error = false;
hComPort = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY);
if (hComPort == -1)
{
cout << "failed to open port\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
if(!isatty(hComPort))
{
cout << "file descriptor is not pointing to a TTY device\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin.get(c);
if ((c == 's' || c == 'S')) Error = true;
}
if(tcgetattr(hComPort, &config) < 0)
{
cout << "configuration of the serial interface not found\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
config.c_oflag = 0;
config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
config.c_cflag &= ~(CSIZE | PARENB);
config.c_cflag |= CS8;
if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0)
{
cout << "Baud Rate did not set correctly\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
if(tcsetattr(hComPort, TCSAFLUSH, &config) < 0)
{
cout << "Configuration set did not work\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
}
ComPort::~ComPort() { close(hComPort); }
So, any ideas?
Thanks for the reply also!!
OdlG
|
|
|
|
|
Use the code from my post. It sets all while you are modifying the existing where something might be still set / cleared. CREAD is probably set but what about CLOCAL ?
|
|
|
|
|
Jochen Arndt wrote: CREAD is probably set but what about CLOCAL ?
I had the same code except .. memset
if(tcgetattr(hComPort, &config) < 0)
{
cout << "configuration of the serial interface not found\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
memset(&config, 0, sizeof(config));
this is baudrate code ..
config.c_cflag = CS8 | CREAD | CLOCAL;
if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0)
{
cout << "Baud Rate did not set correctly\n" << endl;
cout << "Hit <S> to Stop .." << endl;
cin >> c;
c = (char)toupper(c);
if (c == 'S') Error = true;
}
I get the same result as before.
OdlG
|
|
|
|
|
The source is probably here:
read(hComPort, &c, 1);
if ((c >= 0x20) && (c <= 0x7E)) data[cnt++] = c; See read(2): read from file descriptor - Linux man page[^]:
Quote: Return Value
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.
read() will return zero when there are no data available. So you have either to clear your c variable before, or - better - check the return value:
int received = read(hComPort, &c, 1);
if (received == 1 && c >= 0x20 && c <= 0x7E)
data[cnt++] = c;
else if (received < 0)
Another option is setting blocking mode (attributes c_cc[VMIN] to non zero and c_cc[VTIME] to the timeout value). Then you will not get zero return values but still have to check for negative return values indicating timeout or other errors.
|
|
|
|
|
I was beginning to think I needed some way to wait on the USB/Com Port, but wasn't sure how to do that.
This works great. My code change ..
void ComPort::ReadComPortDataMsg(char *data)
{
int cnt = 0;
int received;
char c;
data[0] = 0x00;
do
{
received = read(hComPort, &c, 1);
if ((received == 1) && (c >= 0x20) && (c <= 0x7E))
{
data[cnt++] = c;
}
else if (received < 0)
{
}
} while ((c != dlim) && (cnt < 255));
data[cnt++] = '\0';
}
Not sure how to deal with received < 0. Do you know where can I read up on
struct termios and
global errno variable
?
Thanks!!!
OdlG
|
|
|
|
|
|
Question on how to error handle.
I am falling into the received < 0 but if I just ignore it and keep looking for chars, I get a complete, correct message. Is it safe to do that or should I clear the error? And How?
Thanks in advance
OdlG
|
|
|
|
|
What is the error (errno ) code then?
|
|
|
|
|
Looks like it is 11
modified 14-Jul-18 16:04pm.
|
|
|
|
|
That is EGAIN (you can look it up in errno.h and get the corresponding error message with strerror(3) - Linux manual page[^] ) and in most cases (as here) not an error. You are just calling read again too fast (the next character has not been received so far).
To avoid this you can set a timeout value. Then the read() will block until data are available or a timeout occured.
|
|
|
|
|
hi,
Which do I use ide for learn c ++ ?.
|
|
|
|
|
|
vi (vim perhaps) on a Linux box.
IDEs are for sissies.
|
|
|
|
|
CPallini wrote: IDEs are for sissies.
So are displays and WYSIWYG editors. An ASR-33 and ed are all you need!
(And yes, I'm old enough to have actually written programs that way. )
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
I am young enough to continue using vim .
|
|
|
|
|
Hello, there's a code.
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
int main()
{
char* x = (char*)calloc(sizeof(char),150);
char* p = x;
free(x);
x = NULL;
.....
return 0;
}
<pre>
Tell me how to get NULL under p when x is removed and set to NULL.
|
|
|
|
|
You have to set it explicitly, there is no garbage collector or reference counts in C. The two pointers have no connection, so when you free x , pointer p still points to the original block of memory. You must be very careful to manage dynamic memory properly in C, and even in C++.
|
|
|
|
|
You have not a pointer to pointer. As Richard correctly pointed out, you have two independent pointers to the same block of memory; you have to explicitely set p = NULL; .
With a pointer to pointer the scenario would change:
char* x = (char*)calloc(sizeof(char),150);
char** p = &x;
free(x);
x = NULL;
printf("%p\n", *p);
|
|
|
|
|
CPallini wrote: *p is NULL But p still holds the address of x.
|
|
|
|
|