The USB CNC project ported
My board will be ready soon and this will be the first project for it. Because pinout is different than the one from first version, it needs also a new hardware(shield), not only the firmware. So, it needed a separate html page.
Board compatibility
Schematic: Compatible with all FreeJALduino boards.
Firmware: Compatible with FreeJALduino and FreeJALduino5 boards
The stylesheet
This will be in top of every page using this board, as a reminder and easy access.
The schematic
The firmware
-- Project: XYZ CNC Router on USB using FreeJALduino board
-- Author: Vasile Guta-Ciucur (funlw65)
-- License: Code released under New BSD license
-- For jallib libraries, see de licence inside jal.zip
-- This is first variant, where steppers are connected to the board.
-- No gcode interpreter inside, sorry - this is left for PC Host App.
include freejalduino4 -- FreeJALduino4 pinout layer for the last board model
-- include libraries
include usb_serial
include print
-- include format
include delay
-- setup()
-- var and const definitions
const byte str1[] = "XYZCNC-USB Firmware Nov17, 2009\r\n"
const byte str2[] = "Copyright 2009 by Vasile Guta-Ciucur.\r\n"
const byte hsteps[] = { -- half-steps buffer
0b_0000_0001,
0b_0000_0011,
0b_0000_0010,
0b_0000_0110,
0b_0000_0100,
0b_0000_1100,
0b_0000_1000,
0b_0000_1001 }
const byte fsteps[] = { -- full-steps buffer, high torque
0b_0000_0101,
0b_0000_0110,
0b_0000_1010,
0b_0000_1001,
0b_0000_0101,
0b_0000_0110,
0b_0000_1010,
0b_0000_1001 }
var byte csteps[ 8 ] -- this array will contain settings for
-- half or full seps, according to "step type" jumper.
var byte speed_s[ 36 ] -- string for speed setup
var byte movement_s[ 36 ] -- string for xyz movement
var byte speed_str[ 11 ] -- buffer for speed value
var sbyte memx, memy, memz -- current step for x,y,z axis
var byte i, j -- used as index for cycles
var dword step_speed -- speed per step in microseconds
var byte t_delay -- the type of delay (micro, milli, nano)
-- end var and const definitions
-- procedures and functions
-- convert string to dword
function strtodec(byte in str[ 11 ]) return dword is
var dword tempres
var byte i
--
tempres = 0
i = 0
repeat
tempres = tempres * 10 + (str[i] - "0")
i = i + 1
until str[i] == 0
return tempres
end function
-- process setup string and set the speed
procedure process_and_set is
i = 0
t_delay = speed_s[i]
i = 1
repeat
speed_str[i - 1] = speed_s[i]
i = i + 1
until speed_s[i] == "/"
speed_str[i - 1] = 0
step_speed = strtodec(speed_str)
usb_serial_flush()
end procedure
-- check the limits or stop pressed
procedure emerge is
if (D11 == LOW) then -- if CRASH or STOP or PAUSE
D12 = LOW -- power off steppers
D18 = HIGH -- power on CRASH LED indicator
usb_serial_data = "W" -- send warning char "W"
repeat -- repeat ...
usb_serial_flush() -- (we must keep USB conn. active)
until D0 == LOW -- ... till "Resume" switch is pressed
-- or board is reset because you have a limit reached
D18 = LOW -- switch off CRASH LED
D12 = HIGH -- power on steppers
usb_serial_data = "R" -- send "Resume" char "R"
usb_serial_flush()
end if
end procedure
-- end check the limits or stop pressed
-- move one step on xyz axis, both directions, and then,
-- check if limit reached or stop pressed
procedure x_step_up is
memx = memx + 1
if memx > 7 then
memx = 0
end if
PORTA_low = csteps[memx]
emerge()
end procedure
procedure x_step_down is
memx = memx - 1
if memx < 0 then
memx = 7
end if
PORTA_low = csteps[memx]
emerge()
end procedure
procedure y_step_up is
memy = memy + 1
if memy > 7 then
memy = 0
end if
PORTB_high = csteps[memy]
emerge()
end procedure
procedure y_step_down is
memy = memy - 1
if memy < 0 then
memy = 7
end if
PORTB_high = csteps[memy]
emerge()
end procedure
procedure z_step_up is
memz = memz + 1
if memz > 7 then
memz = 0
end if
PORTB_low = csteps[memz]
emerge()
end procedure
procedure z_step_down is
memz = memz - 1
if memz < 0 then
memz = 7
end if
PORTB_low = csteps[memz]
emerge()
end procedure
-- end one step on xyz axis, both directions
-- process movement string and move axis
procedure process_and_run is
var bit dirx, diry, dirz -- direction on x,y,z axis (0,1)
var byte stage
-- containers for the number of steps as string, null terminated
-- remember me to make routines to clean these arrays - DONE!
var byte x_step_str[ 11 ]
var byte y_step_str[ 11 ]
var byte z_step_str[ 11 ]
var dword m, k, x_steps, y_steps, z_steps -- the number of steps on xyz axis
usb_serial_flush()
-- initialize the number of steps on xyz axis
x_steps = 0
y_steps = 0
z_steps = 0
-- preparing the strings
-- for 11 using i loop
-- x_step_str[i] = 0
-- y_step_str[i] = 0
-- z_step_str[i] = 0
-- end loop
i = 0
j = 0
stage = 1
-- extract the numbers and direction for each axis
repeat
if stage == 1 then
repeat
if movement_s[i] == "X" then
dirx = 1
elsif movement_s[i] == "x" then
dirx = 0
else
x_step_str[j] = movement_s[i]
j = j + 1
end if
i = i + 1
until ((movement_s[i] == "Y") | (movement_s[i] == "y"))
stage = 2
x_step_str[j + 1] = 0
j = 0
end if
if stage == 2 then
repeat
if movement_s[i] == "Y" then
diry = 1
elsif movement_s[i] == "y" then
diry = 0
else
y_step_str[j] = movement_s[i]
j = j + 1
end if
i = i + 1
until ((movement_s[i] == "Z") | (movement_s[i] == "z"))
stage = 3
y_step_str[j + 1] = 0
j = 0
end if
if stage == 3 then
repeat
if movement_s[i] == "Z" then
dirz = 1
elsif movement_s[i] == "z" then
dirz = 0
else
z_step_str[j] = movement_s[i]
j = j + 1
end if
i = i + 1
until movement_s[i] == "/"
z_step_str[j + 1] = 0
j = 0
end if
until movement_s[i] == "/"
usb_serial_flush()
-- Next:
-- convert x dword
x_steps = strtodec(x_step_str)
-- convert y dword
y_steps = strtodec(y_step_str)
-- convert z dword
z_steps = strtodec(z_step_str)
-- see which one is bigger and that will lead the for cycle
if (x_steps > y_steps) then
if (x_steps > z_steps) then
k = x_steps
else
k = z_steps
end if
else
if (y_steps > z_steps) then
k = y_steps
else
k = z_steps
end if
end if
-- DO THE STEPS
for k using m loop
--
if (m + 1) <= x_steps then
if dirx == 1 then
x_step_up()
else
x_step_down()
end if
end if
if (m + 1) <= y_steps then
if diry == 1 then
y_step_up()
else
y_step_down()
end if
end if
if (m + 1) <= z_steps then
if dirz == 1 then
z_step_up()
else
z_step_down()
end if
end if
-- here goes the desired delay between steps
if t_delay == "U" then
for step_speed loop
delay_1us()
end loop
else
delay_1ms(step_speed)
end if
usb_serial_flush()
end loop
end procedure -- end of the best procedure
-- hope you like it :-D
-- end procedures and functions
-- ==================================================
-- PROGRAM START ------------------------------------
-- ==================================================
-- configure pins
enable_digital_io() -- first, all pins set to digital
-- Then, setting direction (input/output)
-- We should do something like this:
-- PORTA_direction = OUTPUT
-- PORTB_direction = OUTPUT
-- but we do it Arduino style for newbies
D0_direction = INPUT -- RX, used for "resume" switch
D1_direction = OUTPUT -- TX, "CNC on operation" LED indicator
D2_direction = OUTPUT -- Boot LED indicator
--
D3_direction = OUTPUT -- L1 - z axis - this is PORTB_low
D4_direction = OUTPUT -- L2 - z axis
D5_direction = OUTPUT -- L3 - z axis
D6_direction = OUTPUT -- L4 - z axis
--
D7_direction = OUTPUT -- L1 - y axis - this is PORTB_high
D8_direction = OUTPUT -- L2 - y axis
D9_direction = OUTPUT -- L3 - y axis
D10_direction = OUTPUT -- L4 - y axis
--
D11_direction = INPUT -- xyz limits and emergency stop
D12_direction = OUTPUT -- Steppers power on/off relay
D13_direction = INPUT -- (full/half step) jumper
--
D14_direction = OUTPUT -- L1 - x axis - this is PORTA_low
D15_direction = OUTPUT -- L2 - x axis
D16_direction = OUTPUT -- L3 - x axis
D17_direction = OUTPUT -- L4 - x axis
--
D18_direction = OUTPUT -- CRASH LED (red) - emergency stop or limit reached
-- equivalent to :
-- pinMode(D19_direction, OUTPUT)
-- assure that steppers are not powered
-- a good thing is to have a second switch in series
D12 = LOW
-- and that "Breakdown" red LED is off
D18 = LOW
-- Operation LED off
D1 = LOW
-- default values
-- initialize index for step buffer
memx = 0
memy = 0
memz = 0
t_delay = "M" -- the type of delay (milliseconds in this case)
step_speed = 10 -- milliseconds
-- read the step style jumper and
-- set step array values accordingly
if D13 == 1 then
for 8 using i loop
csteps[i] = fsteps[i]
end loop
else
for 8 using i loop
csteps[i] = hsteps[i]
end loop
end if
-- "Load" the steper coils
PORTA_low = csteps[memx]
PORTB_low = csteps[memz]
PORTB_high = csteps[memy]
-- power the steppers
D12 = HIGH
-- initialize the USB serial library
usb_serial_init()
-- optionally wait till USB becomes available
--while ( usb_cdc_line_status() == 0x00 ) loop
--end loop
-- Albert, this one is not working :( , I disabled it...
-- Just to be sure (bootloader switch off this LED anyway)
D2 = LOW
-- end setup()
-- main loop
forever loop
var byte ch
-- Service USB, call on a regular base to keep communcaiton going
usb_serial_flush()
-- check for input character
if usb_serial_read( ch ) then
if ch == "?" then
print_string( usb_serial_data, str1 )
print_string( usb_serial_data, str2 )
elsif ch == "A" then -- X axis
x_step_up()
elsif ch == "a" then
x_step_down()
elsif ch == "S" then -- Y axis
y_step_up()
elsif ch == "s" then
y_step_down()
elsif ch == "D" then -- z axis
z_step_up()
elsif ch == "d" then
z_step_down()
elsif ch == "<" then -- a setup string is receiving
j = 0
D1 = HIGH -- light the operation LED
repeat
usb_serial_flush()
if usb_serial_read( ch ) then
if ch != "/" then
speed_s[j] = ch
j = j + 1
if j == 35 then -- protection against malformed string
ch = "/"
end if
end if
speed_s[j] = "/"
end if
until ch == "/"
-- go to evaluate string and set the speed
process_and_set()
D1 = LOW -- switch off the operation LED
usb_serial_data = "O" -- send "OK" to PC
usb_serial_flush()
elsif ch == ">" then -- a movement string is receiving
j = 0
D1 = HIGH -- light the operation LED
repeat
usb_serial_flush()
if usb_serial_read( ch ) then
if ch != "/" then
movement_s[j] = ch
j = j + 1
if j == 35 then -- protection against malformed string
ch = "/"
end if
end if
movement_s[j] = ch
end if
until ch == "/"
-- go to evaluate string and start movement
process_and_run()
D1 = LOW -- switch off the operation LED
usb_serial_data = "O" -- send "OK" to PC
usb_serial_flush()
else
usb_serial_flush()
end if
end if
-- we check limits or emergency stop
emerge()
end loop
--
How the firmware works
The firmware is very basic but hopefully, fast enough (can be considered a basic but efficient engine). If this firmware is used with my FreeJALduino board, at start, the bootloader will wait for 10 seconds and then will start the application. Until then, no lighted LED's. At start, the steppers will be powered and a blue LED will signal that the board is ready to receive data from USB. A default speed of 10 milliseconds delay per step (useful for testings) will be considered. Firmware will check the jumper (the jumper from breadboard or shield and not the one from FreeJALduino board) and will choose between full step and half step.
The firmware have two levels at listening for characters on USB Serial interface.
1. - First level. At this level you can send characters for manually moving axis step by step on both directions, allowing fine (manual) positioning. The characters are:
- A - move one step on X axis in right direction;
- a - move one step on X axis in left direction;
- S - move one step on Y axis in right direction;
- s - move one step on Y axis in right direction;
- D - move one step on Z axis in right direction (or up);
- d - move one step on Z axis in left direction (or down);
- < - determine firmware to enter on the second level for receiving speed data as a string;
- > - determine firmware to enter in the second level for receiving movement data as a string.
- At the end of a complete loop, firmware is testing to see if limit was reached on one of the axis or if STOP button was pressed. Then, the power for steppers will be cut and a red LED will signal the fault. CNC will stay on pause until RESUME button is pressed (in case you stopped the CNC for a break) or, in case a limit was reached, you must reset the board because your CNC setup is wrong.
2. - Second level. At this level, firmware is waiting for an entire string terminated with forward slash character. Then will proceed with processing the string and setting the movement speed or start movement. A LED will be lighted for signaling that the firmware is in the second level. So, this level have two modes, speed setup mode and movement mode:
-- Speed setup mode, determined by "<" char from the first level. You can set de delay between steps starting from 10 microseconds. Here I must give some detailed explanations. The firmware is made in JAL language and here I found two delay procedures which can be used: delay_10us(byte) and delay_1ms(word).
For delay_10us(byte) parameter is byte and means can be up to 255 and is incremented by 10. So you will have this formula: delay = n * 10 microseconds - where n is your byte parameter. If microseconds are required, the setup string will start with "U" character. Example:
U12/ - mean 120 microseconds delay between steps
(around 8000 rpm?)
For delay_1ms(word) parameter is word and means can be up to 65535 (no useful to have that maximum delay :-P) and is incremented by 1 millisecond, no weird formula needed, what you get is what you give. So, if milliseconds are required, then the first character from string must be "M". Example:
M10/ - means 10 milliseconds delay between steps
(see the forward slash that will end the string ).
Maybe I will make my own delay_us(word) procedure with a normal increment by 1 microsecond ... The speed setup will remain until you will give another speed setup. After speed is set, the firmware will return at the first level of listening.
-- Movement mode, determined by ">" char from the first level. Here is simple. Examples:
X40Y0Z0/ - means 40 steps on X axis at right direction
X0y40Z0/ - means 40 steps on Y axis at left direction
X0Y0Z40/ - means 40 steps on Z axis at right (up) direction
X40Y40Z0/
In this case it means 40 steps on X axis and 40 steps on Y axis,
drawing a straight line at 45 degrees (only if you have same setup on both axis,
else use it only for movement and not for routing). The movement is one step
on X axis and one step on Y axis at a time.
Of course you can move all axis at once if is needed. Again, the maximum number
of steps which you can give is a longword or doubleword which mean
4,294,967,295.
Of course is huge and at 10 microseconds delay between steps, it will last
more than a year to finish the movement but a maximum of 65535 steps at once was
not enough.
The type of char (uppercase or not) will determine the
direction on that axis (e.g., X for right and x for left).
At every step, the limit reached or pause is tested. After the operation is finished, the firmware will return in the first level of listening.
Why such a simple firmware?
Well, it will be never obsoleted because it contains no g-code (which must be upgraded or worse, corrected), no CNC setups. All g-code translation and the required interpolations are made by the PC host application. So, you can use the firmware without bootloader if you want (but not the .hex file from attachments, which require bootloader).
Building it...
....
Notes about attachments
....