Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Using RS-232 Port to communicate with external peripherals
#1
I have a Power Brick controller with an RS-232 port. It the documentation it says:

"In general, this port should not be used to communicate to external peripherals, but rather left in case of the need to debug."

Which tells me there is _some_ way to actually use it to communicate with external devices.

Does anyone have experience doing this?
Reply
#2
Yes – see this application note on the Forums’ FileDepot:
“http://forums.deltatau.com/filedepot/download.php?f=Power PMAC/Application Notes/Serial Communication with Power PMAC in C.pdf”
Reply
#3
I am getting the following error both when I paste in your link and when I manually navigate to the PDF through the filedepot

"File does not exist. Make sure you specified correct file name."
Reply
#4
Ok for some reason it worked today when I tried to download it. Thanks for the pointer this looks like exactly what I need.
Reply
#5
Hi Steve,

I have succeeded in getting my serial device connected to my power pmac controller. I can run my C Background Program if I go to the Task Manager, select the PLC tab, fund my application and click "Start". After doing this I can see the expected output from my serial device in the Unsolicited Messages.

Ideally I would have this C program constantly running and dumping the latest serial message into a variable (my serial device is a laser distance sensor). Then from a motion program or normal PLC I could just read this variable to get the latest distance reading from my attached serial device.

Is this possible? Do I have to worry about locking to precent reading from the variable at the same time my program is writing to it? Would it be OK to have this C background program running on essentially an infinite loop? I would like to keep the serial device open and not open/close for every reading.

Here is the current background program:

// Includes
#include <gplib.h> // Global Gp Shared memory pointer
#include "../../Include/pp_proj.h"
#include "../../Libraries/serialportcomm/serialportcomm.h"
#include <RtGpShm.h> // Global Rt/Gp Shared memory pointers

// Definitions
// Can change this if you expect a longer response from the serial device
#define EXAMPLE_CODE_RESPONSE_MAX 256

// Prototype(s)
int ReadExampleDevice(char *Port);

// This is the main executable location
int main(void)
{
InitLibrary();
ReadExampleDevice(USB1_Port);
CloseLibrary();
return 0;
}

//*************************
// Application code snippet
//*************************

// Global Variables
int SerialPort;

// This routine will break into Linux scheduling when called.
// USB serial ports use the Linux scheduler, not Xenomai RTOS.
// Nothing that needs complete determinism should call this routine.
int ReadExampleDevice(char *Port)
{
static const char *SampleCommand = "%01#RMD**\r";
char msg[EXAMPLE_CODE_RESPONSE_MAX];
int ret;

// Initialize the serial port.
// "/dev/ttyUSB0" is where Power PMAC maps the USB-to-Serial device
// The second number is the baud rate. Here are some example baud rates:
// B9600: 9600 baud
// B38400: 38400 baud (the default for talking to a Turbo PMAC)
// B115200: 115200 baud (the default for talking to a host PC or to another Power PMAC)

SerialPort = OpenSerialPort(Port,B38400);
// Change your baud rate to what you need. To talk to Turbo PMAC, use 38400.
// Note: If you want to use this to talk to Turbo PMAC, I1 should be set to 1 on the PMAC to disable CS handshaking
// Otherwise, Turbo will wait until it gets the CS signal to send characters

//Check return code to see if the port opened properly.
if(SerialPort < 0)
{
Send(1, "Problem opening port!");
return SerialPort;
} else printf("Serial port opened properly.\n");

//Write out the command to read display on meter...
ret = write(SerialPort, SampleCommand, strlen(SampleCommand));

//If write() does not return the requested number of bytes to write, we have a problem.
//Pass return code on to the calling routine.
if(ret != strlen(SampleCommand))
{
Send(1, "Problem writing to port!"); // Send to PPMAC's Send1 port
return ret;
}

//Read back the string into the "msg" buffer...
ret = ReadSerialLine(msg, EXAMPLE_CODE_RESPONSE_MAX, '\n', SerialPort, 10.000);

if(ret)
{
Send(1, "Did not get a response from port!"); // Send to PPMAC's Send1 port
return ret;
}

//Echo Response to Power PMAC send port
Send(1, "Response:"); // Send to PPMAC's Send1 port
Send(1, msg); // Send to PPMAC's Send1 port

close(SerialPort);
return 0; // All went well; return good status code.
}
Reply
#6
You can do this. Just make sure to put nanosleep() in your infinite loop, or you'll watchdog the PPMAC. You should also create a break condition variable so you can stop the program when you want.

You can write your message to a global variable for convenience.

First, assign these globals in your global definitions.pmh file; something like:

global MySerialPortMessage, MyBreakConditionVariable = 0.0;

Then, in your C program, you can make a loop like so:

while(pshm->P[MyBreakConditionVariable] < 1.0) {
//Write out the command to read display on meter...
ret = write(SerialPort, SampleCommand, strlen(SampleCommand));

//If write() does not return the requested number of bytes to write, we have a problem.
//Pass return code on to the calling routine.
if(ret != strlen(SampleCommand))
{
Send(1, "Problem writing to port!"); // Send to PPMAC's Send1 port
return ret;
}

//Read back the string into the "msg" buffer...
ret = ReadSerialLine(msg, EXAMPLE_CODE_RESPONSE_MAX, '\n', SerialPort, 10.000);

if(ret)
{
Send(1, "Did not get a response from port!"); // Send to PPMAC's Send1 port
return ret;
}
pshm->P[MySerialPortMessage] = atof(msg);
MySleepSec(0.1); // sec
}

Where
void MySleepSec(double SleepTimeSeconds)
{
struct timespec Timer;
Timer = Sec2TimeSpec(SleepTimeSeconds);
nanosleep(&Timer,NULL);
}

and

struct timespec Sec2TimeSpec(double TimeSec)
{
struct timespec Timer;
Timer.tv_sec = (long int)TimeSec;
Timer.tv_nsec = (long int)((TimeSec-(double)Timer.tv_sec)*1000000000.0);
return Timer;
}

To break your loop, just write MyBreakConditionVariable=1 from any program or the Terminal window.
Reply
#7
Thank you Clopedandle, this is exactly what I was looking for. I can confirm it is working for me.

I have a few more questions about background programs:

1) Using your method I can stop the program from another program by writing to the break condition variable. Is there any way for me to start the program from another program? (I am currently using the Task Manager to start it) Similarly, is it possible for me to query the current Running/NotRunning status?

2) I see you used the "pshm->P[GlobalVar]" to read and write Global variables. What is the difference between using that and the "SetGlobalVar" and "GetGlobalVar" functions?

3) How low can I safely push the time in "MySleepSec"? (currently set to 0.1)

4) Do I have to worry about reading a corrupt value of the global var from another program or are reading/write atomic?
Reply
#8
You're welcome.

1. To start the program (assuming it's capp1.out) from a PLC, you can issue

system "/var/ftp/usrflash/Project/C\ Language/Background\ Programs/capp1.out";

from another C program, use popen() or fork() and the path:

/var/ftp/usrflash/Project/C\ Language/Background\ Programs/capp1.out

2. pshm->P[] and Set/GetGlobalVar are fundamentally the same. I prefer the syntax of the former.

3. I think you could safely push it to 0.001, although it may be pointless if your serial device is not providing data that fast. You can always try setting it that low and checking Task Manager to see if your CPU usage is going too high.

4. I believe it is atomic, but cannot remember completely. Perhaps a member of the ODT staff could answer? Curt?
Reply
#9
Accesses to global variables are atomic, so this is not a concern.
Reply
#10
Thanks for the info everyone
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)