Shop OBEX P1 Docs P2 Docs Learn Events
New Attempt - 3D Printer Controller And Firmware - Page 2 — Parallax Forums

New Attempt - 3D Printer Controller And Firmware

245

Comments

  • idbruceidbruce Posts: 6,197
    edited 2015-04-12 16:29
    For those folks who may be interested....

    I finished porting the stepper drivers for X and Y to C. Since I still had ample memory on the controller board, I just added them to that Prop, so that I could continue testing, without getting into the serial comm code.

    Please note that I created individual files for both of these axes, because I came to the conclusion that it would be difficult to just use one driver file for both. However, perhaps someone can later optimize this so that one file can run all four axes.

    At this time, this firmware will now control two axes, however the offset value for the X axis is off, so syncronization has not yet been achieved, because I made a mistake somewhere. In other words, the X axis is moving very slowly.

    Additionally, this project will generate a lot of warnings due to volatile members. I have not yet come up with a way to get rid of these warnings.



    ####WARNING######
    If you decide to run this project, please ensure that you set the proper IO pins in config.h!!!

  • idbruceidbruce Posts: 6,197
    edited 2015-04-12 20:31
    Just for the heck of it and to see if they would fit, I have now created driver files for the Z axis and the extruder, and uploaded them to the project. Since I have no way to test it at the moment, I have no idea whether this project has run out of cogs or not, but theorhetically, it should now be able to control X, Y, and Z axes, as well as the extruder.

    The build comes in at Code Size bytes 27,264 (30,724 total), and these final four calls at the end of the main function causes X to fail, which is why they are commented out. So I am fairly certain, I have run out of room.
    	/////////////////////////////////////////////////////////////
    	// Test X, Y, Z, and E axes for syncronization with G1 commands.
    	// This will most likely be moved to another Propeller.
    	/////////////////////////////////////////////////////////////
    //	x_axis_stop();
    //	y_axis_stop();
    //	z_axis_stop();
    //	e_axis_stop();
    	/////////////////////////////////////////////////////////////    
    

    Either way, all the drivers are ready for the move to Propeller 2. However as mentioned in the previous post, there is still an error somewhere. I haven't looked for it yet, but that will be the next task.



    ####WARNING######
    If you decide to run this project, please ensure that you set the proper IO pins in config.h!!!

  • idbruceidbruce Posts: 6,197
    edited 2015-04-12 21:09
    I do believe that the while loop at the end of each driver file will need changing. The way I see it the code should run and then the cog will go to 0. Oversight on my part, but I could be wrong.
    	while(y_axis_cog > 0);
    
  • idbruceidbruce Posts: 6,197
    edited 2015-04-13 02:43
    After a little digging and thinking, I have found my error, of course I did not have to dig to far or think to hard :) Within the calc_movement_timing() function, there were several expressions that had the wrong order of precedence. After fixing those expressions, it worked as planned.

    So at this point, I will now say that I have "syncronization" of the X and Y axes, and even though I cannot test the Z axis and the extruder, I would assume they are "syncronized" as well. Of course they are not truly syncronized, but I assume they are close enough, until proven otherwise.

    While I was digging around, I found another flaw and I knew there was a potential for it, but I was too lazy to write the code at the time..... For a couple minutes, I thought perhaps I had toasted my drivers, but as it turns out, I entered equal values for G1 coordinates and nothing happened. And then I remembered that I had not coded for potential equal coordinates :) DUH So the next task will be to doctor that up, because I am certain that equal coordinates are not a rarity.

    There are probably a couple other items that I will want to take care of, before moving onto the next major step. All in all, at this point, things are looking good, although I have only been testing with one line of GCODE, and at the moment, I do not believe it is not setup for more than that, but I will have to check.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-13 07:17
    In reference to a previous post:
    However the goal that I will be striving for is that all movement code will be restricted to one function, so that this function will be the only function that will need modification to achieve ramping and various speed profiles for various axes.

    As it stands now, this function will be named calc_movement_timing().

    Instead of one function, there will be five functions that will need modification to achieve ramping.
  • abecedarianabecedarian Posts: 312
    edited 2015-04-13 10:53
    Am I correct in assuming X / Y motion is synchronous, and the combined velocity of those cannot exceed the extruder's rate when the extruder is operating, and Z is superfluous as insomuch as extrusion most likely will not occur with movement in that axis.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-13 13:23
    abecedarian
    Am I correct in assuming X / Y motion is synchronous, and the combined velocity of those cannot exceed the extruder's rate when the extruder is operating, and Z is superfluous as insomuch as extrusion most likely will not occur with movement in that axis.

    I believe that is correct, but I am not 100% certain at this point. For the moment I have all drivers operating the same way, but I believe you are correct.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-13 14:23
    idbruce wrote: »
    Duane

    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.

    You should be able to run a Bresenham loop for as many axis as you need. You have to find the axis that steps most frequently, and express the other ones as fractions of the major one.

    Here's code that's done it up to 6D:
    https://sites.google.com/site/proyectosroboticos/bresenham-3d

    Also, some things you might do in NewParser.c:

    1) Change the if( gcode_struct_element == 0 ) ... else if( ... ) sequence to a switch case, like this:
      switch( gcode_struct_element )
      {
      case 0:
        // G Field element character
        current_gcode.G_CHAR[value_counter] = gcode_char;
        value_counter++;
        break;
    
      case 1:
        // M Field element character
        ....
        break;
      }
    

    Most compilers will turn a (0...N) set of cases into a jump table, which is more compact and faster than testing each condition. Some compilers will turn any arbitrary set of cases (even non-sequential ones) into a binary tree of if/else clauses, reducing the code size and execution time. I'm not sure if the PropGCC compiler is that smart.

    2) The "continue" statements in ini_gcode_struct() don't do anything. The code is all if/else based, so whenever a condition satisfies one of the clauses, it ends the logic because the rest of them are "else" clauses.

    3) In ini_gcode_struct(), the block of memset operations labelled "Clear any lingering data" would be faster and smaller written like this:
      // Compute the number of bytes from the start of the gcode_fileline member
      // to the start of the G_NUM member.  This is actually constant.
      int numBytes = (int)(&current_gcode.GNUM) - (int)current_gcode.gcode_fileline);
    
      // memset all of those bytes to zero - avoids 11 calls to strlen + memset
      memset( current_gcode.gcode_fileline, 0, numBytes );
    

    If you moved the "Valid" member above "gcode_fileline" you'd avoid clearing that one twice, as the memset I wrote above will currently include it.

    This will end up touching more memory than yours, but it'll be a constant speed, and it'll avoid calling strlen and memset 11 times, instead replacing it with a loop that writes 168 bytes. Depending on how smart the compiler / memset code is, that might become a write of 42 longs. My suspicion is that it'll be significantly smaller in code size, and quite a bit faster.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-13 18:24
    Jason

    Glad you dropped by and thanks for the input. If you do not see all of your suggested changes implemented right away, that does not mean that I considered your suggestions invalid. At this point, I just want to keep pushing forward, because I have a lot to do. However, I will remove those "continue"s immediately, because that was just plain stupid on my part and it is a simple fix.

    Additionally, I will test and most likely use this code:
    	// Compute the number of bytes from the start of the gcode_fileline member
    	// to the start of the G_NUM member.  This is actually constant.
    	int numBytes = (int)(&current_gcode.GNUM) - (int)current_gcode.gcode_fileline);
    
    	// memset all of those bytes to zero - avoids 11 calls to strlen + memset
    	memset(current_gcode.gcode_fileline, 0, numBytes);
    

    Hopefully that will eliminate a huge percentage of the warnings that are currently showing up.
    If you moved the "Valid" member above "gcode_fileline" you'd avoid clearing that one twice, as the memset I wrote above will currently include it.

    Just to be certain that we are comparing apples to apples.... Are you referring to moving it above in gcode_struct.h?
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-13 20:34
    idbruce wrote: »
    Just to be certain that we are comparing apples to apples.... Are you referring to moving it above in gcode_struct.h?

    Yes. The version of the file I saw did this:
    struct {
      char gcode_fileline[100];
      char Valid[10];
      char otherStuff[3];
      ...
    };
    

    You zero it with a memset, and immediately after you use a for loop to clear it to the actual character '0'. Since you're doing the latter, the former isn't necessary.

    Changing the order of the struct to look like this:
    struct {
      char Valid[10];
      char gcode_fileline[100];
      char otherStuff[3];
      ...
    };
    

    ...would mean it gets excluded from the memset() version I posted above. That version figures out the memory address of the gcode_fileline value, and the memory address of the struct member following the last of your G_CHAR[] type arrays. It computes the difference between those two values, which gets the total number of bytes to be zero'd to clear everything, and calls memset with that, instead of clearing them individually.

    It's actually very common to do something like this:
      memset( &myStructure, 0, sizeof(myStructure) );
    

    ...instead of this:
      myStructure.member1 = 0;
      myStructure.member2 = 0;
      myStructure.member3 = 0;
      myStructure.member4 = 0;
    

    A good optimizer will notice that you're doing writes of the same value to a contiguous region of memory and congeal them into a memset, on inline variant of it, but the former is more readable, and I don't have enough experience with PropGCC to know if it'll do that optimization for you.

    I've attached some code I wrote a while back for the robot arm. One is a Bresenham implementation that runs 3 independent axis steppers. You pass it a value which represents the total number of "Ticks" you're going to use to run the move. That number MUST be equal to or higher than the absolute value of your maximum axis move. You use it like this:
      Bresenham B;
    
      B.Init();
    
      int NumTicks = 10000;
      int aDelta = -1500;
      int bDelta = 8500;    // Values are signed to indicate direction
      int cDelta = -210;
    
      // Set the pin directions and initialize the error & delta counters
      B.SetupMove( NumTicks, aDelta, bDelta, cDelta );
    
      while( NumTicks > 0 )
      {
        B.Tick();
        waitcnt( 1000 + CNT );
        --NumTicks;
      }
    

    The other one, Motor, is an implementation of the algorithm in the paper "Calculating stepper motor profiles in real time" (or something like that). You use it like this:
    Motor M1, M2, M3;
    
        M1.Init( 1<<0, 1<<1, m1StepsPerRotation, SpeedMax , AccelMax );
        M2.Init( 1<<2, 1<<3, m2StepsPerRotation, SpeedMax , AccelMax );
        M3.Init( 1<<4, 1<<5, m3StepsPerRotation, SpeedMax , AccelMax );
    
        M1.SetupMove( m1StepsToMove );
        M2.SetupMove( m2StepsToMove );    // Values are signed to indicate direction
        M3.SetupMove( m3StepsToMove );
    
        while( (M1.Done() & M2.Done() & M3.Done()) == 0 )
        {
            M1.Tick();
            M2.Tick();
            M3.Tick();
        }
    

    The Motor code actually looks at the current time and decides when it's appropriate to make each motor step. You just have to make sure to call the function fast enough (and choose a speed that's slow enough) for it to work.

    I figure it's probably too late for you to incorporate the "Motor" object, but the Bresenham one is pretty simple and could easily be extended to support more axis. Thought it might help you to see the implementation.

    Good luck!
    Jason
  • idbruceidbruce Posts: 6,197
    edited 2015-04-14 03:31
    Jason
    It's actually very common to do something like this:


    Code:
    memset( &myStructure, 0, sizeof(myStructure) );...instead of this:


    Code:
    myStructure.member1 = 0;
    myStructure.member2 = 0;
    myStructure.member3 = 0;
    myStructure.member4 = 0;

    If I am not mistaken, there was a particular reason I did it the way that I did. I think there was some data that I did not want cleared, but I may have dummied out.
    and immediately after you use a for loop to clear it to the actual character '0'.

    I missed that.... Good catch.

    I have not examined your Bresenham code yet, but I will. Should I assume that you do not like my strategy of using a cog for each axis? :)

    I may be quite naive, but I still don't believe that Bresenham's will be quite as fast as four cogs using counters. Additionally, assuming that algorithms could be created for the method that I am attempting, I would assume that my method could produce more accurate lines, because the offsets would be avoided. As mentioned, I am not smart enough to create these algorithms, but I would think that four individual axes making independent movement based on time, would be more accurate than looping and offsetting.

    I have not tested my code yet, to see if it will draw a straight line, but I will soon. :) I believe the problem with my code will be the ramping.

    As for Bresenham's algorithm, I also believe there is a slight difference between standard CNC and 3D printing. Of course the determination for X and Y would still be the same, but then one must also consider Z and E. For instance:
    if(application == "CNC")
    {
    	//Use Bresenham algorithm
    }
    
    if(application == "3D Printing")
    {
    	//Use modified Bresenham algorithm for 3D printing application
    }
    

    I am still just making assumptions at this point, until I get a thorough understanding of Bresenham's algo as used in CNC and 3D applications.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-14 12:52
    idbruce wrote: »
    Should I assume that you do not like my strategy of using a cog for each axis? :)

    It's a perfectly workable solution and there's absolutely nothing wrong with it. :) That said, with cogs being at a bit of a premium, if it could be made to work with fewer of them it might be worth doing.
    idbruce wrote: »
    I still don't believe that Bresenham's will be quite as fast as four cogs using counters.

    You're correct that it won't ever be as accurate as a dedicated cog, but that may not actually matter. The Bresenham algorithm as posted has a very simple internal loop:
    On each tick:
       For each axis: {
          Subtract an error term from an accumulator
          If the accumulator becomes negative  {
             Add a "reset" amount to it
             Set the corresponding step pin high
          }
       }
       Set all step pins low
    

    Coded in PASM for four outputs it should be possible to make it accurate to about a 1/2 microsecond or less. My home CNC machine will travel 1800ipm (which is *fast*). On my setup, that's 60,000 steps per second, which works out to 333 instructions per step at 20mips. Most steppers won't go more than about 600rpm without decimating their torque. At 200 steps per revolution, even with 1/8 micro-stepping that's only 16,000 steps per second, giving you about 1200 instructions to spend per step, divided up among however many axis you have. If you over-sample by a factor of 10 (IE, 10 ticks per actual step you take), that's 120 PASM instructions, which should be easily attainable even for 3 or 4 axis, and should be doable even in C with LMM.

    Figure out how fast you actually need the things to move, and from that, how many steps per second you'll need to be able to make. If you can tick 20x faster than that, you're at 5% accuracy per step, which is what most steppers advertise as their per-step accuracy anyway. If you're micro-stepping, your algorithm steps will be much more accurate than the actual positioning accuracy of the motor itself, so I wouldn't worry about it beyond that.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-14 12:56
    In reply to your "difference between CNC and 3D printing", I think the only real difference in the inner loop is that with a CNC, you generally control 3 axis simultaneously (X/Y/Z), whereas with 3D printing, you're usually controlling only two (X/Y) but you also have a filament feed, so I'd just treat that as the "3rd axis" for the normal loop. A 3D printer only really controls Z when changing layers, so there'd be no need to have Z control in the inner loop.

    By treating your filament stepper as the 3rd axis, both a CNC and 3D printer would have the same inner loop. For a single move, with a CNC it'd be X/Y/Z axis steps, and for a 3D printer it'd be X/Y axis + Filament steps.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-14 13:20
    Thanks Jason

    That was definitely some very helpful information.

    It has been a while since I last tested my driver, and there has been several changes since I last tested, but if I remember correctly, the last test came in about 14-3/4 motor revs per sec after ramping, but I believe it is much faster now, probably about 20 revs per sec.

    If my calculations are correct, when using your code, I would have a 30 revs per sec, although I cannot run my motors at top speed anyhow. I may have to rethink my strategy, or code in such a way that the driver could be interchanged for testing.

    Or for that matter, I should just write a simple test and run it.

    You definitely got me thinking.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-14 13:27
    If I am not mistaken, I believe the fastest that I want to go on my belt driven axis X, is about 5000 steps per second.

    EDIT: However for screw drives, much higher step rates would be required or wanted, or at least I temporarily think so :)
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2015-04-14 17:19
    JasonDorie wrote: »
    In reply to your "difference between CNC and 3D printing", I think the only real difference in the inner loop is that with a CNC, you generally control 3 axis simultaneously (X/Y/Z), whereas with 3D printing, you're usually controlling only two (X/Y) but you also have a filament feed, so I'd just treat that as the "3rd axis" for the normal loop. A 3D printer only really controls Z when changing layers, so there'd be no need to have Z control in the inner loop.

    By treating your filament stepper as the 3rd axis, both a CNC and 3D printer would have the same inner loop. For a single move, with a CNC it'd be X/Y/Z axis steps, and for a 3D printer it'd be X/Y axis + Filament steps.
    With many 3D printers you control all three axises at the same time as a quick and easy way of compensating for small imperfection in the bed level relative to the extruder, with out having to spend as much time adjusting the leveling screws on the print bed.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-14 17:30
    True (and especially true for a delta arrangement like mine). Even so, making a 4-axis version of the Bresenham loop is pretty trivial, and you'd simply tell the 4th axis not to move for a CNC. :-)
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 16:12
    My sincerest apologies to anyone that wasted their time on previous uploads, but I now have a new one for you to waste your time on :)

    As it turns out, the previous stepper drivers were giving me some problems. With the help of JasonDorie, the problems appear to be resolved. So I am now uploading the "fixed version" for four axes, although only tested on two :) Additionally, this upload also includes other changes, as suggested by Jason.

    As an extra added bonus, I thought I had included the entire ADS1015 library in previous uploads, but I was WRONG. This upload now includes the entire library. However, the project has now reached maximum size (31,032 total) and it will now become necessary to start shifting some of the code to another Propeller. It is actually good timing though, because I will now begin working on the serial communications, as well as the GCODE processor.

    Anyhow, here it is.



    ####WARNING######
    If you decide to run this project, please ensure that you set the proper IO pins in config.h!!!

  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-15 16:49
    Looking at the code you have, one thing I'd suggest is moving all of your axis-specific variables into a structure type, and creating an array of them, one for each axis you have. Doing this would allow you to generalize a bunch of the code, because you could just pass along which axis you're running.

    For example, you currently have four independent "driver" modules, but the code in them is functionally identical. They toggle different pins, and use different variables for counts, ramps, etc, but if all of those variables were in a single per-axis structure, your driver code could be given a pointer to that struct, and just reference everything from there. Now you'd have one driver module, and just have four instances of it, with each instance being passed a different pointer for the variables to work with.

    It could also make your NewParser.c function simpler, because your calc_?_most_steps function could be generalized as well, like this:
    void calc_most_steps( int MajorAxis )
    {
    	double total_high_pulse_time;
    	double total_offset_time;
    	double temp_offset_time;
    	double total_time;
    
    	total_high_pulse_time = current_gcode.axis[MajorAxis].total_steps * ALL_AXES_PULSE_WIDTH;
    	total_offset_time = current_gcode.axis[MajorAxis].total_steps * ALL_AXES_MAX_SPEED;
    	total_time = total_high_pulse_time + total_offset_time;
    
    	current_gcode.axis[MajorAxis].start_stop_speed = ALL_AXES_MAX_SPEED;
    
    	// Now compute the dependent axis
    	for( int MinorAxis = 0; MinorAxis < NUM_AXES; MinorAxis++ )
    	{
    		if( MinorAxis == MajorAxis ) continue;	// Skip the Major axis - already did this one
    
    		if(current_gcode.axis[MinorAxis].total_steps > 0)
    		{
    			temp_offset_time = (total_time - (current_gcode.axis[MinorAxis].total_steps *
    				ALL_AXES_PULSE_WIDTH)) / current_gcode.axis[MinorAxis].total_steps;
    
    			current_gcode.axis[MinorAxis].start_stop_speed = (uint32_t)floor(temp_offset_time + 0.5);
    		}
    		else
    		{
    			current_gcode.axis[MinorAxis].start_stop_speed = 0;
    		}
    	}
    }
    

    Making the code work this way would result in very slightly slower code because of the extra lookup for which axis you're working with, but the overall code size would be much smaller.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 17:17
    Jason

    I like that idea a lot, however I do not believe I have the full view just yet.

    When you said:
    Making the code work this way would result in very slightly slower code because of the extra lookup for which axis you're working with

    As far as I can tell this would only affect the parsing end of things, because of this trigger:
    app_move_now_x = app_move_now_y = app_move_now_z = app_move_now_e = true;
    

    Is that correct? If so, I do not have a single problem with that and I like the idea, because the moves will be shuttled to an eight member buffer on the other Propeller for processing. However, if there is any way that it will slow down the actual driver, I would not want to do it, because I have learned they are very finicky when you start adding extra calls, as far as speed goes.

    However, I do not believe that my setup could handle the speed that those drivers will produce. In fact, I know it won't, because the speed has already been throttled back immensely.

    Yea, I will take a look at it later tonight. Thanks for another great suggestion.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-15 17:53
    Well, that code would need to change to look like this:

    axis[0].app_move_now = axis[1].app_move_now = axis[2].app_move_now = axis[3].app_move_now = true

    and in the drivers themselves, they'd each be passed the index into their global struct, or a pointer to it, so the driver code would look like what I've attached.

    Look at the included gcode_struct.h and axis_driver files to see what I mean. The driver code does change slightly, but I don't think the performance difference would be much at all, and anything used in a speed-critical loop could be cached into local variables for the duration of the loop anyway.


    I also found another possible bug:
    OUTA &= (~1 << X_DIRECTION);
    

    ...should probably be:
    OUTA &= ~(1 << X_DIRECTION);
    

    I'm not sure what the order of precedence is there, but if the shift is evaluated after the "bitwise not", you're shifting 0xffffffff up, which will zero the lower bits, and might cause you trouble.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 18:05
    Jason
    I also found another possible bug:


    Code:
    OUTA &= (~1 << X_DIRECTION);...should probably be:

    Code:
    OUTA &= ~(1 << X_DIRECTION);

    That is what Dave Hein said also, but then I got to thinking that it did not look right according to something I had seen earlier and I thought perhaps he made a typo.

    Take a peek at the definition of set_output
    Located: \SimpleIDE\Workspace\Learn\Simple Libraries\Utility\libsimpletools\source\setOutput.c
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2015-04-15 18:11
    @idbruce:
    How much of your C code is compiled in COG mode? I am still not sure on everything you wish to support, though I would imagine that a good C compiler would produce smaller code than PropBASIC, and as such would think that you could fit everyting in COG code (except for the data buffer for the G-Code), as it all fits in my PropBASIC firmware so far (and I still have a lot of space to spare in each and every cog). I figure that may increase the available HUB RAM for your version, and get you further before using the second Propeller.

    Just a thought.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 18:43
    Jason

    I just did a lot of useless typing, because I missed the fact that you included GCODE_AXIS_INFO AxisInfo[4] within the GCODE struct :(

    It looks good to me and kind of what I was thinking after your suggestion. In reference to you comment about the travel.... Are you thinking about including that for the calcs of perform_gcode_struct_calcs(), because after those calculations, it probably wouldn't be needed.

    As I said, it looks good and on the mark.

    Another idea that popped into my head while running an errand... Perhaps ramping could be achieved as a percentange of high pulse width offset over time.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 18:47
    David
    How much of your C code is compiled in COG mode?

    You just had to throw a difficult question at me :)

    To be perfectly honest, I don't even know what COG mode is :( So most likely none.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-15 19:05
    Another idea that popped into my head while running an errand... Perhaps ramping could be achieved as a percentange of high pulse width offset over time.

    The more I think about this, the more I think it might work, because total time has already been calculated. That value would have to be brought into the driver and computations would have to be made, which would slow down the driver, but that could simplify the ramping process, instead of developing an algorithm.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-15 21:02
    Yes, I think that's totally workable - it would also affect all four axis simultaneously if done right, which is the behavior you'd want.
  • JasonDorieJasonDorie Posts: 1,930
    edited 2015-04-15 23:18
    I was curious as to how fast the Bresenham stuff would actually be, so I got it running on my test board. It'll go considerably faster than the motors I have are capable of. I reduced the waitcnt() delays to 200 cycles per tick, and then did some timing.

    Running two axes (but doing most of the work for all three) it takes just under 900 cycles per tick, or ~225 PASM instruction cycles. If you were issuing steps on every 4th tick, that would be more than 22000 ticks per second. Using 1/8th micro-stepping on a 200/rev stepper, that would be 13.9 revs/sec, or 833 RPM. Stepping at every tick would be ~3300 RPM.

    This is compiled in LMM mode. If it were running in COG mode, it would be significantly faster (probably 4X at least).

    The code is attached, and the pin assignments are in the Bresenham.h header - Pin 0 & 1 are Step/Dir for motor 1, Pin 2 & 3 are Step/Dir for motor 2, etc.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-16 03:58
    Jason

    I am not sure how your code would run on my drivers, because they are pretty darn finicky. They require a minimun high pulse width of 1uS and they are 1/10 microsteppers. Any attempt to start out to fast, they just sit and complain :)

    Anyhow, I figured I would give it a try, and this is how I set the pins, assuming this is what you were referring to:
    private:
        static const int s0 = 1<<23; // Pin 23 X Step
        static const int d0 = 1<<22; // Pin 22 X Direction
        static const int s1 = 1<<26; // Pin 26 Y Step
        static const int d1 = 1<<27; // Pin 27 Y Direction
        static const int s2 = 1<<1; // Pin 1 Z Step (Non-existent)
        static const int d2 = 1<<2; // Pin 2 Z Direction (Non-existent)
    

    When I tried building it, I got this build error:
    Project Directory: C:/Documents and Settings/Bruce/Desktop/JasonD Motor And Bresenham/BresenhamTest/BresenhamTest/
    
    SimpleIDE Version 1.0.2
    C:/Documents and Settings/Bruce/My Documents/SimpleIDE/Learn/Simple Libraries/
    C:/Documents and Settings/Bruce/My Documents/SimpleIDE/ Updated on: 2015-02-14
    
    propeller-elf-gcc.exe -v GCC 4.6.1 (propellergcc_v1_0_0_2408)
    propeller-elf-c++ -I . -L . -O2 -mlmm -m32bit-doubles -fno-exceptions -Dprintf=__simple_printf -fno-rtti -c Motor.cpp -o lmm/Motor.opp
    propeller-elf-c++: error: Motor.cpp: No such file or directory
    propeller-elf-c++: fatal error: no input files
    compilation terminated.
    Done. Build Failed!
    
    Click error or warning messages above to debug.
    

    Do I need to drag in that previous upload of Motor.cpp?
  • idbruceidbruce Posts: 6,197
    edited 2015-04-16 05:12
    David
    I am still not sure on everything you wish to support
    

    Me either... Ideas and concepts evolve for me... I do not always have a clear vision when starting something.

    I will say that I want to develop a Propeller based system that can be used for both 3D printing and CNC. The basics would include:
      Motor support for four axes
      GCODE input from SD or serial
      LCD terminal, but support for a full blown terminal would be nice
      Homing and overtravel switches for each axis, and supporting code to monitor them
      ADC support for 3D printing applications or for CNC machinery that may need ADC support
      IO support for a user defined GUI

    As I see it, GUI and GCODE processing will most likely be unique for each each user, custom tailored to their individual needs. However the basics, such as GCODE parsing, motor control, switch provisions, ADC support, and GUI IO pin availability would be common for all users.

    Each user will have different requirements for a GUI, as long as there is an ample amount of surplus IO, supporting various types of GUIs should not be a problem, except that custom code would have to be written to support the desired interface. The same holds true for GCODE processing, whereas processing for all 3D printers may be equivalent, and not require any changes, but in the case of CNC machinery, I can envision various GCODE processing schemes. For instance, as long as you have support for the previously mentioned items, and restrict various G commands for movement code, the rest of the processing is pretty much up for grabs, so custom processing can easily be achieved, by modifying existing processing code or creating new processing code. Various commands can activate anything that is configured in the processing code. Although there are common commands for common tasks, any command besides movement codes could be modified.

    E.G.: M100 - Flash IR transmitter to a IR receiver on a different controller to run a specific process.

    As long as there is a parser to read the commands, any processor could be modified to perform specific tasks. However as mentioned, and as you know, there are common GCODEs for common tasks. Modification of the processor and GCODE commands is entirely upto the user.

    My goal is to enable movement through the use of stepper motors, with an ample amount of surplus pins for a custom GUI, as well as a surplus pin or two on the processing end for special processing needs. However for this particular project build, I will be writing GUI interface code and GCODE processing code for a 3D printing application. As long as the controller has the support for such changes, the GUI code and processing code can be modified for a wide variety of projects, besides 3D printing.
Sign In or Register to comment.