I'm in a strange situation with an ATMega2560.
I want to save power by going into PowerDown mode. In this mode there are only a few events only which can wake it up.
On USART1 I have an external controller which sends messages to the AVR.
But when USART1 is used I can not use the INT2 and INT3 for external interrupt (=the CPU will not wake up).
So I had an idea to disable the USART1 just right before going into PowerDown mode, and have the INT2 enabled as external interrupt.
Pseudo code for this:
UCSR1B &= ~(1<<RXEN1); //Disable RXEN1: let AVR releasing it
DDRD &= ~(1<<PD2); //Make sure PortD2 is an input - we need it for waking up
EIMSK &= ~(1<<INT2); //Disable INT2 - this needs to be done before changing ISC20 and ISC221
EICRA |= (1<<ISC20)|(1<<ISC21); //Rising edge on PortD2 will generate an interrupt and wake up the AVR from PowerDown
EIMSK |= (1<<INT2); //Now enable INT2
//Sleep routine
cli();
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
In the ISR of INT2, I change everything back to USART1.
Pseudo:
ISR(INT2_vect) {
EIMSK &= ~(1<<INT2); //Disable INT2 to be able to use it as USART1 again
UCSR1B=(1<<RXEN1)|(1<<TXEN1)|(1<<RXCIE1);
}
However it seems to take long time until the USART1 is working correctly again.
There are too many faulty bits in the beginning (after waking up from PowerDown).
How hackish is this?
Is there any reasonable way to make the change faster?
The main idea was to set the 'RX' port to an interrupt which can wake the CPU up then immediately change it back to USART and process it asap.
PS: I really have to use the same pin for this purpose, there is no other available option. So guiding toward using some other pins won't be accepted as an answer.
The Power-down mode disables the oscillator, so you have to wait for a stable oscillator after wakeup.
Please take a look at the datasheet on page 51:
When waking up from Power-down mode, there is a delay from the wake-up condition occurs until the wake-upbecomes effective. This allows the clock to restart and become stable after having been stopped. The wake-upperiod is defined by the same CKSEL Fuses that define the Reset Time-out period, as described in “ClockSources” on page 40.
You have to wait up to 258 clock cykles assuming you use a high speed ceramic oscillator (see table 10-4 on page 42).
You can use the Standby Mode. If you use an external oscillator the CPU enter the Standby Mode, which is identical to Power-down Mode, but the Oscillator isn´t stopped. Furthermore you can set the Power Reduction Register for additional power save options.
Another option is to use the Extended Standby Mode, which is identical to the Power-save Mode. This mode disables the oscillator, but the oscillator wakes up in six clock cycles.
Assume we have a system with CPU which is fully compatible with Intel 8259 Programmable Interrupt Controller. So, this CPU use vectored interrupts, of course.
When one of eight interrupts occurs, PIC just asserts INTR wire that is connected to the CPU. Now PIC waits for CPU until INTA will be asserted. When so, PIC selects interrupt with the highest priority (depends on pin number), and then send its interrupt vector to data bus. I omitted some timing, but it doesn't matter for now, I think.
Here are questions:
How whole device, that causes interrupt, knows that his interrupt
request was accepted and it can pull off interrupt request? I read about 8259, but I didn't find it.
Is acknowledge device, whose interrupt was accepted, performed in ISR?
Sorry for my English.
The best reference is the original intel doc and is available here: https://pdos.csail.mit.edu/6.828/2012/readings/hardware/8259A.pdf It has full details of these modes, how the device operates, and how to program the device.
Caveat: I'm a bit rusty as I haven't programmed the 8259 in many years, but I'll take a shot at explaining things, per your request.
After an interrupting device, connected to an IRR ["interrupt request register"] pin, has asserted an interrupt request, the 8259 will convey this to the CPU by assserting INTR and then placing the vector on the bus during the three INTA cycles generated by the CPU.
After a given device has asserted IRR, the 8259's IS ["in-service"] register is or'ed with a mask of the IRR pin number. The IS is a priority select. While the IS bit is set, other interrupting devices of lower priority [or the original one] will not cause an INTR/INTA cycle to the CPU. The IS bit must be cleared first. These interrupts remain "pending".
The IS can be cleared by an EOI (end-of-interrupt) operation. There are multiple EOI modes that can be programmed. The EOI can be generated by the 8259 in AEOI mode. In other modes, the EOI is generated manually by the ISR by sending a command to the 8259.
The EOI action is all about allowing other devices to cause interrupts while the ISR is processing the current one. The EOI does not clear the interrupting device.
Clearing the interrupting device must be done by the ISR using whatever device specific register the device has for that purpose. Usually, this a "pending interrupt" register [can be 1 bit wide]. Most H/W uses two interrupt related registers and the other one is an "interrupt enable" register.
With level triggered interrupts, if the ISR does not clear the device, when the ISR does issue the EOI command to the 8259, the 8259 will [try to] reinterrupt the CPU using the vector for the same device for the same condition. The CPU will probably be reinterrupted as soon as it issues an sti or iret instruction. Thus, an ISR routine must take care to process things in proper sequence.
Consider an example. We have a video controller that has four sources for interrupts:
HSTART -- start of horizontal line
HEND -- end of horizontal line [start of horizontal blanking interval]
VSTART -- start of new video field/frame
VEND -- end of video field/frame [start of vertical blanking interval]
The controller presents these as a bit mask in its own special interrupt source register, which we'll call vidintr_pend. We'll call the interrupt enable register vidintr_enable.
The video controller will use only one 8259 IRR pin. It is the responsibility of the CPU's video ISR to interrogate the vidpend register and decide what to do.
The video controller will assert its IRR pin as long as vidpend is non-zero. Since we're level triggered, the CPU may be re-interrupted.
Here is a sample ISR routine to go with this:
// video_init -- initialize controller
void
video_init(void)
{
write_port(...);
write_port(...);
write_port(...);
...
// we only care about the vertical interrupts, not the horizontal ones
write_port(vidintr_enable,VSTART | VEND);
}
// video_stop -- stop controller
void
video_stop(void)
{
// stop all interrupt sources
write_port(vidintr_enable,0);
write_port(...);
write_port(...);
write_port(...);
...
}
// vidisr_process -- process video interrupts
void
vidisr_process(void)
{
u32 pendmsk;
// NOTE: we loop because controller may assert a new, different interrupt
// while we're processing a given one -- we don't want to exit if we _know_
// we'll be [almost] immediately re-entered
while (1) {
pendmsk = port_read(vidintr_pend);
if (pendmsk == 0)
break;
// the normal way to clear on most H/W is a writeback
// writing a 1 to a given bit clears the interrupt source
// writing a 0 does nothing
// NOTE: with this method, we can _never_ have a race condition where
// we lose an interrupt
port_write(vidintr_pend,pendmsk);
if (pendmsk & HSTART)
...
if (pendmsk & HEND)
...
if (pendmsk & VSTART)
...
if (pendmsk & VEND)
...
}
}
// vidisr_simple -- simple video ISR routine
void
vidisr_simple(void)
{
// NOTE: interrupt state has been pre-saved for us ...
// process our interrupt sources
vidisr_process();
// allow other devices to cause interrupts
port_write(8259,SEND_NON_SPECIFIC_EOI)
// return from interrupt by popping interrupt state
iret();
}
// vidisr_nested -- video ISR routine that allows nested interrupts
void
vidisr_nested(void)
{
// NOTE: interrupt state has been pre-saved for us ...
// allow other devices to cause interrupts
port_write(8259,SEND_NON_SPECIFIC_EOI)
// allow us to receive them
sti();
// process our interrupt sources
// this can be interrupted by another source or another device
vidisr_process();
// return from interrupt by popping interrupt state
iret();
}
UPDATE:
Your followup questions:
Why do you use interrupt disable on video controller register instead of mask 8259's interrupt enable bit?
When you execute vidisr_nested(void) function, it will enable nesting the same interrupt. Is it true? And is that what you want?
To answer (1), we should do both but not necessarily in the same place. They seem similar, but work in slightly different ways.
We change the video controller registers in the video controller driver [as it's the only place that "understands" the video controller's registers].
The video controller actually asserts the 8259's IRR pin from: IRR = ((vidintr_enable & vidintr_pend) != 0). If we never set vidintr_enable (i.e. it's all zeroes), then we can operate the device in a "polled" [non-interrupt] mode.
The 8259 interrupt enable register works similarly, but it masks against which IRRs [asserted or not] may interrupt the CPU. The device vidintr_enable controls whether it will assert IRR or not.
In the example video driver, the init routine enables the vertical interrupts, but not the horizontal. Only the vertical interrupts will generate a call to the ISR, but the ISR can/will also process the horizontal ones [as polled bits].
Changing the 8259 interrupt enable mask should be done in a place that understands the interrupt topology of the entire system. This is usually done by the containing OS. That's because the OS knows about the other devices and can make the best choice.
Herein, "containing OS" could be a full OS like Linux [of which I'm most familiar]. Or, it could just be an R/T executive [or boot rom--I've written a few] that has some common device handling framework with "helper" functions for the device drivers.
For example, although it's usual that all devices get their own IRR pin. But, it is possible, with level triggering, for two different devices to share an IRR. (e.g.) IRR[0] = devA_IRROUT | devB_IRROUT. Either through an OR gate [or wired OR(?)].
It's also possible that the device is attached to a "nested" or "cascaded" interrupt controller. IIRC [consult document], it is possible to have a "master" 8259 and [up to] 8 "slave" 8259s. Each slave 8259 connects to an IRR pin of the master. Then, connect devices to the slave IRR pins. For a fully loaded system, you can have 256 interrupting devices. And, the master can have slave 8259s on some IRR pins and real devices on others [a "hybrid" topology].
Usually, only the OS knows enough to deal with this. In a real system, a device driver probably wouldn't touch the 8259 at all. The non-specific EOI would probably have been sent to the 8259 before entering the device's ISR. And, the OS would handle the full "save state" and "restore state" and the driver just handles device specific actions.
Also, under an OS, the OS will call the "init" and "stop" routines. The general OS routines for this will handle the 8259 and call the device specific ones.
For example, under Linux [or almost any other OS or R/T executive], the interrupt sequence goes something like this:
- CPU hardware actions [atomic]:
- push %esp and flags register [has CPU interrupt enable flag] to stack
- clear CPU interrupt enable flag (e.g. implied cli)
- jump within interrupt vector table
- OS general ISR (preset within IVT):
- push all remaining registers to stack
- send non-specific EOI to 8259(s)
- call device-specific ISR (NOTE: CPU interrupt flag still clear)
- pop regs
- iret
To answer (2), yes, you are correct. It would probably interrupt immediately, and might nest (infinitely :-).
The simple ISR version is more efficient and preferable if the actions taken in the ISR are short, quick, and simple (e.g. just output to a few data ports).
If the required actions take a relatively long time (e.g. do intensive calculations, or write to a large number of ports or memory locations), the nested version is preferred to prevent other devices from having entry to their ISRs delayed excessively.
However, some time critical devices [like a video controller] need to use the simple model, preventing interruption by other devices, to guaranteed that they can complete in a finite, deterministic time.
For example, the video ISR handling of VEND might program the device for the next/upcoming field/frame and must complete this within the vertical blanking interval. They, have to do this, even if it means "excessive" delay of other ISRs.
Note that the ISR was "racing" to complete before the end of the blanking interval. Not the best design. I've had to program such a controller/device. For rev 2, we changed the design so the device registers were double-buffered.
That meant that we could set up the registers for frame 1 anytime during the [much longer] frame 0 display period. At VSTART for frame 1, the video hardware would instantly clock-in/save the double-buffered values, and the CPU could then setup for frame 2 anytime during the display of frame 1. And so on ...
With the modified design, the video driver removed the device setup from the ISR entirely. It was now handled from OS task level
In the driver example, I've adjusted the sequencing a bit to prevent infinite stacking, and added some additional information based upon my question (1) answer. That is, it shows [crudely] what to do with or without an OS.
// video controller driver
//
// for illustration purposes, STANDALONE means a very simple software system
//
// if it's _not_ defined, we assume the ISR is called from an OS general ISR
// that handles 8259 interactions
//
// if it's _defined_, we're showing [crudely] what needs to be done
//
// NOTE: although this is largely C code, it's also pseudo-code in places
// video_init -- initialize controller
void
video_init(void)
{
write_port(...);
write_port(...);
write_port(...);
...
#ifdef STANDALONE
write_port(8259_interrupt_enable |= VIDEO_IRR_PIN);
#endif
// we only care about the vertical interrupts, not the horizontal ones
write_port(vidintr_enable,VSTART | VEND);
}
// video_stop -- stop controller
void
video_stop(void)
{
// stop all interrupt sources
write_port(vidintr_enable,0);
#ifdef STANDALONE
write_port(8259_interrupt_enable &= ~VIDEO_IRR_PIN);
#endif
write_port(...);
write_port(...);
write_port(...);
...
}
// vidisr_pendmsk -- get video controller pending mask (and clear it)
u32
vidisr_pendmsk(void)
{
u32 pendmsk;
pendmsk = port_read(vidintr_pend);
// the normal way to clear on most H/W is a writeback
// writing a 1 to a given bit clears the interrupt source
// writing a 0 does nothing
// NOTE: with this method, we can _never_ have a race condition where
// we lose an interrupt
port_write(vidintr_pend,pendmsk);
return pendmsk;
}
// vidisr_process -- process video interrupts
void
vidisr_process(u32 pendmsk)
{
// NOTE: we loop because controller may assert a new, different interrupt
// while we're processing a given one -- we don't want to exit if we _know_
// we'll be [almost] immediately re-entered
while (1) {
if (pendmsk == 0)
break;
if (pendmsk & HSTART)
...
if (pendmsk & HEND)
...
if (pendmsk & VSTART)
...
if (pendmsk & VEND)
...
pendmsk = port_read(vidintr_pend);
}
}
// vidisr_simple -- simple video ISR routine
void
vidisr_simple(void)
{
u32 pendmsk;
// NOTE: interrupt state has been pre-saved for us ...
pendmsk = vidisr_pendmsk();
// process our interrupt sources
vidisr_process(pendmsk);
// allow other devices to cause interrupts
#ifdef STANDALONE
port_write(8259,SEND_NON_SPECIFIC_EOI)
#endif
// return from interrupt by popping interrupt state
#ifdef STANDALONE
pop_regs();
iret();
#endif
}
// vidisr_nested -- video ISR routine that allows nested interrupts
void
vidisr_nested(void)
{
u32 pendmsk;
// NOTE: interrupt state has been pre-saved for us ...
// get device pending mask -- do this _before_ [optional] EOI and the sti
// to prevent immediate stacked interrupts
pendmsk = vidisr_pendmsk();
// allow other devices to cause interrupts
#ifdef STANDALONE
port_write(8259,SEND_NON_SPECIFIC_EOI)
#endif
// allow us to receive them
// NOTE: with or without OS, we can't stack until _after_ this
sti();
// process our interrupt sources
// this can be interrupted by another source or another device
vidisr_process(pendmsk);
// return from interrupt by popping interrupt state
#ifdef STANDALONE
pop_regs();
iret();
#endif
}
BTW, I'm the author of the linux irqtune program
I wrote it back in the mid 90's. It's of lesser use now, and probably doesn't work on modern systems, but the FAQ I wrote has a great deal of information about interrupt device priorities. The program itself did a simple 8259 manipulation.
An online copy is available here: http://archive.debian.org/debian/dists/Debian-1.1/main/disks-i386/SpecialKernels/irqtune/README.html There's probably source code somewhere in this archive.
That's the version 0.2 doc. I haven't found an online copy of version 0.6 which has better explanation, so I've put up a text version here: http://pastebin.com/Ut6nCgL6
Side note: The "where to get" information in the FAQ [and email address] are no longer valid. And, I didn't understand the full impact of "spam" until I posted the FAQ and starting getting [tons of] it ;-)
And, irqtune even drew Linus' ire. Not because it didn't work but because it did: https://lkml.org/lkml/1996/8/23/19 IMO, if he had read the FAQ, he would have understood why [as what irqtune did is standard stuff to R/T guys].
UPDATE #2
Your new questions:
I think that you are missing a destination address in write_port(8259_interrupt_enable &= ~VIDEO_IRR_PIN). Isn't it so?
IRR register is read-only or r/w? If the second case, what is the purpose of writing into it?
Interrupt vectors are stored as logical addresses or physical address?
To answer question (3): No, not really [even if it seemed so]. The code snippet was "pseudo code" [not pure C code], as I mentioned in a code comment at the top, so technically speaking, I'm covered. However, to make it more clear, here is what the [closer to] real C code would look like:
// the system must know _which_ IRR H/W pin the video controller is connected to
// so we _hardwire_ it here
#define VIDEO_IRR_PIN_NUMBER 3 // just an example
#define VIDEO_IMR_MASK (1 << VIDEO_IRR_PIN_NUMBER)
// video_enable -- enable/disable video controller in 8259
void
video_enable(int enable)
{
u32 val;
// NOTE: we're reading/writing the _enable_ register, not the IRR [which
// software can _not_ modify or read]
val = read_port(8259_interrupt_enable);
if (enable)
val |= VIDEO_IMR_MASK;
else
val &= ~VIDEO_IMR_MASK;
write_port(8259_interrupt_enable,val);
}
Now, in video_init, replace the code inside STANDALONE with video_enable(1), and, in video_stop with video_enable(0)
As to question (4): We weren't really writing to the IRR, even though the symbol had _IRR_ in it. As mentioned in the code comments above, we were writing to the 8259 interrupt enable register which is really the "interrupt mask register" or IMR in the documentation. The IMR can be read from and written to by using OCW1 (see doc).
There is no way for software to access the IRR at all. (i.e.) There is no port in the 8259 to read or write the IRR value. The IRR is completely internal to the 8259.
There is a one-to-one correspondence between IRR pin numbers [0-7] and IMR bit numbers (e.g. to enable for IRR(0), set IMR bit 0), but the software has to know which bit to set.
Because the video controller is physically connected to a given IRR pin, it is always the same for a given PC board. The software [on older non-PnP systems] can't probe for this. Even on newer systems, the 8259 knows nothing of PnP, so it's still hardwired. The video controller driver programmer must just "know" what IRR pin is being used [by consulting the "spec sheet" or controller "architecture reference manual"].
To answer question (5): First consider what the 8259 does.
When the 8259 is intialized, the ICW2 ("initialization command word 2") gets set by the OS driver. This defines a portion of interrupt vector number the 8259 will present during the INTR/INTA cycle. In ICW2, the most significant 5 bits are marked T7-T3.
When an interrupt occurs, these bits are combined with the IRR pin number of the interrupting device [which is 3 bits wide] to form an 8 bit interrupt vector number: T7,T6,T5,T4,T3|I2,I1,I0
For example, if we put 0xD0 into ICW2, with our video controller using IRR pin 3, we'd have 1,1,0,1,0|0,1,1 or 0xD3 as the interrupt vector number that the 8259 will send to the CPU.
This is just a vector number [0x00-0xFF] as the 8259 knows nothing of memory addresses. It is the CPU that takes this vector number and, using the CPU's "interrupt vector table" [IVT], uses the vector number as an index into the IVT to properly vector the interrupt to an ISR routine.
On 80386 and later architectures, the IVT is actually called an IDT ("interrupt descriptor table"). For details, see the "System Programming Guide", chapter 6: http://download.intel.com/design/processor/manuals/253668.pdf
As, to whether the resulting ISR address from the IVT/IDT is physical or logical depends on the processor mode (e.g. real mode, protected mode, protected with virtual addressing enabled).
In a sense, all such addresses are always logical. And, all logical addresses undergo a translation to physical on each CPU instruction. Whether the translation is one-to-one [MMU not enabled or page tables have one-to-one mapping] is a question for "How has the OS set things up?"
Strictly speaking, there is no such thing
as "acknowledge an interrupt to device".
The thing that an ISR should do, is to handle
the interrupt condition. For example, if
the UART requested an interrupt because it
has an incoming data, then you should read
that incoming data. After that read operation,
UART no longer has the incoming data, so naturally
it stops asserting the IRQ line. Alternatively,
if your program no longer needs to read the
data and wants to stop the communication, it
would just mask the receiver interrupt via
the UART registers, and, once again, UART
will stop asserting the IRQ line. If the device
just wanted to signal you some state change,
then you should read the new state, and the
device will know that you have an up-to-date
state and will release an IRQ line.
So, in short: there is usually no any device-specific
acknowledge procedure. All you need to do is
to service an interrupt condition, after which,
that condition will disappear, voiding the
interrupt request.
I'm doing a small project, where I want to transmit a text via a cable to my Atmega328p.
I first created the project on an Arduino Uno (with pure C), where the transmission works.
Now I switched to a standalone 328p and tried it there.
But now the Problem is, that my RX-Complete Interrupt is fired too early. In fact it is even fired, when nothing has been transmitted. It will fired when I just touched the cable (the isolated parts) .
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/setbaud.h>
void setup(void){
CLKPR = 0;
//Set Output
DDRC |= (1 << PC0) | (1 << PC1) |(1 << PC2) |(1 << PC3) |(1 << PC4) | (1 << PC5);
DDRD |= (1 << PD6) | (1 << PD7);
// Interrupts
sei();
// Init UART
// Set baud
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
// Enable UART Receive and Receivecomplete Interrupt
UCSR0B = (1<<RXEN0) | (1 << RXCIE0);
// Set frameformat to 8 Data and 1 Stopbit
UCSR0C = ((1<<UCSZ00)|(1<<UCSZ01));
}
int main(void){
setup();
while(1){
}
return 0;
}
ISR(USART_RX_vect){
// Enable some LEDs
}
Edit: Picture of my Setup:
I use the Arduiono just for Powering my Breadboard. 5V and GND are connected.
The AVR MKII ISP is Connected via some Pins to flash the µC. The two cables are used for UART RX.
The Pushbutton is just for RESET
Edit 2: I just tried to power it via an external source and a raspberrypi. There is the same effect everywhere
Of course. RXC flag is set when there are unread data in the receive buffer.
This flag is used to generate the RX interrupt.
Since you never read UDR inside your interrupt, this flag remains set, and, therefore just after interrupt routine is completed, it is starts again. And again. And again....
The Rx line should not be floating. Its a high impedance input and should be driven to a specific level. Your cables act like an antenna and if you touch the cable it gets worse because there is capacitive coupling between the cable and your body. This may result in high frequency noise on your input which may trigger the Rx interrupt.
Further make sure that the 328p local power supply is properly decoupled. I don't see any capacitors near the controller on your breadboard. Check GND connection between Arduino and 328p (mandatory).
Without looking at your setup it's hard to tell what's going wrong, but if you're touching an isolated cable and getting a response from the processor, then I would check common grounds between the devices if you're powering the ATMega via a battery, make sure the battery ground is touching the device that's receiving and transmitting, as any potential difference in power levels could cause the little magnetic field that you give off to be amplified into something that the core registers as a high bit.If possible, post a picture of your setup!
Also when programming with ATMel chips, burning the arduino bootloader and going the simple(r) C code way never hurt.
Is it possible to query serial port tx (send) pin status if it is active or not ?
For example when issuin break command (SetCommBreak) tx pin is set to active (low). I'd like to know when it is active or not. Thanks.
No. (at least not likely)
If you are using the "16550" family of UARTs, then I am confident that you can not query the serial port tx pin status. Of course, if you are using some new version or other UART family, maybe.
You can assume that the TX pin is in the SPACE state ('0', +Volts) whilst performing SetCommBreak(), but I suspect that is not enough for you.
If you are look to debug your code to know if a break occurred, you can short pins 2 & 3 on a 9-pin D-sub, thus loop backing the transmit to the receive. A paper clip will do. Your receive code would detect the incoming BREAK. Shorting to the incorrect pin does not cause a lasting problem with a conforming serial port, but be careful. Try this first with simple data, before testing BREAK condition.
If you have a "16550"-like UART.
You can put the UART into loop-back mode and see if you receiving you own outgoing BREAK signal. Its somewhat complicated in current PCs. Other UART type may support loop-back.
On Windows XP, I've got a standard (on the motherboard) RS-232 serial port opened and configured with
dcb.fRtsControl = RTS_CONTROL_TOGGLE;
This works fine for my needs.
Now while there is no RX pending, I sometimes need to briefly toggle the RTS line under the control of my application. I can't just call EscapeCommFunction, as that fails when RTS_CONTROL_TOGGLE is enabled.
I tried to this:
dcb.fRtsControl = RTS_CONTROL_ENABLE; // manual RTS control and turns on RTS
SetCommState( hDevice, &dcb );
dcb.fRtsControl = RTS_CONTROL_TOGGLE; // turns off RTS since there is no TX pending
SetCommState( hDevice, &dcb );
However, this never seems to work. I never see RTS toggle. It's like the device driver notices that I just set RTS and am clearing it now, so it just aborts what I was doing.
If I put a sleep in there:
dcb.fRtsControl = RTS_CONTROL_ENABLE; // turn on RTS
SetCommState( hDevice, &dcb );
Sleep(1);
dcb.fRtsControl = RTS_CONTROL_TOGGLE; // turns off RTS since there is no TX pending
SetCommState( hDevice, &dcb );
Then I see the toggle, but it's extremely variable in length, anywhere from 1 to 15 ms (which is to be expected I guess). I use timeBeginPeriod to setup 1ms scheduling and my thread is already high priority (should I go to real time priority?).
So, is there something I can to do "flush" the RTS change to the hardware so I don't have to do the Sleep? I can busy wait (using QueryPerformanceCounter to wait for the amount of time I want), but I'm concerned that this would still not "flush" the RTS change to the device.
Thanks.
Turns out that the serial port I was using to monitor the RTS line was faulty. It was USB based and was missing the short transitions.
I put an O-scope on it and I can see a 1ms blip when I just toggle RTS on/off as I tried initially.