Introduction: Automatic Drink Mixer

In this tutorial we will attempt to create a system that will integrate electrical hardware, software, and mechanical systems to complete the task of drink mixing automation. The final product is a drink carousel that has the capability of pouring drinks from 3 separate bottles as indicated from a user computer using USB. For the prototype designed, a Xilinx Zybo development board was used along with the Vivado Design Suite. Specific to the Zybo board is its system on chip architecture allowing us to design the FPGA hardware and software on the Zynq processor included with the Xilinx IP library. The mechanical system is comprised of a drink carousel and rubber belt attached to the continuous servo to locate the desired bottle and the standard servo is used to dispense the liquid. Be aware when reading these instructions that the purpose of this project was to design around the hardware/software platform and the documentation for the development on this board will be far more thorough than that of the mechanical system.

Parts and Softwares list:

  • One Zybo Zynq-7000 Development Board
  • Four AA batteries and a battery holder
  • One continuous servo
  • One standard servo
  • One flow sensor
  • One Hall sensor
  • One LM358 or similar operating amplifier
  • Two 10 kOhms and two 20 kOhms resistors
  • Vivado Design Suite v2005.3
  • Digilent Adept 2
  • FreeRTOS
  • Terminal emulator communications program (We used Tera Term in this instruction)
  • A zip file which contains necessary files for the project. You can download it from here

Mechanical System Considerations:

As described in the introduction, this project was originally designed to accomplish hardware and software integration. With this in mind, we cannot guarantee the methods described below to combine the components are good practices in any way. However, our system did function completely (with the exception of having a weak servo) so including the information can provide a framework for future iterations.

  • Liquid Flow Sensor: The flowmeter is fixed to a funnel to make sure the liquid is not spilled. The woodblocks maintain stability and are screwed in directly to the base of the drink carousel. We used some tubing to and duct tape to create a seal on the funnel to the flowmeter.
  • Continuous Rotation Servo: The continuous servo should be placed at the same level as the belt so the belt does not sag. A sagging belt can make the rotation less efficient and more difficult to work with. To elevate the continuous servo we used two clamps; one clamp attached to the table and one to attach the servo to the first clamp. The setup is pictured below.
  • Hall Effect Sensor: The Hall effect sensor changes state in the presence of a magnetic field, so we super-glued a strong magnet onto each of the liquid dispensers and taped the sensor onto a woodblock. The woodblock was positioned such that when a magnet is in front of the sensor, a bottle would be over the funnel described above. Please note that the orientation of the magnet’s north and south pole does affect the sensor!
  • Standard (non-continuous) Servo: This servo should be placed in such a way that when it is activated, it will activate the mechanism to dispense liquid. Unfortunately, we were not able to implement this functionality due to the servo being too weak. To affix this to the carousel, we super-glued a woodblock to the carousel’s base and if we were to completely implement this servo, we would most likely super glue the servo to the woodblock. Pictured below is an attempt at this setup.

Hopefully, this guide helped you create your own automated drink maker. From building the prototype we ran into a couple considerations for future iterations. It is important to point out again that this project was created to demonstrate knowledge and mastery of RTOS design and there may be better platforms out there to develop your own drink maker that would better suit your needs. When picking the drink carousel for your drink maker, take into consideration the mechanism to dispense liquid because they do vary in how they function and the force required to release them. And, of course, be careful with using your system with liquids. Perform a couple dry tests before using liquid. Finally, we will leave you with a video of the final prototype so you can better see the complete functionality. Good luck and drink responsibly!

