Prolific PL2303 driver
Dave Higton (1515) 3526 posts |
All that matters is that, with the load present, the voltage is either more positive than +3V, or more negative than -3V. You’re only in trouble if the voltage is between +3V and -3V, as the resulting state is then undefined. |
||||||||||||||
Rick Murray (539) 13840 posts |
Steve – as was mentioned just prior, the RS232 specification defines the signal levels as +3V high and -3V low; with a current sinking capability of 45mA. My weather station “fakes” serial communications by switching between RTS (high) and DTR (low) to simulate the serial bitstream. This saves having to have line drivers, voltage generators, and such (which would also risk draining the battery) and instead essentially lets the host power its own communications. It’s a mixture of clever and awful, much like the fruitcake nibble protocol used… Removing the “sleep” from the serial string allowed it to communicate in a TaskWindow. Using the WSAPI description for how to talk to the device, plus the list of known memory locations (in the code archive), I was able to put together a simple program in BASIC. It’s rather horrid, you need to have delays (~20cs) all over the place or the device will lose track of what’s going on and just give up listening. That will be fun in a multitasking program, I guess I’ll need to hang off the NULL event. Still, I managed to get this far:
Looks like the calculation is going Pentium on me. That’s supposed to be 998.6. :-) That’ll do for today. Proof of concept complete, I can now read data from the weather station. Great work Colin, thank you. |
||||||||||||||
Rick Murray (539) 13840 posts |
Is STR$ bugged? Here’s the code that does the calculation:
And here is the result:
Where does STR$ get that errant 0.0000001 from? It seems an equally errant subtraction of 0.0000001 (as in my previous post) is why .6 became .5-and-lots-of-nines. |
||||||||||||||
Rick Murray (539) 13840 posts |
Um… Yeah… It’s five days early.
And:
<scratches head> |
||||||||||||||
Colin (478) 2433 posts |
Just rounding errors
|
||||||||||||||
Colin (478) 2433 posts |
If your program just requires a single thread ie the window is there to manage (start stop, cancel, pause) a single process you can do it with the pattern below – its easier than trying to split the process up into null event chunks. Quitting the program sets must_quit%, cancelling the process sets must_abort%. Starting the process sets start_thread%. You can start the program with NULL events disabled and only enable them when you set start_thread% – disabling them again when PROCthread completes. Disabling null events while in FNyield pauses the thread. You can call FNyield as many times as you like inside PROCthread you just need to ensure that you quit PROCthread when it is true.
|
||||||||||||||
Rick Murray (539) 13840 posts |
I have no API, the program just cycles through the reading mechanism every five minutes. So I cheated and just wrote a simple loop that calls the reading code, and dropped in calls to the polling function when waiting on timeouts. The code is pretty horrible, but it gets the job done. Now that I know how to read from the device, someday I can write a better program. But for now… Rick’s weatherNoting fancy yet. This is just a recording of the current weather read from a La Crosse WS-2300 weather station, using RISC OS. Current values
Assuming it doesn’t crash or anything, you can find it at http://heyrick.ddns.net/ |
||||||||||||||
Dave Higton (1515) 3526 posts |
_swix(DeviceFS_CallDevice, _INR(0,3), DeviceDriver_WakeUpRX, self->usb_devicefs_driver_handle, self->rx.usb_device_driver_stream_handle, (1 << 30)); Cracking, thank you, Colin, now I get the file transferred identically to the source. Interesting what you say about using two buffers. Despite having to shovel from one buffer to another, which is usually something that can only slow you down, in this case it probably speeds things up; with a single buffer, every time you empty it, you have to set up another USB transaction and wait for it to complete before you can take the next byte out. Two buffers allows all that to go on in the background. |
||||||||||||||
Stephen Unwin (1516) 154 posts |
Maybe it’s better with BasicVI *basic ARM BBC BASIC V (C) Acorn 1989 Starting with 651516 bytes free >PRINT STR$(987.6) 987.5999999 >QUIT *basic64 ARM BBC BASIC VI (C) Acorn 1989 Starting with 649468 bytes free >PRINT STR$(987.6) 987.60000000000002 >QUIT * Or maybe not!? |
||||||||||||||
Peter Howkins (211) 236 posts |
All working as expected http://floating-point-gui.de/ |
||||||||||||||
Rick Murray (539) 13840 posts |
Thanks. The first link made sense. The second made my brain melt. Looks like it’d be simpler to work with integers and treat the units and fraction parts separately… |
||||||||||||||
Dave Higton (1515) 3526 posts |
Colin: does an interrupt in endpoint have to be woken up in the same way as a bulk in endpoint, i.e. at start-up and every time the buffer is emptied? In the case of the PL2303, is the interrupt in endpoint where the error status and input control line information comes from? Sorry, I can understand neither your code nor the Linux source in that regard. |
||||||||||||||
Colin (478) 2433 posts |
no you don’t need to wake up an intterupt endpoint at all. Once it starts it just keeps going, but you may only get data when a pin changes – not every interval. I haven’t used the interrupt endpoint but haven’t found an alternative yet for getting the status of the pins. As all these pins are fixed to logic 0 on the device I’ve got I can’t see them changing to test commands. I have figured out a few more control commands 0×40 0×01 0×0000 0×00nn 0×0000 sets the handshake mode where nn = 0×01 = no handshake 0xC0 0×01 0×0080 0×0000 0×0001 reads 1 byte handshake mode – needs data buffer 0×40 0×01 0×0505 0×1311 0×0000 sets the XON/XOFF characters I think 0×21 0×22 0×000n 0×0000 0×0000 n bit 0 sets DTR, bit 1 sets RTS |
||||||||||||||
Rick Murray (539) 13840 posts |
Version 1 of my program was the simple listing shown above. I’m working on a better one that will be a better written, with data logging that works and some other stuff. I’ll need to do some tests to ensure it copes sanely with conditions such as the device not even being plugged in. Hehe… It’s still running on my Pi and updating every five minutes1. http://heyrick.ddns.net/ 1 Don’t hit refresh immediately if you go at a multiple of 5 minutes and the information isn’t up to date – I read the data with large delays so the device is more liable to respond and not get itself in a muddle2, consequently it takes approximately 30 seconds to read the data and update the webpage. 2 The activity log is written to http://heyrick.ddns.net/wstnlog.txt if you want to see what goes on. |
||||||||||||||
Colin (478) 2433 posts |
You must wearing a wet suit – 2500 mm of rain in 24 hrs |
||||||||||||||
Rick Murray (539) 13840 posts |
? I see 7.25mm here… though the device is capable of reporting 1000mm in the last hour (but I’m not sure the sensor would cope with that). The comment I wrote beside that bit of code was “and God help us…”. If you see oddities, might be worth checking the http://heyrick.ddns.net/wstnlog.txt file to see if the data read went haywire or anything. |
||||||||||||||
Rick Murray (539) 13840 posts |
Just saw the 2500mm myself. Weird, as that data comes directly from the station itself (calculated internally from sensor data history). I’ve added an extra line to the log to see what is actually being returned to my request. Pressing the display button shows me that 7.2 is the 24h historical value, so I don’t know where the 2500mm is coming from. Hmm, it is returning &25250000. What?!? Okay. Until I implement the checksum test, I have added a hack to reject the data if it is &25250000. I’ll tell you what. It is interesting writing code for a device on the expectation that it will fail… |
||||||||||||||
Clive Semmens (2335) 3276 posts |
Spike due to distant lightning or some such? Balanced lines (twisted pair especially) aren’t completely immune. Could be the communication rather than the device. |
||||||||||||||
Steve Fryatt (216) 2105 posts |
Isn’t that the default approach adopted when writing software? ;-) |
||||||||||||||
Dave Higton (1515) 3526 posts |
By chance, my PL2303 dongle has all its handshake lines properly buffered and connected. I’ve tried so far connecting the RTS line to either RI or CTS, waggling the RTS line, and looking what happens on the interrupt input. Every transition appears to generate a string of 10 octets (I note from USBInfo that the interrupt in EP has a maximum packet length of 10). What I see is: A1 20 00 00 00 00 02 00 nn 00 where nn changes between 08 and 00 for a change of RI, and 80 and 00 for a change of CTS. What bothers me is how we are supposed to know the initial state of the lines, because there is (as you noted) nothing on the interrupt in EP until there is a transition of one of the lines. Unless, of course, there is an initial packet if the lines are not all in their assumed default state, whatever that is – and I haven’t done any such experiments so far. The numbers I’ve seen so far in that 9th byte agree with the numbers in the Linux driver. More experiments ongoing. My module handles (in theory, at least!) multiple PL2303 devices, and is in quite a usable state already. I’ve requested an allocation. The API documentation is well on its way too. |
||||||||||||||
Colin (478) 2433 posts |
The A1 means read class interface and the 20 is the get_line_coding reason code. The format of the command is A1 20 00 00 ii 00 07 00 – where ii is the interface number the 07 is the number of bytes read. The data read has the structure
see the USB.org CDC class PSTN120.pdf document – it’s in the CDC zip file from the site. the document also shows that reason code 21 is set_line_coding for which you need to change the A1 to 21(write class interface) to write the bytes. Having a wLength field < 7 just reads or writes < 7 bytes. For the value you’ve seen of 8 if you are using snoopy I’d be interested to know if only 7 bytes were actually returned. Commands starting 40 are ‘write vendor device’ commands and C0 are ‘read vendor device’ commands.
Read reason codes have the top bit of the byte set. So a write would be 0505 and a read 8585 or just 0005 and 0085 if the reason code is less than 256. There is a problem with DeviceFS. If you open an tx endpoint, write a file to it and close the file you may have a full buffer – say 1024bytes – which is flushed on closing. This means that it waits for the buffer to empty before closing which for a 300 baud connection could take 35+ seconds. So the machine is locked up for 35 secs. Additionally if you are using handshaking the remote device may stop you sending during this flush and if the remote end disappears at that point the machine locks up as the close never returns. A time out doesn’t really work as who would wait 35 secs and in any case the connection may be legitimately paused. This is the problem that has stopped me releasing my module. Unfortunately its a problem with the lack of PMT. On a PMT system the thread would continue stuck in the background, we have a machine lock up. The only solution I see is to provide a get_buffer_used_size command so that the user can check that the buffers are empty before closing. But then it’s not a standard serial interface. Fortunately regarding documentation mine’s just a standard serial interface. |
||||||||||||||
Colin (478) 2433 posts |
Ah your data was from the interrupt endpoint – my mistake. Seems odd that the interrupt endpoint returns what looks like a control command complete with the 2 bytes it reads yet the command doesn’t work as a control command – which is a shame. |
||||||||||||||
Rick Murray (539) 13840 posts |
Doesn’t the standard serial device behave in the same way? |
||||||||||||||
David Feugey (2125) 2709 posts |
Very impressive, since the official Windows driver is almost unusable :) |
||||||||||||||
Colin (478) 2433 posts |
Yes. As does any DeviceFS device like USB. Ideally the noblock option would have used a non blocking flush and OS_Args 255 (flush) would have returned a value indicating the flush is complete. Then close would close immediately and if you needed to ensure the data you could multi task a flush until it was complete before closing the file. As it is there is no way to abort a flush (except escape – even then it can take up to 3 escapes to abort when you have devicefs frontend and backend and its only useful in a task window) you have to wait it out at some time if the buffer doesn’t empty. I think we need a DeviceFS tx purge command to purge tx buffers and abort tx transfers so that close closes immediately and a deviceFS non blocking flush command to tell you if there is still data in the tx buffers. |