Closed Msq001 closed 5 years ago
Hi I've also been working on exactly this. I've just got things working and can also run using any pin my only extra requirement is the use of the RIT interrupt. I'd be interested to see your implementation. What baud rates have you tested? Have you run into any issues with jitter when printing due to the time taken by the various Marlin interrupt routines? What parts of Marlin did you need to change? My intention was to only need to changes to the SoftwareSerial files (though I ,may also change the baud rate used by the TMC code).
Hi I've also been working on exactly this. I've just got things working and can also run using any pin my only extra requirement is the use of the RIT interrupt. I'd be interested to see your implementation. What baud rates have you tested? Have you run into any issues with jitter when printing due to the time taken by the various Marlin interrupt routines? What parts of Marlin did you need to change? My intention was to only need to changes to the SoftwareSerial files (though I ,may also change the baud rate used by the TMC code).
open file C:\Users\<user name>\.platformio\packages\framework-arduino-lpc176x\cores\arduino\SoftwareSerial.cpp
and uncomment DELAY_TABLE table[]
in line 62-70, then you can use the software serial.
Yes I'm very familiar with that code, but it requires pins that generate a software interrupt on change for the receive line. Have you made other changes?
Yes, I also add some functions in this file and changed the TMC2208Stepper.cpp file line 94 uint32_t TMC2208Stepper::read(uint8_t addr), In order to make the pins without interruption function can also be used as RX, there is a little change in the Arduino framework. I think this is not good, so we need to discuss it. I will update the change to github tomorrow, and let you know
OK, I hope to be able to start testing my solution with a TMC2208 later today. So far I've been testing with a terminal connection and a scope to check on timings etc. Have you had the code that checks the 2208 temperatures during operation (MONITOR_DRIVER_STATUS in Configuration_adv) running? I had a look at the stepper code timing and it looked like that could cause problems for timing of read and write operations when using software serial.
Looking forward to seeing your solution.
Thanks! Will give your version a test and see how I get on. It is much simpler than what I've been doing (which is good!).
What board are you guys testing with ? Following this with interest.
I tested it with BIGTREE SKR V1.3
@Msq001 please submit a PR against bugfix 2.0
i personaly can use this and will use with re-arm+ramps 1.4SB
@boelle Hello, I changed the library file, SoftwareSerial.cpp of lpc1768 and TMC2208Stepper.cpp , Did not change the marlin source file,So, I can't create a PR for marlin , https://github.com/Msq001/marlin_2.0_bugfix_tmc2208. You can download this project to test
Skr v1.3.? v1.2 isn’t available yet.
@PeteBurgess skr v1.2 is a debug version, v1.3 is testing now, and will be released soon. if you have a motherboard with LPC1768, add software series pins to the
@Msq001 I recommend rather than simply uploading the files, you fork Marlin/TMCStepper/whatever. It's way easier to see the changes and allows you to actually make pull requests.
https://github.com/teemuatlut/TMCStepper/pull/13 https://github.com/p3p/pio-framework-arduino-lpc176x/pull/13
@teemuatlut hi~ here is the files have changed
@Msq001 i think he meant to fork marlin git and not his own git
OK So I now have some results from testing this approach and from some further tests on an alternative that uses the RIT timer to generate and read the software serial signals. Before I give the results let me explain the tests I've been using and the types of error I'm trying to detect...
When you talk to a TMC2208 commands are sent to the driver using short 4 byte packets and responses are received as an 8 byte packet. In both cases the packets contain a single byte checksum. The majority of commands do not have a response (as they are used to set values). If the checksum of the command is not correct the command will simply be discarded by the TMC2208. This raises the obvious question of how do we know if there is a problem when we send a command? The long answer is that the TMC2208 does provide a mechanism (using sequence numbers), that will allow you to detect if a command has worked, but this is not used by the current TMC library. So the short answer is that at the moment there is no easy way to test commands that do not send a response back to the control board. However it is possible to use commands that get a response to detect both send and receive errors. Receive errors are easy we simply compute the checksum on the data we get back and if it does not match there has been an error. But what about if there is an error writing the command to the TMC2208? Well in this case the device will simply ignore the command and with the current code we will read a response that is all zeros. Unfortunately the checksum checking code will not identify this as an error (as the checksum of all zeros is zero), however no valid response is ever all zero (as it must have a special start byte ) so we can detect the all zero case. So write errors will show up as an all zero response and read errors as a checksum.
As I said above the majority of commands (used to set values) do not generate a response from the TMC2208 so it is not easy to use these for testing purposes. However if we enable the MONITOR_DRIVER_STATUS option this will read the status registers on a frequent basis and so can be used to test both command and response handling (since a read request sends a command and reads the response). Unfortunately by default the TMC code does not monitor the status of drivers using software serial, nor does it check the response either for a bad checksum or a zero packet (the checksum is computed, and errors are noted, but nothing ever checks the error flag). But these problems are easy to fix by small modifications to the TMC library.
OK so the test setup is one in which we have the modified TMC library that has status monitoring of the device enabled and that has extra code to count the number of CRC (receive errors) and zero responses (write errors). I have a test board (SKR V1.1) with two TMC2208 devices (and 2 TMC2130s but we can ignore them). The test consists of three parts. Part one is simply to power the board on (which initialises the devices) and note any errors detected during this process. Test two is to leave the board idle for two minutes (during which the status of the devices will be tested repeatedly), test three is to run a 30 minute test gcode file. At the end we report the detected errors from each test. So here are the results obtained using the modified SoftwareSerial class running at 115200 baud using polling for receiving data. The results are given as read errors/write errors for each test.
Configuration Startup Static GCode
SS115200 0/0 96/0 7692/0
Obviously we need to dig a little deeper. See next post
Ok so those results do not look very good, interestingly we have a lot of read errors but zero write errors. So what is going on. We need to understand what is causing the errors and it is all about timing. Basically we are are generating the serial pulse train by timing the gaps between the various on/off states of the pins. Similarly when we are receiving a byte we are sampling the state of the pins then waiting a fixed time and sampling again. Just to understand the scale of things at 115200 baud we have a gap between the pulses that represent the data of about 8uS. Now computers are reasonably good at timing things at this sort of scale so what is going wrong? Basically interrupts. These are used in Marlin to do things like generate the stepper pulses, check temperatures, handle serial and USB I/O etc. The thing is an interrupt is a bit of a bully and will push other code out of the way when it needs to run, in this case the code that is being pushed out of the way is our receive loop and its associated timing code. Imagine what happens to our code that is sampling the data every 8uS if along comes a pulse generation interrupt that can run for up to 10uS! Yep that is exactly what can happen. Given that these interrupts can occur every 20uS it is a wonder that we manage to get any good data at all.
Now you may be wondering why we are not getting any write errors, surely the interrupt issue should be screwing up our output pulse generation just as much? Well yes it should except that our write code has a secret weapon, it disables interrupts when generating the pulses for each 8 bit character (which is actually 10 bits long because of start and stop bits). This means that the timing for the pulses is actually pretty good with no "jitter" due to those pesky interrupts. So can we turn off interrupts when receiving data? Well Yes we can and when we do we get the following results....
Configuration Startup Static GCode
SS115200 0/0 96/0 7692/0
SS115200IN 0/0 11/0 1049/0
Which is clearly much better, but why are we still getting read errors. Well we are only turning interrupts of for each 8 bit character, we enable them between each one. This is OK on the write side of things as it doesn't matter how big the gap is between each byte (well not that much). However on the read side we don't control when the bits arrive and if our sampling code is not there ready to go when the first bit of a byte arrives, well we miss it. So simple we just leave the interrupts off until we have all of the 8 bytes we need? Unfortunately those steppers are not going to be running very smoothly if we stop feeding them pulses for 8108uS (8 characters 10 bits per character 8uS per bit) so 640uS. In fact turning interrupts off for 80uS is probably pushing our luck a little.
So what else can we try?
We already use advanced tricks to no miss any of the hardware serials rx-interrupts. With hardware serial we get 1 Interrupt per byte. With software serial and pin-change interrupts we get at least 8/2 + 2 = 6 positive flanks/interrupts per byte (in average). That's a lot more challenging. With software serial and polling we have the choice. Either we can receive a message over the rx-line, or we can step in time. There is no chance to do both at the same time.
The trick how to Address Multiple Slaves with a single serial is described in chapter 4.4 (page 21) of the datasheet. The pins for setting the gates are by far less demanding, don't need to create interrupts, don't have to be on an U(S)ART, could be any output pins. But a tiny bit of additional hardware is required (less then 2 € for 40 drivers). The software is not challenging then.
I have several boards sitting here with direct wire connection for 2208 UART. If you gentlemen don’t mind me snagging a zip of your modified Marlin, I would be more than happy to test.
@AnHardt Yes I agree that a hardware serial solution (possibly with extra switching hardware) is the best way to go. I wonder if anyone would be interested in a hardware add-on that would do the multiplexing?
But it's still an interesting challenge to see what you can actually get out of the hardware you already have, without making hardware changes. Especially if like me you are a software sort of guy! So I'm going to continue to investigate further. Of course if I was a board manufacturer (or someone testing a prototype board) I would definitely be pushing to have that hardware solution added to the board! At the end of the day any software hack is likely not to be 100% perfect and may impact print quality (see my point above about disabling interrupts).
I think a simple polled SoftwareSerial implementation (with interrupts turned off during send/receive), is good enough for performing TMC2208 UART based configuration. But it probably is not a good idea to use this approach for monitoring the drivers or talking to them at all during printing. You can probably use that solution on pretty much any board that has pins available (well 32 bit boards anyway). It's a bit of a hack, a little like using the enable pins as chip select lines for TMC2130 drivers with the SKR V1.1 board, but some people may be happy to have that.
For now I'm still digging that hole trying to get better software monitoring of the drivers.
Continuing on in my investigations and testing. So far we have been running the SoftwareSerial UART at 115200 baud, now that's nice, but it does impose some pretty tight timing requirements (for software at least, even on a 32bit board). At the moment we need to be able to measure reasonably accurately delays of around 8uS or so and we are operating in an environment that has a pulse generating interrupt that can in some circumstances take 10uS or so. This is not a good combination. So an obvious solution is to relax some of the constraints in this case the baud rate. A lower baud rate will make the timing far less critical if we drop the speed to say 38400 we now have a pulse length of around 26uS which is somewhat more manageable and we may even be able. Pushing a little further we really only have a few bytes to move around, so why not drop the speed down towards the minimum speed that the drivers support (9000bps), so 9600 baud. This it turns out is the speed used by Klipper for these devices, so it seems to work fine (at least for configuration). With that our pulse timing goes up to a very manageable 104uS. But with this length of pulse we really can't continue to disable interrupts (arguably we never should have). So what do the numbers look like with these configurations?
Configuration Startup Static GCode
SS115200 0/0 96/0 7692/0
SS115200IN 0/0 11/0 1049/0
SS38400NI 0/0 1/11 1072/1260
SS9600NI 0/0 0/0 82/28
As we can see no longer disabling interrupts mean that we are now getting write errors but the slower speeds clearly help with the timing. Out of interest I did test with the ints disabled and the steppers basically stopped working! Oh and just in case you are wondering all of these tests actually perform the same number of checks on the drivers (the poll routine always runs for the same period).
So the final thing I've taken a look at with this polling approach is how the delays are generated. The code in the LPC176X framework creates some very accurate delays, but these delays are based on executing code that takes a certain number of cycles to run. This code is then executed a number of times based upon the desired delay and the speed of the cpu. But what happens if part way through delay of say 104uS there is an interrupt that takes 10uS? Well you end up with a delay of 114uS (or so). Now it turns out that this can mess with some of our timing. So replacing the delay routines with one that uses one of the hardware timers can have interesting results:
Configuration Startup Static GCode
SS115200 0/0 96/0 7692/0
SS115200IN 0/0 11/0 1049/0
SS38400NI 0/0 1/11 1072/1260
SS9600NI 0/0 0/0 82/28
SS38400NINT 0/0 0/0 48/53
SS9600NINT 0/0 0/0 0/1
As you can see correcting the timing to allow for any interrupts helps a lot (final two results above), lowering the error rate pretty much to zero when running at 9600 baud.
@gloomyandy I was considering using a coprocessor like the STM32F030K6T6 or STM32F103C8T6 to handle all of the UART setup and monitoring for the drivers. There should be enough IO’s to handle the process for plenty of drivers and I think that the M3 processor of the F103 would be easily up to the task. It would take a lot of load off processors such as this and reduce the pin count to ideally a single hardware UART connection back to the main CPU that reports back if the driver throws an error, etc. I have no problem handling the hardware aspect but I cannot handle any of the software.
@Msq001 I flashed your code and do not have RX errors anymore but I am getting command not recognized when I try a M911. Current to drivers do not seem to change either whereas I could definitely note a holding torque change when using the current bug fix version.
@gloomyandy With regard to MONITOR_DRIVER_STATUS when printing, I intend to replace both RX and TX in the interrupt of the timer instead of using delay_us, and I am working on that now. I think it would be much better. The only disadvantage is that it takes up extra timer. If there's any progress, I'll let you know in the first time
@griffin-117 hi~ Have you tried M906 command? I can change the current with M906 command.
@Msq001 you may want to wait a couple of days and take a look at the code I'm working on. It uses the RIT interrupt for timing and also only needs a single I/O pin per TMC2208. I have it working reasonably well at the moment but want to clear up a few things. It does not require any polling for the I/O.
Ok so I've got a first pass of my solution to this problem. It basically uses the RIT timer to generate the output stream and to sample (using oversampling) the input stream. It supports the use of any pin and supports half duplex operation so only a single pin is needed per TMC2208. It supports the use of MONITOR_DRIVER_STATUS (though changes are needed to Marlin to enable this). No changes are needed to the TMC library to use it (though it does include a bit of a hack to make this possible and I'll be working to put together a PR for the TMC lib in the future to clean this up). The approach used is based on one originally suggested by @p3p and uses input from this thread and a discussion over on discord.
I have tested it a little with four TMC2208s on an SKR V1.1 board (with an LCD display) and on my "production" printer that uses an SBase board with two external TMS2208s. I have not seen any issue with various test prints. I have not tested this code using separate TX/RX pins nor have I tested it as general purpose software serial code.
The changes are based upon a version of Marlin from about a week or so ago. I will be updating and testing with the latest version as soon as I get chance but I would expect things to work with the current version. Note that my LPC1768 framework tree contains other changes. I provide instructions on how to use my SoftwareSerial code below, you do not need to use my trees.
My Marlin tree is here: https://github.com/gloomyandy/Marlin/tree/myskr My LPC2768 tree is here: https://github.com/gloomyandy/pio-framework-arduino-lpc176x/tree/software-serial
The two files that contain the new implementation are: https://github.com/gloomyandy/pio-framework-arduino-lpc176x/blob/software-serial/cores/arduino/SoftwareSerial.cpp https://github.com/gloomyandy/pio-framework-arduino-lpc176x/blob/software-serial/cores/arduino/SoftwareSerial.h
NOTE: The SoftwareSerial code is now part of the LPC1768 framework version 0.1.0 so you should no longer need to perform steps 1 and 2 below. You may need to force an update of the platformio libs.
If you wish to try this code then these are the steps you will need. Note that this is not final code and you use it at your own risk! You should make a backup of any files you overwrite or change.
#define X_SERIAL_TX_PIN P2_06
#define X_SERIAL_RX_PIN P2_06
#define Y_SERIAL_TX_PIN P1_31
#define Y_SERIAL_RX_PIN P1_31
#define Z_SERIAL_TX_PIN P1_23
#define Z_SERIAL_RX_PIN P1_23
#define E0_SERIAL_TX_PIN P0_03
#define E0_SERIAL_RX_PIN P0_03
//#define E1_SERIAL_TX_PIN P0_02
//#define E1_SERIAL_RX_PIN P0_02
22:31:24.530 : Driver registers:
22:31:24.537 : X 0xC0:0C:00:00
22:31:24.544 : Y 0xC0:0C:00:00
22:31:24.552 : Z 0xC0:0C:00:00
22:31:24.559 : E 0xC0:0C:00:00
22:31:24.567 : Testing X connection... OK
22:31:24.574 : Testing Y connection... OK
22:31:24.581 : Testing Z connection... OK
22:31:24.589 : Testing E connection... OK
Here is my SKR configured as above....
Following…
@Msq001 I tried M906 with no luck. Seems to not see any of the 2208 commands aside from M911 for checking for status.
@gloomyandy will this single wire solution be capable of handling real time feedback from the drivers even while printing? I can give it a shot but not sure if I will have hysteresis with a second line hooked up but floating.
@griffin-117 what second line? Not sure what you are referring to, sorry. Yes it will happily monitor the temperatures of the drivers (which is the only monitoring you get really) during printing.
@gloomyandy my apologies. My boards have two lines hard wired on the pcb so I am concerned with leaving the second floating although I think it should be fine. Can always remove the 1k resistor if need be.
Pretty busy this week but will try to flash it. Any known bugs to be aware of with it ATM?
Well if you have the pins available you could try it using 2 pin mode (though I have not tested that mode and there is a good chance of bugs, also I'm not around for the next week or so to test things and fix problems). I would suggest just trying it using the direct connected pin (with the other pin set as input/floating). If you run into problems then I would suggest that you set the other pin (the one with the 1K resistor) to be high. The TMC2208 likes to have the UART line pulled high when nothing else is happening and this may help with that. Let me know how you get on.
@Msq001 somehow forgot to enable Debug in configuration_adv. All seems good now.
@gloomyandy I will test with all three scenarios- single wire, two with the old TX floating through the 1k, and old TX pulled high. I’ll let you know my results. Thanks for working on this!!
Quick update. @p3p has included this serial code as part of the LPC1768 framework 0.1.0 so no need for steps 1 and 2 above. You may need to force an update of the platformio libs by restarting vscode or using: "platformio update", there may be other ways to do this!
@gloomyandy excellent news. Should be trying it within the next few days. Thank you for your work on this!
@gloomyandy Meanwhile SKR v1.3
is released with "no-wire" support for TMC2208
. As I can see here and here there is 102
resistor (1K Ohm) next to the "UART" jumpers.
On the firmware (electronic schematics are not provided yet) they are using "2 pin mode". Hope it will work with 1 pin mode you are working on. I've already ordered one, so I will test it when I get hands on it...
1 pin mode should be fine, though it may need extra code to set the other pin into a suitable state. I've also got a V1.3 board on order so will be able to test with it.
Oh and you should still be able to use this code in two pin mode, I've just not tested it. The 1 pin operation is of most benefit on boards like the V1.1 that have limited pins available.
so we don't need anymore interruptable pins with that ? i need to move from standalone to uart mode on my mks sgen :)
Yes. interruptable is not necessary. See the method mentioned above by gloomyandy
@gloomyandy Thank you for your work, and do I need to close this issue?
@gloomyandy so, I think I get it now, but can you summarize the state of 2208 support on SKR 1.1, for those of us who don't speak code? :wink:
There is a new SoftwareSerial implementation for LPC1768 based boards. This new version can operate with TMC2208 drivers using just a single pin per driver and that pin does not need to be interrupt capable. The driver should also work with the TMC2208 in two pin mode, but this has not been tested. The new driver is now included as part of the LPC1768 framework and so should "just work" if you configure Marlin to use it, no special version of the TMC libs are required.
Some additional notes:
@gloomyandy Ok. I've updated Marlin to commit 2513f6b5, and the soft serial library is already at 0.1.0... using single-wire connections, with your previously-mentioned pins definition (wrapped in #if HAS_DRIVER(TMC2208) [...] #endif
), the drivers report back as good, if I'm reading the output of M122
correctly:
>>> m122 SENDING:M122 X Y Z E Enabled false false false false Set current 600 600 800 400 RMS current 581 581 795 397 MAX current 819 819 1121 560 Run current 18/31 18/31 25/31 12/31 Hold current 9/31 9/31 12/31 6/31 CS actual 9/31 9/31 12/31 6/31 PWM scale 33 11 14 7 vsense 1=.18 1=.18 1=.18 1=.18 stealthChop true true true true msteps 16 16 16 16 tstep max max max max pwm threshold 0 0 0 0 [mm/s] - - - - OT prewarn false false false false off time 3 3 3 0 blank time 24 24 24 24 hysteresis -end -1 -1 -1 -1 -start 1 1 1 1 Stallguard thrs DRVSTATUS X Y Z E stst X X X X olb ola s2gb s2ga otpw ot 157C 150C 143C 120C s2vsa s2vsb Driver registers: X 0xC0:09:00:00 Y 0xC0:09:00:00 Z 0xC0:0C:00:00 E 0xC0:06:00:00 Testing X connection... OK Testing Y connection... OK Testing Z connection... OK Testing E connection... OK
.
However, the motors won't move in TMC2208
mode. If I switch them to TMC2208_STANDALONE
mode, they try to move, as long as the UART wires are not connected (I have no jumper shunts on the board, so the default microstepping is way wrong, whatever it is. :stuck_out_tongue: ).
Did I miss a step?
Configs: Marlin-configs-32bit-20190304-2.zip
You have SOFTWARE_DRIVER_ENABLE set, unless you have added a wire from the driver enable pin to ground (and disconnected the pin that goes to the board connector) I suspect that may cause problems. I've also not tried that mode of operation with TMC2208 drivers. I would also be tempted to set the current to at least 800mA to begin with and reduce it once you have things working. I would also enable MONITOR_DRIVER_STATUS when you have things working as it provides very useful information about driver temps.
By default you will be operating with 1/8th microstepping I think.
Oh and and on this board you may run into problems using LIN_ADVANCE it seems to mess up the pulse timing. You may need to set a MINIMUM_STEPPER_PULSE value of 1 or more. Search the issues for details of why.
SOFTWARE_DRIVER_ENABLE
,MONITOR_DRIVER_STATUS
,MINIMUM_STEPPER_PULSE
Now unset, set, and set, respectively. This got me going, thanks! Now I can actually test stuff. :smiley:
Oh, as for currents, those values are from earlier use of my 2208's on an Arduino/RAMPS stack, and are already mostly appropriate for my hardware. Had to turn the E current up a bit from there, though.
Are you using 12V or 24V? If 24V you may want to change CHOPPER_TIMING as well. Lots of folks seem to have to run E steppers in spreadCycle mode (especially with linear advance), but I guess that is all down to fine tuning.
I'm using a 12v system. I looked at that setting a while back and couldn't make sense of it.
Interesting.... I have to run E in spreadCycle mode also (it's not apparent from my config, but it's "forced" by fast retracts that exceed the hybrid threshold).
I have modified the firmware for the TMC2208 UART and LPC1768 software serial lib sections, Now it's not necessary to use interrupt IO for RX, Does anyone want to test it? I can send you the complete project.