Step 1: Warm Up! Let's Read Some Papers

  • Before working with devices listed above, we will need to read their datasheets.
  • In this project, we use two type of servos: standard and continuous rotation servo. Standard servo can hold any position over a 180-degree range, and continuous rotation servo is designed for continuous rotation at some specific speed. These servos can be controlled through pulse width modulation, and for we need to read their datasheets so that we can know at what period and how wide the pulse is in order to control their speed and position.
  • Next, we will discuss about water flow sensor. Water flow sensor consists of a plastic valve body, a water rotor, and a hall-effect sensor. When water flows through the rotor, rotor rolls. Its speed changes with different rate of flow. The hall-effect sensor outputs the corresponding pulse signal. The flow rate range and precision will vary for different type of flow sensor.
  • Lastly, let's talk about the Hall effect sensor. this device is used to detect presence of magnetic field. Holding a magnet near the sensor will cause the output pin to toggle. It depends on what kind of Hall sensor you have to use pull-up or pull-down resistor.
  • One more thing you need to consider is that you should know their operating voltages, control signal and output signal voltages, especially the maximum values, so that you can control them at more powerful

Step 2: Basic Setup and Adding IP Blocks

  • Firstly, download the zip file and unzip it.
  • Next, open Vivado, create new project, and type in name for new project
  • Choose the RTL project option, click “Next”
  • When Vivado asks for Xilinx parts for the project, search for “xc7z010clg400-1” and click on it. Click “Next” then “Finish”
  • In the left hand panel called “Flow Navigator”, under “IP Integrator”, click on “Create Block Design”. This opens up a block design window. You can choose Design name, then click “OK”
  • Right click on the white space of the “Diagram” tab and click on “Add IP”. In the search box, type in “ZYNQ7 Processing System”. A Zynq7 block should be placed in the Diagram windows
  • Repeat the step above to add six “AXI GPIO” blocks to the diagram
  • On top of the Diagram tab, click on “Run Block Automation”, and a new window should open up. Click “OK”
  • On top of the Diagram tab, click on “Run Connection Automation” and a new window should open up. Check “All Automation”, then uncheck every box with “GPIO” text next to it. Click “OK”
  • After the Vivado connects all block together, the Diagram may look messy. Right click on Diagram tab’s white space, and click on “Regenerate Layout”. Vivado will automatically organize all blocks.

Step 3: Setup for Zynq7 IP Block

  • Double click on Zynq block, and we start setting up Zynq7 IP on the new window
  • Click on “Import XPS Settings” and select “zybo_zynq_def.xml” from the folder you just unzipped, then click “OK”. This step prepopulates the Vivado block configuration with all of Zybo board’s built-in peripherals and pin assignments.
  • On “Page Navigator” panel, click on “MIO Configuration.” On the right panel, open “I/O Peripherals”, open “GPIO” then check “GPIO MIO”. We will let “ENET Reset”, “USB Reset”, and “I2C Reset” checked automatically. Open “Application Processor Unit”, check “Timer 0” and “Watchdog”
  • On “Page Navigator” panel, click on “Clock Configuration.” On the right panel, open “PL Fabric Clocks”, check “FCLK_CLK0” then set its “Requested Frequency(MHz)” to 1 MHz. This step sets the clock used for GPIOs.
  • On “Page Navigator” panel, click on “Clock Configuration.” On the right panel, check “Fabric Interrupt”, open “PL-PS Interrupt Ports” then check “IRQ_F2P[15:0]”
  • Click “OK.” We finished Zynq7 Configuration.

Step 4: Adding Custom PWM Controller Modules

  • In this step, we will add an IP block called “PWM_Controller” to the project. This IP is used to set pulse width modulation and its period to control speed and directions of continuous servo. It can be also used to make the standard servo turn a specific angle.
  • In Vivado, click on “Tools” menu and click on “Create and Package New IP.” A new window should open up.
  • Click “Next”, check “Package a specified directory” then click “Next.”
  • In “Directory,” select PWM_Controller folder from the folder you just unzipped. Check “Package as a library core” then click “Next.”
  • A message window saying that the IP definition already exists in this directory should open up. Click “Open”
  • “Package IP - PWM_Controller” tab should open up. In this tab, in “Package Steps, click on “Review and Package,” click on “Package IP.”
  • A message window should appear saying that Vivado finished packaging the IP. Click “OK” then click “Open Block Design” to open the project Diagram.
  • Right click on whitespace of the Diagram tab and click “Add IP”. In search box, type “PWM_Controller_v1_0” to add the PWM_Controller IP block. Add one more to the project Diagram.
  • Right now you should see two new blocks. We will use PWM_Controller_0 block to control the continuous servo, and PWM_Controller_1 block to control the standard servo.

