Project 6.1 Using the SPI and I2C bus

Configuring and using common serial busses

1242

Introduction

This project introduces the Serial Peripheral Interface (SPI) and Inter-Integrated Circuit (I2C) busses. In the project these busses are used to communicate between the ZYNQ Chip and peripheral devices on the blackboard. The Blackboard has an ST Microelectronics multi-sensor package which includes an accelerometer, gyroscope, and magnometer. This Inertial module is connected through the SPI bus. The Blackboard also has an NXP temperature sensor connectted on the I2C bus. The Zynq’s SPI and I2C modules will be configured and used to talk with devices on each bus.

Background

Serial Peripheral Interface (SPI) is a full-duplex serial bus. SPI uses 2 data lines (one for each direction), a clockline and a number of ‘slave select’ lines. The slave select lines allow the master to choose which slave device to communicate with. SPI is often used to achieve high speed transfers between devices. While high-speed, the requirement for dedicated slave select lines limits how many devices can be connected to the master. However dedicated select lines eliminate overhead for address decoding on the bus’ actual data lines.

Inter-Integrated Circut (I2C) is a half-duplex serial bus. I2C has only 2 connections: Serial Clock Line (SCL) and Serial Data Line (SDA). In contrast to SPI, I2C needs no additional lines for more devices ; Regardless of how many devices are on the bus, there is always only SCL and SDA in a I2C topology. In lieu of the slave select lines of SPI, I2C prefixes each transaction with an address. The address denotes which slave the master wishes to read or write to. I2C makes for very flexible designs with its two wire topology. However, being only half-duplex and requiring addresses before each transaction, the throughput of I2C is limited. Additionally, the parasitic capacitances of devices connected to the I2C bus can have an affect on how fast the clock can run; I2C has a standard maximum of 3.4 Mbit/s but commonly is run at 400kbit/s or below.

Both the SPI and I2C modules in the ZYNQ have transmit and receive FIFOs. The processor can queue transfers and read from the RX FIFO after transfers complete. Both modules can be configured to generate interrupts based on FIFO status and other events.

Requirements

1. Read data from the Blackboard’s inertial module

Configure the Zynq’s SPI module (Use SPI0) to communicate with the SPI-connected iNemo inertial module. The inertial module is connected with 2 slave select lines; The accelerometer and gyro are accessed through asserting slave select 0, and the magnetometer is accessed using slave select 1. Since the different modules have their own slave selects, you may want to write seperate functions for accessing accelerometer/gyro data and magnetometer data; Each function should be the same but assert a different slave select. Make sure you read the data sheet and background material for the inertial module so you understand how SPI read transactions work with it. Two example function prototypes are given below.

uint32_t acc_gyro_read(uint32_t address);
uint32_t mag_read(uint32_t address);

After you write the functions, use them to read from the WHO_AM_I registers in both the acc/gyro and the magnetometer. Send the value stored in each register to your PC via uart formatted as a hexidecimal number. The WHO_AM_I registers can be accessed at address 0xF for both the acc/gyro and magnetometer.

2. Use SPI to write configuration data to the inertial module

Write a c function that writes data over spi to an accelerometer or gyroscope register. Have the function take the register’s address and the write data as arguments. An example function prototype is given.

void write_acc_gyro(uint32_t addr, uint32_t data);

Use your new function to write to the accelrometer’s control register (found at address 0x10). Writing 0x20 to the register will enable readings from the accelerometer. You can then read data from the accelerometer in addresses 0x18 through 0x1D; addresses 0x18 and 0x19 are the X-axis read value, 0x1A and 1B are the Y value, 0x1C and 0x1D are the Z-axis reading. Write a program that enables the accelerometer and then sends accelerometer readings from the device periodically. Read from each axis, format the reading into strings (you can use hexidecimal numbers), and then send the values of each axis over UART. An example of what your formatted output can look like is given below.

X: 0xDEAD
Y: 0xBEEF
Z: 0xCAFE

3. Configure the SPI module to generate interrupts

Configure the SPI module to generate interrupts when there is data availible in the receive FIFO. You can test if the interrupt works by queuing any SPI transfer to the module. Once there is any data in the FIFO, the processor should enter interrupt. The interrupt ID for the SPI module is 58

Write a program that configures 2 interrupts; Setup the global timer to generate interrupts every second and setup the SPI module to interrupt when there is data availible in the receive FIFO. Each time the global timer interrupt occurs, initiate a read for accelerometer data. When the data is received, in the SPI FIFO interrupt service routine, send the accelerometer data via UART to the PC terminal. You can format the data the same as in requirement 2.

Have your program configure Timer and SPI interrupts then enter an endless loop. Your program should just consist of interrupt initialization in main and a service routine that handles timer and SPI interrupts.

4. Read the temperature sensor via I2C

Configure the I2C module in the Zynq. Write a program polls a button and sends a reading from the Temperature Sensor (Connected via I2C1) every time it is pushed. As before, send the data to the PC terminal via the Zynq’s UART. Convert your temperature sensor data to degrees in Celsius before transmitting.

5. Setup timer and I2C interrupts to read the temperature sensor

As in requirement 3, setup the Global timer to interrupt every second. Every time the global timer interrupt occurs, initiate a read from the temperature sensor. Configure interrupts for I2C to occur when there is data availible in the receive FIFO and in the service routine for I2C, send the temperature data over UART. The I2C Module’s interrupt ID is 80.

Your program can be structured similarly to the program you wrote for requirement 3.

Challenge

Create a system that reads from a variety of sensors.

Design a program that can read the acceleromenter, gyroscope, Magnetometer, and XADC voltage (potentiometer value). Have the MIO connected buttons (BTN4/BTN5) start and stop reading data and use the first 2 switches to change which sensor is being read. Use the UART to send readings to the PC. Use the following settings for the switches to choose which data to display:

Switch Value Display Function
0b00 Accelerometer X,Y,Z
0b01 Gyroscope X,Y,Z
0b10 Magnetometer X,Y,Z
0b11 XADC Potentiometer Voltage

Have button 4 start collecting data and sending it via UART once per second. Use the value of the switches to determine the source of data. When button 5 is pressed, stop collecting data. Use interrupts for button presses, timers, and SPI/I2C receives.