New Attempt - 3D Printer Controller And Firmware
idbruce
Posts: 6,197
Alrighty folks
I am going to try this again with a different strategy.
The new strategy will consist of two Propellers:
Propeller 1) Parser, Processor, and GUI (And I believe the GUI might be pushing the limits)
Propeller 2) Machine controller.
Propeller 1) GCODE Parser, Motor Control Algorithms, and GUI (And I believe the GUI might be pushing the limits)
Propeller 2) Machine Controller, GCODE processor, and perhaps LCD.
I am sure there will be those that disagree, but after toying around with Teacup, I now realize that the parser and processor alone, take up quite a bit of memory. Then of course the controller end of things also requires substantial space.
My goal is going to be setting up the skeleton code for further enhancements. I will be attempting to provide G and M handlers that exist in the current Teacup code.
Basically raw GCODE data will be going into one Propeller for parsing and processing. For each line processed, the resulting data will be sent to the second Propeller serially. When the data reaches the second Propeller, it will be immediately added to a movement and processing queue.
Instead of trying to describe it, this struct or something similar represents the data that will be going to the second Propellers queue:
Although indicated above, I most likely will not include ramping, because it is going to take a BIG brain to figure this all out. Four cogs will run three to four motors simultaneously. So setting up the ramping will be very difficult to say the least, but that will all be determined ahead of time by the first propeller, before it reaches the second Propeller.
In the archive below, you will find the starting code that will be going into Propeller 1. It is far from complete.... There is quite a bit of redundancy and there are many print commands for testing purposes.
I have additionally included a SPIN file that still needs to be ported to C, but this code should give you an idea of how I intend to run the steppers on Propeller 2.
I am going to try this again with a different strategy.
The new strategy will consist of two Propellers:
Propeller 1) Parser, Processor, and GUI (And I believe the GUI might be pushing the limits)
Propeller 2) Machine controller.
Propeller 1) GCODE Parser, Motor Control Algorithms, and GUI (And I believe the GUI might be pushing the limits)
Propeller 2) Machine Controller, GCODE processor, and perhaps LCD.
I am sure there will be those that disagree, but after toying around with Teacup, I now realize that the parser and processor alone, take up quite a bit of memory. Then of course the controller end of things also requires substantial space.
My goal is going to be setting up the skeleton code for further enhancements. I will be attempting to provide G and M handlers that exist in the current Teacup code.
Basically raw GCODE data will be going into one Propeller for parsing and processing. For each line processed, the resulting data will be sent to the second Propeller serially. When the data reaches the second Propeller, it will be immediately added to a movement and processing queue.
Instead of trying to describe it, this struct or something similar represents the data that will be going to the second Propellers queue:
typedef struct { char Valid[5]; uint8_t g_num; uint8_t m_num; uint8_t t_num; uint16_t s_num; uint16_t p_num; uint8_t x_dir; uint32_t x_ramp_steps; uint32_t x_run_steps; uint32_t x_ramp_inc_dec; uint8_t y_dir; uint32_t y_ramp_steps; uint32_t y_run_steps; uint32_t y_ramp_inc_dec; uint8_t z_dir; uint32_t z_ramp_steps; uint32_t z_run_steps; uint32_t z_ramp_inc_dec; uint8_t e_dir; uint32_t e_ramp_steps; uint32_t e_run_steps; uint32_t e_ramp_inc_dec; } MOVE_STRUCT;
Although indicated above, I most likely will not include ramping, because it is going to take a BIG brain to figure this all out. Four cogs will run three to four motors simultaneously. So setting up the ramping will be very difficult to say the least, but that will all be determined ahead of time by the first propeller, before it reaches the second Propeller.
In the archive below, you will find the starting code that will be going into Propeller 1. It is far from complete.... There is quite a bit of redundancy and there are many print commands for testing purposes.
I have additionally included a SPIN file that still needs to be ported to C, but this code should give you an idea of how I intend to run the steppers on Propeller 2.
zip
1016K
Comments
As it stands now, this function will be named calc_movement_timing(). This function will start off simple, but no doubt could and most likely will become very complex. And this function will be responsible for determining the values of some of the most critical members of the GCODE_STRUCT, which pertain to the stepper motor pulse trains.
If you downloaded the archive above, you will see that the project contains a GCODE_STRUCT and a MOVE_STRUCT. These structs have changed since the last upload to include more members in both structs, which should allow for easy modification, as previously outlined in this post. The GCODE_STRUCT will reside on Propeller #1 and the MOVE_STRUCT will reside on Propeller #2.
The files for these two structs are shown below:
Since the Propeller Memory Card (PMC) is really of no great use to this project, the socket for the PMC will be removed, and the empty pins will now be utilized for extending the amount of available limit and homing switches, but I will have to look at that in more detail. With that in mind, most of the wiring going to the headers allocated for the limit and homing switches will be changed. I don't see the MCP23008 as causing me any grief and perhaps it may even be beneficial in the future to retain it, so I have decided to leave it. I still need to make a decision on what I intend to do with my LCD.
Instead of going through a lot of trouble and effort, I am simply going to use another Propeller Proto Board for Propeller #1, and instead of using the PMC for SD card access, I will most likely use just an SD card holder, for Propeller #1.
Anyhow the following controller board will be altered.
EDIT: I have decided that it would be in my best interest to establish the basics in the new firmware before making any modifications to the controller. As it stands now, the controller should be in good enough shape to do preliminary testing with the firmware. On second thought, I need to open up at least one pin for serial communication between the two Propellers.
Me thinks you should probably make up a flow chart or state diagram enumerating the various tasks and how they're handled before jumping into code.
What I mean by that is define your problems and organize the solutions before you jump in to code. Start at the bottom and work your way up.
Start with controlling the steppers, and see if you can get two steppers in one cog, then, get the extruder working.
Once you've done that, you can expose the necessary control variables to the system and determine how the parser translates things so as to control the steppers and extruder.
Your schematics will evolve as you establish the control mechanisms.
This discussion started long ago.... Many folks said there was no need for a second Propeller. After struggling to find a solution for everything that I want on a controller and struggling to fit various sections of firmware within the available memory..... Well I am tired..... I am not going to struggle with it anymore.... I am going with my first instinct and I am going to use two Propellers.
If you think you can fit a gcode parser, a gcode processor, motor control code, and a GUI on a single Propeller, then I say give it your best shot. I can't do it. Considering my previous attempt at a Teacup firmware port..... If I enabled all the features that I wanted and then eliminated all g-codes except for G1 and eliminated all m-codes except M114, the firmware barely fit, but I still did not have enough room to increase my stack space for various cogs. So I needed extra room for all the various commands and more stack space.
As for using two Propellers, I pretty much know exactly what I want to do and I don't think I will have any major problems, except for the algorithms pertaining to the motor control.
I just wish it were easier to write code that is readable to others (including the comments). It took me all of 3 days to get the version in the printer working, and it has already taken over a week to get about half way done with a version for distribution (written for others to modify and use).
Though I have learned that SPIN is a difficult way to do it, though PropBASIC actually seems to be simple for this purpose.
Back to the question:
Assuming a two extruder 3D printer, the pin usage would is:
One pin for the zeroing switches.
4 pins for the Sigma Delta ADC's to read the thermistors to monitor the heat of the two extruders (two pins for each).
4 pins for each stepper (X,Y,Z), for 12 pins.
2 pins each for the continuous rotation servos to feed the filament in the extruders (one each).
2 pins for serial IO.
That is a grand total of 21 GPIO's. If using steppers instead of continuous rotation servos to feed the filament it would be 27 pins. So the pin count is not an issue.
Assuming well simple code mostly running in COG memory, it would take:
1 COG for both serial IO and parsing G-Code.
1 Cog to control the X,Y, and Z steppers, and detect the zero switches..
2 COG's to control the extruders heating element and filament feed servo (or stepper), that is one COG per extruder.
1 COG to read the Sigma Delta ADC's for both extruders.
That is a grand total of 5 COGS So that is not the issue. Though it also depends on some choices of how things get divided.
There should be almost no code left running from HUB mem (excepting a small part of the G-Code parser), so all 32KB should be available to use as a buffer (minus a few hundred bytes for COG to COG communication, and a few hundred bytes for part of the G-Code interpreter), and the way that G-Code is normally handled you only need a few hundred bytes buffer to handle G-Code. So memory is not the reason for two Propellers.
The only thing that I can think of is that he is thinking about future expandability, or is attempting to handle some of the rarer more esoteric G-Code commands (that are almost never used, and unlikely to find use on a 3D printer).
__________________________________________________________
Though I do not know. I know that the attempt to port TeaCup ran out of memory, though that was not designed for a system like the Propeller to start with. Now that it appears that idbruce is writing a ground up firmware implementation I see no reason for it. Though I do not know what the plan is on that side.
Sometimes it can help partition the tasks and debug, and if one part is short of any resource (pins,Code,Ram,Timers..) then it also solves ceiling issues too.
It is also valid to use more than one type of controller, if that helps split things further.
Little MCUs like the Silabs EFM8 cost less than the 12b ADC they contain. (eg 56c, one off, SO16, 8KF)
( IIRC Peter Jakacki can even pgm these, on their 2 wire ISP, via a Prop )
- That's always the way, good documenting and testing, takes longer than coding.
Yes, the original porting was trying to use an established code-base, but one that was not coded for a Prop.
Given how tightly MCUs code on embedded controllers is coupled to peripherals, that is not too surprising.
General elements may be re-usable, but the lower level stuff is usually where the fish-hooks lie.
"Using the same language"; can be more illusion than use.
Using 2 (or more) MCUs for development is not a bad thing - if the end result uses less than half of each one, it is easy enough to merge back into a single chip.
May I ask what features you need that you feel are going to take so much. I am always wanting to learn.
I understand why attempting to use Teacup would run out of RAM, it was never intended to be able to run on anything close to the Propeller.
I am just curious why with the attempt to do a ground up rewrite.
After reviewing NewParser.c, and since code space is a large concern, I see a lot of redundant code. Have you thought about putting the message type identifiers (G, M, etc...) into a table and basing the conditionals (in the code below) on tables?
There are 10 of the "if" cases like the following.
What about something like:
The same sort of code could be applied to reduce the size of:
dgately
Then I return to see a lot of other posts that proceeded his... I must run now, but I will be back soon to respond.
Everyone see things differently and this is especially true when it comes to CNC and the Propeller.
Some people want to control servos, others want steppers
Some people just want homing switches, others want homing and limit switches
Some people want to combine switches to limit pin usage, others want individual switches
Some people want to print with ABS, which requires a heated bed, others are happy with PLA
Some people have no interest in 3D printing, but would rather control a router or mill
The list goes on and on...
Of course, I am thinking about future expansion. If you do not think about expansion ahead of time, you will soon hit the boundaries when you do want to expand. For example, as you go through your coding efforts, have you considered projection planning? Most of your 3D printer and CNC firmware now include trajectory planning or commonly called LOOKAHEAD. This trajectory planning takes up quite a bit of memory space all by itself.
When I started this adventure, a little over a year ago, I decided that I wanted all the features that I could possibly support, instead of limiting myself to making it fit within one Propeller, but one way or another, I got sucked into the one Propeller trap, and wasted a lot of time and resulting in failure. For me, this is not just a 3D printer project, it is a CNC project. 3D printers do not require 4 individual homing and 4 individual limit switches, but I want to support them for CNC endeavors.
To get a real good feel for the whole scenario, take a real good look at the Teacup firmware. Even though it was not intended for the Propeller, it is some high quality coding, with a lot various options for ramping, printheads, trajectory planning, etc.... It will make you think.
This is not an attempt to rewrite Teacup, but rather a development of CNC firmware designed around the Propeller architecture and usage of cogs. Instead of using Bresenham to control various motors, I want the various motors to all run in seperate cogs.
@jmg
Exactly... It may cost a little more, but having plenty of options and ample room to manuever is worth it's weight in gold.
Exactly again.... As dgately points out, as others have before, my coding is not optimized for space. When I am done, some can perhaps come along, eliminate some of the options, commands, etc... and get it to fit on one Propeller, but that is not my goal.
@dgately
I had a nice response all typed up for you, but then lost it. To sum it all up, I never took the time to really learn size optimization skills. I always programmed for the Windows environment and the hard drives just kept getting bigger and bigger So there was never no need to learn those skills. My goal has always been to spit out working code, instead of size optimized code, and I suppose the same holds true for this project.
My current task is to fill the timing variables of the GCODE_STRUCT of Propeller 1 for syncronized movement, which will then go to the MOVE_STRUCT queue on Propeller 2. Once I accomplish that and achieve movement, then I can move onto other things, such as the GUI, etc....
As far as size optimization goes, there are other programmers that are better trained for that type of work. This is an open source project and anyone can join in and optimize away. And it is my hope that others will join in, but I hoppe they will at least wait until I achieve syncronized movement from Prop 1 to Prop 2.
https://www.youtube.com/watch?v=JEpsKnWZrJ8
There are 3D printers running on AVR, obviously, and MSP430's too, so no particular reason I can see a Propeller not being able to do so as well.
Time to start thinking outside the box, as they say... I suppose. Personally, I don't think there ever was a box in the first place.
The "box" can only be there after you've built it.
You did not upset me in the least. I was just telling you my viewpoint. As mentioned after my initial response to you, I do not want to attempt a minimal type approach, but more a maximum possibility. If there is any chance that something may be needed, I would prefer to have provisions for it.
A. For the most compact code, microcontrollers prefer binary interger maths - signed or unsigned. GCODE presumes decimal maths that may require some floating point.
The solution may be to write a pre-processor that converts a GCODE file to an optimized Propeller code file on your PC.
B. The more advanced 3D printer control software includes on the fly calculation of accelleration and decelleration of the print head in conjuction with feed of the material. To do so requires anticipation of each and every print head move in its entirety. That means larger buffers and these eat into other code space on the Hub Ram.
The solution -- not sure. In some cases, there appear to be feedback loops that complicate the problems.
++++++++++
Control of a quadcopter is a very different algorythm and optimized in the different manner. The hexapod is yet another separate solution. Each requires optimization in their own specific way.
The fact that the 3D printers accept GCODE and then do all the conversion internally adds a significant extra load. Data inputs for the quadcopter and hexapod are much less of a burden.
It definitely looks like a good project. I hope that you are quite successful. I had not realized that you were attempting to support any kind of CNC, that does make a deference (you may need more than 64 GPIO for that). And since you wish to be universal you are going to need external memory to hold the entire G-Code file, as using commands like M98 and M99 require that in order to be efficient, ok you could just use "rs" responses to refetch the code and save memory, though better to buffer the entire file.
I look forward to seeing your results. Good luck. It really looks as though you are attempting to support every possible CNC style device, that will be a challenge.
To answer your questions on my firmware:
I am thinking about expandability for 3D Printers, not for other stuff. I have 4 pins set aside for Prop to Prop communication so I can have more pins if needed in the future.
My 3D Printer works quite well printing in ABS with out a heated print bed. It took some time to figure out, though if you print the first two layers very slow you can print in ABS with out using a heated print bed (yes it does add a few minutes to the print time).
Yes I have thought about motion look ahead, and implemented such. You will find that it does not take nearly as much memory as you think.
I have looked at TeaCup, which seems to try to do everything possible 3 or 4 different ways for each thing. It is very complete, and quite functional, though it is not efficient.
Optimizing for size should be important regardless of the target system. I know that such is no longer taught, though think how much smaller all those windows programs could be, and thus how much less space they could take away from other uses (including the components of Windows itself), same goes for RAM usage.
I will not be attempting to support every gcode or any other type of "particular" machine, besides a 3D printer, but I do want to create firmware and a controller that can be altered or adapted to various machines. There are still several other machines that I would like to build, and I just want to have options.
I would imagine they still teach size optimization, especially for embedded systems, but I am self-taught, and gained all my programming experience from trial, error, books, online documentation, and online help from others.
How is it not efficient? Give me an example.
As it has been pointed out, this project can be optimized for size. No attempt was made to make it as small as possible
At this point, and although very limited at this point, the GCODE_STRUCT should now have enough data filled to create syncronized movement for four axes. I am not saying that it is without error, but I believe that it is.
I still have to implement the code to send the proper data from Propeller 1 to Propeller 2.
The main question now is.... Will X and Y axes be able to draw a straight line from this code, in combination with the previously uploaded spin file (which still needs to be ported)?
Let's say the Teacup or whatever 3D printer application was a megabyte, or 10, or a 100 megabytes. Who cares? You can run it on a machine with a gig of RAM that is far cheaper than the one you are trying to optimize for!
As far as I can gather modern Computer Science departments gave up on efficiency a long time ago. They teach programming and algorithm analysis in languages like Java and Python today. Heck they used things like Scheme in the past. Ironically the point of that education in algorithms is making them efficient. A good algorithm can save you orders of magnitude of time. At which point the slowness of the language you use becomes almost irrelevant.
Tight, optimized code comes from the EE departments where they are using DSP chips, MCUs and so on.
I would have to agree, although I do believe that optimized code shows better expertise and professionalism. Nowadays the size of applications and RAM usage is really up there, but then again, so are the size of the hard drives and the memory sticks. I also believe that it takes more time and thought to optimize for size. Instead of coding, time is spent on how to code efficiently
In my case, the optimizers can think of my code as an oversized buffer. Storage space that can be regained with a little organization
I agree that code should not be needlessly long winded. A "professional" should have the experience to know the quick, easy, idiomatic way to do things.
It's just that if you spend time rearranging your code to save a byte here and a byte there you have probably made it less straightforward and easy for anyone to read and understand. Even yourself when you come back to it half a year later. You are wasting their time. This is not optimal.
Hence the famous expression about "avoid premature optimization". Get it working in the simplest way possible first.
Modern apps are huge. Often accused of being "bloated". I don't know, we have unicode instead of ASCII, we have 64 bit machines rather than 8, we have GUI's instead of command lines. I'd like those who complain about "bloat" point to the "fat" of an app that can be removed without degradinging it's functionality.
Sorry, off topic again....
LOL You know that I rarely care about such things unless it is just someone stirring trouble.
Okay to hop back on topic.... You and I were briefly discussing the passing of structs serially from one Propeller to another. I have several data members of one struct on one Propeller that must go into another struct on another Propeller. Any recommendations?
If you you think this will ever need to talk between different architectures or compilers we have to think of something else.
The structs are different, but have similar struct members of the same data type. For instance, both structs will have the same members shown below, and of course there are a few others that must be sent. This firmware will be strictly for Propeller usage, due to the use of cogs.
I have not worked with serial communications that much except for the wire bender. I was thinking that the beginning of the struct should have some type of indicator, followed by successive struct members with a break of some sort in between, followed by an end of struct indicator. Most likely there will be other communication also, which would have different beginning and ending indicators. The struct will always have the same amount of items sent.
EDIT: However, I code make one of the structs with a union, that would make a portion of it identical. In other words, insert a MOVE_STRUCT within the GCODE_STRUCT.
The actual format of the data going over the wire is a totally different matter. For sure you need some kind of protocol for that.
You need:
1) Something to indicate the start of a message,
2) Something to indicate which type of message it is. Which struct or whatever.
3) The length of the binary blob of the data content. The structs in your case.
4) The binary blob bytes themselves. From the stucts.
5) A check sum of some kind.
This could all be done in some compact binary format. Or you could use a text based format if speed is not an issue, that can make testing easier.
There are of course a million different serial line protocols....
Sorry it took so long to respond, but thanks. I will most likely be getting into that, after I port the stepper code.
Thanks
Bruce
I thought this article about stepper motor acceleration very interesting. Equation #12 can be used to calculate the delay of each step during the acceleration. As the article mentions, the equation is an approximation but it appear the approximation is pretty good for all but the first step.
An alternate method of accelerating stepper motors is to decrease the step time at regular time intervals.
Yea, I have read that article a couple of times, but if you dig into the provided C code, it looks pretty darn complicated and of course relies on a 1 Mhz timer.
If you study my code, you will see that is exactly what I do. First I ramp up by decreasing the time intervals between the high pulse widths, then when I achieve maximum speed for the stage, I maintain the timing intervals between high pulse widths, and then when I want to ramp down, I increase the time intervals between high pulse widths. In my opinion, the use of counters is the way to go for controlling the step pulse rate. Because you can get more speed then by just using waitcnts. If you have not tried my drivers, I would recommend that you do, and if you have tried them and couldn't get them to work, I will help.
As a side note, the effort of this thread is where it is really going to get tricky with my stepper code, and that is coming up with an algorithm that can compute the proper speeds and timing intervals for the steppers to operate simultaneously, instead of one after another, as is with Bresenham's algorithm.