Step 5: Setup for AXI GPIO Blocks and Interrupts

  • In this step, we will set up six AXI GPIOs we added previously. Double click on each AXI GPIO then follow table below which describes functions and how to set up configurations of each AXI GPIO.
  • After finishing configurations for all AXI GPIOs, we need to create their input/output signals. You will see “GPIO+” for each block. Click on the “+” symbol which will expands the interface connection, then select the individual signals. You can then right click and select “Make External.” Select the GPIO input/output and then rename it as described in the table you just used for GPIO configurations. This step is used to tie a pin on an IP to an I/O port on the block design. IP integrator simply connects the port on the IP to an external I/O. The table in this step shows names of the external signals for each hardware block.
  • You will notice that we have not made any external connections to axi_gpio_0 and axi_gpio_3. These two blocks will connect to two PWM_Controller we just added. TO make connection, first click on the “+” symbol of each GPIO block, then move your mouse on the interface connection. The mouse cursor will change to a pencil, and you will click on the connection and draw a straight line which make a connection to the “Time_Control” signal of the PWM_Controller. axi_gpio_0 connects to PWM_Controller_0, and axi_gpio_3 connects to PWM_Controller_1.
  • Make external connection to the “PWM” signal of the PWM_Controller block. External signals of PWM_Controller_0 and PWM_Controller_0 are called PWM_OUT_0 and PWM_OUT_1, respectively. Next, “Clock” signal of each PWM_Controller block will be connected to “FCLK_CLK0” of the Zynq block.
  • Next, we will connect interrupt signals of axi_gpio_1 and axi_gpio_4 to the Zynq block. Since “IRQ_F2P” interface of the Zynq block cannot be connected to two interrupt signals at the same time, “Concat” IP will be added to the Diagram to concatenate the individual interrupt signals into a bus. We only have two interrupt signals so we do not need to configure xlconcat_0 block. Draw connections from “ip2intc_irpt” interfaces of axi_gpio_1 and axi_gpio_4 to “In0” and “In1” signals of the xlconcat_0 respectively. Then we connect “dout” signal to “IRQ_F2P” interface of the Zynq.
  • After regenerating layout, right now you should have a diagram similar to the image below. You basically finished connecting all blocks together. Now we will create a design HDL wrapper. Next to the “Flow Navigator”, we have a small box that has “Sources/Design/Signals/Board” . Sources will be code, design will get us to where we are now, and signals and board manage signals internally in our design, and the board that we are using. Now check out the “Sources” pane in that small box. Right click on the design then click on “Create HDL Wrapper.” We will want to “Copy generated wrapper to allow user edits,” then click “OK.” A message will pop up and say the HDL wrapper was generated and copied into the project. Click “OK.”

Step 6: Making Board Connection Definitions

  • Right now you should have the HDL wrapper. Let’s open it and take notes of all signals which control the external devices, like flow and hall sensors. A table shows all inputs and outputs we need to control the external devices.
  • We will now need constraints to define the actual board connection of the external devices to the signal mappings. Check out the “Sources” pane again, right click on “Constraints” folder and choose “Add Sources….” Choose “Add or create constraints” then click “Next.” Click on “Add Files” and select “ZYBO_Master.xdc” file from the folder you unzipped before. Click “OK” then click “Finish”
  • The constraints file should be added to the project. Check out the “Constraints” folder, and double click to open the constraints file.
  • We will enable the built-in LEDs on the Zybo board. Find the comments “##LEDs.” . There are four LEDs on the Zybo board that we have pin mappings for. Let’s set these to point to our exposed LEDS 4-bit wide bus. Rename all the instances of “led” to port name of the LEDs you found in the wrapper. Uncomment the PACKAGE and IOSTANDARD properties similar to the figure below. You can find an image as an example for mapping LEDs signals to the board.
  • For this project, we will use Pmod connectors to control the external devices. Similarly, we will map the input/output ports of the wrapper to the Pmod connectors. Feel free to choose the connectors you like but remember to take notes of what connectors you use for what devices. Figure and table below shows the pinout diagram and Pmod pinouts (from Zybo’s datasheet) of the Pmod connectors. One figure shows an example of using Pmod JE connectors for the project.

