Cannot read data from terminal until restarted by other process - terminal

Before adding O_NOCTTY option, my process was killed once by unknown per every booting. I don't know why the process was killed :( and I thought the initializing has some problems. so that I added O_NOCTTY option, and the process was not killed. But it cannot read any data from the buffer until restarted by other process. Please help me :(
The following is code about initializing and reading.
void Init() {
mFd = open("/dev/ttyS2", O_RDWR | O_NOCTTY);
if (mFd > 0)
{
(void)tcgetattr(mFd, &mTermios_p);
speed = B115200;
mTermios_p.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
mTermios_p.c_oflag &= ~OPOST;
mTermios_p.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
mTermios_p.c_cflag &= ~(CSIZE | PARENB);
mTermios_p.c_cflag |= CS8;
mTermios_p.c_cc[VMIN] = 0U;
mTermios_p.c_cc[VTIME] = 0U;
(void)cfsetispeed(&mTermios_p, speed);
(void)cfsetospeed(&mTermios_p, speed);
(void)tcflush(mFd, TCIOFLUSH);
(void)tcsetattr(mFd, TCSANOW, &mTermios_p);
}
else
{
LOGE("uart open failed %s", strerror(errno));
}
}
int32_t Read() {
int32_t bytes = -1;
if (mFd > 0)
{
(void)pthread_mutex_lock(&mMutexLock);
bytes = static_cast<int32_t>(read(mFd, buf, nMaxRead));
(void)pthread_mutex_unlock(&mMutexLock);
}
if (bytes < 0)
{
LOGE("read failed");
}
return bytes;
}

Your serial terminal initialization is essentially the near-equivalent of cfmakeraw() plus setting the baudrate.
However that is insufficient to fully initialize a serial terminal.
At the very least the receiver also has to be enabled:
mTermios_p.c_cflag |= CREAD;
To eliminate any modem handshake issues (especially when there is no modem):
mTermios_p.c_cflag |= CLOCAL;
To eliminate any hardware handshake issues:
mTermios_p.c_cflag &= ~CRTSCTS;
BTW
mTermios_p.c_cc[VMIN] = 0U;
mTermios_p.c_cc[VTIME] = 0U;
Setting both VMIN and VTIME to zero is an ill-advised configuration.
This guide describes this configuration as one that should be used only if "you really, really know what you're doing."
The code that you posted for reading is not capable of (efficiently) handling the consequences of setting both VMIN and VTIME to zero. Unless your program is synchronized with the transmitting program, your read code is likely to "successfully" read zero bytes, i.e. no data.

Related

Pwm Pin continuously high

I am trying to make a Phase and frequency correct pwm in Atmega 88pb. this is my pwm init method.
void PWM_INIT2()
{
DDRB|=(1<<PINB1) | (1<<PINB2);
TCCR1B |= (1<<WGM13) | (1<<CS10);
TCCR1A |= (1<<COM1B0) | (1<<COM1B1) | (1<<COM1A1);
}
After adding it to my main my OCR1A pin keeps high always
int main(void)
{
PWM_INIT2();
while (1)
{
}
}
Can anyone help me to find a solution
TCCR1B |= (1<<WGM13) | (1<<CS10);
TCCR1A |= (1<<COM1B0) | (1<<COM1B1) | (1<<COM1A1);
You have set OC1A output to inverting, and OC1B to direct output, in mode 8 (PWM, Phase and Frequency correct) where the TOP value is defined by the content of register ICR1.
Therefore:
You have also to set some TOP value (which also defines the period duration of the timer), E.g.:
ICR1 = 9999;
You need to set some output values in OCR1A and OCR1B:
OCR1A = 1000;
OCR1B = 5000;

I2C not reading

