I am migrating (finally) from MSDOS to Windows XP for controlling a meter via the serial port. My old C DOS code works fine.
I want to do as follows:
meter is continuously taking readings
every few sec, but does not send any
info until it is requested by the
computer
when computer is ready to receive
info from meter, it requests it. It does not accept info otherwise.
My problem is that the readings are just coming into the computer as they are generated by the meter.
I have set the DCB serail params as follows, intending to control communication using RTS and DTR:
dcbSerialParams.BaudRate=CBR_4800;
dcbSerialParams.ByteSize=7;
dcbSerialParams.StopBits=TWOSTOPBITS;
dcbSerialParams.Parity=EVENPARITY;
dcbSerialParams.fDtrControl=DTR_CONTROL_ENABLE;
dcbSerialParams.fRtsControl=RTS_CONTROL_ENABLE;
My old code under DOS was like this:
outportb(COM1+4,0x03); /* start Minolta reading */
for(j=0;j<=10;j++) /*each reading consists of 11 ascii characters*/
{
while(!((inportb(COM1+5)) & 1)); /*wait until char received*/
reading[j]=inportb(COM1);
}
sscanf ( &reading[4], "%f", &lum[k] );
outportb(COM1+4,0x00); /* stop Minolta reading */
It seems to me that this should work:
void serial_notready(void)
{
EscapeCommFunction(hSerial,CLRDTR);
EscapeCommFunction(hSerial,CLRRTS);
}
void serial_ready(void)
{
EscapeCommFunction(hSerial,SETDTR);
EscapeCommFunction(hSerial,SETRTS);
}
int serial_read(char reading[])
{
DWORD dwBytesRead = 0;
int nbytes=11;
ReadFile(hSerial, reading, nbytes, &dwBytesRead, NULL);
return(dwBytesRead);
}
serial_ready(void);
x = 0; while(x == 0){x=serial_read(reading);}
serial_notready(void);
HOWEVER, the Minolta does not wait to receive the RTS from the computer. It just goes ahead and sends readings as each becomes available. At the same time, the computer does not reject any unwanted reading, but accepts it.
I have been bashing my head against the wall trying to figure this out, trying all kinds of permutations to no avail. Any help greatly appreciated!
Update:
The underlying story is that I present a given luminance (brightness) on a display and then need the corresponding luminance reading. This is done for a whole set of luminances.
L ---
U ---
M ---
TIME
I present lum1, lum2, lum3, lum4,.... If the measurements are not synchronised to the display, then I may get a supposed reading3 that is actually lum2, or some sort of average because the reading crossed the border between the lum2 and lum3 displays. And, as you said, Hans,the readings will always lag behind the display luminances. Even if I were always systematically one reading behind it would be bad (my situation is worse-- it is a a random relation between the reading and the luminance).
So the behaviour of the windows serial routines is a nightmare for me. Thanks again for the help!
dcbSerialParams.fDtrControl=DTR_CONTROL_ENABLE;
dcbSerialParams.fRtsControl=RTS_CONTROL_ENABLE;
You enable the DTR and RTS signals right away. The meter will immediately start sending data when you open the port. That data gets buffered in the driver's receive buffer. You didn't have a buffer before in the DOS code. It depends how long it takes for you to call serial_notready(). You'll have a pretty full buffer if that takes a second or so. Yes, that makes it look like the meter is just sending data. And you are always reading an old sample.
Start with the DCB values set to DISABLE. Beware that the scheme is brittle, you could turn the signal off pretty reliably back in the DOS. Now you've got a driver in between. You may well end up turning RTS off too late. Which risks getting a stale reading. An alternative is to startup a thread that just reads continuously. And have your main code just use the last value that it read. The overhead is quite low, serial ports are slow.
The first thing to do would be to check the return values of the calls to EscapeCommFunction(). If the return value is zero, the call failed and you should use GetLastError() to receive more error information.
I use a free third party serial port emulator VPS. its got a interval request timer that dectate when excatly the data need to be updated/grapped. also allow me to log the bus packets into an excel file.
Related
I'm having a dickens of an issue with this one.
I have a remote installation with a raspberry pi installed in it (specifically a Kunbus RevPi connect) that is hooked up to an RS485 Modbus network with a total of 6 sensors on it. 4 Of these sensors are Dat3018 devices which serve to digitize thermocouples and make their values available over the network.
I wrote a C program that performs the following:
Sends a sync command (specified in the data sheet) to the dat3018 which causes them all to read values into their internal memory
Perform some other stuff
Read the saved values in to the raspberry pi over Modbus.
All is well and good, and I'm able to talk to each of the devices, no issues as far as I can tell on the physical communication link. The issue appears when I actually run the software and observe the Modbus request and response.
The first loop of the program runs normally, the sync command goes out, a request and response is received from each of the target slaves on the network. Starting with the second run of the loop each request results in the program receiving the exact same frame it did during the first run of the loop even through there should be at least some values changing. This continues for however long the program runs, always sending the same request from the server and always receiving the same values back.
As per the data sheet for the Modbus devices the following command is sent:
[FF][06][00][00][00][0A][1C][13]
I verified with the company that makes the device that this was the right call to request the device to sync. I also verified that calling this again will cause new values to be read into the synced value registers, meaning that when I read them again there is a very low chance that they are all exactly the same.
I send the following modbus command with the following code:
int read_tempature_synced_values(modbus_t *RTU_context, int slave_addr, uint16_t output[]){
modbus_set_slave(RTU_context, slave_addr);
return modbus_read_registers(RTU_context, _temp_sync_tempatures_start_addr-1, 8, output); //Decrement to account for the inherient +1 from the library
}
For slave # 20:
[14][03][00][16][00][08][A7][0D]
Restarting the program pulls in new values for the first run of the loop before having the same issue.
I wrote a python script to call the sync command and pull in all the measurements from the same registers and that script gives me changing values as I make more calls. That would seem to indicate to me that the issue is somewhere in the Modbus code.
The part of the code that calls the above function looks like this:
//Read in the temperature measurements
for (int i = 0; op_args->settings_instance->tempatureSlaveAddr[i] != -1; ++i){
uint16_t *raw_measures = malloc(8 * sizeof(uint16_t));
read_tempature_synced_values(op_args->modbus_instance, op_args->settings_instance->tempatureSlaveAddr[i], raw_measures);
usleep(op_args->settings_instance->delay_ms);
for (int u = 0; u <= 7; ++u){
if(valid_sensors[i][u] == FALSE){ //Check that there is a valid sensor for this measurement
measurements[measurements_taken].TYPE = TEMPATURE_MEASUREMENT_TYPE;
measurements[measurements_taken].bankNumber = i;
measurements[measurements_taken].sensorNumber = u;
measurements[measurements_taken].measurementTime = sync_time;
measurements[measurements_taken].tempatueReading = ((float)raw_measures[u])/10;
measurements_taken += 1;
}
}
free(raw_measures);
modbus_flush(op_args->modbus_instance);
}
It seems to me that there is something that is remembering what the responses to the calls were and then bouncing them back to me. I can't see any way that the code I have wrote could be doing this.
One other strange thing:
When running my code on the raspberry pi (and only on the pi) my program will randomly print out the two following lines at the start and then at seemingly random times:
ERROR Connection timed out: select
ERROR Connection timed out: select
This happens before the loading/connection to the serial port or sqlite database that I am using. Googling this issue did not appear to give me anything that makes sense in the context of where it is occuring.
Any help would be greatly appreciated
Edit -> The sync command is run at the start of every loop
I verified with the company that makes the device that this was the right call to request the device to sync. I also verified that calling this again will cause new values to be read into the synced value registers, meaning that when I read them again there is a very low chance that they are all exactly the same.
That's how the device works. The SYNC command tells the device(s) to latch the values into a register.
When you do the read, the devices return the latched value and not the "current" value.
From https://www.datexel.com/datexel-user-guides/dat3018-thermocouple-input-to-rs485-modbus.pdf we have:
SYNCHRONISM
The Synchronism function is performed by a command sent to all devices connected on the net. When the devices receive the Sync command, all
input states are saved in the relative register, to be read after time. Doing this, it is possible to read the value of all inputs at the Sync command time.
To send the synchronism command, write the value 10 in the “Test” register (40001) at the address 255.
NOTE: The sync values are not saved in EPROM.
So, for every new value you want you have to send a new SYNC command:
while (1) {
send SYNC command
for (x in all_devices)
read(x)
}
I'm fairly new to VxWorks OS and hence wouldn't mind explanations in case I differ in my understanding of things under the hood when they differ from more traditional OSes like Linux and the likes. With that out of the way, let me begin my actual question.
I am trying to create a loop-back test for testing changes I made to the serial UART driver on the board. Since I do not want to use a cross cable to actually short two UART ports externally, I've connected both of those ports to my dev machine. One configured as an output port from the dev machine's perspective (and consequently as an Input port on the board) and the other an input port (an output port on the board). I am actually doing the loopback using a shared memory buffer which I am protecting using a mutex. So there are two threads on the board, one of which reads from the input port, copies data to the shared memory and the other reads from the memory and sends it over the output port.
And I am using regular open, read and write calls in my VxWorks application (by the way I think it is part of the application code as I call the functions from usrAppInit.c not withstanding the fact that I can even call driver routines from here! (Is it because of a flat memory model vis-a-vis Linux?? Anyhow).
Now I these ports on VxWorks have been opened in a non blocking mode and here's the code snippet which configures one of the ports:
if( (fdIn = open(portstrIn, O_RDONLY | O_NOCTTY, 0) ) == ERROR) {
return 1;
}
if(((status = ioctl(fdIn, FIOSETOPTIONS, OPT_RAW))) == ERROR)
{
return 1;
}
/* set the baud rate to 115200
*/
if((status = ioctl(fdIn, FIOBAUDRATE, 115200)) == ERROR)
{
return 1;
}
/* set the HW options
*/
if((status = ioctl(fdIn, SIO_HW_OPTS_SET, (CS8 | 0 | 0))) == ERROR)
{
return 1;
}
And similarly the output port is also configured. These two are part of two separate tasks spawned using taskSpawn and have the same priority of 100. However what I am annoyed by, is that when I write to the in port from my dev machine (using a python script), the read call on the board get's sort of staggered (I wonder if that's the right way to refer to it). It is most likely due to the short availability of hardware buffer space on the UART input buffer (or some such). This is usually not much of a problem if that is all I am doing.
However, as explained earlier, I am trying to copy the whole received character stream into a common memory buffer (guarded by a mutex of course) which is then read by another task and then re-transmitted over another serial port (sort of an in memory loopback if you will)
In lieu of that aforementioned staggering of the read calls, I thought of holding the mutex as long as there are characters to be read from the Inport and once there are no chars to be read, release the mutex and since this is VxWorks, do an explicit taskDelay(0) to schedule the next ready task (my other task). However since this is a blocking read, I am (as expected) stuck on the read call due to which my other thread never gets a chance to execute.
I did think of checking if the buffer was full and then doing the explicit task switch however if any of you have a better idea, I'm all ears.
Also just to see how this staggered read thing works from the perspective of the kernel, I timed it using a time(NULL) call just before and right after the read. So surprisingly, the very first chunk shows up a number, every other chunk after that (if it's a part of the same data block coming from the outside) shows 0. Could anyone explain that as well?
Keen to hear
I don't have 50 rep points for commenting, but without a loopback cable attached, the only way you can arrive at testing serial loopback behavior is to switch the uart into loopback mode. This often means making changes to the specific hardware part driver.
When using snd_pcm_writei() in non-blocking mode everything works perfect for a while but eventually the audio gets choppy. It sounds like the ring buffer pointers are getting out of sync (ie. sometimes I can tell that the audio is playing out of order). How long it takes for the problem to start it's hardware dependent. On a Gentoo box on real hardware it seldom happens, but on a buildroot system running on QEMU it happens after about 5 minutes. On both cases draining the pcm stream fixes the problem. I have verified that I'm writing the samples correctly by also writting them to a file and playing them with aplay.
Currently I'm setting avail_min to the period size (1024 frames) and calling snd_pcm_wait() before writting chunks of the period size. But I tried a number of different variations (different chunk sizes, checking avail myself and use pthread_cond_timedwait() instead of snd_pcm_wait(), etc). But the only thing that works fine is using blocking mode but I can not do that.
You can see the current source code here: https://bitbucket.org/frodzdev/mediabox/src/5a6471316c7ae481b329e7e0d4af1bb68a32e71d/src/audio.c?at=staging&fileviewer=file-view-default (it needs a little cleanup since I'm trying all kinds of things). The code that does the actual IO starts at line 375.
Edit:
I think I got a solution but I don't understand why it seems to work. It seems that it does not matter if I'm using non-blocking mode, the problem is when I wait to make sure there's room on the buffer (either through snd_pcm_wait(), pthread_cond_timedwait(), or usleep()).
The version that seems to work is here: https://bitbucket.org/frodzdev/mediabox/src/c3eb290087d9bbe0d5f37653a33a1ba88ef0628b/src/audio.c?fileviewer=file-view-default. I switched to blocking mode while still waiting before calling snd_pcm_writei() and it didn't made a difference. Then I added the call to snd_pcm_avail() before calling snd_pcm_status() on avbox_audiostream_gettime(). This function is called constantly by another thread to get the stream clock and it only uses snd_pcm_status() to get the timestamps. Now it seems to work (at least it is a lot less probable to happen) but I don't understand exactly why. I understand that snd_pcm_avail() will synchronize the pointers with the kernel but I don't really understand when it needs to be called and the difference between snd_pcm_state() et al and snd_pcm_status(). Does snd_pcm_status() also synchronize anything? It seems not because sometimes snd_pcm_status_get_state() will return RUNNING when snd_pcm_avail() returns -EPIPE. The ALSA documentation is really vague. Perhaps understanding these things will help me understand my problem?
Now, when I said that it seems to be working I mean that I cannot reproduce it on real hardware. It still happens on QEMU though way less often. But considering that on the next commit I switched to blocking mode without waiting (which I've used in the past and never had a problem with on real hardware) and it still happens in QEMU and also the fact that this is a common issue with QEMU I'm starting to think that I may have fixed the issue on my end and now it's just a QEMU problem. Is there any way to determine if the problem is a bug on my end that is easier to trigger on the emulator or if it's just an emulator problem?
Edit: I realize that I should fill the buffer before waiting but at this point my concern is not to prevent underruns but to make sure that my code can handle them when they happen. Besides the buffer is filling up after a few iterations. I confirmed this by outputing avail, buffer_size, etc before writing each packet and the numbers I get don't make perfect sense, they show an error of 1 or 2 periods about every 8th period. Also (and this is the main problem) I'm not detecting any underruns, the audio get choppy but all writes succeed. In fact, if the problem start happening and I trigger an underrun by overloading the CPU it will correct itself when the pcm is reset.
In line 505: You're using time as argument to malloc.
In line 568: Weren't you playing audio? In this case you should do wait only after you wrote the frames. Let's think ...
Audio device generates an interrupt when it terminates to process a period.
| period A | period B |
^ ^
irq irq
Before you start the pcm, audio device doesn't generate any interrupt. Notice here that you're waiting and you haven't started the pcm yet. You only starts it when you call snd_pcm_writei().
When you wait for audio data you'll be awake only when the current period has been fully processed -- in your first wait the first period wasn't even written -- so in a comfortable situation you should write the whole buffer, wait for the first interrupt, and then write the just processed period, and on and on.
Initially, buffer is empty:
| | |
write():
|############|############|
wait():
..............
When we wake up:
| |############|
write():
|############|############|
I found the problem is you're writing audio just before it be played, then sometimes it may arrive delayed in the buffer.
I'm working with a PIC18F and am trying to send data via hyperterminal. When I send data at a slow rate by pressing one key every half secondish it recieves the data and echos it correctly but when I start pressing the keys at a faster rate the MCU locks up. Not sure what causes this.
while(index<length){
while(PIR1bits.RCIF==0); // Wait till char recieved
sendData(str2,9); // confirm reception
Delay1KTCYx(5); //delay because without it, it messes up.
rxData[index]= RCREG; //char array
index++;
}
baudrate is 2400 On both PIC and hyperterminal.
This is our receive loop. sendData is just debug code that we send saying "recieved". It's how we know when it has frozen.
It does not freeze at the same amount of loops everytime, it is solely on how fast we input data.
(I did work on MCUs but haven't a deal with PICs, so i'll try to help with common problems)
You do not check any receiver error flags. Receiver may lock up in Overrun Error state and do not receive further, until you clear Overrun flag. Add check for error conditions and resolve them accordinly to PIC documentation.
Good practice is to read received byte as early, as possible when receive complete is indicated, so try to move rxData[index]= RCREG; imediately after while(PIR1bits.RCIF==0);. This lowers possibility
You didn't shown code for sendData. There may be missed checking for TX ready state and error conditions, so it may also lock up.
Unmotivated delay indicates that you're already going wrong somewhere. Try to remove it and THEN debug code.
You should test your receive and transmit separately. At first, check transmitter: try to output long line of text through UART without any receiving. (Say, write "Hello world!" program:))
Check receiver code alone: remove transmission from program, connect LED (voltmeter, oscillosocope, whatever you have) to free GPIO pin, and then make it toggle logic level on it every time it receives a byte. Is it takes only several clock ticks to do, it should not intervene receiving or lockup.
Maybe when you send 2 characters while it is busy sending the "received" one of them is discarded and you never reach your length?
On most microcontrollers, a UART receiver overrun will cause the newly-received byte to be discarded and a flag to be set, but the receiver will continue to operate normally. On the PIC, a receiver overrun will cause the UART to die until the CREN bit is cleared and re-set.
I am sending/receiving data over a serial line in Linux and I would like to find the delay between characters.
Modbus uses a 3.5 character delay to detect message frame boundaries. If there is more than a 1.5 character delay, the message frame is declared incomplete.
I'm writing a quick program in C which is basically
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
// setup newtio
....
tcsetattr(fd, TCSANOW, &newtio);
for(;;) {
res = read(fs, buf, 1);
if (res > 0) {
// store time in milliseconds?
//do stuff
}
}
Is there some way of measuring the time here? Or do I need to look at retrieving data from the serial line in a different way?
I've also tried hooking into SIGIO to get a signal whenever there is data but I seem to get data 8 bytes at a time.
(yes, I know there exist some modbus libraries but I want to use this in other applications)
The simple answer is... you cannot (not without writing you own serial driver)!
If you are writing a MODBUS master there is some hope: You can either detect the end of a slave response by waiting any amount of time (provided its longer than 3.5 chars) without receiving anything (select(2) can help you here), or by parsing the response on the fly, as you read it (the second method wastes much less time). You must also be careful to wait at least 3.5 characters-time before staring to transmit a new request, after receiving the response to the previous request. "At least" is operative here! Waiting more doesn't matter. Waiting less does.
If you a writing a MODBUS slave then you' re out of luck. You simply cannot do it reliably from userspace Linux. You have to write you own serial driver.
BTW, this is not Linux's fault. This is due to the unbelievable stupidity of MODBUS's framing method.
MODbus is like a lot of old protocols and really hates modern hardware.
The reason you're getting 8 bytes at a time is :
Your PC has a (at least) 16 byte serial FIFO on receive and transmit, in the hardware. Most are 64byte or bigger.
It is possible to tell the uart device to time out and issue a received interrupt after a number of char times.
The Trigger Level is adjustable, but the low-level driver sets it "smartly". try low-latency mode using setserial)
You can fiddle with the code in the serial driver if you must. Google it (mature content warning) it is not pretty.
so the routine is as pseudocode
int actual=read (packet, timeout of 1.5 chars)
look at actual # of received bytes
if less than a packet, has issues, discard.
not great.
You can't use timeouts. On higher baud rates 3.5 character timeout means a few milliseconds, or even hundreds of microseconds. Such timeouts can't be handled in the Linux user space.
On the client side, it isn't a big deal since Modbus doesn't send asynchronous messages. So it's up to you not to send 2 consecutive messages within 3.5 character timeout.
On the server side, the problem is that if your clients have an extremely short response timeouts and Linux is too busy you can't write a bullet-proof framing solution. There is a chance that read() function will return more than one packet. Here is (a little contrived) example.
Client writes a packet to server. Timeout is let's say 20 ms.
Let's say that Linux is at the moment very busy, so kernel doesn't wake up your thread within next 50 ms.
After 20 ms client detects that it didn't receive any response so it sends another packet to server (maybe resent the previous one).
If Linux wakes up your reading thread after 50 ms, read() function can get 2 packets or even 1 and half depending to how many bytes were received by the serial port driver.
In my implementation I use a simple method that tries to parse bytes on-the-fly - first detecting the function code and then I try to read all remaining bytes for a specific function. If I get one and half packet I parse just the first one and remaining bytes are left in the buffer. If more bytes come within a short timeout I add them and try to parse, otherwise I discard them. It's not a perfect solution (for instance some sub-codes for function 8 doesn't have a fixed size) but since MODBUS RTU doesn't have any STX ETX characters, it's the best one I were able to figure out.
I think you are going about this the wrong way. There is a built in mechanism for ensuring that characters come in all together.
Basically, you are going to want to use ioctl() and set the VMIN and VTIME parameters appropriately. In this case, it seems like you'd want VMIN (minimum number of characters in a packet) to be 0 and VTIME (minimum amount of time allowed between characters to be 15 (they are tenths of seconds).
Some really basic example code:
struct termio t;
t.c_cc[ VMIN ] = 0;
t.c_cc[ VTIME ] = 15;
if (ioctl( fd, TCSETAW, &t ) == -1)
{
printf( msg, "ioctl(set) failed on port %s. Exiting...", yourPort);
exit( 1 );
}
Do this before your open() but before your read(). Here's a couple of links that I've found wildly helpful:
Serial Programming Guide
Understanding VMIN and VMAX
I hope that at least helps/points you in the right direction even if it isn't a perfect answer for your question.