Step 7: Implementation and Generating Bitstream

  • Now you have finished configuring hardware for this project. Now is the time for implementing hardware and generating its bitstream.
  • Check out the “Flow Navigator” panel. Click on “Run Implementation” under “Implementation.” It will take a while, and you may get errors during implementation. Be sure to read error messages thoroughly and fix all errors. You can ignore warning messages but it would a practise for you to understand those warnings so that you can avoid them on your next project.
  • When the implementation is complete and all errors are fixed, click on “Generate Bitstream” under “Program and Debug.” When it’s done, click on “File” menu, “Export”, then “Export Hardware.” A new window will pop up, and make sure to check “Include bitstream” before clicking on “OK”
  • Click on “File” menu, then “Launch SDK,” then click “OK.” The Software Development Kit of Vivado will open.

Step 8: Building Voltage Amplifiers

  • Before we start working with coding, we may need to build a simple electronic circuit. You may notice that the supply voltage for the continuous and standard servos is 6V, but the PWM signal from the Pmod connectors you mapped on previous steps swings from 0V to 3.3V maximum. This may not enough to control the servos.
  • One simple solution for that is building a non-inverting amplifier. You may choose any operational amplifier (op amp) since the output frequency of the PWM is not too high. You do not have to worry too much about the gain bandwidth of the op amp. Figure below shows an example of how you can build a non-inverting amplifier. You may need to build one more for another servo.

Step 9: Creating Board Support Package

  • Now let’s get back to the SDK. The Vivado SDK provides an environment for creating software platforms and applications targeted for Xilinx embedded processors, and it is based on the Eclipse open source standard. SDK features include feature-rich C/C++ code editor and compilation environment, well-integrated environment for seamless debugging and profiling of embedded targets, and much more. You can find information about the SDK by Google-ing it.
  • On the left of the SDK window, you can see “Project Explorer” tab where you can open and edit your code for the project. Currently you will see a folder already there. That folder contains all hardware platform specification. Before working with some C code, let’s create a Board Support Packages (BSP) to wrap up the platform we just created. Click on “File” menu then “New Board Support Package.” A wizard will pop up to help you create the BSP.
  • We want to tie this BSP with our Platform that we just created, so “Hardware Platform” should have the same name with the folder mentioned above.. Our CPU will be the “ps7_cortexa9_0” which is the first core CPU. We can also choose for the BSP to be for our “ps7_cortexa9_1” CPU (the second core). Upon clicking “Finish”, you will see the drivers that are included in “Project Explorer.” Be sure to check off “lwip141” for inclusion with the BSP and hit “OK”
  • Now you see that a file called “system.mss” is opened. This file, called Board Support Package” includes all peripheral drivers that we included when building hardware blocks in previous steps. For example, you can see axi_gpio_0 is included in the peripheral drivers. You can find some useful information and examples of how to use the AXI GPIO in “system.mss.”
  • Another useful file is “xparameters.h” which can be found inside “include” folder in “ps7_cortexa9_0” folder (if you chose first core CPU) or “ps7_cortexa9_1” (if you chose second core CPU). This is the parameter file for our Vivado defined Zynq processor. Figure in this step shows some useful parameters of axi_gpio_0.