I'm trying to interface with a BNO055 breakout board from a Teensy 2.0, but I'm having trouble reading from the BNO055. It has different registers that you can access to read data from the chip. To start, I'm just trying to read the IDs of some internal parts. No matter what I do, I seem to only get the last value that I put in TWDR. Trying to do a read doesn't seem to populate it. This is what I have in my main:
void main(void) {
CPU_PRESCALE(CPU_16MHz);
init_sensor();
char buffer[50];
sprintf(buffer, "Chip ID: %X\n", read_address(0x00));
while(1) {
_delay_ms(20);
}
}
This is my BNO055.c:
#include "BNO055.h"
#include <avr/io.h>
// The breakout board pulls COM3_state low internally
#define DEVICE_ADDR 0x29
#define READ 0
#define WRITE 1
#define F_CPU 16000000UL
#define SCL_CLOCK 400000L
inline void start(void) {
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while ( !(TWCR & (1<<TWINT)));
}
inline void stop(void) {
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
while(TWCR & (1 << TWSTO));
}
void send_address(uint8_t w) {
TWDR = (DEVICE_ADDR<<1) | w ;
TWCR = (1 << TWINT) | (1<<TWEN);
while(!(TWCR & (1 << TWINT)));
}
uint8_t read(void) {
TWCR = (1 << TWINT) | (1 << TWEN);
while(!(TWCR & (1 << TWINT)));
return TWDR;
}
void write(uint8_t data) {
TWDR = data;
TWCR = (1 << TWINT) | (1<<TWEN);
while(!(TWCR & (1 << TWINT)));
}
uint8_t read_address(uint8_t addr) {
start();
send_address(WRITE);
write(addr);
stop();
start();
send_address(READ);
const uint8_t value = read();
stop();
return value;
}
void write_address(uint8_t addr, uint8_t value) {
start();
send_address(WRITE);
write(addr);
write(value);
stop();
}
uint8_t status(void) {
return TWSR & 0xF8;
}
void init_sensor(void) {
// Setup I2C
DDRD &= ~((1 << 0) | (1 << 1));
PORTD |= ((1 << 0) | (1 << 1));
TWBR = ((( F_CPU / SCL_CLOCK ) - 16) / 2);
TWSR = 0;
TWCR = ((1 << TWEN) | (1 << TWIE) | (1 << TWEA));
}
This answer was written before the code in the question was updated in line with this answer. Not knowing this would confuse you as to how/why this answer(s|ed) the question.
The basic problem you're having is that you're issuing I2C start conditions and I2C stop conditions in places where they don't belong. The START an STOP conditions go at the beginning and end of an entire I2C transaction and not around each individual byte.
The data portion of a transaction is always entirely composed of data to be read or entirely composed of data to be written. So, performing a read of a register of a typical I2C device requires two I2C transactions. One involving writing the register address and the next involving reading the register's data, looking something like the following:
[I2C-START] [WRITE(I2C-DEVICE-WRITING-ADDRESS)] [WRITE(REGISTER-ADDRESS)] [I2C-STOP]
The write-transaction were we inform the I2C device of the register we want to read data from.
[I2C-START] [WRITE(I2C-DEVICE-READING-ADDESS)] [READ(REGISTER-DATA)] [I2C-STOP]
The read-transaction that conveys the data itself.
Potentially the last stop of the first could/should be omitted to create a I2C "repeated start" (which you probably don't need). So as not to confuse matters, I'm just going to go with the above scheme.
What you have, currently, looks more like this instead:
[I2C-START] [WRITE(I2C-DEVICE-WRITING-ADDRESS)] [I2C-STOP]
You've an empty write transaction.
[I2C-START] [WRITE(REGISTER-ADDRESS)] [I2C-STOP]
Now a transaction that tries to use a device register address as an I2C bus address
[I2C-START] [WRITE(I2C-DEVICE-READING-ADDRESS)] [I2C-STOP]
You've started a read transaction but didn't read any of the data that would follow if you issued a READ.
[I2C-START] [READ(?)] [I2C-STOP]
An attempt to read when you're supposed to writing either a I2C address (for either a read or write transaction).
The latter one is probably where you're getting in the most trouble and why you're seeing an unchanged TWDR; as an I2C master, you're never going to read a byte immediately following the start condition like that, since there's always supposed a byte written there containing address bits.
Put in terms of your code, you'd want something like:
uint8_t read_address(uint8_t addr) {
// The first transaction
// that tell the I2C device
// what register-address we want to read
start();
send_address(WRITE);
write(addr);
stop();
// The read transaction to get the data
// at the register address
// given in the prior transaction.
start();
send_address(READ);
const uint8_t r = read();
stop();
return r;
}
... where none of send_address(), write(), and read() contain any calls to either start() or stop().
I did test the above code, though not on a ATMega32U4; I used a different AVR that has the same TWI peripheral though and I do not have your I2C device.
Moving the start() and stop() into correct places did bring things from non-functional to functional in my test rig. So, when I say "something like" the above code,
I mean a lot like that. =) There may yet be other bugs, but this is the principal problem.

