By the way ... I posted a ConfigReader in obex, which already handles reading a file line by line, parsing the content into an array.
Okay I downloaded the object and took a brief look at it, and I must say that it caught my interest, however I will have to study it quite a bit more to get a thorough understanding.
As I see it, either your object or a similar object, can be used for parsing both the G-Code files and Configuration files, and if there are any discrepancies in the management of data, perhaps a simple switch to go from G-Code file parsing to Configuration file parsing.
I suppose a well defined parsing stategy is of an absolute necessity, but before that can occur, a decision must be made as to whether floating point support should be added. And of course floating point support will most likely be determined by the algorithms being utilized. So I suppose, my first goal should be to find the appropriate algorithms and movement strategies.
Anyhow, I liked what I saw in your object and it definitely provided food for thought. Thanks for sharing.
Strings in flash:
I assume with serial LCD you mean a text-display with 2 or 4 lines with 16 or 20 characters, correct? This makes a maximum number of 80 characters for the full LCD screen. I also assume that the flash is not much slower than the SD card (where I usually store my strings). So, we talk about a reading speed of at least some kB per second?
Question1: How fast can you read?
Question2: How fast can you update the LCD without getting the text to blur?
So, my answer to your doubts is: The propeller / flash will beat you and your LCD ;o)
Machine parameters in flash:
Well, ok, got your point. Maybe a mixture would be the best. Allow an easy update of the parameters via config-file on SD card, but store a set in flash as well. If the file on SD is newer it will be parsed freshly and stored in flash. If the file has already been read, skip the parsing and use the prepared set in flash. The advantage: If the SD is removed the propeller can still boot with a valid set of parameters.
2 or 3 images:
Well ... there really is a memory problem with bigger programs. As I already mentioned the FAT32 driver, which allows directory access (Kyes driver), already needs a bunch of HUB RAM. So, it is very unlikely that all the things you want to do fit into a single 32kB HUB RAM image - well at least all the things that I want to do will not fit. On the other hand it is very unlikely that all parts of the program are needed at the same time. This allows to split the program into parts which are placed in different images (*.bin -files). So, if you select something in the menu, the according image is loaded and started. It is possible to have a COG loading another image and start it (see Femto-Basic). Here we also talk about a fraction of a second.
It is also possible to load a *.bin file of a program into a running environment (as long as there is enough free HUB-RAM) and start it, pass parameters forth and back and let it access for example the serial driver for the LCD. Already did that.
COG drivers in EEPROM:
With this subject for the time being I'd like to refere you to my BLOG entries. Only a few more words so far: I already mentioned this approach in several threads, but I think nobody was interested enough to follow this concept. But for me it is best practice and all of my propeller boards have their drivers stored in upper EEPROM. In short words: we have 7 COGs to fill with other stuff than the main program. Usually drivers go there. If all 7 COGs have different tasks and the needed drivers use a good amount of COG RAM you have 7 PASM blocks with a max. size of 2kB (512 longs) that are occupied (total up to 14kB). What I do is, store those PASM blocks in upper EEPROM (with 32kB you can have 15 PASM drivers) and have
a) a HUB RAM buffer of 2kB
b) a SPIN code sequence which loads the needed drivers and start the COGs in a sequence
After initializing the COGs the HUB RAM buffer can be used for other things. So, in total the gain is up to 14kB minus some hundred bytes for the load and start sequence).
A side-effect is, that the program using this technique can be run on several different hardware setups as long as the driver is available, because the drivers are stored in EEPROM with all the settings (for example which pins to use for the SD card) already being made in the storing process. So, the main programs become a little bit more hardware independent.
Did I say a few more words? ;o)
SWAP:
Yep, another concept I really like to use regularly. Accessing the swap file is easier and faster than FAT file access. Keyword is VirtualMemory. Also mentioned several times here in the forum. CassLan utilized it to write a text editor (Preditor).
Ok ... I rechecked the code I wrote for proof of concept of the motion control. Unfortunately it also uses the EEPROM driver concept. But it is fairly easy to replace the FullDuplexSerial_NP (NP stands for NO PASM ;o) with an original FullDuplexSerial and deactivate the code for loading the driver from EEPROM.
The ramping stuff would simply be done in the repeat loop of the function stepper. The current code for the axes only do a bresnham line algorithm. So, it should already be good for g-code which only contains lines.
I wrote a response yesterday morning, but accidentally closed the window for my reply:( Later this morning or later today, I will type out another response.
I apologize for not responding yet, but I have been going through some troubles.
I wrote some code for parsing the G-code file, and I got to the point of seperating all of the field entries into seperate data entries. The code is not exactly what I envisioned, but it was working good. Anyhow, I was just adding some comments, so that I could upload the code here, and it stopped working. After fiddling with my code for hours, I finally broke it down to the bare minimum code, only to discover there is now something wrong in between the Propeller Memory Card and SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), and even perhaps much sooner, such as SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE)
At this point, I am not exactly sure what went wrong. Perhaps a loose breadboard connect, corrupted SD card, or even a defective PBOE.
If I comment out SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), both Terminal.Str(STRING("Hello")) output. If I don't comment out the call, only the top Hello outputs.
Good thing, I saved most of my code in a backup copy before destroying all of my hard work
EDIT: After determining that my code was not the source of my troubles (hours of work), I pulled the SD card and checked it on a PC, only to discover that my G-Code file no longer existed. So I reformatted the card and reinstalled the file. The code above now gives me both Hellos I am not exactly sure how the file got deleted though.
EDIT: Okay, so it got deleted again, after editing the the following line from ANDs to ORs.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
Apparently, just guessing at this point, the ORs cause the program to hang, and by shutting down the PBOE or reloading EEPROM, the partition is not being properly unmounted. There is a comment in the source code provided with the FAT16/FAT32 Full File System Driver Documentation v1.0, which states:
Not unmounting the partition may cause bad things to happen.
I suppose file deletion was one of those bad things
Here is a piece of source code that will open up a G-Code file and parse it line by line, seperating the values into 12 different fields, being designated by 12 different letters common to reprap G-Code. Source: http://reprap.org/wiki/G-code
The various fields are G, M, T, S, P, X, Y, Z, F, R, E, and N.
In additon to showing the main file, I have included an attachment to maintain the file format and to include the objects utilized.
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
'Propeller Memory Card Assignments
DI = 0 'IO0 / DI / MOSI / CD Pin
DO = 1 'IO1 / DO / MISO Pin
WP = 2 'IO2 / WP Pin
HOLD = 3 'IO3 / HOLD Pin
SD_CS = 4 'CS Pin For MicroSD Card Memory
FLASH_CS = 5 'CS Pin For Flash Memory
SRAM_CS = 6 'CS Pin For SRAM Memory
CLK = 7 'CLK Pin
'Full Duplex Serial
RX = 31
TX = 30
COMM_MODE = 0
COMM_BAUD_RATE = 57_600
'FAT Engine Assignment for unused pins or RTC. Please refer to the
'FAT16/FAT32 Full File System Driver Documentation for proper usage
NEG_ONE = -1
VAR
'G-Code file line characters
BYTE bChBuffer[100]
'The following variables are intended to store G-Code into various fields.
'Please note the number of fields may change, depending upon which parameters and commands
'I actually intend to support. For more information about these fields, please refer to
'RepRap G Code Fields at [URL]http://reprap.org/wiki/G-code[/URL]
BYTE bFieldElement_0[10] 'G Field
BYTE bFieldElement_1[10] 'M Field
BYTE bFieldElement_2[10] 'T Field
BYTE bFieldElement_3[10] 'S Field
BYTE bFieldElement_4[10] 'P Field
BYTE bFieldElement_5[10] 'X Field
BYTE bFieldElement_6[10] 'Y Field
BYTE bFieldElement_7[10] 'Z Field
BYTE bFieldElement_8[10] 'F Field
BYTE bFieldElement_9[10] 'R Field
BYTE bFieldElement_10[10] 'E Field
BYTE bFieldElement_11[10] 'N Field
OBJ
SDCard: "SD-MMC_FATEngine.spin"
Memory: "SPI Memory Driver"
Terminal: "FullDuplexSerial"
PUB Main
'Start FullDuplexSerial
Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
WAITCNT(CNT + (1 * CLKFREQ))
'Start FATEngine
SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
'Mount Partition
SDCard.mountPartition(0)
'Open G-Code File "CAL_HOLD.TXT" for reading
SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
REPEAT
'Fill bChBuffer with 0s
BYTEFILL(@bChBuffer, 0, 100)
'Grab file lines that are not comments or blank
GetNextValidFileLine
IF(bChBuffer[0] == 0)
QUIT
'Fill in the various field elements
FillFieldElements
'Display the various field seperations
Terminal.Str(STRING("G="))
Terminal.Str(@bFieldElement_0)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("M="))
Terminal.Str(@bFieldElement_1)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("T="))
Terminal.Str(@bFieldElement_2)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("S="))
Terminal.Str(@bFieldElement_3)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("P="))
Terminal.Str(@bFieldElement_4)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("X="))
Terminal.Str(@bFieldElement_5)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Y="))
Terminal.Str(@bFieldElement_6)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Z="))
Terminal.Str(@bFieldElement_7)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("F="))
Terminal.Str(@bFieldElement_8)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("R="))
Terminal.Str(@bFieldElement_9)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("E="))
Terminal.Str(@bFieldElement_10)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("N="))
Terminal.Str(@bFieldElement_11)
Terminal.Tx(32) {Space}
'Seperate G-Code field values line by line
Terminal.Tx(13) {Carriage Return}
'Unmount Partition
SDCard.unmountPartition
'Stop FATEngine
SDCard.fatEngineStop
WAITCNT(CNT + (1 * CLKFREQ))
'Stop FullDuplexSerial
Terminal.Stop
PUB GetNextValidFileLine
REPEAT
'Grab a file line as a string
SDCard.readString(@bChBuffer, 99)
'Keep repeating while the following conditions are met,
'because we don't want or need these lines beginning with
'these characters.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
QUIT
PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
'Empty the field elements of any possible data
BYTEFILL(@bFieldElement_0, 0, 10)
BYTEFILL(@bFieldElement_1, 0, 10)
BYTEFILL(@bFieldElement_2, 0, 10)
BYTEFILL(@bFieldElement_3, 0, 10)
BYTEFILL(@bFieldElement_4, 0, 10)
BYTEFILL(@bFieldElement_5, 0, 10)
BYTEFILL(@bFieldElement_6, 0, 10)
BYTEFILL(@bFieldElement_7, 0, 10)
BYTEFILL(@bFieldElement_8, 0, 10)
BYTEFILL(@bFieldElement_9, 0, 10)
BYTEFILL(@bFieldElement_10, 0, 10)
BYTEFILL(@bFieldElement_11, 0, 10)
Index := 0
ValueIndex := 0
'Parse file line into seperate field values. Various fields will remain empty
REPEAT
Char := bChBuffer[Index]
'Exclude these characters from becoming part of are field entries
IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
QUIT
ELSEIF(Char == 32 {Space})
Index++
ValueIndex := 0
NEXT
ELSEIF(Char == 71 {G Character})
'Set current field element
Field_Element := 0
Index++
NEXT
ELSEIF(Char == 77 {M Character})
'Set current field element
Field_Element := 1
Index++
NEXT
ELSEIF(Char == 84 {T Character})
'Set current field element
Field_Element := 2
Index++
NEXT
ELSEIF(Char == 83 {S Character})
'Set current field element
Field_Element := 3
Index++
NEXT
ELSEIF(Char == 80 {P Character})
'Set current field element
Field_Element := 4
Index++
NEXT
ELSEIF(Char == 88 {X Character})
'Set current field element
Field_Element := 5
Index++
NEXT
ELSEIF(Char == 89 {Y Character})
'Set current field element
Field_Element := 6
Index++
NEXT
ELSEIF(Char == 90 {Z Character})
'Set current field element
Field_Element := 7
Index++
NEXT
ELSEIF(Char == 70 {F Character})
'Set current field element
Field_Element := 8
Index++
NEXT
ELSEIF(Char == 82 {R Character})
'Set current field element
Field_Element := 9
Index++
NEXT
ELSEIF(Char == 69 {E Character})
'Set current field element
Field_Element := 10
Index++
NEXT
ELSEIF(Char == 78 {N Character})
'Set current field element
Field_Element := 11
Index++
NEXT
ELSE
IF(Field_Element == 0)
'G Field element character
bFieldElement_0[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 1)
'M Field element character
bFieldElement_1[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 2)
'T Field element character
bFieldElement_2[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 3)
'S Field element character
bFieldElement_3[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 4)
'P Field element character
bFieldElement_4[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 5)
'X Field element character
bFieldElement_5[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 6)
'Y Field element character
bFieldElement_6[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 7)
'Z Field element character
bFieldElement_7[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 8)
'F Field element character
bFieldElement_8[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 9)
'R Field element character
bFieldElement_9[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 10)
'E Field element character
bFieldElement_10[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 11)
'N Field element character
bFieldElement_11[ValueIndex] := Char
ValueIndex++
Index++
NEXT
I am sure you are correct, however I never got that far with my SPIN programming.
My goal at this point is to make a working strategy from small bits and pieces, which can later be optimized with methods such as you suggest. I still have not determined the actual data size required for each field element, whereas some of the data fields will only have three characters or less.
Still trying to figure it out, but thanks for your suggestion. I will take a much deeper look at this, when I get further along.
This post will be referring to the G-Code command list provided in Post#6 and the G-Code provided in Post #26.
After closely examining the G-Code file, created by KISSlicer, it was determined that only the following commands were used to create the 3D print.
G1: Linear Motion at Feed Rate - Example: G1 X90.6 Y13.8 E22.4
G21: Set Units to Millimeters - Example: G21
G28: Home - Example: G28
G90: Set to Absolute Positioning - Example: G90
M83: Set E codes relative
M104: Set Extruder Temperature (Fast) - Example: M104 S190
M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
M109: Set Extruder Temperature and Wait - Example: M109 S185
M107: Fan Off Used in g-Code
M140: Set heated bed temperature - Example: M140 S55
So I am assuming, that I can reasonably assume that these are the only actual commands that I need to run a 3D printer. However to make slicing and machine setup easier, I suppose I should also include the counterparts to some of the above commands, such as:
G20: Set Units to Inches - Example: G20
G91: Set to Relative Positioning - Example: G91
M82: Set E codes absolute
Additionally, some commands were used in the G-Code that were not present in the list of Post #6. Considering that I am making a lot of assumptions, I might as well keep assuming, so I assume that the required G-Code commands are determined by the slicer utilized. Additionally, I would imagine that many developers have taken pain staking measures into placing simple machine functions into proper G-Code format, which I also imagine takes up a fair bit of memory. Take for example the following command:
M112: Emergency Stop - Example: M112
I can only imagine how much time, effort, and memory it would take to incorporate this command into the code, when a simple WAITPEQ monitoring switch input should be able to provide a machine stop. However an orderly machine stop may be a different issue.
In the case of a Propeller driven 3D printer, I truly believe the command and coding should be kept to a bare minimum, until it has been determined that there is room for expansion.
In addition to my previous post and in an effort to move forward, I would like to state the direction I am heading.
A previous employer always told me to tackle the known problems first. With this concept rearranged a little in my mind, this is what I know:
Since the controller of Post #1 will only support one extruder, I will only be writing code to support one extruder.
Since I only have a slight amount of experience with one slicer, being the KISSlicer, that is the only slicer I will be attempting to support.
I will only be writing code to support some of the G-Codes utilized by the KISSlicer, which currently consists of the following:
G1: Linear Motion at Feed Rate - Example: G1 X90.6 Y13.8 E22.4
G20: Set Units to Inches - Example: G20
G21: Set Units to Millimeters - Example: G21
G28: Home - Example: G28
G90: Set to Absolute Positioning - Example: G90
G91: Set to Relative Positioning - Example: G91
M82: Set E codes absolute
M83: Set E codes relative
M104: Set Extruder Temperature (Fast) - Example: M104 S190
M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
M109: Set Extruder Temperature and Wait - Example: M109 S185
M107: Fan Off Used in g-Code
M140: Set heated bed temperature - Example: M140 S55
EDIT: Additionally, considering that it slightly affects memory, the build volume area will be limited to 999.99mm X 999.99mm X 999.99mm
EDIT: As it pertains to the source code of Post #66, Fields P, R, and N, will no longer be supported, because apparently KISSlicer does not support them.
I have looked more into the practicalities of using the Propeller for my 3D Printer controller and reached the conclusion that it's a non-starter for the following reasons:
1) Code space - At only 496 words of available code and data space per cog I would be forced to use an external code memory model. For PropGCC this would be XMMC which leads to...
2) External code memory model performance hit - I get the impression that employing the PropGCC XMMC memory model hits execution time by a factor of x30 (or thereabouts) for I2C EEPROM. It also requires the use of an instruction cache for each cog taken from Hub memory.
3) Delays in accessing global Hub resources - It is quite likely that globals in Hub memory would be heavily used and the cog synchronisation delays would impact performance considerably.
3) More Propellers? - I considered increasing the number of Propellers from 1 to 4 (in order to increase the distributed code space and keep code internal) and link these master-slave fashion using SPI but it increases complexity considerably.
4) Hardware interface overhead - With the Propeller's lack of on-chip peripherals you need to code hardware interfaces e.g. UART and SPI. This uses valuable resources that the application itself requires.
The Propeller has a unique architecture which suits smaller, demanding applications. Its lack of a good external memory interface is a severely limiting factor in larger applications which, I think, necessitates the use of multiple Propellers. Given that I already had a dual-PIC solution on the drawing board, which I am sure will be practical, I think I shall pursue that avenue.
The following source code and attachments actively reflect a more accurate and detailed solution. Besides the source code shown, the attachment contains all the files necessary for testing and accuracy checking.
In the attachment below, CAL_HOLD.TXT is a G-Code file compiled by the KISSlicer and is utilized as a source for the parser.
In the attachment below, TRIMMED.TXT is identical to CAL_HOLD.TXT, with the exception that all comments and linefeeds have been removed. This file can be utilized to check the accuracy of the output, as well as checking the assignments of my variables.
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
'Propeller Memory Card Assignments
DI = 0 'IO0 / DI / MOSI / CD Pin
DO = 1 'IO1 / DO / MISO Pin
WP = 2 'IO2 / WP Pin
HOLD = 3 'IO3 / HOLD Pin
SD_CS = 4 'CS Pin For MicroSD Card Memory
FLASH_CS = 5 'CS Pin For Flash Memory
SRAM_CS = 6 'CS Pin For SRAM Memory
CLK = 7 'CLK Pin
'Full Duplex Serial
RX = 31
TX = 30
COMM_MODE = 0
COMM_BAUD_RATE = 115_200
'FAT Engine Assignment for unused pins or RTC. Please refer to the
'FAT16/FAT32 Full File System Driver Documentation for proper usage
NEG_ONE = -1
VAR
'The following variable holds the G-Code file line characters. It has been
'established that the G-Code file used as a guide in this code, had a maximum
'character count of 72. To be on the safe side, the maximum file line size
'has been increased to 100.
BYTE bChBuffer[100]
'The following variables are intended to store G-Code into various fields.
'For more information about these fields, please refer to RepRap G Code
'Fields at: [URL]http://reprap.org/wiki/G-code[/URL] . Additionally, these are the
'only known fields to be supported by the KISSlicer. Please note that the
'maximum character count may change in the future, because I am not exactly
'sure on bFieldElement_8 maximum character count, but support for 7 characters
'should be sufficient. The maximum character count should support a build
'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).
BYTE bFieldElement_0[2] 'G Field
BYTE bFieldElement_1[3] 'M Field
BYTE bFieldElement_2[1] 'T Field
BYTE bFieldElement_3[3] 'S Field
BYTE bFieldElement_4[7] 'X Field - Floating Point
BYTE bFieldElement_5[7] 'Y Field - Floating Point
BYTE bFieldElement_6[7] 'Z Field - Floating Point
BYTE bFieldElement_7[7] 'F Field - Floating Point
BYTE bFieldElement_8[7] 'E Field - Floating Point
'The following G-Code commands are currently the only known (by me) commands
'supported by the KISSlicer, therefore these are the only G-Code commands
'that I will currently support.
{
G1: Controlled Move With E, F, X, Y, And Z Fields.
G20: Set Measurement Units To Inches With No Fields
G21: Set Measurement Units To Millimeters With No Fields
G28: Home Axes With No Fields
G90: Set Absolute Positioning With No Fields
G91: Set Relative Positioning With No Fields
M82: Set E Codes Absolute With No Fields
M83: Set E Codes Relative With No Fields
M104: Set Extruder Temperature And No Wait With S Field
M106: Fan On With No Fields
M109: Set Extruder Temperature And Wait With S Field
M107: Fan Off With No Fields
M140: Set Bed Temperature With S Field
T0: Select Tool With No Fields
}
OBJ
SDCard: "SD-MMC_FATEngine.spin"
Memory: "SPI Memory Driver"
Terminal: "FullDuplexSerial"
PUB Main
'Start FullDuplexSerial
Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
WAITCNT(CNT + (1 * CLKFREQ))
'Start FATEngine
SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
'Mount Partition
SDCard.mountPartition(0)
'Open G-Code File "CAL_HOLD.TXT" for reading
SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
REPEAT
'Fill bChBuffer with 0s
BYTEFILL(@bChBuffer, 0, 100)
'Grab file lines that are not comments or blank
GetNextValidFileLine
IF(bChBuffer[0] == 0)
QUIT
'Fill in the various field elements
FillFieldElements
'Display the various field seperations
Terminal.Str(STRING("G="))
Terminal.Str(@bFieldElement_0)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("M="))
Terminal.Str(@bFieldElement_1)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("T="))
Terminal.Str(@bFieldElement_2)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("S="))
Terminal.Str(@bFieldElement_3)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("X="))
Terminal.Str(@bFieldElement_4)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Y="))
Terminal.Str(@bFieldElement_5)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Z="))
Terminal.Str(@bFieldElement_6)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("F="))
Terminal.Str(@bFieldElement_7)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("E="))
Terminal.Str(@bFieldElement_8)
Terminal.Tx(32) {Space}
'Seperate G-Code field values line by line
Terminal.Tx(13) {Carriage Return}
'Unmount Partition
SDCard.unmountPartition
'Stop FATEngine
SDCard.fatEngineStop
WAITCNT(CNT + (1 * CLKFREQ))
'Stop FullDuplexSerial
Terminal.Stop
PUB GetNextValidFileLine
REPEAT
'Grab a file line as a string
SDCard.readString(@bChBuffer, 99)
'Keep repeating while the following conditions are met,
'because we don't want or need these lines beginning with
'these characters.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
QUIT
PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
'Empty the field elements of any possible data
BYTEFILL(@bFieldElement_0, 0, 2)
BYTEFILL(@bFieldElement_1, 0, 3)
BYTEFILL(@bFieldElement_2, 0, 1)
BYTEFILL(@bFieldElement_3, 0, 3)
BYTEFILL(@bFieldElement_4, 0, 7)
BYTEFILL(@bFieldElement_5, 0, 7)
BYTEFILL(@bFieldElement_6, 0, 7)
BYTEFILL(@bFieldElement_7, 0, 7)
BYTEFILL(@bFieldElement_8, 0, 7)
Index := 0
ValueIndex := 0
'Parse file line into seperate field values. Various fields will remain empty
REPEAT
Char := bChBuffer[Index]
'Exclude these characters from becoming part of are field entries
IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
QUIT
ELSEIF(Char == 32 {Space})
Index++
ValueIndex := 0
NEXT
ELSEIF(Char == 71 {G Character})
'Set current field element
Field_Element := 0
Index++
NEXT
ELSEIF(Char == 77 {M Character})
'Set current field element
Field_Element := 1
Index++
NEXT
ELSEIF(Char == 84 {T Character})
'Set current field element
Field_Element := 2
Index++
NEXT
ELSEIF(Char == 83 {S Character})
'Set current field element
Field_Element := 3
Index++
NEXT
ELSEIF(Char == 88 {X Character})
'Set current field element
Field_Element := 4
Index++
NEXT
ELSEIF(Char == 89 {Y Character})
'Set current field element
Field_Element := 5
Index++
NEXT
ELSEIF(Char == 90 {Z Character})
'Set current field element
Field_Element := 6
Index++
NEXT
ELSEIF(Char == 70 {F Character})
'Set current field element
Field_Element := 7
Index++
NEXT
ELSEIF(Char == 69 {E Character})
'Set current field element
Field_Element := 8
Index++
NEXT
ELSE
IF(Field_Element == 0)
'G Field element character
bFieldElement_0[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 1)
'M Field element character
bFieldElement_1[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 2)
'T Field element character
bFieldElement_2[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 3)
'S Field element character
bFieldElement_3[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 4)
'X Field element character
bFieldElement_4[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 5)
'Y Field element character
bFieldElement_5[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 6)
'Z Field element character
bFieldElement_6[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 7)
'F Field element character
bFieldElement_7[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 8)
'E Field element character
bFieldElement_8[ValueIndex] := Char
ValueIndex++
Index++
NEXT
This post, source code, and attachment is nearly identical to Post #72 and I apologize for the redundancy, however the comments provided in the source code of Post #72 were a little misleading. In an effort to provide a more clear understanding, I am now providing new source code, with rewritten source code comments.
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
'Propeller Memory Card Assignments
DI = 0 'IO0 / DI / MOSI / CD Pin
DO = 1 'IO1 / DO / MISO Pin
WP = 2 'IO2 / WP Pin
HOLD = 3 'IO3 / HOLD Pin
SD_CS = 4 'CS Pin For MicroSD Card Memory
FLASH_CS = 5 'CS Pin For Flash Memory
SRAM_CS = 6 'CS Pin For SRAM Memory
CLK = 7 'CLK Pin
'Full Duplex Serial
RX = 31
TX = 30
COMM_MODE = 0
COMM_BAUD_RATE = 115_200
'FAT Engine Assignment for unused pins or RTC. Please refer to the
'FAT16/FAT32 Full File System Driver Documentation for proper usage
NEG_ONE = -1
VAR
'The following variable holds the G-Code file line characters. It has been
'established that the G-Code file used as a guide in this code, had a maximum
'file line character count of 72. To be on the safe side, the maximum file
'line size has been increased to 100.
BYTE bChBuffer[100]
'The following variables are intended to store G-Code into various fields.
'For more information about these fields, please refer to RepRap G Code
'Fields at: [URL]http://reprap.org/wiki/G-code[/URL] . Additionally, these are the
'only known fields to be supported by the KISSlicer. Please note that the
'maximum character count may change in the future, because I am not exactly
'sure on bFieldElement_8 maximum character count, but support for 7 characters
'should be sufficient. The maximum character count should support a build
'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).
BYTE bFieldElement_0[2] 'G Field
BYTE bFieldElement_1[3] 'M Field
BYTE bFieldElement_2[1] 'T Field
BYTE bFieldElement_3[3] 'S Field
BYTE bFieldElement_4[7] 'X Field - Floating Point
BYTE bFieldElement_5[7] 'Y Field - Floating Point
BYTE bFieldElement_6[7] 'Z Field - Floating Point
BYTE bFieldElement_7[7] 'F Field - Floating Point
BYTE bFieldElement_8[7] 'E Field - Floating Point
'The following G-Code commands are currently the only known (by me) commands
'supported by the KISSlicer, therefore these are the only G-Code commands
'that I will currently support.
{
G1: Controlled Move
G Field - bFieldElement_0, X Field - bFieldElement_4,
Y Field - bFieldElement_5, Z Field - bFieldElement_6,
F Field - bFieldElement_7, and E Field - bFieldElement_8
filled during line parse
G20: Set Measurement Units To Inches
G Field - bFieldElement_0 filled during line parse
G21: Set Measurement Units To Millimeters
G Field - bFieldElement_0 filled during line parse
G28: Home Axes
G Field - bFieldElement_0 filled during line parse
G90: Set Absolute Positioning
G Field - bFieldElement_0 filled during line parse
G91: Set Relative Positioning
G Field - bFieldElement_0 filled during line parse
M82: Set E Codes Absolute
M Field - bFieldElement_1 filled during line parse
M83: Set E Codes Relative
M Field - bFieldElement_1 filled during line parse
M104: Set Extruder Temperature With No Wait
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
M106: Fan On
M Field - bFieldElement_1 filled during line parse
M109: Set Extruder Temperature And Wait
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
M107: Fan Off
M Field - bFieldElement_1 filled during line parse
M140: Set Bed Temperature
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
T0: Select Tool With No Fields
T Field - bFieldElement_2 filled during line parse
}
OBJ
SDCard: "SD-MMC_FATEngine.spin"
Memory: "SPI Memory Driver"
Terminal: "FullDuplexSerial"
PUB Main
'Start FullDuplexSerial
Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
WAITCNT(CNT + (1 * CLKFREQ))
'Start FATEngine
SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
'Mount Partition
SDCard.mountPartition(0)
'Open G-Code File "CAL_HOLD.TXT" for reading
SDCard.openFile(STRING("CAL_HOLD.TXT"), "R")
REPEAT
'Fill bChBuffer with 0s
BYTEFILL(@bChBuffer, 0, 100)
'Grab file lines that are not comments or blank
GetNextValidFileLine
IF(bChBuffer[0] == 0)
QUIT
'Fill in the various field elements
FillFieldElements
'Display the various field seperations
Terminal.Str(STRING("G="))
Terminal.Str(@bFieldElement_0)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("M="))
Terminal.Str(@bFieldElement_1)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("T="))
Terminal.Str(@bFieldElement_2)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("S="))
Terminal.Str(@bFieldElement_3)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("X="))
Terminal.Str(@bFieldElement_4)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Y="))
Terminal.Str(@bFieldElement_5)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Z="))
Terminal.Str(@bFieldElement_6)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("F="))
Terminal.Str(@bFieldElement_7)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("E="))
Terminal.Str(@bFieldElement_8)
Terminal.Tx(32) {Space}
'Seperate G-Code field values line by line
Terminal.Tx(13) {Carriage Return}
'Unmount Partition
SDCard.unmountPartition
'Stop FATEngine
SDCard.fatEngineStop
WAITCNT(CNT + (1 * CLKFREQ))
'Stop FullDuplexSerial
Terminal.Stop
PUB GetNextValidFileLine
REPEAT
'Grab a file line as a string
SDCard.readString(@bChBuffer, 99)
'Keep repeating while the following conditions are met,
'because we don't want or need these lines beginning with
'these characters.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
QUIT
PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
'Empty the field elements of any possible data
BYTEFILL(@bFieldElement_0, 0, 2)
BYTEFILL(@bFieldElement_1, 0, 3)
BYTEFILL(@bFieldElement_2, 0, 1)
BYTEFILL(@bFieldElement_3, 0, 3)
BYTEFILL(@bFieldElement_4, 0, 7)
BYTEFILL(@bFieldElement_5, 0, 7)
BYTEFILL(@bFieldElement_6, 0, 7)
BYTEFILL(@bFieldElement_7, 0, 7)
BYTEFILL(@bFieldElement_8, 0, 7)
Index := 0
ValueIndex := 0
'Parse file line into seperate field values. Various fields will remain empty
REPEAT
Char := bChBuffer[Index]
'Exclude these characters from becoming part of are field entries
IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
QUIT
ELSEIF(Char == 32 {Space})
Index++
ValueIndex := 0
NEXT
ELSEIF(Char == 71 {G Character})
'Set current field element
Field_Element := 0
Index++
NEXT
ELSEIF(Char == 77 {M Character})
'Set current field element
Field_Element := 1
Index++
NEXT
ELSEIF(Char == 84 {T Character})
'Set current field element
Field_Element := 2
Index++
NEXT
ELSEIF(Char == 83 {S Character})
'Set current field element
Field_Element := 3
Index++
NEXT
ELSEIF(Char == 88 {X Character})
'Set current field element
Field_Element := 4
Index++
NEXT
ELSEIF(Char == 89 {Y Character})
'Set current field element
Field_Element := 5
Index++
NEXT
ELSEIF(Char == 90 {Z Character})
'Set current field element
Field_Element := 6
Index++
NEXT
ELSEIF(Char == 70 {F Character})
'Set current field element
Field_Element := 7
Index++
NEXT
ELSEIF(Char == 69 {E Character})
'Set current field element
Field_Element := 8
Index++
NEXT
ELSE
IF(Field_Element == 0)
'G Field element character
bFieldElement_0[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 1)
'M Field element character
bFieldElement_1[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 2)
'T Field element character
bFieldElement_2[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 3)
'S Field element character
bFieldElement_3[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 4)
'X Field element character
bFieldElement_4[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 5)
'Y Field element character
bFieldElement_5[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 6)
'Z Field element character
bFieldElement_6[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 7)
'F Field element character
bFieldElement_7[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 8)
'E Field element character
bFieldElement_8[ValueIndex] := Char
ValueIndex++
Index++
NEXT
My goal at this point is to make a working strategy from small bits and pieces, which can later be optimized with methods such as you suggest. I still have not determined the actual data size required for each field element, whereas some of the data fields will only have three characters or less.
Implementing a structure that defines data relationships reduces the amount of conditional statements in the code base. Besides, it is good design strategy to define this structures upfront before the code base becomes too complex.
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
#0, G_FIELD, M_FIELD, T_FIELD, S_FIELD, X_FIELD,{
} Y_FIELD, Z_FIELD, F_FIELD, E_FIELD
G_LEN = 2
M_LEN = 3
T_LEN = 1
S_LEN = 3
X_LEN = 7
Y_LEN = 7
Z_LEN = 7
F_LEN = 7
E_LEN = 7
NUMBER_OF_FIELDS = 9
DAT
gf byte $0[G_LEN]
mf byte $0[M_LEN]
tf byte $0[T_LEN]
sf byte $0[S_LEN]
xf byte $0[X_LEN]
yf byte $0[Y_LEN]
zf byte $0[Z_LEN]
ff byte $0[F_LEN]
ef byte $0[E_LEN]
ikey byte "G", "M", "T", "S", "X", "Y", "Z", "F", "E"
ivalue long @gf, @mf, @tf, @sf, @xf, @yf, @zf, @ff, @ef
ilen byte 2, 3, 1, 3, 7, 7, 7, 7, 7
OBJ
pst : "Parallax Serial Terminal"
PUB Main | i, idx
pst.Start(115_200)
pause(500)
pst.str(string("Complex Data Structure", 13))
'Get the "S" field and fill the field with data
idx := GetTableIndex("S")
'file S with ASCII bytes 0, 1, and 2
repeat i from 0 to (ilen[idx] - 1)
byte[@@ivalue[idx] + i] := i + "0"
'Zero terminate
byte[@@ivalue[idx] + idx] := 0
'View results
pst.str(@@ivalue[idx])
PRI GetTableIndex(key) | i
repeat i from 0 to NUMBER_OF_FIELDS - 1
if ikey[i] == key
return i
return -1
PRI pause(Duration)
waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt)
return
That looks a whole lot better than I am capable of at this point in time In fact, it looks real good. As mentioned earlier, I still consider myself a beginner in SPIN programming, and my skills are no where near your skills.
Just struggling my way through it, and trying to get a working model.
That looks a whole lot better than I am capable of at this point in time In fact, it looks real good. As mentioned earlier, I still consider myself a beginner in SPIN programming, and my skills are no where near your skills.
Just struggling my way through it, and trying to get a working model.
Looks interesting..... However, I am going to attempt it from scratch. I want to attempt the machine control with my own interpretation of the G-Codes and applying my own strategy for a positioning queue. However, I am more than willing to borrow the necessary algorithms
By looking at the included objects of my example, I suppose "from scratch" would not be an accurate definition
But thanks for sharing, because someone may want to use that information to their benefit.
Okay.... I must admit that it was kind of cool to parse the lines and see all of the fields broken down to their values, just zipping through the terminal, however for testing and coding purposes, the huge G-Code file (actually quite small in comparison to large objects) made it quite cumbersome. To speed up the testing and development process, I have decided to switch over to a much smaller file, which includes all test conditions.
The switch to a smaller file is only temporary and I will let you know when I switch back to the original file.
For those of you who might be following along or developing your own version, I am including a copy of that minimalistic file.
Since you are participating, let me throw my strategy at you for some feedback.
Cog 0:
Although I am not 100% positive, because I have not reached the math yet, I believe one cog can parse and compute far faster than the machine can possibly move. Therefore I want to read one line and compute and write the results to a predetermined location in SRAM, for a maximum of 10 lines to start. Monitor SRAM locations for deletion, and upon deletion, add another set of computations for a line.
Cog 1:
Read the computations made from Cog 0 per active line at the predetermined locations in SRAM. Initiate proper machine procedures, for simple commands such as M106, simply perform the task, and delete SRAM contents for that line procedure. For complex procedures such as G1, initiate several new cogs 2, 3, 4, & 5 to perform movement, during which time, Cog 1 could have read the appropriate SRAM location, deleted the contents, and be waiting with the computations when Cogs 2, 3, 4, & 5 return.
Cog 2:
X motor movement
Cog 3:
Y motor movement
Cog 4:
Z motor movement
Cog 5:
E motor movement
Of course Cogs 2, 3, 4, & 5 are arbitrary, since there will be times when it will not be necessary for all these motors to move, so next avail cog.
As far as I can foresee, the only downside would be a small waiting period to initially fill the SRAM in Cog 1, and a small waiting time in Cog 1, when simple tasks are performed, because there will have to be a following read to SRAM for new movement computations or machine procedure. However, since there are several cogs still available, the read of Cog 1 could be passed to another cog, while Cog 1 performs another read.
I know there are those that say the motor movement can be performed in one cog, but right now, this is my strategy.
My knee jerk reaction is to implement an observer pattern to manage caching. I would probably do something similar, if not the same, to manage X, Y, Z, and E movements.
I am not convinced this is the way to go, but here is a link to Bresenham's algorithm implemented in C for a 3D printer. It appears that a port would be real simple.
Okay so the values can be seperated and you say NOTHING SPECIAL
I totally agree, but it was a first step. Now comes the hard part, but before we can work on the hard part, a strategy or several strategies must be outlined. One of these rough strategies was outlined in Post #82, however this is very far from a complete solution.
Before I go any further with this discussion, let me just state for the record that I am no mathematical genius. In fact, math is my weakest link, but I have managed to struggle through life so far with this handicap.
Many 3D print strategies rely on a constant speed for each axis, however I see this as an easy way out, just to get operational software and printers. Several years ago, when I received my first Gecko G251 stepper drives, I was using traditional SPIN to run my steppers, but the speed of these motors was way to slow for my satisfaction. I then started a thread about using counters to run these stepper drives. Several people participated in the discussion and I am thankful to all of them, however between MagIO2 and JonnyMac, a snippet was produced which really made my stepper motors "ZING", from that point on, I have periodically experimented with counter based stepper drivers. The stepper drivers have developed to this stage:
Start the motor at a pulse rate which is capable of overcoming the moment of inertia.
Keep decreasing the time between pulses until we lose necessary torque (MAXIMUM SPEED) for axis movement. - RAMP UP
Maintain maximum speed until ramp down becomes necessary - RUN MAINTAINED SPEED
Keep increasing the time between pulses until we reach the stopping point - RAMP DOWN
By using the longest travel of an axis, like in the Bresenham algorithm, I believe profiles could be determined for all axes and run the steppers with ramping by use of the counters. As mentioned in Post #82 of this thread and in various other threads, all steppers would run in seperate cogs, with seperate counters.
I suppose the main question is, can a straight line be computed by time? I think it can, but then comes the issue of the feedrate or F field, so I am really not sure how it all plays out. To be honest, I really do not believe that I have a firm grasp on F just yet.
Im currently also building an firepick delta , pick&place /3d print/ solderpaste dispencer/ chocolate printer ( hihi) . machine , it could do allot more
anyway , we have been using an atmega1284p running with an 20mhx crystal , marlin driver loaded .
but its just not handeling the inverse kinematics good enought to get the precision needed
currently there there is an driver change going on , to go from marlin to repeater firmware for the motor controller board. still long way from working
but I always had the idea our little friend (the propeller ) could do a way better job .
Your lined out approuche below is kinda what I think we also need.
But im also starting from scratch, just to be able to fit it exactly on our needs and make a custom board for it . looking if it may be an good alternative to our current , or our future probable (ARM ) design
its a opensource project , wich was best known tru hackaday . there is a page there with allot of info on it
also github pages with all the till now released files and sources , google group where the chatting happens ( its an active community) . so its going pretty good with progress
I have propeller chips lying around to do actual testing and hacking ,
drivers used are polou DRV8825 wich are set and shoudl stay at 1/16 microsteps .
the inverse kinematics is what killing the slow 8 bit processor
think the splitting up of the math in different cogs , and letting couters run them so we can push them pretty hard somehow should made a great driver
Iill take some time reading the old post , but would love some links code snippets to get some steppers moving etc . all help appriciated
Since you are participating, let me throw my strategy at you for some feedback.
Cog 0:
Although I am not 100% positive, because I have not reached the math yet, I believe one cog can parse and compute far faster than the machine can possibly move. Therefore I want to read one line and compute and write the results to a predetermined location in SRAM, for a maximum of 10 lines to start. Monitor SRAM locations for deletion, and upon deletion, add another set of computations for a line.
Cog 1:
Read the computations made from Cog 0 per active line at the predetermined locations in SRAM. Initiate proper machine procedures, for simple commands such as M106, simply perform the task, and delete SRAM contents for that line procedure. For complex procedures such as G1, initiate several new cogs 2, 3, 4, & 5 to perform movement, during which time, Cog 1 could have read the appropriate SRAM location, deleted the contents, and be waiting with the computations when Cogs 2, 3, 4, & 5 return.
Cog 2:
X motor movement
Cog 3:
Y motor movement
Cog 4:
Z motor movement
Cog 5:
E motor movement
Of course Cogs 2, 3, 4, & 5 are arbitrary, since there will be times when it will not be necessary for all these motors to move, so next avail cog.
As far as I can foresee, the only downside would be a small waiting period to initially fill the SRAM in Cog 1, and a small waiting time in Cog 1, when simple tasks are performed, because there will have to be a following read to SRAM for new movement computations or machine procedure. However, since there are several cogs still available, the read of Cog 1 could be passed to another cog, while Cog 1 performs another read.
I know there are those that say the motor movement can be performed in one cog, but right now, this is my strategy.
I have just recently started to toy around with the CNC controller discussed in this thread: http://forums.parallax.com/showthread.php/155404-Input-Needed-Combining-Propeller-Proto-Board-Prop-DIP-40-and-ADC-for-3D-Printer. In additon to testing various aspects of the controller, I have also been testing X and Y axes. Both X and Y are driven by Gecko G251 stepper drives, which have 10 microsteps. Although I do no not have any coordinated movement at this time, the code below contains all the constants for the controller board pins, as well as some miscellaneous code, which includes stepper movement code. That code is currently setup for the G251 drives, but it should be able to drive any drivers that have step, direction, and disable inputs, providing the correct value for the constants are given. The main method for driving the steppers is called PulseTheStepPin. More information about this method can be found at this location: http://forums.parallax.com/showthread.php/132373-PulseTheStepPin-revisited-Questions-answers-problems-solutions
There is also other information available elsewhere, which can most likely be found with a seach of the function name.
Hope this helps
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
'Z Axis Pin Assignments
Z_AXIS_DISABLE = 0
Z_AXIS_STEP = 1
Z_AXIS_DIRECTION = 2
'ESTOP And Limit Switches Pin Assignments
ESTOP_AND_LIMIT = 3
'Serial LCD Display Pin Assignment
SERIAL_LCD = 4
'Homing Switch Pin Assignments
X_HOMING = 5
Y_HOMING = 6
Z_HOMING = 7
'Propeller Memory Card Pin Assignments
DI = 8 'IO0 / DI / MOSI / CD Pin
DO = 9 'IO1 / DO / MISO Pin
WP = 10 'IO2 / WP Pin
HOLD = 11 'IO3 / HOLD Pin
SD_CS = 12 'CS Pin For MicroSD Card Memory
FLASH_CS = 13 'CS Pin For Flash Memory
SRAM_CS = 14 'CS Pin For SRAM Memory
CLK = 15 'CLK Pin
'Daughterboard Pin Assignments
PLATFORM_HEATER = 16
EXTRUDER_HEATER = 17
EXTRUDER_FAN = 18
'E Axis Pin Assignments
E_AXIS_DIRECTION = 19
E_AXIS_STEP = 20
E_AXIS_DISABLE = 21
'X Axis Pin Assignments
X_AXIS_DIRECTION = 22
X_AXIS_STEP = 23
X_AXIS_DISABLE = 24
'Additional X Axis Constants
X_AXIS_CYCLE_OFFSET = 15_000
X_AXIS_MIN_SPEED = 4_000 '15_000
X_AXIS_MAX_SPEED = 6_000 '20_000
X_AXIS_SPEED_RANGE = X_AXIS_MAX_SPEED - X_AXIS_MIN_SPEED
X_AXIS_RAMPING_RANGE = X_AXIS_SPEED_RANGE * 2
X_AXIS_RAMP_ADJUSTER = 4 '20
'Y Axis Pin Assignments
Y_AXIS_DISABLE = 25
Y_AXIS_STEP = 26
Y_AXIS_DIRECTION = 27
'Additional Y Axis Constants
Y_AXIS_CYCLE_OFFSET = 18_000
Y_AXIS_MIN_SPEED = 3_000 '15_000
Y_AXIS_MAX_SPEED = 4_000 '20_000
Y_AXIS_SPEED_RANGE = Y_AXIS_MAX_SPEED - Y_AXIS_MIN_SPEED
Y_AXIS_RAMPING_RANGE = Y_AXIS_SPEED_RANGE * 2
Y_AXIS_RAMP_ADJUSTER = 4 '20
'Full Duplex Serial
RX = 31
TX = 30
COMM_MODE = 0
COMM_BAUD_RATE = 115_200
'FAT Engine Assignment for unused pins or RTC. Please refer to the
'FAT16/FAT32 Full File System Driver Documentation for proper usage
NEG_ONE = -1
'Clock Frequency Equation
CLK_FREQ = ((_CLKMODE - XTAL1) >> 6) * _XINFREQ
'This is the setting for the minimal step pulse width.
HIGH_PULSE_WIDTH = CLK_FREQ / 1_000_000 'CLK_FREQ / 500_000
MIN_EXECUTION_TIME = CLK_FREQ / 21_500
VAR
'The following variable holds the G-Code file line characters. It has been
'established that the G-Code file used as a guide in this code, had a maximum
'file line character count of 72. To be on the safe side, the maximum file
'line size has been increased to 100.
BYTE bChBuffer[100]
'The following variables are intended to store G-Code into various fields.
'For more information about these fields, please refer to RepRap G Code
'Fields at: http://reprap.org/wiki/G-code . Additionally, these are the
'only known fields to be supported by the KISSlicer. Please note that the
'maximum character count may change in the future, because I am not exactly
'sure on bFieldElement_8 maximum character count, but support for 7 characters
'should be sufficient. The maximum character count should support a build
'volume area of 999.99mm(X) X 999.99mm(Y) X 999.99mm(Z).
BYTE bFieldElement_0[2] 'G Field
BYTE bFieldElement_1[3] 'M Field
BYTE bFieldElement_2[1] 'T Field
BYTE bFieldElement_3[3] 'S Field
BYTE bFieldElement_4[7] 'X Field - Floating Point
BYTE bFieldElement_5[7] 'Y Field - Floating Point
BYTE bFieldElement_6[7] 'Z Field - Floating Point
BYTE bFieldElement_7[7] 'F Field - Floating Point
'BYTE bFieldElement_8[7] 'E Field - Floating Point Temporary comment
BYTE bFieldElement_8[8] 'E Field - Floating Point
BYTE bValidFields[9]
'The following G-Code commands are currently the only known (by me) commands
'supported by the KISSlicer, therefore these are the only G-Code commands
'that I will currently support.
{
G1: Controlled Move
G Field - bFieldElement_0, X Field - bFieldElement_4,
Y Field - bFieldElement_5, Z Field - bFieldElement_6,
F Field - bFieldElement_7, and E Field - bFieldElement_8
filled during line parse
G20: Set Measurement Units To Inches
G Field - bFieldElement_0 filled during line parse
G21: Set Measurement Units To Millimeters
G Field - bFieldElement_0 filled during line parse
G28: Home Axes
G Field - bFieldElement_0 filled during line parse
G90: Set Absolute Positioning
G Field - bFieldElement_0 filled during line parse
G91: Set Relative Positioning
G Field - bFieldElement_0 filled during line parse
M82: Set E Codes Absolute
M Field - bFieldElement_1 filled during line parse
M83: Set E Codes Relative
M Field - bFieldElement_1 filled during line parse
M104: Set Extruder Temperature With No Wait
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
M106: Fan On
M Field - bFieldElement_1 filled during line parse
M109: Set Extruder Temperature And Wait
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
M107: Fan Off
M Field - bFieldElement_1 filled during line parse
M140: Set Bed Temperature
M Field - bFieldElement_1 and S Field - bFieldElement_3
filled during line parse
T0: Select Tool With No Fields
T Field - bFieldElement_2 filled during line parse
}
OBJ
SDCard: "SD-MMC_FATEngine.spin"
Memory: "SPI Memory Driver"
Terminal: "FullDuplexSerial"
Expander: "SimpleMCP23008"
PUB Main
WAITCNT(CLKFREQ * 1 + CNT)
DIRA[X_AXIS_DIRECTION]~~
DIRA[X_AXIS_STEP]~~
DIRA[X_AXIS_DISABLE]~~
DIRA[Y_AXIS_DIRECTION]~~
DIRA[Y_AXIS_STEP]~~
DIRA[Y_AXIS_DISABLE]~~
' TestXandY
' Parse
Test_MCP23008
PUB Test_MCP23008 | Value
'GP0 Menu Button
'GP1 Select Button
'GP2 Scroll Button
'GP3 Pause Button
'GP4 Back Button
'GP5 Exit Button
'GP6 Yellow LED
'GP7 Red LED
Expander.Initialize
Expander.Configure_IODIR_Register(%00111111) 'Configure the IODIR register to 2 outputs and 6 inputs
Expander.Configure_GPIO_Register(%10000000) '0 is low, GP7, OUTPUT - RED LED
' Expander.Configure_GPIO_Register(%01000000) 'Write to the GPIO register, which sets high or low . 0 is low, GP6, OUTPUT - YELLOW LED
REPEAT
Value := Expander.GetPinStates
IF Value == %10010000
Expander.Configure_GPIO_Register(%11000000)
ELSE
Expander.Configure_GPIO_Register(%10000000) '0 is low, GP7, OUTPUT - RED LED
PUB TestXandY
OUTA[X_AXIS_DIRECTION]~~
PulseTheStepPin(15000, X_AXIS_STEP, X_AXIS_RAMPING_RANGE, X_AXIS_SPEED_RANGE, X_AXIS_CYCLE_OFFSET, X_AXIS_RAMP_ADJUSTER)
WAITCNT(CLKFREQ / 8 + CNT)
OUTA[X_AXIS_DIRECTION]~
PulseTheStepPin(15000, X_AXIS_STEP, X_AXIS_RAMPING_RANGE, X_AXIS_SPEED_RANGE, X_AXIS_CYCLE_OFFSET, X_AXIS_RAMP_ADJUSTER)
OUTA[Y_AXIS_DIRECTION]~~
PulseTheStepPin(16000, Y_AXIS_STEP, Y_AXIS_RAMPING_RANGE, Y_AXIS_SPEED_RANGE, Y_AXIS_CYCLE_OFFSET, Y_AXIS_RAMP_ADJUSTER)
WAITCNT(CLKFREQ / 8 + CNT)
OUTA[Y_AXIS_DIRECTION]~
PulseTheStepPin(16000, Y_AXIS_STEP, Y_AXIS_RAMPING_RANGE, Y_AXIS_SPEED_RANGE, Y_AXIS_CYCLE_OFFSET, Y_AXIS_RAMP_ADJUSTER)
PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset, RampSpeedAdjuster) | RampingSteps, RunningSteps, CompensationSteps, Counter
IF TotalSteps > RampingRange
RampingSteps := SpeedRange
RunningSteps := TotalSteps - RampingRange
ELSE
RampingSteps := TotalSteps / 2
RunningSteps := TotalSteps // 2
CTRA[30..26] := %00100
CTRA[5..0] := StepPin
FRQA := 1
DIRA[StepPin]~~
Counter := CNT
CompensationSteps := 0
REPEAT RampingSteps
PHSA := -HIGH_PULSE_WIDTH
IF CycleOffset => MIN_EXECUTION_TIME
WAITCNT(Counter += CycleOffset -= RampSpeedAdjuster)
ELSE
WAITCNT(Counter += CycleOffset)
CompensationSteps++
REPEAT RunningSteps += CompensationSteps
PHSA := -HIGH_PULSE_WIDTH
WAITCNT(Counter += CycleOffset)
REPEAT RampingSteps -= CompensationSteps
PHSA := -HIGH_PULSE_WIDTH
WAITCNT(Counter += CycleOffset += RampSpeedAdjuster)
PUB Parse
'Start FullDuplexSerial
Terminal.Start(RX, TX, COMM_MODE, COMM_BAUD_RATE)
WAITCNT(CNT + (1 * CLKFREQ))
'Start FATEngine
SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE, NEG_ONE)
'Mount Partition
SDCard.mountPartition(0)
'Open G-Code File "MINIMUM.TXT" for reading
SDCard.openFile(STRING("MINIMUM.TXT"), "R")
REPEAT
'Fill bChBuffer with 0s
BYTEFILL(@bChBuffer, 0, 100)
'Fill bValidFields with 0s
BYTEFILL(@bValidFields, 0, 9)
'Grab file lines that are not comments or blank
GetNextValidFileLine
IF(bChBuffer[0] == 0)
QUIT
'Fill in the various field elements
FillFieldElements
Terminal.Str(@bChBuffer)
'Display the various field seperations
Terminal.Str(STRING("G="))
IF(bFieldElement_0 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_0)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("M="))
IF(bFieldElement_1 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_1)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("T="))
IF(bFieldElement_2 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_2)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("S="))
IF(bFieldElement_3 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_3)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("X="))
IF(bFieldElement_4 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_4)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Y="))
IF(bFieldElement_5 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_5)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("Z="))
IF(bFieldElement_6 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_6)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("F="))
IF(bFieldElement_7 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_7)
Terminal.Tx(32) {Space}
Terminal.Str(STRING("E="))
IF(bFieldElement_8 == 0)
Terminal.Str(STRING("NULL"))
ELSE
Terminal.Str(@bFieldElement_8)
Terminal.Tx(32) {Space}
'Seperate G-Code field values line by line
Terminal.Tx(13) {Carriage Return}
IF STRCOMP(bChBuffer + 0, STRING("M107"))
Terminal.Str(STRING("Fan Off"))
Terminal.Tx(13) {Carriage Return}
'Unmount Partition
SDCard.unmountPartition
'Stop FATEngine
SDCard.fatEngineStop
WAITCNT(CNT + (1 * CLKFREQ))
'Stop FullDuplexSerial
Terminal.Stop
PUB GetNextValidFileLine
REPEAT
'Grab a file line as a string
SDCard.readString(@bChBuffer, 99)
'Keep repeating while the following conditions are met,
'because we don't want or need these lines beginning with
'these characters.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
QUIT
PUB FillFieldElements | Char, Index, Field_Element, ValueIndex
'Empty the field elements of any possible data
BYTEFILL(@bFieldElement_0, 0, 2)
BYTEFILL(@bFieldElement_1, 0, 3)
BYTEFILL(@bFieldElement_2, 0, 1)
BYTEFILL(@bFieldElement_3, 0, 3)
BYTEFILL(@bFieldElement_4, 0, 7)
BYTEFILL(@bFieldElement_5, 0, 7)
BYTEFILL(@bFieldElement_6, 0, 7)
BYTEFILL(@bFieldElement_7, 0, 7)
'BYTEFILL(@bFieldElement_8, 0, 7) Temporary
BYTEFILL(@bFieldElement_8, 0, 8)
Index := 0
ValueIndex := 0
'Parse file line into seperate field values. Various fields will remain empty
REPEAT
Char := bChBuffer[Index]
'Exclude these characters from becoming part of are field entries
IF(Char == 13 {Carriage Return} OR Char == 10 {Line Feed})
QUIT
ELSEIF(Char == 32 {Space})
Index++
ValueIndex := 0
NEXT
ELSEIF(Char == 71 {G Character})
'Set current field element
Field_Element := 0
bValidFields[0] := 1
Index++
NEXT
ELSEIF(Char == 77 {M Character})
'Set current field element
Field_Element := 1
bValidFields[1] := 1
Index++
NEXT
ELSEIF(Char == 84 {T Character})
'Set current field element
Field_Element := 2
bValidFields[2] := 1
Index++
NEXT
ELSEIF(Char == 83 {S Character})
'Set current field element
Field_Element := 3
bValidFields[3] := 1
Index++
NEXT
ELSEIF(Char == 88 {X Character})
'Set current field element
Field_Element := 4
bValidFields[4] := 1
Index++
NEXT
ELSEIF(Char == 89 {Y Character})
'Set current field element
Field_Element := 5
bValidFields[5] := 1
Index++
NEXT
ELSEIF(Char == 90 {Z Character})
'Set current field element
Field_Element := 6
bValidFields[6] := 1
Index++
NEXT
ELSEIF(Char == 70 {F Character})
'Set current field element
Field_Element := 7
bValidFields[7] := 1
Index++
NEXT
ELSEIF(Char == 69 {E Character})
'Set current field element
Field_Element := 8
bValidFields[8] := 1
Index++
NEXT
ELSE
IF(Field_Element == 0)
'G Field element character
bFieldElement_0[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 1)
'M Field element character
bFieldElement_1[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 2)
'T Field element character
bFieldElement_2[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 3)
'S Field element character
bFieldElement_3[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 4)
'X Field element character
bFieldElement_4[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 5)
'Y Field element character
bFieldElement_5[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 6)
'Z Field element character
bFieldElement_6[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 7)
'F Field element character
bFieldElement_7[ValueIndex] := Char
ValueIndex++
Index++
NEXT
ELSEIF(Field_Element == 8)
'E Field element character
bFieldElement_8[ValueIndex] := Char
ValueIndex++
Index++
NEXT
PUB ElementConversion
DAT
Tag1 BYTE "M107",0
Comments
Okay I downloaded the object and took a brief look at it, and I must say that it caught my interest, however I will have to study it quite a bit more to get a thorough understanding.
As I see it, either your object or a similar object, can be used for parsing both the G-Code files and Configuration files, and if there are any discrepancies in the management of data, perhaps a simple switch to go from G-Code file parsing to Configuration file parsing.
I suppose a well defined parsing stategy is of an absolute necessity, but before that can occur, a decision must be made as to whether floating point support should be added. And of course floating point support will most likely be determined by the algorithms being utilized. So I suppose, my first goal should be to find the appropriate algorithms and movement strategies.
Anyhow, I liked what I saw in your object and it definitely provided food for thought. Thanks for sharing.
Bruce
I assume with serial LCD you mean a text-display with 2 or 4 lines with 16 or 20 characters, correct? This makes a maximum number of 80 characters for the full LCD screen. I also assume that the flash is not much slower than the SD card (where I usually store my strings). So, we talk about a reading speed of at least some kB per second?
Question1: How fast can you read?
Question2: How fast can you update the LCD without getting the text to blur?
So, my answer to your doubts is: The propeller / flash will beat you and your LCD ;o)
Machine parameters in flash:
Well, ok, got your point. Maybe a mixture would be the best. Allow an easy update of the parameters via config-file on SD card, but store a set in flash as well. If the file on SD is newer it will be parsed freshly and stored in flash. If the file has already been read, skip the parsing and use the prepared set in flash. The advantage: If the SD is removed the propeller can still boot with a valid set of parameters.
2 or 3 images:
Well ... there really is a memory problem with bigger programs. As I already mentioned the FAT32 driver, which allows directory access (Kyes driver), already needs a bunch of HUB RAM. So, it is very unlikely that all the things you want to do fit into a single 32kB HUB RAM image - well at least all the things that I want to do will not fit. On the other hand it is very unlikely that all parts of the program are needed at the same time. This allows to split the program into parts which are placed in different images (*.bin -files). So, if you select something in the menu, the according image is loaded and started. It is possible to have a COG loading another image and start it (see Femto-Basic). Here we also talk about a fraction of a second.
It is also possible to load a *.bin file of a program into a running environment (as long as there is enough free HUB-RAM) and start it, pass parameters forth and back and let it access for example the serial driver for the LCD. Already did that.
COG drivers in EEPROM:
With this subject for the time being I'd like to refere you to my BLOG entries. Only a few more words so far: I already mentioned this approach in several threads, but I think nobody was interested enough to follow this concept. But for me it is best practice and all of my propeller boards have their drivers stored in upper EEPROM. In short words: we have 7 COGs to fill with other stuff than the main program. Usually drivers go there. If all 7 COGs have different tasks and the needed drivers use a good amount of COG RAM you have 7 PASM blocks with a max. size of 2kB (512 longs) that are occupied (total up to 14kB). What I do is, store those PASM blocks in upper EEPROM (with 32kB you can have 15 PASM drivers) and have
a) a HUB RAM buffer of 2kB
b) a SPIN code sequence which loads the needed drivers and start the COGs in a sequence
After initializing the COGs the HUB RAM buffer can be used for other things. So, in total the gain is up to 14kB minus some hundred bytes for the load and start sequence).
A side-effect is, that the program using this technique can be run on several different hardware setups as long as the driver is available, because the drivers are stored in EEPROM with all the settings (for example which pins to use for the SD card) already being made in the storing process. So, the main programs become a little bit more hardware independent.
Did I say a few more words? ;o)
SWAP:
Yep, another concept I really like to use regularly. Accessing the swap file is easier and faster than FAT file access. Keyword is VirtualMemory. Also mentioned several times here in the forum. CassLan utilized it to write a text editor (Preditor).
Ok ... I rechecked the code I wrote for proof of concept of the motion control. Unfortunately it also uses the EEPROM driver concept. But it is fairly easy to replace the FullDuplexSerial_NP (NP stands for NO PASM ;o) with an original FullDuplexSerial and deactivate the code for loading the driver from EEPROM.
The ramping stuff would simply be done in the repeat loop of the function stepper. The current code for the axes only do a bresnham line algorithm. So, it should already be good for g-code which only contains lines.
StepperTest - Archive [Date 2014.06.04 Time 21.02].zip
I wrote a response yesterday morning, but accidentally closed the window for my reply:( Later this morning or later today, I will type out another response.
I apologize for not responding yet, but I have been going through some troubles.
I wrote some code for parsing the G-code file, and I got to the point of seperating all of the field entries into seperate data entries. The code is not exactly what I envisioned, but it was working good. Anyhow, I was just adding some comments, so that I could upload the code here, and it stopped working. After fiddling with my code for hours, I finally broke it down to the bare minimum code, only to discover there is now something wrong in between the Propeller Memory Card and SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), and even perhaps much sooner, such as SDCard.fatEngineStart(DO, CLK, DI, SD_CS, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE, NEGATIVE_ONE)
At this point, I am not exactly sure what went wrong. Perhaps a loose breadboard connect, corrupted SD card, or even a defective PBOE.
If I comment out SDCard.openFile(STRING("CAL_HOLD.TXT"), "R"), both Terminal.Str(STRING("Hello")) output. If I don't comment out the call, only the top Hello outputs.
Good thing, I saved most of my code in a backup copy before destroying all of my hard work
EDIT: After determining that my code was not the source of my troubles (hours of work), I pulled the SD card and checked it on a PC, only to discover that my G-Code file no longer existed. So I reformatted the card and reinstalled the file. The code above now gives me both Hellos I am not exactly sure how the file got deleted though.
EDIT: Okay, so it got deleted again, after editing the the following line from ANDs to ORs.
IF(bChBuffer[0] <> 59 {; Character} AND bChBuffer[0] <> 13 {Carriage Return} AND bChBuffer[0] <> 10 {Line Feed})
Apparently, just guessing at this point, the ORs cause the program to hang, and by shutting down the PBOE or reloading EEPROM, the partition is not being properly unmounted. There is a comment in the source code provided with the FAT16/FAT32 Full File System Driver Documentation v1.0, which states:
I suppose file deletion was one of those bad things
Here is a piece of source code that will open up a G-Code file and parse it line by line, seperating the values into 12 different fields, being designated by 12 different letters common to reprap G-Code. Source: http://reprap.org/wiki/G-code
The various fields are G, M, T, S, P, X, Y, Z, F, R, E, and N.
In additon to showing the main file, I have included an attachment to maintain the file format and to include the objects utilized.
I am sure you are correct, however I never got that far with my SPIN programming.
My goal at this point is to make a working strategy from small bits and pieces, which can later be optimized with methods such as you suggest. I still have not determined the actual data size required for each field element, whereas some of the data fields will only have three characters or less.
Still trying to figure it out, but thanks for your suggestion. I will take a much deeper look at this, when I get further along.
Bruce
After closely examining the G-Code file, created by KISSlicer, it was determined that only the following commands were used to create the 3D print.
G21: Set Units to Millimeters - Example: G21
G28: Home - Example: G28
G90: Set to Absolute Positioning - Example: G90
M83: Set E codes relative
M104: Set Extruder Temperature (Fast) - Example: M104 S190
M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
M109: Set Extruder Temperature and Wait - Example: M109 S185
M107: Fan Off Used in g-Code
M140: Set heated bed temperature - Example: M140 S55
So I am assuming, that I can reasonably assume that these are the only actual commands that I need to run a 3D printer. However to make slicing and machine setup easier, I suppose I should also include the counterparts to some of the above commands, such as:
G91: Set to Relative Positioning - Example: G91
M82: Set E codes absolute
Additionally, some commands were used in the G-Code that were not present in the list of Post #6. Considering that I am making a lot of assumptions, I might as well keep assuming, so I assume that the required G-Code commands are determined by the slicer utilized. Additionally, I would imagine that many developers have taken pain staking measures into placing simple machine functions into proper G-Code format, which I also imagine takes up a fair bit of memory. Take for example the following command:
I can only imagine how much time, effort, and memory it would take to incorporate this command into the code, when a simple WAITPEQ monitoring switch input should be able to provide a machine stop. However an orderly machine stop may be a different issue.
In the case of a Propeller driven 3D printer, I truly believe the command and coding should be kept to a bare minimum, until it has been determined that there is room for expansion.
A previous employer always told me to tackle the known problems first. With this concept rearranged a little in my mind, this is what I know:
G20: Set Units to Inches - Example: G20
G21: Set Units to Millimeters - Example: G21
G28: Home - Example: G28
G90: Set to Absolute Positioning - Example: G90
G91: Set to Relative Positioning - Example: G91
M82: Set E codes absolute
M83: Set E codes relative
M104: Set Extruder Temperature (Fast) - Example: M104 S190
M106: Set Fan Speed / Set Device Power - Example: M106 S120 Simply M106 in G-Code
M109: Set Extruder Temperature and Wait - Example: M109 S185
M107: Fan Off Used in g-Code
M140: Set heated bed temperature - Example: M140 S55
EDIT: As it pertains to the source code of Post #66, Fields P, R, and N, will no longer be supported, because apparently KISSlicer does not support them.
1) Code space - At only 496 words of available code and data space per cog I would be forced to use an external code memory model. For PropGCC this would be XMMC which leads to...
2) External code memory model performance hit - I get the impression that employing the PropGCC XMMC memory model hits execution time by a factor of x30 (or thereabouts) for I2C EEPROM. It also requires the use of an instruction cache for each cog taken from Hub memory.
3) Delays in accessing global Hub resources - It is quite likely that globals in Hub memory would be heavily used and the cog synchronisation delays would impact performance considerably.
3) More Propellers? - I considered increasing the number of Propellers from 1 to 4 (in order to increase the distributed code space and keep code internal) and link these master-slave fashion using SPI but it increases complexity considerably.
4) Hardware interface overhead - With the Propeller's lack of on-chip peripherals you need to code hardware interfaces e.g. UART and SPI. This uses valuable resources that the application itself requires.
The Propeller has a unique architecture which suits smaller, demanding applications. Its lack of a good external memory interface is a severely limiting factor in larger applications which, I think, necessitates the use of multiple Propellers. Given that I already had a dual-PIC solution on the drawing board, which I am sure will be practical, I think I shall pursue that avenue.
Regards,
Neil Darlow
I found a nice web page today http://www.repetier.com/documentation/repetier-firmware/rf-installation/
Implementing a structure that defines data relationships reduces the amount of conditional statements in the code base. Besides, it is good design strategy to define this structures upfront before the code base becomes too complex.
That looks a whole lot better than I am capable of at this point in time In fact, it looks real good. As mentioned earlier, I still consider myself a beginner in SPIN programming, and my skills are no where near your skills.
Just struggling my way through it, and trying to get a working model.
Bruce
You might want to take a look here: https://github.com/rosco-pc/propmaker
It has a fully working g-code parser, but no implementation of the actual commands. I actually lost interest after awhile
Looks interesting..... However, I am going to attempt it from scratch. I want to attempt the machine control with my own interpretation of the G-Codes and applying my own strategy for a positioning queue. However, I am more than willing to borrow the necessary algorithms
By looking at the included objects of my example, I suppose "from scratch" would not be an accurate definition
But thanks for sharing, because someone may want to use that information to their benefit.
Bruce
The switch to a smaller file is only temporary and I will let you know when I switch back to the original file.
For those of you who might be following along or developing your own version, I am including a copy of that minimalistic file.
EDIT: This file made an error. See the next post.
To temporarily resolve this issue, I did the following:
BYTE bFieldElement_8[8] 'E Field - Floating Point
'BYTEFILL(@bFieldElement_8, 0, 7)
BYTEFILL(@bFieldElement_8, 0, 8)
Since you are participating, let me throw my strategy at you for some feedback.
Cog 0:
Although I am not 100% positive, because I have not reached the math yet, I believe one cog can parse and compute far faster than the machine can possibly move. Therefore I want to read one line and compute and write the results to a predetermined location in SRAM, for a maximum of 10 lines to start. Monitor SRAM locations for deletion, and upon deletion, add another set of computations for a line.
Cog 1:
Read the computations made from Cog 0 per active line at the predetermined locations in SRAM. Initiate proper machine procedures, for simple commands such as M106, simply perform the task, and delete SRAM contents for that line procedure. For complex procedures such as G1, initiate several new cogs 2, 3, 4, & 5 to perform movement, during which time, Cog 1 could have read the appropriate SRAM location, deleted the contents, and be waiting with the computations when Cogs 2, 3, 4, & 5 return.
Cog 2:
X motor movement
Cog 3:
Y motor movement
Cog 4:
Z motor movement
Cog 5:
E motor movement
Of course Cogs 2, 3, 4, & 5 are arbitrary, since there will be times when it will not be necessary for all these motors to move, so next avail cog.
As far as I can foresee, the only downside would be a small waiting period to initially fill the SRAM in Cog 1, and a small waiting time in Cog 1, when simple tasks are performed, because there will have to be a following read to SRAM for new movement computations or machine procedure. However, since there are several cogs still available, the read of Cog 1 could be passed to another cog, while Cog 1 performs another read.
I know there are those that say the motor movement can be performed in one cog, but right now, this is my strategy.
http://gist.github.com/nitsky/6146728
EDIT: Here is another article and source code for 3D printing
http://ravehgonen.wordpress.com/2013/02/19/stereolithography-3d-printing-algorithms-and-thoughts/
Bresnham is exactly what is in the StepperTest.zip
I totally agree, but it was a first step. Now comes the hard part, but before we can work on the hard part, a strategy or several strategies must be outlined. One of these rough strategies was outlined in Post #82, however this is very far from a complete solution.
Before I go any further with this discussion, let me just state for the record that I am no mathematical genius. In fact, math is my weakest link, but I have managed to struggle through life so far with this handicap.
Many 3D print strategies rely on a constant speed for each axis, however I see this as an easy way out, just to get operational software and printers. Several years ago, when I received my first Gecko G251 stepper drives, I was using traditional SPIN to run my steppers, but the speed of these motors was way to slow for my satisfaction. I then started a thread about using counters to run these stepper drives. Several people participated in the discussion and I am thankful to all of them, however between MagIO2 and JonnyMac, a snippet was produced which really made my stepper motors "ZING", from that point on, I have periodically experimented with counter based stepper drivers. The stepper drivers have developed to this stage:
- Start the motor at a pulse rate which is capable of overcoming the moment of inertia.
- Keep decreasing the time between pulses until we lose necessary torque (MAXIMUM SPEED) for axis movement. - RAMP UP
- Maintain maximum speed until ramp down becomes necessary - RUN MAINTAINED SPEED
- Keep increasing the time between pulses until we reach the stopping point - RAMP DOWN
By using the longest travel of an axis, like in the Bresenham algorithm, I believe profiles could be determined for all axes and run the steppers with ramping by use of the counters. As mentioned in Post #82 of this thread and in various other threads, all steppers would run in seperate cogs, with seperate counters.I suppose the main question is, can a straight line be computed by time? I think it can, but then comes the issue of the feedrate or F field, so I am really not sure how it all plays out. To be honest, I really do not believe that I have a firm grasp on F just yet.
Please don't be discouraged.... I am sorry I still have not responded..... I am just trying to figure all this out.
Up for a new challange ?
Im currently also building an firepick delta , pick&place /3d print/ solderpaste dispencer/ chocolate printer ( hihi) . machine , it could do allot more
anyway , we have been using an atmega1284p running with an 20mhx crystal , marlin driver loaded .
but its just not handeling the inverse kinematics good enought to get the precision needed
currently there there is an driver change going on , to go from marlin to repeater firmware for the motor controller board. still long way from working
but I always had the idea our little friend (the propeller ) could do a way better job .
Your lined out approuche below is kinda what I think we also need.
But im also starting from scratch, just to be able to fit it exactly on our needs and make a custom board for it . looking if it may be an good alternative to our current , or our future probable (ARM ) design
its a opensource project , wich was best known tru hackaday . there is a page there with allot of info on it
also github pages with all the till now released files and sources , google group where the chatting happens ( its an active community) . so its going pretty good with progress
I have propeller chips lying around to do actual testing and hacking ,
drivers used are polou DRV8825 wich are set and shoudl stay at 1/16 microsteps .
the inverse kinematics is what killing the slow 8 bit processor
think the splitting up of the math in different cogs , and letting couters run them so we can push them pretty hard somehow should made a great driver
Iill take some time reading the old post , but would love some links code snippets to get some steppers moving etc . all help appriciated
Igor
https://github.com/firepick-delta
https://groups.google.com/forum/?hl=en#!forum/firepick
http://hackaday.io/project/963-300-pick-and-place-3d-printer
Image Links
http://imgur.com/sJytsPP
http://imgur.com/XeE8o4A
http://imgur.com/WzkbjCv
I have just recently started to toy around with the CNC controller discussed in this thread: http://forums.parallax.com/showthread.php/155404-Input-Needed-Combining-Propeller-Proto-Board-Prop-DIP-40-and-ADC-for-3D-Printer. In additon to testing various aspects of the controller, I have also been testing X and Y axes. Both X and Y are driven by Gecko G251 stepper drives, which have 10 microsteps. Although I do no not have any coordinated movement at this time, the code below contains all the constants for the controller board pins, as well as some miscellaneous code, which includes stepper movement code. That code is currently setup for the G251 drives, but it should be able to drive any drivers that have step, direction, and disable inputs, providing the correct value for the constants are given. The main method for driving the steppers is called PulseTheStepPin. More information about this method can be found at this location: http://forums.parallax.com/showthread.php/132373-PulseTheStepPin-revisited-Questions-answers-problems-solutions
There is also other information available elsewhere, which can most likely be found with a seach of the function name.
Hope this helps