Step 10: Importing FreeRTOS Project

  • Download the latest FreeRTOS release from Sourceforge. You can Google it easily. After that, extract the FreeRTOS files into your project directory.
  • FreeRTOS is a popular real-time operating system kernel for embedded devices, and it is designed to be small and simple. The kernel itself consists of some C files. It also provide methods for multiple threads or tasks, mutexes, semaphores, and software timers. You can find a lot of examples and instructions how to use FreeRTOS for your next project.
  • Let’s import FreeRTOS into our project. Click on “File” menu then “Import.” Under “General” click on “Existing Projects into Workspace” then click “Next.” We want to navigate to where we downloaded and extracted FreeRTOS.
  • Our demo will be located in “FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702.” Upon selecting this folder, we should see three projects pop up: “RTOSDemo,” “RTOSDemo_bsp,” and “ZC702_hw_platform.” Only import “RTOSDemo” into your current workspace. It will build the projects, and you can see this status in the “Console” tab or on the bottom right of the SDK window.
  • The most frustrating time has started! After you imported the FreeRTOS, if you find errors with inclusion of header files, you may have to check what you missed when you creating and building hardware in previous steps. Sometimes you have to redo from the beginning with your hopes that you will not see any errors in the SDK. Be patient if you are in that situation!
  • Next, find your way to “main.c” and find “#define mainSELECTED_APPLICATION” then set mainSELECTED_APPLICATION to 1. We will use demo application of the FreeRTOS.

Step 11: Installing Drivers for the Zybo

  • Now we got the FreeRTOS ready to run, but we may need drivers for the Zybo board. Now let’s Google “Digilent Adept 2.” Download and install the drivers in your computer.
  • After all installations, plug in the Zybo board, and if there are any issues with drivers, try to fix them before we can move on.

Step 12: Debug Configurations

  • Select “Debug Configurations” from “Run” menu. On the left column, click on “Xilinx C/C++ application (System Debugger)” then click on “New launch configuration” (a paper with a plus symbol on its top right).
  • On second column, under “Target Setup” tab, choose “Standalone Application Debug” for “Debug Type” then check “Reset entire system,” “Program FPGA,” “Run ps7_init,” and “Run ps7_post-config.” Next to those check boxes there is summary of operations to be performed for each check box.
  • Now click on “Application” tab then click on “Download application.” You can choose the application you want to run with the Zybo. Now make sure that the project name is “RTOSDemo” then click on “Apply.” Close the window, and now you are done with debug configurations.

Step 13: Interfacing With Zybo

  • Now let’s download a terminal emulator communications program. You can use PuTTY or Teraterm on Windows to open a COM port. Please check the Device Manager to see what COM port is active for the Zybo. Open it and set the speed to 115200.
  • Now get back to the SDK and debug the application. Plug in the Zybo board and turn it on. Go to “Run” menu and click on “Debug.” The program will stop at the first line of code in “main” method of “main.c.” Find the “Resume” button to go past the breakpoint, and also make sure that you have the right core selected.
  • Now check the terminal. You should see a welcome message in the terminal. If you do not see it, please check to make sure you do not miss any steps. If you see it, let’s move on to the last step

Step 14: Editing RTOSDemo Application and Running the Final Application

  • Now let’s get our hand dirty! We will fix the system. Fortunately, you will need to fix one file only. Now check the folder of “RTOSDemo” application. Open “src” folder then open “Full_Demo” folder. Open “UARTCommandConsole.c” and we will fix this file. Before making changes, you may want to make a copy of this file in case you want to work with FreeRTOS on your next projects.
  • After backing up data, now delete all code lines in “UARTCommandConsole.c” file and open “UARTCommandConsole - New.c” file in the folder you unzipped from the beginning of the project. Copy all code lines and paste into “UARTCommandConsole.c”
  • Check to make sure there is no errors. If you are free from errors, now debug the application. You will see “Please type in a command:” message in the terminal. Type in “213,” and the continuous servo will rotate to continuously since we have not implemented mechanical system for the project. Hall sensor with the presence of a magnet update the current bottle whose valve will be opened by the standard servo. The flow meter will count how much liquid goes through it. The standard servo will close the valve when desired volume of liquid is poured. Then the external LED will turn on, and the continuous servo will rotate to the next bottle.
  • You can check the code for useful information and additional commands for the system. You may need to change values of some parameters to satisfy your requirements.