This build extends Adam Taylor's project Mini But Mighty, the MiniZed and Vitis for Motor Control. Completing that project is necessary before adding the feature described in this one. After Vivado processing system edits, experienced users can skip to the end for the application source code.
The MiniZed hardware user guide (p31) gives specifications for the MiniZed's onboard accelerometer, the ST Micro LIS2DS12. The accelerometer's datasheet provides details for interfacing with it using SPI or I2C. This build uses I2C to fetch accelerometer readings for controlling the MM10 motor.
In order to implement an I2C interface for communicating with the LIS2DS12, we must revisit the Vivado IP integrator flow from Adam's project. This is his configuration which we begin from:
The minized.org website provides tutorials for creating SPI and I2C interfaces in the Zynq PS and PL. This build incorporates instructions from the PL I2C tutorial into Adam's Zynq IP configuration. We start by deleting the IIC_0 interface; this build will leverage the AXI IIC IP instead.
Now add an AXI IIC interface from the IP catalog:
Now click "Run Connection Automation" in the IP canvas popup banner, then check "All automation" for mapping the IIC and S_AXI interfaces of the AXI IIC IP.
Regenerate the IP canvas layout using this button:
Our finished I2C motor control block design should appear as follows:
The minized.org website provides baseline pin constraints for the MiniZed rev 1 board. We can see from the XDC file that Zynq pins G15 and F15 are mapped to the I2C pins of the ST Micro LIS2DS12TR.
So we must map G15 and F15 to the AXI IIC's block design interface port (code attached). Lines 1-4 were used in Adam's baseline, lines 6-10 specify the IIC ports.
Click Generate Bitstream to re-run the full synthesis, implementation, and bitstream flows.
Once complete, re-export the XSA hardware specification (File>Export). We'll use this in Vitis.
Click Tools>Launch Vitis and set a workspace directory. Create a new application project.
As in Adam's project, we must create the hardware platform from our Vivado XSA:
Select the correct domain options:
And begin with the hello world application.
Now navigate to BSP Settings
In "standalone, " modify BSP settings:
Ensure stdin and stdout are mapped to uart1:
Back on the board support package page, verify that the IIC driver has been included as a peripheral driver:
Clicking Documentation will bring you to the SDK IIC drivers API documentation. Navigate the dropdown menu to APIs>Functions, then scroll to the index for -X-. We are interested in XIic_Recv() and XIic_Send(). We will return to this later.
Back in Vitis, select both items from the explorer pane. Right click>Build Projects. The console should report "Build Finished"
If running Linux, the build step may fail with an error such as "Program "make" not found in PATH." If so, ensure you have the make package:
sudo apt-get install make and/or sudo apt-get install build-essential
Then create a link with gmake:
sudo ln -s /usr/bin/make /usr/bin/gmake
See this forum thread for reference.
With MiniZed connected, open a serial terminal at 115200 baud, e.g.
screen /dev/ttyUSB1 115200
Back in Vitis, right click the application project:
Select "Single Application Debug" and from the Application tab, verify that the Application points to the.elf file in the Debug directory:
Verify in the Target Setup tab that the bitstream and psinit files are sourced from the
Now click Run and observe the Hello World output in the serial terminal session.
If hello world was successful, navigate back to the application source code in Vitis:
Revisit the IIC driver API documentation to see the IIC functions we need to implement:
Now return to the Avnet Tutorial 8 documentation. I2C source code is provided in the ZIP package:
Note the i2c write and read functions:
We can leverage these functions to read & write the LIS2DS12 accelerometer.
Udhay Kumar has also written about using the MiniZed's LIS2DS12. I've made use of his provided code as a baseline for the I2C motor control application.
i2c_mtrctrl.c for the final I2C motor control application code.
Explanations of the code will follow.
Adam's main and supporting functions begin after line 200. Note
display_menu() on line 284: an 8th option is added for running the motor from an accelerometer read. Therefore we add an entry to the motor speed control lookup table in the
main() function (see line 266).
The main function first calls Udhay's
sensor_init() function on line 128. Sensor init sets the IIC device address of the LIS2DS12 to SA0_HI_ADDR. The address for SA0_HI_ADDR is sourced from page 35 of the LIS2DS12 accelerometer datasheet (binary 0001_1101, hex 0x1D).
First, an IIC read is requested at the WHOAMI register address 0x0F (see datasheet p40). IIC data is read into who_am_i, such that who_am_i can be compared with a known, fixed value 0x43 which is provided in datasheet p43. If who_am_i doesn't match 0x43, another read is requested to see if who_am_i instead matches the address SA0_LO_ADDR (datasheet p35).
The init step is completed once who_am_i is matched and the correct LIS2DS12 control registers have been written to. Revisit datasheet p45 to see that control register 2 must be written 0x00 for initialization. Then Table 42 shows operation modes which are set by writing to control register 1. We write 0x60 to select 14-bit operation at 400 Hz. The init step is now complete.
while() portion of
main(), starting in line 230, accelerometer data is read into
acc_din using Udhay's
acc_read() function (line 148).
acc_read() fetches accelerometer data beginning in line 162. You should experiment with changing the return of this function between directions x, y, z, or an average of them.
Scroll to datasheet p50. Raw accelerometer data must be read in groups of 2 bytes (2 for x, 2 for y, 2 for z). For each direction, the most significant and least significant bytes should be concatenated to form a 16-bit word, expressed in 2's complement. Lines 164, 169, and 173 perform the concatenations.
Lines 175-177 call Udhay's
signed_to_int() function (see line 109). If required, the 2's complement conversion is performed. Now recall that we are operating the accelerometer in 14-bit resolution (datasheet 44). The two's complement number is left-aligned in the 16-bit word, therefore we're only interested in bits 15 downto 2. Shift the bits 2 positions to the right and convert to integer.
Back in the main fucntion, I call a scaling function for mapping the returned accelerometer data into a suitable duty cycle (range 0-100). I do this with
scale() in line 185.
Back in Vitis, add the final application code instead of the hello world code. Build the project. Now reconnect to MiniZed with a serial session.
Launch the I2C application on the MiniZed using "Run As" in the same manner as we did for the hello world application run.
You should see the application menu display in the serial terminal:
- Hit the 8 key and observe the motor speed.
- Tilt the MiniZed and hit the 8 key again to observe the change (Be careful tilting; I would recommend soldering the jumpers to the MM10 motor if possible).
- Optional: strap a contact microphone to the motor and do something weird.