The output of the parallel port is controlled by the I/O expander. The reason why the parallel port wasn’t directly connected to the microcontroller is that we don’t have enough I/O pins to our disposal.
In figure 3.1 we can see how the microcontroller is connected to the I/O expander and parallel port. If data needs to be written to the parallel port, that data will have to be sent to the I/O expander via SPI. The strobe pin is connected from the microcontroller to one NOT gate (for level conversion), to the parallel port.
The CNC machine has a control unit. The way the machine is controlled is by sending commands to that control unit. By hooking up a logic analyzer to the parallel port, and trying to move the machine via the application on MS-DOS, we were able to extract the commands that need to be sent to the control unit to control the machine. This process of measuring the actual voltage levels is called reverse engineering.
3.5.1
CNC routines
For more information about the circular buffer, please refer to section 3.5.4.
One case where we use a circular buffer is to control the CNC-machine. For example, when it is required to move the head a certain distance in one direction (read more at section 3.5.3). Commands have to be sent to the CNC-machine via the parallel port. The way the machine works is as follows:
The axes of the machine are controlled by stepper motors. In our case, one step of te stepper motor results in a movement of 0,0254 mm ( 1
1000th of an inch or also refered to as one thou[6] or one mill) in
a certain direction. The speed at which the head moves is determined by how fast commands are sent. The commands control the machine. We have a list of commands that controls the movement in all three axes, spindle control, initialization of the machine etc.
The delays between these commands are in the order of a couple hundred microseconds. The way we are able to control these rather small and precise timings is by means of a timer interrupt. For the implementation two buffers are used. Each command is accompanied by a specific delay.
3.5.2
Speed up and slow down
The InitiateSpeed function does several things. First the circular buffer is initialized. Also, an initial delay is put into the buffer so that the timer can run once. Afterwards timer 0 is initialized and configured as an interrupt. Finally we come to the ‘delay initialization’.
When the head of the machine moves, we can’t just tell it to go a certain speed. The machines inertia could and probably would make the stepper motors skip a couple of step. We don’t want this. Very precise control over the movement of the head is required. For this reason we need to speed up and slow down all movements.
When we use the InitiateSpeed function, the given argument is the desired maximum speed. Because the speed of movement is determined by the delay in between commands, we need a small delay for fast movement and a big delay for slow movement. If you would plot these delays on a graph, it would look like the graph below. As you can see, the delay is decremented in the first 40 steps and incremented for the last 40 steps. This example was calculated for a speed of 5000 mm/min.
0 20 40 60 80 100 120 300 1,500 4,000 6,000 8,000 step dela y [µ s]
3.5.3
Move the head
One function that is central to make the CNC machine move in any direction, is the setParam_cmd function. This function takes two arguments, a command and a delay. The ‘command’ is an opcode that makes the machine move. An example of such a command would be:
#define X_INC (0x4B)
Anywhere where X_INC is used, the value will be substituted by 0x4B and the execution of the func- tion will result in the movement of the head in the x-axis. Every movement will call the setParam_cmd command. If this command is executed, a new entry is added to the queue that stores a pair of commands and delays. Figure 3.15 shows this.
Figure 3.15: Circular buffer - setParam_cmd command
These delays and commands are then processed by a timer interrupt. An interrupt is used to achieve very precise control over timing of commands. The timer is configured in such a way that it will stop (and not restart) counting after a set time.
When the setParam_cmd function is called, the time given as an argument is configured as the time the counter will count and the command is processed. This involves setting the data pins of the parallel port to the correct value (the value depends on the action you want the machine to perform) and putting a low pulse on the strobe pin of the port. When the interrupt triggers, after the period of the previous command, a check is performed to see if there are commands to process. If so, this process of enabling the timer and processing the data is repeated with new values.
3.5.4
Circular buffer[7]
Different circular buffers are used in this project. A circular buffer is a data structure that uses a buffer of a fixed size, as well as two ’pointers’ to indicate where to read or write data.
The buffer starts out empty. When something is written to the buffer (a command, an instruction, some data...), the write pointer needs to be increased by one, so that the next write operation can write data on a free entry of the buffer. Now assume that the buffer is totally filled with data. We don’t want unprocessed data to be overwritten. That is why we also have a read pointer. When data from the buffer is processed, the read pointer gets incremented by one.
If data must now be written to the buffer, a check is first made to see if there is writable entry in the buffer. In other words, data can be written if the entry is empty or if the entries content has been processed. The read pointer needs to be behind the write pointer. If the read pointer is in front of the write pointer, the program has to wait until more data has been processed. Figure 3.16 illustrates this working.
The Wikipedia article about circular buffers [7] has an easy to understand animation that explains this functionality very well.