This is just one of several manufacturing projects that I am working on.
Steps is a program that converts from a limited subset of RS-274 (i.e. CNC "G-codes") to much simpler timed step and direction file. In other words, the output is basically a sequence of commands that specify that at time T, please increment or decrement the following stepper motors by one position. All of the issues with accelleration, maximum velocity, backlash, etc. are taken care of by the software. What this means is that the controller that uses these codes can be quite inexpensive and simple.
Below is a small screenshot of the jog program used to move the mill table around to set things up.
I won't try and explain everything, since it was thrown together in a real hurry and will undoubtly change as I get more experience using it.
The input file format is a restricted subset of RS-274. The following codes are accepted:
As usual for RS-274 comments are enclosed in
(...). The machine
and controller configuration information is
encoded in comments of the form:
The following configuration options are available:
- cycles specifies the desired baud rate for machine controller.
- cycles specifies the number of cycles per second that the controller is operating at. Fundamentally, this specifies the maximum stepping rate that the controller can (theoretically) achieve. In practice, the actual machine may not be able to come any where close to this performance level.
- path specifies the path to I/O port to use to talk to the controller.
- backlash is a floating point number that specifies the the amount of backlash on the axis. axis must be one of
- acceleration is a floating point number that specifies the maximum acceleration for the axis and it is measured in units/sec2. axis must be one of
- length is a floating point number that specifies the length of the axis in units of length. axis must be one of
- feedrate is a floating point number that specifies the maximum velocity for the axis measured in units/min. axis must be one of
- steps is an integer that specifies the number of steps per unit for the axis. axis must be one of
Below is an example of configuration for a small table top milling machine:
(Sherline Mill) (steps:Cycles: 100000) (steps:Baud_Rate: 115200) (steps:Path: /dev/ttyUSB0) (X Axis) (steps:X_Acceleration: 1) (steps:X_Backlash: 0) (steps:X_Length: 12) (steps:X_Rapid_Feedrate: 15) (steps:X_Steps: 8000) (Y Axis) (steps:Y_Acceleration: 1) (steps:Y_Backlash: 0) (steps:Y_Length: 8) (steps:Y_Rapid_Feedrate: 15) (steps:Y_Steps: 8000) (Z Axis) (steps:Z_Acceleration: 1) (steps:Z_Backlash: 0) (steps:Z_Length: 8) (steps:Z_Rapid_Feedrate: 15) (steps:Z_Steps: 8000)
The basic concept behind the steps file format is that a dedicated microcontroller generates interrupts at regular intervals. At each interrupt interval, the microcontroller can increment, decrement, or leave alone each axis -- X, Y, Z, or A. The steps software reads in RS-274 code and figures out how what the correct sequence and time of pulses should be for eaxh axis. All of the heavy math is done on the host computer and the CNC controller can be quite simple.
The controller has two delay registers called D and P. The D register is the current delay register and the P register is the previous delay register. For the purposes of writing into these registers, they are partioned into A, B, C, and D as shown below:
where the 4-bit quantity aaaa corresponds to the high order 4 bits, bbbb corresponds to the next 4 bits, cccc corresponds to the next 4 bits, and dd corresponds to the low order 2 bits. The SetA command is used to set aaaa of D, the SetB command is used to set bbbb of D, the SetC command is used to set cccc of D, and the Step command is used to set dd of D.
D and P Registers 13:10 9:6 5:2 1:0 0 0 a a a a b b b b c c c c d d 13:12 11:8 7:4 3:0
The output is a sequence of commands of the following format:
Bit Cycles Description 7 6 5 4 3 2 1 0 Step 0 d d c a x y z D + 1 Set D<1:0> to dd. If c is 1, clear D<13:2>. After delaying by D cycles step the axes marked with a 1 in axyz. SetA 1 0 0 0 a a a a 0 Set D<10:13> to aaaa. SetB 1 0 0 1 b b b b 0 Set D<9:6> to bbbb. SetC 1 0 1 0 c c c c 0 Set D<5:2> to cccc. SetDirection 1 0 1 1 a x y z 0 Set the direction for the A, X, Y, and Z axes to axyz where 1 is increment and 0 is decrement. Reserved 1 1 0 r r r r r Reserved Reserved 1 1 1 0 r r r r Reserved Reserved 1 1 1 1 0 r r r Reserved Reserved 1 1 1 1 1 0 r r Reserved Reserved 1 1 1 1 1 1 0 r Reserved Start 1 1 1 1 1 1 1 1 0 Mark end of current chunk.
The overall design of the protocol between the host and the controller is to allow the host to remain in complete control over what is going on. The protocol is designed to be resiliant enough to recover even if an occasional byte is dropped.
The controller contains a static memory buffer that is either 128K, 256K, or 512K bytes in size. The primary purpose of the protocol is reliably transfer commands from the host into the controller memory buffer. It is assumed that the communications channel is relatively reliable, so we are just trying to uncover occasional errors. New data downloading can proceed in parallel with command that the controller is processing out of the buffer. The host computer is responsible for ensuring that no buffer overrun condition occurs.
The protocol downloads commands as a block of data. A pointer is kept to the begining of the data block. As each byte of data is downloaded, a simple checksum is computed. Eventually, the host processor decides to ask for the checksum back. If the checksum is correct the, data block is committed; otherwise, the insertion point is rolled back and the block is retransmitted.
In addition, there are two 3-bit chunk counters. The current chunk counter specifies the number (modulo 8) of the chunk that is being processed. The enabled chunk counter specifies the number (modulo 8) of the chunk that is allowed to be processed. Thus, the host controller, can download up to 7 blocks and enable them. The controller is idle whenever the two counters are equal.
Bit Description 7 6 5 4 3 2 1 0 Memory Command Send 0 d d c a x y z Store into Memory Buffer Memory Command Send 1 0 x x x x x x Store into Memory Buffer Control Low Set Send 1 1 0 0 l l l l Set low 4 bits of "control" byte to llll. Control High Set Send 1 1 0 1 h h h h Set high 4 bits of "control" byte ot hhhh. Address Low Set Send 1 1 1 0 0 0 0 0 Set Address<7:0> to control. Return llll llll the value that was set. Receive l l l l l l l l Address Middle Set Send 1 1 1 0 0 0 0 1 Set Address<15:8> to control. Return mmmm mmmm the value that was set. Receive m m m m m m m m Address High Set Send 1 1 1 0 0 0 1 0 Set Address<23:16> to control. Return hhhh hhhh the value that was set. Receive h h h h h h h h Static RAM Get Send 1 1 1 0 0 0 1 1 Return ssss sss the value of at address in the static RAM. Receive s s s s s s s s RAM Get (Unimplemented) Send 1 1 1 0 0 1 0 0 Read address from microcontroller RAM and and return as dddd dddd Receive d d d d d d d d Memory In Low Get Send 1 1 1 0 0 1 0 1 Return Memory_In<7:0> as llll llll Receive l l l l l l l l Memory In Middle Get Send 1 1 1 0 0 1 1 0 Return Memory_In<15:8> as mmmm mmmm Receive m m m m m m m m Memory In High Get Send 1 1 1 0 0 1 1 1 Return Memory_In<23:16> as hhhh hhhh Receive h h h h h h h h Memory Out Low Get Send 1 1 1 0 1 0 0 0 Return Memory_Out<7:0> as llll llll Receive l l l l l l l l Memory Out Middle Get Send 1 1 1 0 1 0 0 1 Return Memory_Out<15:8> as mmmm mmmm Receive m m m m m m m m Memory Out High Get Send 1 1 1 0 1 0 1 0 Return Memory_Out<23:16> as hhhh hhhh Receive h h h h h h h h Checksum Low Get Send 1 1 1 0 1 0 1 1 Return Checksum<7:0> as llll llll Receive l l l l l l l l Checksum High Get Send 1 1 1 0 1 1 0 0 Return Checksum<7:0> as llll llll Receive h h h h h h h h Chunk Currenth Get Send 1 1 1 0 1 1 0 1 Return Chunk Current; as cccc cccc Receive c c c c c c c c Chunk Enabled Get Send 1 1 1 0 1 1 1 0 Return Chunk Enabled; as eeee eeee Receive e e e e e e e e Synchronize Send 1 1 1 0 1 1 1 1 Return 1010 0101 to synchronize return stream Receive 1 0 1 0 0 1 0 1 Chunk Enabled Increment Send 1 1 1 1 0 c c c Commit chunk ccc to be processeed. Clear checksum. Advance rollback pointer. This command is idempotent. Return 0x81 on success and 0x80 on failure. Receive 1 0 0 0 0 0 0 x Commit Data Block Send 1 1 1 1 1 1 0 0 Comment current data block. Clear checksum. Advance rollback pointer. Advance rollback pointer. Return 0xa5. Receive 1 0 1 0 0 1 0 1 Rollback Data Block Send 1 1 1 1 1 1 0 1 Rollback the Data Block. Clear the checksum. This command is idempotent. Return 0xaa Receive 1 0 1 0 1 0 1 0 Read Chunk Counters Send 1 1 1 1 1 1 1 0 Return the current chunk counter ccc and eee and the end chunk counter eee. Receive 0 0 c c c e e e End of Chunk Send 1 1 1 1 1 1 1 1 End of Chunk marker stored into buffer
The command line for steps is:
-T] cnc_file ...
- Generate an output summary on standard output that summarizes elapsed time and the amount of travel on each axis.
- Trace the bytes that will be sent to the buffer controller.
- Trace the RS-274 comments that are processed.
- Trace the RS-274 G and M codes.
- Open communcations port that is embedded in the .cnc file.
- Generate an I/O file for Wayne's PIC simulator.
- Trace times.
- cnc_file ...
- cnc_file is a list of one or more files containing RS-274 commands as described in the Input File Format section. The suffix of each cnc_filemust be
In order to interactively test the firmware, the following command line interface was developed:
- x|X|y|Y|z|Z|a|A [count]
- Increment/decrement the A/X/Y/Z axis by [count]. This triggers automatic sending.
- dump [address]
- Dump 8 locations from the static RAM staring at address.
- Send the current buffer. This only works when the x|X|y|Y|z|Z|a|A commands are not auto sending.
- go chunk
- Increment the current chunk to trigger stored commands.
- Commit the current buffer.
- Rollback the current buffer.
- Delay value
- Set current delay to value.
The code is written in my own programming language called STIPPLE. The source code currently resides in the following files:
- A manual tester with terminal interface.
- Main body of library code.
- The command line interface to the library.
- The graphical user interface to the library.
Currently, I am not keeping the code under strict version control. The lastest version of manual, steps_gen and jog are available for Linux only. Be sure to set the execute bit (i.e. "chmod +x steps_gen jog") after you download it.