I am writing a program to control a relay switch connected thru the serial (RS-232) COM1 port. The device I am using has contains two relay switches. These are either 'open' or 'closed'.
By default, both relays are open. The number 1 relay is closed by setting bit 1 in the modem control register. Number 2 relay is closed by setting bit 0 in the modem control register.
In C, this can be achieved as follows:
x = inportb(0x3FC);
x=x & ~2; //Set bit 1 to zero
x=x | 2; //Set bit 1 to one
x=x & ~1; //Set bit 0 to zero
x=x | 1; //Set bit 0 to one
C++ however, does not make COM port access so easy (or so I am led to believe). I currently have the following code, which successfully opens and closes the COM port so that it can be communicated with in C++ and Windows:
#include <windows.h>
//Initialise Windows module
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance,
LPSTR lpszArgument, int nFunsterStil)
{
//Define the serial port precedure
HANDLE hSerial;
//Initialise relay
hSerial = CreateFile("COM1",GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//SOME CODE MUST GO HERE
//Deactivate relay
CloseHandle(hSerial);
return 0;
}
However, I am at a loss as to how to 'set bits'. I'm still very new to C++. Any help would be gratefully appreciated.
You need to understand how your device works. You cannot "set bits" directly in hSerial. Having COM port opened, you can send/receive bytes. Possibly you need to send something to device using WriteFile(hSerial, ...). Maybe you need to read information from device using ReadFile(hSerial, ...). COM port is not port as it was called in DOS, this is not address or register. This is input/output stream. You work with COM port like C program works with STDIN and STDOUT - read and wrire information from/to port.
Once you understand what your device needs (this means, define communication protocol), implement it in the program. Read this article: Serial Communications http://msdn.microsoft.com/en-us/library/ff802693.aspx - everything Win32 programmer needs to know about serial communications.
I am presuming that C example you give is not actually on a Windows box and is from some embedded platform is that correct? As it looks like you have direct access to the COM port hardware.
It is not as such C++ that makes this more challenging it is that Windows will not give you direct access to the hardware as you have on your embedded system. It is in fact not possible to set the level of specific pins on the COM port through the standard Windows API, you would need to write a driver that runs with increased privileges to achieve that. It might be impossible to do what you are trying to without writing a driver, something I wouldn't recommend.
The best solution would be to write the embedded end to accept commands as data sent over the RS-232 rather than look for levels on specific pins. The answer from Alex Farber provides a good link to get you started with serial comms in windows.
Not 100% sure what you are doing here, or what you want. The data on the serial port is, well, serial and you cannot drive relays directly by sending data to the UART tx buffer on 0x3FC. That and, as pointed out by other posters, access to the IO map is privileged since W95/98/ME/otherWintendo.
There may be good news - if you only need to drive two, (high sensitivity/low current), relays, you can do it by utilising the RTS and DTR control lines. The current available from these pins is enough to drive a 12V coil via. a diode, (line shifts from +12 to -12 so you need the diode on an ordinary non-polarised relay plus, if the relay does not already contain one, another diode to clamp any back EMFs during switching).
RTS/DTR can be driven from the serial APIs without any special privilege.
I am doing this now to simulate a flow sensor switch when testing an embedded controller.
Note that the relays cannot be toggled at any great rate - the IO is very slow. I can get about 15Hz on a good day. 4*4GHz cores, 12GB of RAM and my PC can only toggle 1 bit at 15Hz!
Rgds,
Martin
Related
I'm writing a small program using QModbusDevice over the serial port (using the QModbusRtuSerialMaster class) and have some problems.
One of the problems seems to be that the flow control of the serial port is incorrect. Checking in a serial port sniffer I see that a working client sets RTS on when it sends requests, and then RTS off to receive replies. When I use QModbusRtuSerialMaster to send messages that doesn't happen.
The message is sent correctly (sometimes, subject for another question) compared to the working client. It's just the control flow that doesn't work and which causes the servers to be unable to reply.
I have set the Windows port settings for the COM-port in question to hardware flow control but it doesn't matter, the sniffer still reports no flow control.
Is there a way to get QModbusRtuSerialMaster to set the flow control as I would like? Or is there a way to manually handle the flow control (which is what the working client does)? Or is the only solution to skip the Qt modbus classes and make up my own using the serial port directly?
A short summary of what I'm doing...
First the initialization of the QModbusRtuSerialMaster object:
QModbusDevice* modbusDevice = new QModbusRtuSerialMaster(myMainWindow);
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM3");
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud115200);
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
modbusDevice->setTimeout(100);
modbusDevice->setNumberOfRetries(3);
modbusDevice->connectDevice();
Then how I send a request:
auto response = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::Coils, 0, 1), 1);
QtModbus does not implement an automatic toggling for the RTS line because it expects your hardware to do it on its own (with a dedicated line instead).
This should be the case for most RS485 converters (even cheap ones). You would only need the RTS line if you have a separate transceiver like this one with a DE/~RE input.
If you were on Linux and had some specific hardware you could try to use the RS485 mode to toggle the RTS line for you automatically. But you don't seem to be on Linux and the supported hardware is certainly very limited.
You can also toggle the line manually with port.setRequestToSend(true), see here. But note that depending on the timing needs of the device you are talking too, this software solution might not be very reliable. This particular problem has been discussed at length here. Take a look at the links on my answer too, I made some benchmarks with libmodbus that show good results.
Enabling or disabling flow control on the driver won't have any effect on this issue because this is not actually a flow control problem but a direction control one. Modbus runs on two-wire half-duplex links very often, and that means you need a way to indicate which device is allowed to talk on the bus at all times. The RTS (flow control) from an RS232 port can be used for this purpose as a software workaround.
In the end, it would be much less of a headache if you just replace your transceiver with one that supports hardware direction control. If you have a serial port with an FTDI engine you should be able to use the TXEN line for this purpose. Sometimes this hardware line is not directly routed and available on a pin but you can reroute it with MProg.
I would like to highlight that you did not mention if you are running your Modbus on RS485. I guess it's fair to assume you are, but if you have only a couple of devices next to each other you might use RS232 (even on TTL levels) and forget about direction control (you would be running full-duplex with three wires: TX, RX and GND).
I am trying to understand how to use the Windows API to communicate with RS232/RS422.
I need to interface to some hardware where I don't really have control of the communication protocol, so I am forced to work with this.
I need to set the TX + (pin 3) to high (~5 volts), and the TX - (pin 7) to low (~0 Volts).
I know there is
SetCommBreak
But that puts both pins (3,7) to ~5 volts. I need only pin 3 to be 5 volts, and pin 7 to be ~ 0 volts. Is there a doable way to do this? I would love if I could just control these lines like wire, but is there a way to manipulate the baud rate and transmit data to achieve this? Or any sort of solution?
You can not manipulate the RX and TX pins arbitrarily and without limitations *without writing your own RS485/RS422 protocol is hardcoded on the chip. The reason why you are limited in manipulating the pins when the windows serial driver is loaded ( automatically ) is that the specification of a serial port ( what RS485 / RS 422 is ) requires a specific data structure, i.e. start bits, stop bits, signals like RTS ( ready-to-send ) , ... this required data structure is implemented in the windows serial driver and you can not overwrite it. You can custom your data in a way that the required pin states are produced but your data will always be enveloped in the structure that is required by the serial protocol ( start / stop bits, ... ), cf this graphic from https://en.wikipedia.org/wiki/RS-485
in windows you initialize a serial port in a DCB structure ( https://learn.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_dcb )
within this serial port structures the several protocols that run on RS485 / RS422 are defined like MODBus, ...
you can think of a RS-485/RS-422 serial port as a 'smart file'. 'smart' because of the multiple slaves / addresses that can be accessed. if you write a correct address to the file / serial bus you get an answer, i.e. if you write a 23 what means "Slave 23, send me your register values !"
This overlying protocol is what has to be implemented in software. For this you do not need to manipulate single pins except possibly the control lines like RTS, ... and actually you can not do this without writing your own driver.
An example how to implement MODBus protocol is in http://libmodbus.org/documentation/
The following citation shows that the protocols that run over RS-485 are written over the serial port layer :
You cannot easily craft MODBUS messages "by hand", as you would have
done with ASCII protocols used on RS232: each MODBUS message ends with
a checksum code, computed from the full content of the message. To
exchange MODBUS messages, you must therefore use:
either a specific program provided by the device vendor, with a compatible interface;
or a simple RS485 interface with a programming library which encodes
and decodes MODBUS messages;
or a smart RS485 interface able to
encode and decode by itself the MODBUS messages, such as the Yocto-RS485.
source : http://www.yoctopuce.com/EN/article/a-quick-tutorial-on-rs485-and-modbus
https://social.msdn.microsoft.com/Forums/vstudio/en-US/1751dafb-2fd5-48b8-8c16-08dd95d7db6d/writing-a-string-to-an-rs485-port-vs-2010-c?forum=vcgeneral
I want to connect a simple switch to my computer using a serial port. Whenever the switch gets closed, I want to increment a variable. I am using Visual C++ for the project.
Can anyone show me an approach for this task? I used Google and found examples for reading and writing data over via serial interface but I don't know how to implement a counter.
I don't think this works on a normal serial port, as the serial port in general uses a pulses (several pulses may be the same level for consecutive ones or zeros, but there will at least be pulses for the start/end of a sequence, and typically in the middle). There is a set number of bits in a message, typically 1 start, 8 data and 1 stop bit.
You could do this with a parallel port, or a GPIO pin, if there is such a thing on the system you are working on.
Or you have to implement some more logic than a simple switch, such that the thing sends a sequence of pulses to make up a complete packet, and have a message for "close" and one for "open".
One can connect DTR pin of the serial port to pins like DSR or RI via a switch or wire. It's easy to controll the serial port with the serial port class of .NET framework. Code samples can be found on msdn serial port class entry.
I'm helping out a friend with his Electrical Engineering project. He is building a device that will use a serial port to communicate to some software. On a windows platform (Win7), how would one read and write directly to a specific pin on the serial port? Is there an API that Windows exposes for this sort of thing?
Yes, essentially you open a serial port device with a special name, such as COM1, and read and write to it much as you would a file. The pins used will (naturally) be the serial transmit and receive pins.
If you want to control specific pins but not necessarily in a serial way, you might be better off working with a parallel port instead. The parallel port voltages are usually more friendly to TTL level logic and can often be driven directly.
Update: If you just need to toggle one pin as per your comment, you may be able to use the DTR line for this. See the EscapeCommFunction function documentation for how to do this.
You can use WaitCommEvent function to monitor a specific pin. Suppose the voltage change triggers CTS signal, it can be like this
hCommn = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
......
WaitCommEvent(hCommn, EV_CTS, NULL);
......
WaitCommEvent from MSDN
I know that serial ports work by sending a single stream of bits in serial. I can write programs to send and receive data from that one pin.
However, there are a lot more other pins on the serial port connection that normal aren't used but from documentation all seem to have some sort of function for signalling as opposed to data transfer.
Is it possible in any way to cause the other pins that are not used for direct data transfer to be controlled individually? If so, how would i go about doing that?
EDIT: more information
I am working with a modern CPU running windows 7 64-bit on an intel core i7 870 processor. I'm using serial to usb ports because its imposable for me to do anything directly with a usb port and my computer does not come with serial ports and also for some inexplicable reason i have a bunch of these usb to serial port adapters lying around.
My goal is to control mutipul stepper motors (200 steps per rotation, 4 phase motors). My simple circuitry accepts single high pulses and interprets it as a command to cause the motor to rotate one step. The circuit itself will handle the power supply and phase switching. I wish to use the data transfer pin to send the rotation signals (we can control position and velocity by altering the number of high pulses and frequency of high pulses through the pin, however there is no real pulse width modulation).
I have many motors to control but they do not need to be controlled simultaneously. I hope to use the rest of the pins and run them through a simple combination logic circuit to identify which motor is being moved and which direction it is to move in. This is part of the power switching circuitry.
The data transfer pin will operate normally at some low end frequency. However, i want to control the other pins to allow me to give a solid on or off signal (they wont be flipping very quickly, only changes when i switch to controlling another motor).
Based of the suggestion of Hans Passant , I'd like to suggest that you use an Arduino instead of an USB-to-serial converter. The "Duemilanove" is an Arduino-based board that provides 6 PWM outputs (as well as 8 other digitial I/Os and 6 analog). Some more specialized boards might be even cheaper (Arduino Pro Mini, $15 in volume, some soldering required).
Using the handshaking pins to send data can work very well, though probably not on a multitasking OS, it's just very processor intensive (because the port needs to be polled constantly) and requires some custom cables. In fact, back in the day, this is exactly how Laplink got such high transfer rates over serial connections (and why to get those rates you needed a special 'Laplink' cable). And you need both sides of hte link to be aware of what's going on and be able to deal with the custom communications. Laplink would send a packet of data over both the normal UART pins while trying to send data from the other end of the packet over the handshaking pins. If the correct cable wasn't used (or there was some other problem with sending over the handshaking pins) there was no problem - all the data would just get send normally.
Embedded developers might know this as 'bit banging' - often on small embedded systems there's no dedicated UART circuitry - to get serial communications to work they have to toggle a general I/O pin with the correct timing. The same can be done on a UART's handshaking pins. But like I said, it can be detrimental to the system if other work needs to be done.
You can use DTR and RTS only, but that is four possible states. You do need to be careful that the device on the other end uses TTL levels. At he end of this link Serial there are tips on hardware if you need it.
What kind of data rate are you thinking of when you say high frequency? What kind of serial port do you have? With the old 9 pin connectors on the back of the computer the best you can do is around 115Kbps. With a USB adapter I have done test where I could push close to 1Mbps through the port.
Here's an article from Microsoft that goes into great detail on how to work with serial ports:
http://msdn.microsoft.com/en-us/library/ms810467.aspx
It mentions EscapeCommFunction for directly controlling the DTR line.
Before you check out this information, I'm joining in with the others that say a serial port is inappropriate for your application.
I´ve been trying to find an answer to your question for 3 hours, seems like there is no "simple way" to get a simple boolean signal from a computer...
But, there is always a way, and jet, as simple (maybe even stupid) as this may sound, have you considered using the audio jack connector as an output?, It is stereo so you would have 2 outputs available,the programming would is not that difficult. and you don#t need to buy expensive shit to make it work.
If you also need an input, just disassemble a mouse... and bridge the sensors to the servos, probably the most cheap and easiest way of doing it...
Another way would be using the leds for the Num-lock, caps-lock and the dspl-lock on the keyboard, these can be activated using software, and you just need to take a cheap external keyboard, and use the connectors for these 3 leds.
you are describing maybe a parallel port - where you can set bit patterns all at once - then toggle the xmit line to send it all...
Lets take a look from the "bottom up" point of view:
The serial port pins
Pins on the serial port may be connected to a "controller" or directly connected to the processor. In order for the processor to have access (control) the pins, there must be an electrical connection from the pins to the processor. If not, the processor nor the program can control the pins.
Using a serial controller
A controller, such as a USART, would be connected between the serial port and the processor. The controller may function as to convert 8 parallel data bits into serial bitstream. In the big picture, the controller must provide access to the port pins in order for them to be controlled. If it doesn't, the pins can't be accessed. The controller must be connected to the processor in order to control the pins if a controller is connected.
The Processor and the Serial port
Assuming that the pins you want to control are connected to the processor, the processor must be able to access them. Sometimes they are mapped as physical addresses (such as with an ARM processor), or they may be connected to a port (such as the intel 8086). A program would access the pins via a pointer or using a i/o instruction. In some processor, the i/o ports must be enabled and initialized before they can be used.
Support from the OS
Here's a big ticket item: If your platform has an Operating System, the Operating System must provide services to access the pins of the serial port. The services could be a driver or an API function call. If the OS doesn't provide services, you can't access the serial port pins.
Permission from the OS
Assuming the OS has support for the serial port, your program must now have permission to access the port. In some operating systems, permission may only be granted to root or drivers and not users. If your account does not have permission to access the pins, you are not going to read them.
Support from the Programming Language
Lastly, the programming language must have support for the port. If the language doesn't provide support for the port you may have to change languages, or even program in assembly.
Accessing the "unused" pins of a serial port require extensive research into the platform. Not all platforms have serial ports. Serial port access is platform dependent and may change across different platforms.
Ask another, more detailed question and you will get more detailed answers. Please provide the kind of platform and OS that you are using.