CAN 1 RX works in loopback mode, but not in Normal mode

I am using a STM32F413RG based custom board and trying to run some CAN bus based applications on it.
I am trying to start with CAN 1 and wrote a bare metal code using CMSIS library to transmit and receive.
Here is the code for three functions - Init,Send and Receive.
void initCAN()
{
// enable clock
// note: because CAN2 is a "slave CAN" you have to enable CAN1 clock
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
// and reset CAN1, so request reset
CAN1->MCR |= CAN_MCR_RESET;
// wait for it to say it has gone to sleep
while ((CAN1->MSR & CAN_MSR_SLAK_Msk) == 1) {}
//CAN1
// enable port A clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
__DSB();
// CAN_RX = CAN1R on PA11, so set alt function
GPIOA->MODER &= ~GPIO_MODER_MODER11_Msk;
GPIOA->MODER |= GPIO_MODER_MODER11_1;
// CAN 1 is AF9 (AFSEL 9 = 0b1001 -> bit 3 & bit 0
GPIOA->AFR[1] |= (GPIO_AFRH_AFSEL11_3 | GPIO_AFRH_AFSEL11_0);
// CAN_TX = CAN1T on PA12
GPIOA->MODER &= ~GPIO_MODER_MODER12_Msk;
GPIOA->MODER |= GPIO_MODER_MODER12_1;
// open-drain
GPIOA->OTYPER |= GPIO_OTYPER_OT_11;
GPIOA->OTYPER |= GPIO_OTYPER_OT_12;
// CAN 1 is AF9 (AFSEL 9 = 0b1001 -> bit 3 & bit 0
GPIOA->AFR[1] |= (GPIO_AFRH_AFSEL12_3 | GPIO_AFRH_AFSEL12_0);
//bring it our from sleep by setting the sleep bit in MCR to 0
CAN1->MCR &= ~CAN_MCR_SLEEP_Msk;
//if sleep ACK(SLAK) not zero, wait i.e. exited from sleep
while ((CAN1->MSR & CAN_MSR_SLAK_Msk) != 0) {}
//put CAN 1 to Init mode
CAN1->MCR |= CAN_MCR_INRQ;
// wait for it to say it has gone there
while ((CAN1->MSR & CAN_MSR_INAK_Msk) != CAN_MSR_INAK_Msk) {}
uint32_t APB1Clock = SystemCoreClock/2000000; // in MHz
CAN1->BTR = 0x00050007U;
CAN1->FMR |= CAN_FMR_FINIT;
// and make sure the CANSB value is zero
CAN1->FMR &= ~(CAN_FMR_CAN2SB_Msk);
//Give 14 filter banks to CAN 1 and 2 each
CAN1->FMR |= (((uint32_t) 14) << CAN_FMR_CAN2SB_Pos) & CAN_FMR_CAN2SB_Msk;
// set all as 32 bit filter in identifier mask mode (zeros)
CAN1->FM1R = 0;
// use all as 32 bit filter with mask
CAN1->FS1R |= CAN_FS1R_FSC_Msk;
// assign FIFO0 to CAN1, and FIFO1 to CAN2 (well, all to FIFO0, except filter 14)
CAN1->FFA1R = CAN_FFA1R_FFA14;
// make sure it is deactivated (clear FACT in CAN_FAR)
CAN1->FA1R &= ~CAN_FA1R_FACT0;
CAN1 ->FA1R &= ~CAN_FA1R_FACT14;
// setup a filter that accepts everything (w/ extended id)
// Each filter bank i (i= 0 to 27 in dual CAN configuration and i= 0 to 13 in single CAN configuration)
//is composed of two 32-bit registers, CAN_FiR\[2:1\].
CAN1->sFilterRegister[0].FR1 = CAN_F0R1_FB2;
CAN1->sFilterRegister[0].FR2 = CAN_F0R1_FB2;
CAN1->sFilterRegister[14].FR1 = CAN_F0R1_FB2;
CAN1->sFilterRegister[14].FR2 = CAN_F0R1_FB2;
// now activate filter
CAN1->FA1R |= CAN_FA1R_FACT0;
CAN1->FA1R |= CAN_FA1R_FACT14;
// take out of init mode for filters
CAN1->FMR &= ~CAN_FMR_FINIT;
//loopback mode. need to do it while on init mode
// CAN1->BTR |= CAN_BTR_LBKM;
//bring CAN1 out from init mode.
CAN1->MCR &= ~CAN_MCR_INRQ;
while ((CAN1->MSR & CAN_MSR_INAK_Msk) != 0) {}
}
void sendCAN1( uint32_t pgn, uint8_t bytes[], uint8_t len)
{
// wait for the mailbox to be empty
while ((CAN1->TSR & CAN_TSR_TME0_Msk) == 0) {};
// put this in next empty mailbox
int boxnum = (CAN1->TSR & CAN_TSR_CODE_Msk) >> CAN_TSR_CODE_Pos;
CAN1->sTxMailBox[boxnum].TDTR &= ~CAN_TDT0R_DLC_Msk;
CAN1->sTxMailBox[0].TIR = ((pgn << CAN_TI0R_EXID_Pos) & CAN_TI0R_EXID_Msk) | CAN_TI0R_IDE;
// for standard (11-bit) ids, replace the above with:
// CAN1->sTxMailBox[boxnum].TIR = ((pgn << CAN_TI0R_STID_Pos) & CAN_TI0R_STID_Msk);
CAN1->sTxMailBox[boxnum].TDHR = ((bytes[7]<<CAN_TDH0R_DATA7_Pos) |
(bytes[6]<<CAN_TDH0R_DATA6_Pos) |
(bytes[5]<<CAN_TDH0R_DATA5_Pos) | bytes[4]);
CAN1->sTxMailBox[boxnum].TDLR = ((bytes[3]<<CAN_TDL0R_DATA3_Pos) |
(bytes[2]<<CAN_TDL0R_DATA2_Pos) |
(bytes[1]<<CAN_TDL0R_DATA1_Pos) | bytes[0]);
CAN1->sTxMailBox[boxnum].TDTR = (len << CAN_TDT0R_DLC_Pos) & CAN_TDT0R_DLC_Msk;
CAN1->sTxMailBox[boxnum].TIR |= CAN_TI0R_TXRQ;
}
uint8_t recCAN1()
{
uint8_t data[8];
uint32_t pgn;
uint8_t len;
uint8_t msgcount;
static uint8_t ret = 0;
// check for any msg in FIFO
// we only use FIFO 0 here
msgcount = CAN1->RF0R & CAN_RF0R_FMP0_Msk;
ret += msgcount;
// read them out one at a time
while (msgcount > 0)
{
pgn = CAN1->sFIFOMailBox[0].RIR >> 3;
len = CAN1->sFIFOMailBox[0].RDTR & 0xF;
// NOTE: case fall through is intentional and critical
switch (len)
{
case 8:
data[7] = (CAN1->sFIFOMailBox[0].RDHR >> 24) & 0xFF;
case 7:
data[6] = (CAN1->sFIFOMailBox[0].RDHR >> 16) & 0xFF;
case 6:
data[5] = (CAN1->sFIFOMailBox[0].RDHR >> 8) & 0xFF;
case 5:
data[4] = CAN1->sFIFOMailBox[0].RDHR & 0xFF;
case 4:
data[3] = (CAN1->sFIFOMailBox[0].RDLR >> 24) & 0xFF;
case 3:
data[2] = (CAN1->sFIFOMailBox[0].RDLR >> 16) & 0xFF;
case 2:
data[1] = (CAN1->sFIFOMailBox[0].RDLR >> 8) & 0xFF;
case 1:
data[0] = CAN1->sFIFOMailBox[0].RDLR & 0xFF;
// do nothing as data is empty
}
// process as they are read
// processCAN(pgn, data, len);
// release that mailbox
CAN1->RF0R |= CAN_RF0R_RFOM0;
// update the message count
msgcount = CAN1->RF0R & CAN_RF0R_FMP0_Msk;
}
return ret;
}
Sending a packet from CAN 1 is fine. When receiving, I cannot see any status changing on the Rx Registers. But, I can see an ACK being transmitted from the TX pin upon receiving a packet when scoping the signals.
The CAN 1 Error Status is completely empty (0x0).
However, when I try the same code with Loopback mode activated, I can see a message received on the registers and my code picks it up too.
Attaching screenshots of CAN 1 RF0R register when in loopback mode which indicates reception.
This scope (Channel 0 for RX and Channel 2 for TX) shows that its receiving a message on RX and it sends back an ACK on its TX under normal mode.
I have checked the termination too and its fine. (120 ohms added. Anyway, getting an ACK, so I am thinking that's not the problem).
I am out of ideas of where I might be going wrong.
No explicit error messages. However, the Rx message is not reflecting on any one of the registers under normal mode.
From the reference manual in CAN_FMR register : to use CAN1 only: stop the clock on CAN2 and/or set the CAN_MCR.INRQ on CAN2, I haven't seen instruction that does this explicitly in your code.
Also in the comments from your code it says that you are not filtering anything but you set CAN_F0R1_FB2 in sFilterRegister thus not everything will be accepted. The ID you are sending is one of those.

keyboard emulator device behavior on ubuntu

I'm building a device driver of sorts that consumes data from a keyboard emulating device.
The device is a card swipe, so its behavior is as follows:
User walks up, swipes card
I get a string of characters (key codes, really, including modifier keys for capital letters)
I don't know how many characters I'm going to get
I don't know when I'm getting something
Since I don't know how many characters I'm going to get, blocking reads on the keyboard tty aren't useful - I'd end up blocking after the last character. What I'm doing is, in Ruby, using the IO module to perform async reads against the keyboard device, and using a timeout to determine that the end of data was reached. This works fine logically (even a user swiping his or her card fast will do so slower than the send rate between characters).
The issue is that sometimes, I lose data from the middle of the string. My hunch is that there's some sort of buffer overflow happening because I'm reading the data too slowly. Trying to confirm this, I inserted small waits in between each key process. Longer waits (20ms+) do exacerbate the problem. However, a wait of around 5ms actually makes it go away? The only explanation I can come up with is that the async read itself is expensive (because Ruby), and doing them without a rate limit is actually slower than doing them with a 5ms delay.
Does this sound rational? Are there other ideas on what this could be?
The ruby is actually JRuby 9000. The machine is Ubuntu LTS 16.
Edit: here's a snippet of the relevant code
private def read_swipe(buffer_size, card_reader_input, pause_between_reads, seconds_to_complete)
limit = Time.now + seconds_to_complete.seconds
swipe_data = ''
begin
start_time = Time.now
sleep pause_between_reads
batch = card_reader_input.read_nonblock(buffer_size)
swipe_data << batch
rescue IO::WaitReadable
IO.select([card_reader_input], nil, nil, 0.5)
retry unless limit < start_time
end while start_time < limit
swipe_data
end
where card_reader_input = File.new(event_handle, 'rb')
I am not sure about Ruby code but you can use linux sysfs to access the characters coming out of keyboard 'like' device, and if feasible you can call C code from ruby application. I had done this for barcode reader and following is the code:
static int init_barcode_com(char* bcr_portname)
{
int fd;
/* Open the file descriptor in non-blocking mode */
fd = open(bcr_portname, O_RDONLY | O_NOCTTY | O_NDELAY);
cout << "Barcode Reader FD: " << fd <<endl;
if (fd == -1)
{
cerr << "ERROR: Cannot open fd for barcode communication with error " << fd <<endl;
}
fcntl(fd, F_SETFL, 0);
/* Set up the control structure */
struct termios toptions;
/* Get currently set options for the tty */
tcgetattr(fd, &toptions);
/* Set custom options */
/* 9600 baud */
cfsetispeed(&toptions, B9600);
cfsetospeed(&toptions, B9600);
/* 8 bits, no parity, no stop bits */
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
/* no hardware flow control */
toptions.c_cflag &= ~CRTSCTS;
/* enable receiver, ignore status lines */
toptions.c_cflag |= CREAD | CLOCAL;
/* disable input/output flow control, disable restart chars */
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
/* disable canonical input, disable echo,
* disable visually erase chars,
* disable terminal-generated signals */
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* disable output processing */
toptions.c_oflag &= ~OPOST;
/* wait for n (in our case its 1) characters to come in before read returns */
/* WARNING! THIS CAUSES THE read() TO BLOCK UNTIL ALL */
/* CHARACTERS HAVE COME IN! */
toptions.c_cc[VMIN] = 0;
/* no minimum time to wait before read returns */
toptions.c_cc[VTIME] = 100;
/* commit the options */
tcsetattr(fd, TCSANOW, &toptions);
/* Wait for the Barcode to reset */
usleep(10*1000);
return fd;
}
static int read_from_barcode_reader(int fd, char* bcr_buf)
{
int i = 0, nbytes = 0;
char buf[1];
/* Flush anything already in the serial buffer */
tcflush(fd, TCIFLUSH);
while (1) {
nbytes = read(fd, buf, 1); // read a char at a time
if (nbytes == -1) {
return -1; // Couldn't read
}
if (nbytes == 0) {
return 0;
}
if (buf[0] == '\n' || buf[0] == '\r') {
return 0;
}
bcr_buf[i] = buf[0];
i++;
}
return 0;
}
Now that you do not know how many characters your going to get you can use VMIN and VTIME combination to address your concern. This document details various possibilities with VMIN and VTIME.

Cause of serial port transmitting bad data and WriteFile return wrong number of bytes written?

Bugfix update:
As of Jun, 2013 FTDI did acknowledge to me that the bug was real. They have since released a new version of their driver (2.8.30.0, dated 2013-July-12) that fixes the problem. The driver made it through WHQL around the first of August, 2013 and is available via Windows Update at this time.
I've re-tested running the same test code and am not able to reproduce the problem with the new driver, so at the moment the fix seems to be 'upgrade the driver'.
The original question:
I've got an 8 port USB-serial device (from VsCOM) that is based on the FTDI FT2232D chip. When I transmit at certain settings from one of the ports, AND I use the hardware handshaking to stop and start the data flow from the other end, I get two symptoms:
1) The output data sometimes becomes garbage. There will be NUL characters, and pretty much any random thing you can think of.
2) The WriteFile call will sometimes return a number of bytes GREATER than the number I asked it to write. That's not a typo. I ask for 30 bytes to be transmitted and the number of bytes sent comes back 8192 (and yes, I do clear the number sent to 0 before I make the call).
Relevant facts:
Using FTDI drivers 2.8.24.0, which is the latest as of today.
Serial port settings are 19200, 7 data bits, odd parity, 1 stop bit.
I get this same behavior with another FTDI based serial device, this time a single port one.
I get the same behavior with another 8 port device of the same type.
I do NOT get this behavior when transmitting on the built-in serial ports (COM1).
I have a very simple 'Writer' program that just transmits continuously and a very simple 'Toggler' program that toggles RTS once per second. Together these seem to trigger the issue within 60 seconds.
I have put an issue into the manufacturer of the device, but they've not yet had much time to respond.
Compiler is mingw32, the one included with the Qt installer for Qt 4.8.1 (gcc 4.4.0)
I'd like to know first off, if there's anything that anyone can think of that I could possibly do to trigger this behavior. I can't conceive of anything, but there's always things I don't know.
Secondly, I've attached the Writer and Toggler test programs. If anyone can spot some issue that might trigger the program, I'd love to hear about it. I have a lot of trouble thinking that there is a driver bug (especially from something as mature as the FTDI chip), but the circumstances force me to think that there's at least SOME driver involvement. At the least, no matter what I do to it, it shouldn't be returning a number of bytes written greater than what I asked it to write.
Writer program:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Writer, ctrl-c to end" << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
char fixedbuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Transmitting on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// HW flow control
ccfg.dcb.fOutxCtsFlow=TRUE;
ccfg.dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
DWORD nwrite = 1;
for(;;) {
nwrite++;
if (nwrite > 30) nwrite = 1;
DWORD nwritten = 0;
if (!WriteFile(ph, fixedbuf, nwrite, &nwritten, NULL)) {
cerr << "f" << endl;
}
if ((nwritten != 0) && (nwritten != nwrite)) {
cerr << "nwrite: " << nwrite << " written: " << nwritten << endl;
}
}
return 0;
}
Toggler program:
#include <iostream>
#include <string>
using std::cerr;
using std::endl;
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
cerr << "COM Toggler, ctrl-c to end" << endl;
cerr << "Flips the RTS line every second." << endl;
if (argc != 2) {
cerr << "Please specify a COM port for parameter 2";
return 1;
}
std::string portName = "\\\\.\\";
portName += argv[1];
cerr << "Toggling RTS on port " << portName << endl;
HANDLE ph = CreateFileA( portName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0, // must be opened with exclusive-access
NULL, // default security attributes
OPEN_EXISTING, // must use OPEN_EXISTING
0, // overlapped I/O
NULL ); // hTemplate must be NULL for comm devices
if (ph == INVALID_HANDLE_VALUE) {
cerr << "CreateFile " << portName << " failed, error " << GetLastError() << endl;
return 1;
}
COMMCONFIG ccfg;
DWORD ccfgSize = sizeof(COMMCONFIG);
ccfg.dwSize = ccfgSize;
GetCommConfig(ph, &ccfg, &ccfgSize);
GetCommState(ph, &(ccfg.dcb));
ccfg.dcb.fBinary=TRUE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
ccfg.dcb.fAbortOnError=FALSE;
ccfg.dcb.fNull=FALSE;
// Camino is 19200 7-O-1
ccfg.dcb.BaudRate = 19200;
ccfg.dcb.Parity = ODDPARITY;
ccfg.dcb.fParity = TRUE;
ccfg.dcb.ByteSize = 7;
ccfg.dcb.StopBits = ONESTOPBIT;
// no flow control (so we can do manually)
ccfg.dcb.fOutxCtsFlow=FALSE;
ccfg.dcb.fRtsControl=RTS_CONTROL_DISABLE;
ccfg.dcb.fInX=FALSE;
ccfg.dcb.fOutX=FALSE;
COMMTIMEOUTS ctimeout;
DWORD tout = 10;// 10 ms
ctimeout.ReadIntervalTimeout = tout;
ctimeout.ReadTotalTimeoutConstant = tout;
ctimeout.ReadTotalTimeoutMultiplier = 0;
ctimeout.WriteTotalTimeoutMultiplier = tout;
ctimeout.WriteTotalTimeoutConstant = 0;
SetCommConfig(ph, &ccfg, sizeof(COMMCONFIG));
SetCommTimeouts(ph, &ctimeout);
bool rts = true;// true for set
for(;;) {
if (rts)
EscapeCommFunction(ph, SETRTS);
else
EscapeCommFunction(ph, CLRRTS);
rts = !rts;
Sleep(1000);// 1 sec wait.
}
return 0;
}
I don't have a good answer yet from FTDI, but I've got the following suggestions for anyone dealing with this issue:
1) Consider switching to a non-FTDI usb-serial converter. This is what my company did, but certainly this isn't an option for everyone (we're putting the chip in our own product). We're using Silicon Labs chips now, but I think there are one or two other vendors as well.
2) Per Hans Passant in the comments - reconsider the use of RTS/CTS signalling. If the writes don't fail due to blocking, then you shouldn't trigger this bug.
3) Set all writes to infinite timeout. Again, no fail due to blocking, no triggering of the bug. This may not be appropriate for all applications, of course.
Note that if pursuing strategy #3, if Overlapped IO is used for writes, then CancelIo and it's newer cousin CancelIoEx could be used to kill off the writes if necessary. I have NOT tried doing so, but I suspect that such cancels might also result in triggering this bug. If they were only used when closing the port anyway, then it might be you could get away with it, even if they do trigger the bug.
If anyone else is still seeing this -- update your FTDI driver to 2.8.30.0 or later, as this is caused by a driver bug in earlier versions of the FTDI driver.

Resources