Actions

EmSys

Hardware Interface Programming

From EdWiki

Hardware Interface Programming

Interfacing the Keyboard to the CPU

4x4 matrix keypad

To reduce the microcontroller I/O pin usage, keyboards are organized in a matrix of rows and columns. The CPU accesses both rows and columns through ports; therefore, with two 8-bit ports, an 8x8 matrix of 64 keys can be connected to a microprocessor.

  • Keys on a keyboard are organized in a matrix of rows and columns
  • The microcontroller accesses both rows and columns through GPIO ports.
  • When a key is pressed, a row and a column make contact in order to detect the particular key.

Before proceeding further it is assumed that you are familiar with the following:

4x4 Keypad on EduARM4 Trainer Board

4x4 Matrix Keypad Connected to Ports
Figure: EduARM4 Trainer Board - 4x4 keypad

The above figure shows a 4x4 keypad matrix connected to two ports on the EduARM4 board. The rows are connected to an output port (PE0 - PE4) and the columns are connected to an input port (PC4 - PC7). All the input pins should have pull-up resistor connected (select an internal pull-up resistor). If no key has been pressed, reading the input port will yield 1s for all columns. If all the rows are driven low and a key is pressed, the column of that key will read back a 0 since the key pressed shorted that column to the row that is driven low. It is the function of the microprocessor to scan the keyboard continuously to detect and identify the key pressed.

Key press detection

Remember, as mentioned above, the keys are not simply connected to the micro controller as input pins, rather they are arranged in columns and rows in order to save the number of GPIO pins required to read from a keyboard.

To detect the key pressed, the microprocessor drives all rows low, then it reads the columns. If the data read from the columns is D7(PC7) - D4(PC4) = 1111, no key has been pressed and the process continues until a key press is detected. However, if one of the column bits has a zero, this means that a key was pressed. For example, if D7 - D4 = 1101, this means that a key in the D5 column has been pressed.

Key identification

After a key press is detected, the microprocessor will go through the process of identifying the key. Starting from the top row, the microprocessor drives one row low at a time; then it reads the columns. If the data read is all 1s, no key in that row is activated and the process is moved to the next row. It drives the next row low, reads the columns, and checks for any zero. This process continues until a row is identified with a zero in one of the columns. The next task is to find out which column the pressed key belongs to. This should be easy since each column is connected to a separate input pin.

Jumper Selection:
All the Jumpers (J7 - J10) Should be Open on the EduARM4 Trainer Board for 4x4 keypad project.

  1. Port E pins 3-0 are used for rows and configure as output digital pins. configure these pins as open drain output also, which means they are driven low actively but not driven high.
    • Open drain output prevents port damage when two keys of the same column are pressed at the same time.
    • If output pins are driven high and low and two keys of the same column are pressed, it will short the output low to output high of the adjacent pin and cause damages to the output pins.
  2. configure Port C pins 7-4 as input digital pin with the internal pull-up resistors enabled. This will ensure that the input pins read 1 when no key is pressed.

Flowchart for Key Press Detection and Identification

Keypad flow chart.png

Debounce

Keypad debounce.png

When a mechanical switch is closed or opened, the contacts do not make a clean transition instantaneously, rather the contacts open and close several times before they settle. This event is called contact bounce. So it is possible when the program first detects a switch in the keypad is pressed but when interrogating which key is pressed, it would find no key pressed. This is the reason we have a return 0 after checking all the rows. Another problem manifested by contact bounce is that one key press may be recognized as multiple key presses by the program.

See Also

Task 1

  1. Assign a key code to each switch (SW0 - SW15). Whenever a particular key is pressed its corresponding key code should be displayed on the CCS CIO console. (You should take care of debounce and multiple key pressed at the same time).

Seven-segment LED interfacing

7 Segment LED

The 7-seg LED can have common anode or common cathode.


7 Segment LED - CC and CA

With common anode, the anode of the LED is driven by the positive supply voltage (HIGH) and the micro controller drives the individual cathodes LOW for current to flow through LEDs to light up. In this configuration, the sink current capability of the micro controller is critical.

With common cathode, the cathode of the LED is grounded and micro controller drives the individual anodes HIGH to light up the LED. In this configuration, the micro controller pins must provide sufficient source current for each LED segment. In either configurations, if the micro controller does not have sufficient drive or sink current capacity, we must add a buffer between the 7-seg LED and the micro controller. The buffer for the 7-seg LED can be an IC chip or transistors.

Displaying Letters

7 segments Indicator.gif

The seven segments of LED are designated as a, b, c, d, e, f, and g as shown in the figure:


A byte of data should be sufficient to drive all of the segments. In the example below, segment a is assigned to bit D0, segment b is assigned to bit D1, and so on as shown below:

Assignments of port pins to each segments of a 7-seg LED
D7 D6 D5 D4 D3 D2 D1 D0
. g f e d c b a

The D7 bit is assigned to decimal point. One can create the following patterns for numbers 0 to 9 for the common cathode configuration:

Segment patterns for the 10 decimal digits for a common cathode 7-seg LED
Num D7 D6 D5 D4 D3 D2 D1 D0 Hex
Value
0 0 0 1 1 1 1 1 1 0x3F
1 0 0 0 0 0 1 1 0 0x06
2 0 1 0 1 1 0 1 1 0x5B
3 0 1 0 0 1 1 1 1 0x4F
4 0 1 1 0 0 1 1 0 0x66
5 0 1 1 0 1 1 0 1 0x6D
6 0 1 1 1 1 1 0 1 0x7D
7 0 0 0 0 0 1 1 1 0x07
8 0 1 1 1 1 1 1 1 0x7F
9 0 1 1 0 1 1 1 1 0x6F

EduARM4 Board 7-Segment LED Interface

7 segment led interface.png
Figure: EduARM4 Board - 7-Segment Interface

Jumper Selection:
J5 - Short 1 & 2

Since the same segment for four digits are connected to the same I/O port pin, the common cathode of each digit must be driven separately so that only one digit is ON at a time. The 4-digits are turned on one-by-one at a time . For example, if we want to display number 14 on the 7-seg LED, the following steps should be used:

  1. Configure Port B (PB0 - PB7) as output port to drive the segments,
  2. Configure Port A (PA4 - PA6) as output port to select the digit,
  3. Write the pattern of numeral 1 from the above table to Port B
  4. Write one bit of Port A to activate the tens digit,
  5. Delay for some time,
  6. Write the pattern of numeral 4 from the above table to Port B,
  7. Write one bit of Port A to activate the ones digit,
  8. Delay for some time,
  9. Repeat from step 3 to 8.

At low frequency of alternating digits, the display will appear to be flickering. To eliminate the flickering display, each digit should be turned on and off at least 60 times per second.

Notice in the above figure, a single pin is used to select each digit. That means for 4-digits we have used a total of 12 pins i.e, 8 pins for the segments a through g, decimal point, and 4 pins to select each digit.

See Also

Task

  1. Implement minutes and seconds using software Delay and Display it on the 4-digit 7-segment LED

Interfacing to an LCD

LCD module pin descriptions

Hitachi HD44780 LCD controller dominated the character LCD modules. Most of the character LCD modules still use HD44780 or a variation of it. The HD44780 controller has a 14 pin interface for the microprocessor.

Pin Symbol I/O Description
1 VSS -- Ground
2 VCC -- +5v Power Supply
3 VEE -- Power Supply to Control Contrast
4 RS O RS = 0 to Select Command Register,
RS = 1 to Select Data Register
5 R/W I/O RW = 0 for Write
RW = 1 for Read
6 E O Enable
7 DB0 I/O 8-bit data bus (DB0 - DB7)
8 DB1 I/O
9 DB2 I/O
10 DB3 I/O
11 DB4 I/O 4/8-bit data bus
12 DB5 I/O
13 DB6 I/O
14 DB7 I/O
15 LED+ Supply Voltage
for LED+
+5V
16 LED- Supply Voltage
for LED-
0V

VCC, VSS, and VEE: While VCC and VSS provide +5V power supply and ground, respectively, VEE is used for controlling the LCD contrast.

RS, register select: There are two registers inside the LCD and the RS pin is used for their selection as follows. If RS = 0, the instruction command code register is selected, allowing the user to send a command such as clear display, cursor at home, and so on (or query the busy status bit of the controller). If RS = 1, the data register is selected, allowing the user to send data to be displayed on the LCD (or to retrieve data from the LCD controller).

R/W, read/write: R/W input allows the user to write information into the LCD controller or read information from it. R/W = 1 when reading and R/W = 0 when writing.

E, enable: The enable pin is used by the LCD to latch information presented to its data pins. When data is supplied to data pins, a High-to-Low pulse must be applied to this pin in order for the LCD to latch in the data present at the data pins. This pulse must be a minimum of 230 ns wide, according to Hitachi datasheet.

D0 ... D7: The 8-bit data pins are used to send information to the LCD or read the contents of the LCD's internal registers. The LCD controller is capable of operating with 4-bit data and only D4-D7 are used.

There are four types of access cycles to the LCD controller depending on RS and R/W as shown in the following Table. Normally, you write ASCII characters into the data buffer (called DDRAM in the data sheets) to have them displayed on the screen. However, you can create up to 8 new characters by writing to the character generator RAM (CGRAM). These new characters exist as ASCII data 0 to 7.

RS R/W Operation
0 0 Write a command to the LCD instruction register
0 1 Read Busy Flag (bit 7)
1 0 Write data to the LCD data buffer
1 1 Read data from the LCD to the microcontroller

To display letters and numbers, we send ASCII codes for the letters A-Z, a-z, numbers 0-9, and the punctuation marks to these pins while making RS = 1.

There are also instruction command codes that can be sent to the LCD in order to clear the display, force the cursor to the home position, or blink the cursor. The following table lists some commonly used LCD Commands.

Code
(Hex)
Command to LCD Instruction Register
1 clear Display Screen
2 Return Cursor Home
6 Increment Cursor (Shift Cursor to Right)
F Display ON, Cursor Blinking
80 Force Cursor to beginning of 1st Line
C0 Force Cursor to beginning of 2nd Line
38 2 Lines and 5x7 character (8-bit data, D0 to D7)
28 2 Lines and 5x7 character (4-bit data, D4 to D7)

EduARM4 board LCD Connection

LCD Interfacing
Figure: LCD Connection on the EduARM4 Board

Jumper Selection:
J5 - Short 3 & 4

Sending commands to LCDs

To send any of the commands to the LCD, make pin RS = 0 and send a High-to-Low pulse on the E pin to enable the internal latch of the LCD.

Sending data to the LCD

In order to send data to the LCD to be displayed, we must set pin RS = 1, and also send a High-to-Low pulse to the E pin to enable the internal latch of the LCD.

LCD runs much slower than the micro controller. The first two commands in the above table take up to 1.64 ms to execute and all the other commands and data take up to 40 us. After one command or data is written to the LCD controller, one must wait until the LCD controller is ready before issuing the next command/data otherwise the second command/data will be ignored. An easy way is to delay the microcontroller for the maximam time it may take for the previous command.

  • The LCD's data pins are connected to PORTB of the micro controller.
  • The LCD's RS pin is connected PORTA6 of the micro controller.
  • The LCD's R/W pin is connected to the ground (Always in write mode).
  • The LCD's E pin is connected to PORTA7 of the micro controller.

Both Ports x and y are configured as output ports.

Checking LCD busy flag

The above programs used a time delay before issuing the next data or command. This allows the LCD a sufficient amount of time to get ready to accept the next data. However, the LCD has a busy flag. We can monitor the busy flag and issue data when it is ready. This will speed up the process. To check the busy flag, we must read the command register (R/W = 1, RS = 0). The busy flag is the D7 bit of that register. Therefore, if R/W = 1, RS = 0; when D7 = 1 (busy flag = 1), the LCD is busy taking care of internal operations and will not accept any new information. When D7 = 0, the LCD is ready to receive new information.

Doing so requires switching the direction of the port connected to the data bus to input mode when polling the status register then switch the port direction back to output mode to send the next command. If the port direction is incorrect, it may damage the micro controller or the LCD module.

LCD Cursor Position

In the LCD, one can move the cursor to any location in the display by issuing an address command. The next character sent will appear at the cursor position. For the two-line LCD, the address command for the first location of line 1 is 0x80, and for line 2 it is 0xC0. The following table shows address locations and how they are accessed:

RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
0 0 1 A6 A5 A4 A3 A2 A1 A0

where A6 A5 A4 A3 A2 A1 A0 = 0000000 to 0100111 for line 1 and A6 A5 A4 A3 A2 A1 A0 = 1000000 to 1100111 for line 2. See the following Table.

DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
Line 1 (min) 1 0 0 0 0 0 0 0
Line 1 (max) 1 0 1 0 0 1 1 1
Line 2 (min) 1 1 0 0 0 0 0 0
Line 2 (max) 1 1 1 0 0 1 1 1

The upper address range can go as high as 0100111 for the 40-character-wide LCD while for the 20-character-wide LCD the address of the visible positions goes up to 010011 (19 decimal = 10011 binary). Notice that the upper range 0100111 (binary) = 39 decimal, which corresponds to locations 0 to 39 for the LCDs of 40x2 size.

LCD Instructions

Instruction RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 Description Execution
Time(Max)
Clear
Display
0 0 0 0 0 0 0 0 0 1 Clears entire display and sets
DD RAM address 0 in
address counter
1.64 ms
Return
Home
0 0 0 0 0 0 0 0 1 - Sets DD RAM address to 0 as
address counter. Also returns
display being shifted to
original positions. DD RAM
contents remain unchanged.
1.64 ms
Entry
Mode Set
0 0 0 0 0 0 0 1 I/D S Sets cursor move direction
and specifies shift of display.
These operations are
performed during data write
and read.
40us
Display
On/Off
Control
0 0 0 0 0 0 1 D C B Sets On/Off of entire display
(D), cursor On/Off (C), and
blink of cursor position
character (B)
40us
Cursor or
Display
shift
0 0 0 0 0 1 S/C R/L - - Moves cursor and shifts
display without changing DD
RAM contents
40us
Function
Set
0 0 0 0 1 DL N F - - Sets interface data length
(DL), number of display lines
(N), and character font (F)
40us
Set CG
RAM
Address
0 0 0 1 AGC Sets CG RAM address. CG
RAM data is sent and
received after this setting.
40us
Set DD
RAM
Address
0 0 1 ADD Sets DD RAM address. DD
RAM data is sent and
received after this setting.
40us
Read Busy
Flag &
Address
0 1 BF AC Reads Busy flag (BF)
indicating internal operation
is being performed and reads
address counter contents.
40us
Write Data
CG or DD
RAM
1 0 Write Data Writes data into DD or CG
RAM.
40us
Read Data
CG or DD
RAM
1 1 Read Data Reads data from DD or CG
RAM.
40us

Abbreviations:

DD RAM: Display data RAM
CG RAM: Character generator RAM
AGC: CG RAM address
ADD: DD RAM address, corresponds to cursor address
AC: address counter used for both DD and CG RAM addresses
I/D: 1 = Increment, 0: Decrement
S =1: Accompanies display shift
S/C: 1 = Display shift, 0: Cursor move
R/L: 1: Shift to the right, 0: Shift to the left
DL: 1 = 8 bits, 0 = 4 bits
N: 1 = 2-line, 0 = 1-line
F: 1 = 5 x 10 dots, 0 = 5 x 7 dots
BF: 1 = Internal operation, 0 = Can accept instruction

LCD Initialization (8-Bit Interface)

Lcd init flowchart 8bit.png


LCD Datasheet

  1. LCD Module 1602A

See Also

Tasks

  1. Display "Welcome to DESE" on the 1st line and "Embedded Systems" on the 2nd line.
  2. The above text on both the lines should scroll from right to left at a reasonable rate.
  3. Create your own custom characters and Display it on 16x2 LCD screen.

SPI Protocol and DAC Interfacing

Two terminologies are widely implemented in the serial communication systems:

  1. Serial Communication Interface (SCI) and
  2. Serial Peripheral Interface (SPI).

Generally, the SCI belongs to the asynchronous serial communication interface but SPI belongs to the synchronous serial communication interface. In the TM4C123GH6PM MCU system, the SPI is used to work as a synchronous serial interface.

The fundamental difference between a UART, which implements an asynchronous protocol, and a SSI, which implements a synchronous protocol, is the manner in which the clock is implemented. Two devices communicating with asynchronous serial interfaces (UART) operate at the same frequency (baud rate) but have two separate clocks. With a UART protocol, the clock signal is not included in the interface cable between devices. Two UART devices can communicate with each other as long as the two clocks have frequencies within ±5% of each other.

Two devices communicating with synchronous serial interfaces (SSI) operate from the same clock (synchronized). With a SSI protocol, the clock signal is included in the interface cable between devices. Typically, the master device creates the clock, and the slave device(s) uses the clock to latch the data in and send data out.

Like any other serial communication interfaces, both SCI and SPI can use either full-duplex or half-duplex mode to perform the data transfers between terminals and peripherals.

The SPI (serial peripheral interface) is a bus interface incorporated in many devices such as ADC, DAC, and EEPROM. The SPI bus was originally started by Motorola (now Freescale), but in recent years has become a widely used by many semiconductor chip companies.

SPI Bus Protocol

SPI devices use only 2 pins for data transfer, called SDI (Din) and SDO (Dout), instead of the 8 or more pins used in traditional buses. This reduction of data pins reduces the package size and power consumption drastically, making them ideal for many applications in which space is a major concern. The SPI bus has the SCLK (serial clock) pin to synchronize the data transfer between two chips. The last pin of the SPI bus is CE (chip enable), which is used to initiate and terminate the data transfer. These four pins, SDI, SDO, SCLK, and CE, make the SPI a 4-wire interface.

In many chips, the SDI, SDO, SCLK, and CE signals are alternatively named as MOSI (Master Out Slave In), MISO (Master In Slave Out), SCK, and SS as shown in the following Figure. There is also a widely used standard called a 3-wire interface bus. In a 3-wire interface bus, we have SCLK and CE, and only a single pin for data transfer. The SPI 4-wire bus can become a 3-wire interface when the SDI and SDO data pins are tied together. However, there are some major differences between the SPI and 3-wire devices in the data transfer protocol. For that reason, a device must support the 3-wire protocol internally in order to be used as a 3-wire device. Many devices support both SPI and 3-wire protocols.

Spi architecture.png
Figure:SPI Architecture

How SPI works

SPI consists of two shift registers, one in master and the other in the slave side. Also there is a clock generator in the master side that generates the clock for the shift registers.

As you can see in the above figure, serial-out (MOSI) pin of the master shift register is connected to the serial-in (MISO) pin of the slave shift register. The master clock generator provides clock to shift register in both master and slave shift registers. The clock input of the shift registers can be falling- or rising-edge triggered.

In SPI, the shift registers are 8 bits long. It means that after 8 clock pulses, the contents of the two shift registers are interchanged. When the master wants to send a byte of data, it places the byte in its shift register and generates 8 clock pulses. After 8 clock pulses, the byte is transmitted to the slave shift register. When the master wants to receive a byte of data, the slave side should place the byte in its shift register and after 8 clock pulses the data will be received by the master shift register. It must be noted that SPI is full duplex meaning that it sends and receives data at the same time.

Clock polarity and phase in SPI device

In UART communication, transmitter and receiver must agree on a clock frequency (baud rate). In SPI communication, both master and slave uses the same clock but the master and slave(s) must agree on the clock polarity and phase with respect to the data. Freescale names these two options as CPOL (clock polarity) and CPHA (clock phase) respectively. At CPOL=0 the idle value of the clock is zero while at CPOL=1 the idle value of the clock is one.

  • CPHA=0 means sample data on the leading (first) clock edge, while
  • CPHA=1 means sample data on the trailing (second) clock edge.

Notice that if the idle value of the clock is zero the leading (first) clock edge is a rising edge but if the idle value of the clock is one, the leading (first) clock edge is a falling edge.

Spi clock polarity and phase.png
Figure: SPI Clock Polarity and phase


CPOL CPHA Data Read and Change Time SPI Mode
0
0
read on rising edge, changed on a falling edge
0
0
1
read on falling edge, changed on a rising edge
1
1
0
read on falling edge, changed on a rising edge
2
1
1
read on rising edge, changed on a falling edge
3
Table: SPI Clock Polarity and phase



SPI programming in TI ARM Tiva

Tm4c spi port pins.png

The TM4C123GH6PM microcontroller system includes four Synchronous Serial Interface modules (SSI0, SSI1, SSI2 and SSI3). Each SSI module provides a master or a slave interface for synchronous serial communication (SSI) with peripheral devices that have either Freescale SPI, MICROWIRE, or Texas Instruments synchronous serial interfaces (SSI). The SSI modules are located at the following base addresses:


SSI Module Base Address
SSI0
0x40008000
SSI1
0x40009000
SSI2
0x4000A000
SSI3
0x4000B000

The SSI protocol includes four I/O lines:

  1. The slave select SSI0Fss is an optional negative logic control signal from master to slave signal signifying the channel is active.
  2. The second line, SSI0Clk, is a 50% duty cycle clock generated by the master.
  3. The SSI0Tx (master out slave in, MOSI) is a data line driven by the master and received by the slave.
  4. The SSI0Rx (master in slave out, MISO) is a data line driven by the slave and received by the master.

In order to work properly, the transmitting device uses one edge of the clock to change its output, and the receiving device uses the other edge to accept the data.

On the TM4C the shift register can be configured from 4 to 16 bits. The shift register in the master and the shift register in the slave are linked to form a distributed register. The following figure illustrates communication between master and slave.

Tm4c spi and io device.png
Figure: A synchronous serial interface between a microcontroller and an I/O device

Typically, the micro controller and the I/O device slave are so physically close we do not use interface logic. The SSI on the TM4C employs two hardware FIFOs. Both FIFOs are 8 elements deep and 4 to 16 bits wide, depending on the selected data width. When performing I/O the software puts into the transmit FIFO by writing to the SSI0_DR_R register and gets from the receive FIFO by reading from the SSI0_DR_R register. If there is data in the transmit FIFO, the SSI module will transmit it. With SSI it transmits and receives bits at the same time. When a data transfer operation is performed, this distributed 8- to 32-bit register is serially shifted 4 to 16 bit positions by the SCK clock from the master so the data is effectively exchanged between the master and the slave. Data in the master shift register are transmitted to the slave. Data in the slave shift register are transmitted to the master. Typically, the micro controller is master and the I/O module is the slave, but one can operate the micro controller in slave mode.

The SPI transmits data at the same time as it receives input. In the Freescale modes, the SPI changes its output on the opposite edge of the clock as it uses to shift data in. There are three mode control bits (MS, SPO, SPH) that affect the transmission protocol. If the device is a master (MS=0) it generates the SCLK, and data is output on the SSI0Tx pin, and input on the SSI0Rx pin. The SPO control bit specifies the polarity of the SCLK. In particular, the SPO bit specifies the logic level of the clock when data is not being transferred. The SPH bit affects the timing of the first bit transferred and received. If SPH is 0, then the device will shift data in on the first (and 3rd, 5th, 7th, … etc.) clock edge. If SPH is 1, then the device will shift data in on the second (and 4th, 6th, 8th, … etc.) clock edge. The data is transmitted MSB first.

Tm4c ssi ti single transfer.png
Figure: Synchronous serial TI single transfer mode

Enabling Clock to SSI

To enable and use any of the peripheral modules in the Tiva chip, we must enable the clock to it. We use RCGCSSI register to enable the clock to SSI modules. Notice again RCGCSSI is part of the SYSCTL registers. Writing a one to each of the R3 to R0, enables the corresponding SSI module. We need RCGCSSI = 0x0F to enable the clock to all four SSI modules.

Tm4c rcgcssi r.png
Figure: RCGCSSI
Bit/Filed Name Description
0
R0
SSI Module 0 Run Mode Clock Gating Control
0: SSI module 0 is disabled.
1: Enable and provide a clock to SSI module 0 in Run mode.
1
R1
SSI Module 1 Run Mode Clock Gating Control
0: SSI module 1 is disabled.
1: Enable and provide a clock to SSI module 1 in Run mode.
2
R2
SSI Module 2 Run Mode Clock Gating Control
0: SSI module 2 is disabled.
1: Enable and provide a clock to SSI module 2 in Run mode.
3
R3
SSI Module 3 Run Mode Clock Gating Control
0: SSI module 3 is disabled.
1: Enable and provide a clock to SSI module 3 in Run mode.
Table: Synchronous Serial Interface Run Mode Clock Gating Control (RCGCSSI)

Configuring the SSI Module

The SSICR0 (SSI Control register 0) sets SSI configuration. The following figure shows the bits of SSICR0 and the table describes the function of each bit. Although the conventional SPI uses only 8-bit data, the SSI modules allow transfer of data between 4 bits to 16 bits.

Tm4c ssicr0 r.png
Figure: SSICCR0


Bits/Fields Name Function Description
0-3
DSS
SSI Data Size Select 0x0000: Reserved
0x0001: Reserved
0x0010: Reserved
0x0011: 4-bit data,
0x0100: 5-bit data,
0x0101: 6-bit data,
0x0110: 7-bit data,
0x0111: 8-bit data
0x1000: 9-bit data,
0x1001: 10-bit data,
0x1010: 11-bit data,
0x1011: 12-bit data,
0x1100: 13-bit data
0x1101: 14-bit data,
0x1110: 15-bit data,
0x1111: 16-bit data
4-5
FRF
SSI Frame Format Select 0x00: SPI,
0x01: TI,
0x10: MICROWIRE frame format and
0x11: Reserved
6
SPO
SSI Serial Clock Polarity
(Only works for Freescale SPI Frame)
0: The inactive level for the SSInClk pin is Low.
1: The inactive level for the SSInClk pin is High.
Note: If this bit is set, the software must also configure the GPIO port pin corresponding to the SSInClk signal as a pull-up in the GPIO Pull-Up Select ( GPIOPUR) register.
7
SPH
SSI Serial Clock Phase
(Only work for Freescale SPI Frame)
0: Data is captured on the First clock edge transition.
1: Data is captured on the Second clock edge transition.
8-15
SCR
SSI Serial Clock Rate This bit field is used to generate the transmit and receive
bit rate of the SSI (0–255)
BR=SysClk/(CPSDVSR * (1 + SCR))
Table: SSICR0

Setting Bit Rate

SSI module clock source can be either of System Clock or PIOSC (Precision Internal Oscilator). The selected frequency is fed to prescaler before it is used by the Bit Rate circuitry, as shown in the following figure.

Tm4c spi bitrate selection.png
Figure: Bit Rate selection

The CPSDVSR (CPS Divisor) value comes from the prescaler divisor register shown in the following figure.

Tm4c ssicpsr r.png
Figure: SSI Clock Prescale (SSICPSR)

The lower 8 bits of SSICPSR (SSI Clock Prescale) register are used to divide the CPU clock before it is fed to the Bit Rate circuitry. Only even values can be used for the prescaler since the D0 must be 0. For the prescaler register, the lowest value is 2 and the highest is 254.

The SSICR0 (SSI Control register 0) allows the Bit Rate selection among other things. The output of clock prescaler circuitry is divided by 1 + SCR and then used as the SSI baud rate clock. The value of SCR can be from 0 to 255.

We can use the following formula to calculate the Bit Rate (BR):

BR=SysClk/(CPSDVSR × (1 + SCR))

Example 1: Assume the prescaler register has SSICPSR=0x20 and system clock frequency is 16MHz. Find the values for the SCR part of the SSOCR0 register for the bit rate of (a) 100K, (b) 250K, and (c) 500K.

Solution: Since the prescaler is 0x20 (32 in decimal), we have 16MHz / 32 = 500KHz. Now:
(a) 16MHz / (32 x (1 + 4)) = 100KHz.
(b) 16MHz / (32 x (1 + 1)) = 250KHz.
(c) 16MHz / (32 x (1 + 0)) = 500KHz.

Example 2: In a given program, we have Bit Rate=50KHz and SCR=03 in SSICR0 register. Find the prescaler register value if system clock frequency is 16MHz.

Solution:

BR = SysClk / (CPSDVSR × (1 + SCR))

50KHz = 16MHz / (X × (1 + 3). Now, the prescaler value is 80 in decimal or 0x50 in Hex. Therefore the SSICPSR = 0x50.

Other SPI configurations

The D3-D0 bits of SSICR0 are used to select the data size. In most cases, we use 8-bit data size. The bits 5-4 are used to select various protocols. Upon Reset, the default protocol is Freescale SPI.

Example 3: In example 1, find the values for the control register SSICR0 register if we want the Freescale SPI with 8-bit data size and SPH=1, SPO=1.

Solution:

(a) SSICR0=00000100 00110111 in binary or = 0x437. Notice, we have set SPH and SPO bits to 1.
(b) 0x137, and
(c) 0x037.

Master or Slave?

The SSI Module inside the Tiva chip can be Master or Slave. We use MS bit in SSI control register 1 (SSICR1) to designate the Tiva chip as master or slave. Another important bit in the SSICR1 register is enable/ disable bit (SSE). We must disable SSIx module during the initialization process and enable it afterward.

Tm4c ssicr1 r.png
Figure: SSI Control 1 (SSICR1)


Bit Name Function Description
0 LBM SSI Loopback Mode 0: Normal serial port operation enabled.
1: Output of the transmit serial shift register is connected internally
to the input of the receive serial shift register.
1 SSE SSI Synchronous Serial Port Enable 0: SSI operation is disabled.
1: SSI operation is enabled.
This bit must be cleared before any control registers are reprogrammed.
2 MS SSI Master/Slave Select Select Master or Slave mode
0: The SSI is configured as a Master.
1: The SSI is configured as a Slave.
Note: Can be modified only when the SSI is disabled (SSE = 0)
3 Reserved
-
-
4 EOT End Of Transmission This bit is only valid for Master mode devices and operations (MS = 0x0)
0: The TXRIS interrupt indicates that the transmit FIFO is half-full or less.
1: The End of Transmit interrupt mode for the TXRIS interrupt is enabled.
31:5 Reserved
-
-
Table: SSI Control 1 (SSICR1)

Data Register

The data is placed in SSIDR (SSI Data register) for transmission. The SSIDR is also used for the received data buffer. In 8-bit data size selection, we must place the data into the lower 8-bits of the register and the rest of the register are unused. In the receive mode, data is right-justified meaning the lower 8-bit holds the received data.

Tm4c ssidr r.png
Figure: SSIDR register

Status Flag Register

We use the SSI Status flag register (SSISR) to monitor to see whether a byte has been received or if the transmission buffer is empty and ready for the next byte to be transmitted.

Tm4c ssisr r.png
Figure: SSI Status (SSISR) register


Bits Name Function Description
0 TFE SSI Transmit FIFO Empty The bit is 1 when the transmit FIFO is empty
1 TNF SSI Transmit FIFO Not Full The bit is 1 when the SSI transmit FIFO not full
2 RNE SSI Receive FIFO Not Empty The bit is 1 when the receive FIFO is not empty
3 RFF SSI Receive FIFO Full The bit is 1 when the receive FIFO is full
4 BSY SSI Busy Bit The bit is 1 when the SSI is currently transmitting or receiving
Table: SSI Status (SSISR)

Interrupt using NVIC

We can use an interrupt handler to do the job of transmit and receive. By enabling the interrupt bits in SSIIM (SSI Interrupt Mask) register, we direct the interrupt to the NVIC interrupt controller and write an interrupt handler. Upon Reset, the interrupts are masked and raising of the flag will not direct it to NVIC.

Tm4c ssiim r.png
Figure: SSIIM register


Bits Name Function Description
0 RORIM SSI Receive Overrun Interrupt Mask The receive FIFO overrun interrupt is masked when
RORIM is zero and not masked when it is one
1 RTIM SSI Receive Time-Out Interrupt Mask The receive FIFO time-out interrupt is masked when
RTIM is zero and not masked when it is one
2 RXIM SSI Receive FIFO Interrupt Mask The receive FIFO interrupt is masked when
RXIM is zero and not masked when it is one
3 TXIM SSI Transmit FIFO Interrupt Mask The transmit FIFO interrupt is masked when
TXIM is zero and not masked when it is one
Table: SSIIM


LTC1661 SPI DAC

In this section we show an SPI-based DAC and its interfacing to TM4C. The LTC1661 is a 10-bit SPI serial DAC from Linear Technology. It has two separate output channels, named A and B, as shown in the following figure.

Ltc1661 block dia.png

The relation between the input number and the output voltage is as follows:

VOUT = ( VREF × DIN) / 1024

We can control the LTC1661 by sending 2 bytes of data. As shown in the following figure, the 16-bit is made of 3 parts: control code (4 bits), data (10 bits), and not used (2 bits). The control code are used to control the internal parts of the LTC1661.

Ltc1661 data pkt.png

As shown in above figure, each DAC is double buffered to provide simultaneous update. To do so, we load input buffers with proper values and then load the DAC buffers simultaneously. The following table shows the list of available control codes. To decrease power consumption, the DAC has a sleep mode, as well. We can switch between sleep and awake modes using proper control code.

A3 A2 A1 A0 Interrupt Register DAC Register Power Down
Status
Comments
0 0 0 0
No Change No Update No Change No operation. power-down status unchanged
0 0 0 1
Load DAC A No Update No Change Load input register A with data. DAC outputs unchanged.
power-down Status unchanged
0 0 1 0
Load DAC B No Update No Change Reserved
0 0 1 1
-
-
-
Reserved
0 1 0 0
-
-
-
Reserved
0 1 0 1
-
-
-
Reserved
0 1 1 0
-
-
-
Reserved
0 1 1 1
-
-
-
Reserved
1 0 0 0
No Change Update Outputs Wake Load both DAC Regs with existing contents of input Regs.
Outputs update. Part wakes up
1 0 0 1
Load DAC A Update Outputs Wake Load input Reg A. Load DAC Regs with new contents of
input Reg A and existing contents of Reg B. Outputs update.
1 0 1 0
Load DAC B Update Outputs Wake Load input Reg B. Load DAC Regs with existing contents of
input Reg A and new contents of Reg B. Outputs update
1 0 1 1
-
-
-
Reserved
1 1 0 0
-
-
-
Reserved
1 1 0 1
No Change No Update Wake Part wakes up. Input and DAC Regs unchanged.
DAC outputs reflect existing contents of DAC Regs
1 1 1 0
No Change No Update Sleep Part goes to sleep. Input and DAC Regs unchanged.
DAC outputs set to high impedance state
1 1 1 1
Load ADCs A, B
with same10-bit code
Update Outputs Wake Load both input Regs. Load both DAC Regs with new
contents of input Regs. Outputs update. Part wakes up

Example

Assuming that VREF=5V, find the result of sending the following packets to LTC1661:

  1.  0001 0001 0000 0000 binary  (0x1100)              
  2. 1010 1000 1111 1100 binary (0xA8FC)

  Solution:

  1. In 0001 0001 0000 0000, control code is 0001. As a result it loads data = 0001000000 (decimal 64) to the input buffer register for channel A. Note that the output is not updated with this control code. The output will be updated after a control code of 1000 is sent. Therefore VOUTA = VREF * DIN / 1024 = 5 * 64 / 1024 = 0.31V.
  2. In 1010 1000 1111 1100, the control code is 1010. As a result it loads data 1000111111 (decimal 575) to the input buffer register of channel B and also updates the output. Therefore, VOUTB = VREF * DIN / 1024 = 5 * 575 / 1024 = 2.81V.

Configuring GPIO for SSI

In using SSI, we must also configure the GPIO pins to allow the connection of the CPU pins to SPI device pins. See the following table. In this regard, it is the same as all other peripherals. The steps are as follows:

  1. Enable the clock to the appropriate GPIO modules via the RCGCGPIO register.
  2. The related AFSEL bits (lower 8 bits) in the GPIO Alternate Function Select (GPIOAFSEL) register must be set to 1.
  3. The related bits in the GPIO Digital Enable (GPIODEN) register must be set to 1 to enable the digital function and disable the analog function.
  4. The related PMCx field in the GPIO Port Control (GPIOPCTL) register must be assigned with appropriate codes to enable the GPIO pins to work as desired SSI signal pins.
  5. Optionally the related AMSEL bits in the GPIO Analog Mode Select (GPIOAMSEL) register should be 0 to enable the analog isolation circuits to enable the selected pin to work as digital function pin.


SSI Pin GPIO Pin Pin Type PIN Function
SSI0Clk PA2
I/O
SSI Module 0 Clock
SSI0Fss PA3
I/O
SSI Module 0 Frame
SSI0Rx PA4
I
SSI Module 0 Receive
SSI0Tx PA5
O
SSI Module 0 Transmit
SSI1Clk PD0 or PF2
I/O
SSI Module 1 Clock
SSI1Fss PD1 or PF3
I/O
SSI Module 1 Frame
SSI1Rx PD2 or PF0
I
SSI Module 1 Receive
SSI1TX PD3 or PF1
O
SSI Module 1 Transmit
SSI2Clk PB4
I/O
SSI Module 2 Clock
SSI2Fss PB5
I/O
SSI Module 2 Frame
SSI2Rx PB6
I
SSI Module 2 Receive
SSI2TX PB7
O
SSI Module 2 Transmit
SSI3Clk PD0
I/O
SSI Module 3 Clock
SSI3Fss PD1
I/O
SSI Module 3 Frame
SSI3Rx PD2
I
SSI Module 3 Receive
SSI3TX PD3
O
SSI Module 3 Receive
Table: GPIO Pin Assignment for all 4 SSI Modules


DAC LTC1661 on EduARM4 Trainer Board

Tm4c123 launchpad to lt1661 conn.png
Fig:TM4C123 LaunchPad to LTC1661 Connection on EduARM4 Trainer Board

Note: Short 1 & 2 of J8 (EduARM4 Trainer Board)

Configuring SSI for SPI data transmission

After the GPIO configuration, we need to take the following steps to configure the SSI for the SPI protocol:

  1. Enable the clock to SSI module using RCGCSSI (SYSCTL_RCGCSSI_R).
  2. Disable the SSI via bit 1 of SSICR1 (SSIn_CR1_R) register before initialization.
  3. Set the Bit Rate with the SSICPSR (SSIn_CPSR_R) prescaler and SSICR0 (SSIn_CR0_R) control registers.
  4. Also select the SPI mode, phase, polarity, and data size in SSICR0 (SSIn_CR0_R) control register.
  5. Select the master mod in SSISCR1 (SSIn_CR1_R) register.
  6. Enable SSI using SSICR1 (SSIn_CR1_R) register.
  7. Assert slave select signal.
  8. Wait until the TNF flag (transmit FIFO not full) in SSISR (SSIn_SR_R status register) goes high, then load a byte of data into SSIDR (SSIn_DR_R data register) to be transmitted.
  9. Repeat step 8 above until all the data are in the FIFO.
  10. Wait until transmit is complete (transmit FIFO empty and SSI not busy).
  11. Deassert the slave select signal.

(where n = 0 to 3)

SPI Examples and Tasks

Sample Program

  1. /*
  2.  * Using SSI1 to send A to Z characters via SPI1
  3. */
  4.  
  5. #include <stdint.h>
  6. #include "inc/tm4c123gh6pm.h"
  7.  
  8. void Init_SSI1(void);
  9. void SSI1_Write(unsigned char data);
  10.  
  11. int main(void)
  12. {
  13.     unsigned char i;
  14.  
  15.     Init_SSI1();
  16.  
  17.     for(;;) {
  18.         for (i = 'A'; i <= 'Z'; i++) {
  19.             SSI1_Write(i);      /* write a character */
  20.         }
  21.     }
  22. }
  23.  
  24. void SSI1_Write(unsigned char data)
  25. {
  26.     GPIO_PORTF_DATA_R &= ~0x04;  /* assert SS low */
  27.  
  28.     while((SSI1_SR_R & 2) == 0)
  29.         ;                        /* wait until FIFO not full */
  30.     SSI1_DR_R = data;            /* transmit high byte */
  31.  
  32.     while(SSI1_SR_R & 0x10)
  33.         ;                        /* wait until transmit complete */
  34.     GPIO_PORTF_DATA_R |= 0x04;   /* keep SS idle high */
  35. }
  36.  
  37. void Init_SSI1(void)
  38. {
  39.     SYSCTL_RCGCSSI_R |= 2;       /* enable clock to SSI1 */
  40.     SYSCTL_RCGCGPIO_R |= 8;      /* enable clock to GPIOD for SSI1 */
  41.     SYSCTL_RCGCGPIO_R |= 0x20;   /* enable clock to GPIOF for slave select */
  42.  
  43.     /* configure PORTD 3, 1 for SSI1 clock and Tx */
  44.     GPIO_PORTD_AMSEL_R &= ~0x09; /* disable analog for these pins */
  45.     GPIO_PORTD_DEN_R |= 0x09;    /* and make them digital */
  46.     GPIO_PORTD_AFSEL_R |= 0x09;  /* enable alternate function */
  47.     GPIO_PORTD_PCTL_R &= ~0x0000F00F; /* assign pins to SSI1 */
  48.     GPIO_PORTD_PCTL_R |= 0x00002002;  /* assign pins to SSI1 */
  49.  
  50.     /* configure PORTF 2 for slave select */
  51.     GPIO_PORTF_DEN_R |= 0x04;    /* make the pin digital */
  52.     GPIO_PORTF_DIR_R |= 0x04;    /* make the pin output */
  53.     GPIO_PORTF_DATA_R |= 0x04;   /* keep SS idle high */
  54.  
  55.     /* SPI Master, POL = 0, PHA = 0, clock = 4 MHz, 16 bit data */
  56.     SSI1_CR1_R = 0;             /* disable SSI and make it master */
  57.     SSI1_CC_R = 0;              /* use system clock */
  58.     SSI1_CPSR_R = 2;            /* prescaler divided by 2 */
  59.     SSI1_CR0_R = 0x0007;        /* 8 MHz SSI clock, SPI mode, 8 bit data */
  60.     SSI1_CR1_R |= 2;            /* enable SSI1 */
  61. }


# Generate a saw tooth waveform with LTC1661 SPI DAC
  1. (Observe the waveform on J9 of 1/2 Pin)
  2. Generate a sine waveform with LTC1661 SPI DAC
    (Observe the waveform on J9 of 1/2 Pin)

Inter-Integrated Circuit (I2C) Interface

The inter-integrated circuit I2C interface was proposed by Philips in the late 1980s as a means to connect external devices to the micro controller using just two wires. The SSI interface has been very popular, but it takes 3-wires for simplex and 4-wires for full duplex communication. The I2C bus is a simple two-wire bi-directional serial communication system that is intended for communication between micro controllers and their peripherals over short distances. This is typically, but not exclusively, between devices on the same printed circuit board, the limiting factor being the bus capacitance. I2C is ideal to attach low-speed peripherals to a motherboard or embedded system or anywhere that a reliable communication over a short distance is required.

I2C provides a connection oriented communication with acknowledge. I2C devices use only 2-pins for data transfer, instead of the 8 or more pins used in traditional parallel buses. These two signals are called "Serial Clock" (SCL) which synchronize the data transfer between two chips, and "Serial Data" (SDA). The interface will operate at baud rates of up to 100 kbps with maximum capacitive bus loading. The module can operate up to a baud rate of 400 kbps provided the I2C bus slew rate is less than 100ns. The maximum interconnect length and the number of devices that can be connected to the bus are limited by a maximum bus capacitance of 400pF in all instances. These parameters support the general trend that communication speed can be increased by reducing capacitance. Version 2.0 supports a high speed mode with a baud rate up to 2.4 MHz (supported by TM4C).

I2C Bus Functional Overview

I2c bus config.png
Figure:I2C Bus Configuration

The Serial Clock Line (SCL) and the Serial Data line (SDA) are both bi-directional. Each line is open-drain, meaning a device may drive it low or let it float. A logic high occurs if all devices let the output float, and a logic low occurs when at least one device drives it low. The value of the pull-up resistor depends on the speed of the bus. 4k7 is recommended for baud rates below 100 kbps, 2k2 is recommended for standard mode, and 1kΩ is recommended for fast mode.

The SCL clock is used in a synchronous fashion to communicate on the bus. Even though data transfer is always initiated by a master device, both the master and the slaves have control over the data rate. The master starts a transmission by driving the clock low, but if a slave wishes to slow down the transfer, it too can drive the clock low (called clock stretching). In this way, devices on the bus will wait for all devices to finish. Both address (from Master to Slaves) and information (bi-directional) are communicated in serial fashion on SDA.

I2C Nodes

In I2C protocol, more than 100 devices can share an I2C bus. Each of these devices is called a node. Each node can operate as either master or slave. Master is a device that generate the Clock for the system, it also initiate and terminate a transmission. Slave is node that receives the clock and is addressed by the master. In I2C, both master and slave can receive or transmit data. So there are 4 modes of operation. They are:

  • master transmitter,
  • master receiver,
  • slave transmitter and
  • slave receiver.

Notice that each node can have more than one mode of operation at different times but it has only one mode of operation at the same time.

START and STOP conditions

I2C is a connection oriented communication protocol, it means that each transmission is initiated by a START condition and is terminated by STOP condition. Remember that the START and STOP conditions are generated by the master.

  • A High-to-Low transition on the SDA line while the SCL is High is defined as a START condition, and a
  • Low-to-High transition on the SDA line while SCL is High is defined as a STOP condition.

The bus is considered busy after a START condition and free after a STOP condition.

I2c start and stop conditions.png
Figure:I2C Bus START and STOP Conditions

The bus is considered busy between each pair of START and STOP conditions and no other master tries to take control of the bus when it is busy. If a master, which has the control of the bus, wishes to initiate a new transfer and does not want to release the bus before starting the new transfer, it issues a new START condition between a pair of START and STOP condition. It is called REPEATED START condition or simply RESTART condition.

Message format in I2C

In I2C, each address or data to be transmitted must be framed in 9-bit long. The first 8-bits are put on SDA line by the transmitter and the 9th bit is the acknowledge (ACK) by the receiver or it may be NACK (negative acknowledge). Notice that the clock is always generated by the master, regardless of it being transmitter or receiver. To allow acknowledge, the transmitter releases the SDA line during the 9th clock so the receiver can pull the SDA line low to indicate an ACK. If the receiver doesn’t pull the SDA line low, it is considered as NACK.

I2c byte format.png
Figure: I2C Byte Format

In I2C, each byte may contain either address or data. Also notice that:

START condition + slave address byte + one or more data byte + STOP condition

together form a complete data transfer.

Address Byte Format

All address bytes transmitted on the I2C bus are nine bits long. It consists of seven address bits, one READ/WRITE control bit and an acknowledge bit.

I2c address byte format.png
Figure: Address Byte Format in I2C

Slave address bits are used to address a specific slave device on the bus. 7 bit address let the master to address maximum of 128 slaves on the bus. Although address 0000 000 is reserved for general call and all address of the format 1111 xxx are reserved in many devices. That means 119 devices can share an I2C bus. In I2C bus the MSB of the address is transmitted first.

The 8th bit in the byte is READ/WRITE control bit. If this bit is set, the master will read the next byte from the slave, otherwise, the master will write the next byte on the bus to the slave. When a slave detects its address on the bus, it knows that it is being addressed and it should acknowledge in the ninth clock cycle by pulling SDA to low. If the addressed slave is not ready or for any reason does not want to respond to the master, it should leave the SDA line high in the 9th clock cycle. It is considered as NACK. In case of NACK, the master can transmit a STOP condition to terminate the transmission, or a REPEATED START condition to initiate a new transmission.

Data Byte Format

Like other bytes, data bytes are 9 bits long too. The first 8 bits are a byte of data to be transmitted and the 9th bit, is ACK. If the receiver has received the last byte of data and does not wish to receive more data, it may signal a NACK by leaving the SDA line high. The master should terminate the transmission with a STOP after a NACK appears. In data bytes, like address bytes, MSB is transmitted first.

Combining Address and Data Bytes

In I2C, normally, a transmission is started by a START condition, followed by an address byte (SLA+R/W), one or more data bytes and finished by a STOP condition.

I2c data transmission.png
Figure: Data Transmission in I2C

Clock stretching

One of the features of the I²C protocol is clock stretching. It is a kind of flow control. If an addressed slave device is not ready to process more data it will stretch the clock by holding the clock line (SCL) low after receiving (or sending) a bit of data so that the master will not be able to raise the clock line (because devices are wire-ANDed) and will wait until the slave releases the SCL line to show it is ready for the next bit.

I2c clock stretching.png
Figure: Clock Stretching in I2C

Arbitration

I2C protocol supports multi-master bus system. It doesn’t mean that more than one master can use the bus at the same time. Each master waits for the current transmission to finish and then start to use the bus. But it is possible that two or more masters initiate a transmission at about the same time. In this case the arbitration happens.

Each master has to check the level of the bus and compare it with the levels it is driving; if it doesn't match, that master has lost the arbitration, and will switches to slave mode. In the case of arbitration, the winning master will continue its job. Notice that neither the bus is corrupted nor the data is lost.

Multi-byte burst write

Burst mode writing is an effective means of loading data into consecutive memory locations. It is supported in I2C like SPI and many other serial protocols. In burst mode, we provide the address of the first memory location, followed by the data for that location. From then on, consecutive bytes are written to consecutive memory locations. In this mode, the I2C device internally increments the address location as long as STOP condition is not detected. The following steps are used to send (write) multiple bytes of data in burst mode for I2C devices.

  1. The master generates a START condition.
  2. The master transmits the slave address followed by a zero bit (for write).
  3. The master transmits the address of the first location.
  4. The master transmits the data for the first location and from then on, the master simply provides consecutive bytes of data to be placed in consecutive memory locations in the slave.
  5. The master generates a STOP condition.

The following figure shows how to write 0x05, 0x16 and 0x0B to 3 consecutive locations starting from location 00001111 of slave 1111000.

I2c multi byte burst write.png
Figure: Multi-byte Burst Write

Multi-byte burst read

Burst mode reading is an effective means of bringing out the contents of consecutive memory locations. In burst mode, we provide the address of the first memory location only. From then on, contents are brought out from consecutive memory locations. In this mode, the I2C device internally increments the address location as long as STOP condition is not detected. The following steps are used to get (read) multiple bytes of data using burst mode for I2C devices.

  1. The master generates a START condition.
  2. The master transmits the slave address followed by a zero bit (for writing the address).
  3. The master transmits the address of the first memory location.
  4. The master generates a RESTART condition to switch the bus direction from write to read.
  5. The master transmits the slave address followed by a one bit (for read).
  6. The master clocks the bus 8 times and the slave device provides the data for the first location.
  7. The master provides an ACK.
  8. The master reads the consecutive locations and provides an ACK for each byte.
  9. The master gives a NACK for the last byte received to signal the slave that the read is complete.
  10. The master generates a STOP condition.

The following figure shows how to read three consecutive locations starting from location 00001111 of slave number 1111000.

I2c multi byte burst read.png
Figure: Multi-byte Burst Read


The following table lists some addresses that have special meaning. A write to address 0 is a general call address, and is used by the master to send commands to all slaves. The 10-bit address mode gives two address bits in the first frame and 8 more address bits in the second frame. The direction bit for 10-bit addressing is in the first frame.

Address R/W Description
0000 000 0 General call address
0000 000 1 Start byte
0000 001 x CBUS address
0000 010 x Reserved for different bus formats
0000 011 0 Reserved
0000 1xx x High speed mode
0000 0xx x 10-bit address
0000 1xx X Reserved
Table: Special addresses used in the I2C network


I2C Programming in TI ARM Tiva

Tm4c i2c ports.png

The TI ARM Tiva micro controllers have zero to ten I2C modules. The TM4C micro controllers implement just a subset of the standard. They support master and slave modes, can generate interrupts on start and stop conditions, and allow I2C networks with multiple masters. Microcontroller pins SDA and SCL can be connected directly to an I2C network, because I2C networks are intended to connect devices on the same PCB, no special hardware interface electronics are required. The I2C modules are located at the following base addresses:

SSI Module Base Address
I2C0 0x4002.0000
I2C1 0x4002.1000
I2C2 0x4002.2000
I2C3 0x4002.3000

Table: I2C Module Base Address for TI Tiva

Enabling Clock to I2C Module

To enable and use any of the peripherals, we must enable the clock to it. We use RCGCI2C register to enable the clock to I2C modules. Notice again RCGCI2C is part of the SYSCTL registers. We need SYSCTL_RCGCI2C_R |= 0x0F, enables the clock to all four I2C modules.

Tm4c rcgci2c r.png
Figure: Inter-Integrated Circuit Run Mode Clock Gating Control (RCGCI2C)


Bits Name Function Description
0 R0 I2C0 Run Mode Clock Gating Control 1: enable, 0: disable
1 R1 I2C1 Run Mode Clock Gating Control 1: enable, 0: disable
2 R2 I2C2 Run Mode Clock Gating Control 1: enable, 0: disable
3 R3 I2C3 Run Mode Clock Gating Control 1: enable, 0: disable
Table: RCGCI2C Description

I2C Clock speed

The I2CMTPR (I2C Master Timer Period) register allows us to set the clock rate for the SCL. The SCL clock comes from the system clock and goes through clock divider circuit controlled by I2CMTPR register. The I2C standard defines four clock speeds for the SCL. They are:

  1. Standard mode of 100Kbps (bits per second),
  2. Fast mode of 400Kbps,
  3. Fast Plus mode of 1Mbps. and there is also a
  4. High-Speed (HS) mode which can be as high as 3.3Mbps.

The lower 7 bits (D6-D0, TPR) of I2CMTPR register are used to set the I2C clock speed.

Tm4c i2cmtpr r.png
Figure: I2C Master Timer Period (I2CMTPR)

We use the following formula to set the I2C clock speed:

SCL_PERIOD = 2×(1 + TPR)×(SCL_LP + SCL_HP)×CLK_PRD

In the above formula, SCL_PERIOD is the I2C clock period, CLK_PRD is the system clock period.

  • The SCL_LP is the SCL Low Period and is fixed at 6.
  • The SCL_HP is the SCL High Period and is fixed at 4.

Now, if we rearrange the above formula, we have:

SCL_PERIOD = 2×(1 + TPR)×(4 + 6)×CLK_PRD
SCL_PERIOD = 2×(1 + TPR)×10×CLK_PRD
SCL_PERIOD = (20×(1 + TPR))/System_Clock_Freq

Now, solving for TPR (the value we need for the I2CMTPR register) we have:

TPR = ( (System_Clock_Freq x SCL_PERIOD) / 20 ) - 1
TPR = (System_Clock_Freq) / (20 x I2C Clock) - 1

Example: Assume the system clock frequency is 16MHz. Find the values for the I2CMTPR register if we want I2C clock of (a) 100 Kbps, (b) 400 Kbps, and (c) 1 Mbps.

Solution: Using 16MHz for the CPU Frequency, we have:
TPR = (System_Clock_Freq) / (20 x I2C Clock) - 1 ,
TPR = (16MHz) / (20 x I2C Clock) - 1

(a) TPR = (16MHz) / (20 x 100K) - 1 = 8 - 1 = 7
(b) TPR = (16MHz) / (20 x 400K) - 1 = 2 - 1 = 1
(c) TPR = (16MHz) / (20 x 1M) - 1. This is not allowed due to the system clock frequency being too low.

Master or Slave?

The I2C Module inside the ARM chip can be Master or Slave. We use I2CMCR (I2C Master Configuration register) to designate the ARM chip as master or slave. Setting bit D4 to 1 makes the I2C of ARM chip as Master. Therefore, we need

I2CMCR = 00010000 = 0x10 for Master.
Tm4c i2cmcr.png
Figure: I2C Master Configuration (I2CMCR)


Bits Name Function Description
0 LPBK I2C Loopback 0: Normal operation, 1: Loopback
4 MFE I2C Master Function Enable 1: Enable Master Function, 0: Disable
5 SFE I2C Slave Function Enable 1: Enable Slave Function, 0: Disable
6 GFE I2C Glitch Filter Enable 1: Enable Glitch Filter, 0: Disable
Table: I2CMCR Description

Slave Address

To transmit a byte of data to an I2C device, we must specify its I2C address. We use I2CMSA (I2C Master Slave Address) register to hold the address of the slave device. Notice, the addresses in I2C are only 7 bits (maximum of 127 devices). In the I2CMSA register, the D7-D1 bits are used for the slave address and the LSB of D0 is used to indicate if we are transmitting to slave device or receiving from a slave device.

Tm4c i2cmsa r.png
Figure: I2C Master Slave Address (I2CMSA)

Data Register

In Master transmit mode, we place a byte of data in I2CMDR (I2C Master Data register) for transmission. Only the lower 8 bits of this register is used.

Tm4c i2cmdr r.png
Figure: I2C Master Data (I2CMDR)

Control and Status Flag Register

We have two registers with the same name. One we write to control the I2C module, the other one we read from to get the status of the I2C module. We use the I2CMCS (I2C Master Control/Status) register for both control and status. When we write to I2CMCS register, it configures the I2C controller operation. Upon reading it, we get the status to see if a byte has been transmitted and transmission buffer is empty and ready for the next byte.

Basically we have two registers with the same name. One we write to control the I2C module, the other one we read from to get the status of the I2C module.

Tm4c i2cmcs w r.png
Figure: I2CMCS - Write-Only Control Register


Bits Name Function Description
0 RUN I2C Master Enable 1: Master is enabled to transfer data
1 START Generate START It should be 1 to generate START condition
2 STOP Generate STOP It should be 1 to generate STOP condition
3 ACK Data Acknowledge Enable It should be 1 to generate auto ACK
4 HS High-Speed Enable It should be 1 to run in high speed mode
Table: I2CMS Write-Only Register


Tm4c i2cmcs r r.png
Figure: I2CMCS - Read-Only Control Register


Bits Name Function Description
0 BUSY I2C Busy 0: The controller is idle
1: The controller is busy.
1 ERROR Error 0: No error was detected on the last operation.
1: An error occurred on the last operation.
2 ADRACK Acknowledge Address 0: The transmitted address was acknowledged
1: The transmitted address was not acknowledged.
3 DATACK Acknowledge Data 0: The transmitted data was acknowledged
1: The transmitted data was not acknowledged.
4 ARBLST Arbitration Lost 0: The I2C controller won arbitration
1: The I2C controller lost arbitration.
5 IDLE I2C Idle 0: The I2C controller is not idle.
1: The I2C controller is idle.
6 BUSBSY Bus Busy 0: The I2C bus is idle
1: The I2C bus is busy.
7 CLKTO Clock Timeout Error 0: No clock timeout error
1: The clock timeout error has occurred.
Table: I2CMS Read-Only Register

After placing a byte of data in I2C Data register and the slave address in I2C Master Slave address register, we may write a value of 0x07 to I2CMCS register for the I2C to start a single byte data transmission from Master (microcontroller) to slave device. Notice, writing 0x07 to this register has all the three of STOP = 1, RUN = 1, and START = 1 in it. Because the START and STOP are set the I2C module will:

  • generate a START condition,
  • send the slave address with R/W bit,
  • check the acknowledge bit,
  • send the data byte,
  • check the acknowledge bit and
  • generate the STOP condition to terminate the transmission.

For multiple byte burst write or read, the protocols are different.

For a single byte write, after the START, the bus will go busy until the data byte is written and the STOP is sent. To monitor the progress of the transmission, we need to check the BUSBSY bit (bit 6) of the I2CMCS register. When this bit goes low, the transmission is complete.

Because ARM Cortex architecture incorporates buffered writes, the write of 0x07 to I2CMCS does not take effect immediately. Checking the BUSBSY bit immediately after setting the I2CMCS register to 0x07 may return a 0 to indicate the transmission has not started yet. To avoid this, the program should read the I2CMCS back to flush the write first before reading it again to check the BUSBSY bit.

When the transmission is complete, the program should check the ERROR bit (bit 1) to make sure there was no error in the transmission. Transmission error may be caused by either slave address was not acknowledged or the data write was not acknowledged. In either case, the ADRACK (bit 2) or the DATACK (bit 3) will be set.

In a system with multiple I2C masters, the program should check the BUSBSY (bit 6) of I2CMCS register to be sure that the bus is idle before starting a transmission. After the transmission is started, the program should check the ARBLST (bit 4) to see whether it lost the arbitration or not. If the arbitration is lost, the program should attempt the transmission after the bus is not busy anymore.

Enabling Open Drain

Most of the digital output pins are configured as totem-pole output (because the transistors of the output driver are stacked up like as totem-pole). The other name for this configuration is push-pull because it is pushing the current out when high and pulling the current in when low. This configuration allows for faster transition when the output is switching from high to low or from low to high. The problem with a totem-pole output is when more than one output is connected together and one output is high the other is low, the high outputs push the current out and the low outputs pull in the current. A large amount of current could flow between the outputs and damages the circuit.

One common solution to allow multiple outputs connected together is to use the open-drain output. (open-drain for CMOS devices or open-collector for TTL devices). In this output configuration, the output pin is connected to the drain of the output transistor while the source of that transistor is grounded. When the transistor is on, the output pin is grounded and when the transistor is off, the output pin (the drain) is open. The open-drain outputs may be connected together. A pull-up resistor is added so that the signal is high when none of the outputs is active. When any one of the outputs is active, the signal is low. It forms a "wired-AND" logic and is exactly what is required for the I2C bus.

For the I2C Module inside the TI ARM Tiva, we must enable the open-drain option for the I/O pins used by the I2C buses. We use the GPIOODR (GPIO Open Drain) register to enable the open drain. Depending on which pins we use for the I2C connection, we have to enable the GPIOODR register for that port.

Tm4c gpioodr r.png
Figure: GPIO Open Drain Select (GPIOODR)

Configuring GPIO for I2C

In using I2C, we must configure the GPIO pins to allow the connection of the CPU pins to I2C device pins. In this regard, it is same as all other peripherals. The steps are as follows:

  1. Enable the clock to GPIO pin by using RCGCGPIO.
  2. Set the GPIO AFSEL (GPIO alternate function) for I2C pins.
  3. Enable digital pins in the GPIODEN (GPIO Digital enable) register.
  4. Assign the I2C signals to specific pins using GPIOCTL register.
  5. Enable the open-drain option for the I/O pins used by the I2C signals.
    • With TI Tiva devices, the I2C pins are configured as open-drain by only setting the I2CSDA pin to open-drain.
    • The I2CSCL pin will become open-drain with I2CSDA.
    • Setting I2CSCL pin as open-drain will not work.


SSI Module Pin GPIO Pin
I2C0SCL PB2
I2C0SDA PB3
I2C1SDA PA6
I2C1SDA PA7
I2C2SCL PB4
I2C2SDA PB5
I2C3SCL PD0
I2C3SDA PD1
Table: I2C Pin Assignment

Configuring I2C for data transmission

I2c data transmit.png

After the GPIO configuration, we need to take the following steps to configure the I2C and send a byte of data to an I2C slave device.

  1. Enable the clock to I2C module using RCGCI2C (SYSCTL_RCGCI2C_R) register.
  2. Initialize the I2C as Master using I2CMCR (I2Cn_MCR_R) register.
  3. Set the I2C clock speed using I2CMTPR (I2Cn_MTPR_R) register.
  4. Place the slave address with the R/W bit cleared for write in the I2CMSA (I2Cn_MSA_R) register.
  5. Place the byte of data to be transmitted into the I2CMDR (I2Cn_MDR_R) register.
  6. Initiate the data transmission by writing value 0x07 to the I2CMCS (I2Cn_MCS_R) register. With I2CMCS = 0x07, will make the STOP = 1, RUN = 1, and START = 1.
  7. Read back I2CMCS (I2Cn_MCS_R) register to force the write out of the write buffer.
  8. Keep reading the I2CMCS (I2Cn_MCS_R) register and check the BUSY flag. Wait until it goes low.
  9. Now, read the I2CMCS (I2Cn_MCS_R) register again and check the ERROR flag to make sure there was no error.
    (Where n = 0 to 3)

To send n bytes of data, you should send the first bit like single bit transmission but leave STOP bit of I2CMCS 0 because we do not want to generate Stop Condition after sending the first byte (Instead of writing value 0x07 to the I2CMCS register, write value 0x03). Now repeat from step 5 to send more data by writing value 0x01 to the I2CMCS register. Note that you should leave the START and STOP bits of I2CMCS 0 because no Stop or Start Condition should be generated. Before sending the last byte you should make STOP bit of I2CMCS 1 to generate STOP Condition after sending the last byte of data.


DS1307 RTC Interfacing and Programming

The real-time clock (RTC) is a widely used device that provides accurate time and date information for many applications. Although some microcontrollers come with the RTC already embedded into the chip, we have to interface the vast majority of them to an external RTC chip. The DS1307 is a serial RTC with an I2C bus. In this section, we interface and program the DS1307 RTC. According to the DS1307 data sheet from Maxim, ”The clock/calendar provides seconds, minutes, hours, day, date, month, and year information". The end of the month date is automatically adjusted for months with fewer than 31 days, including corrections for leap year. The clock operates in either the 24-hour or 12-hour format with AM/PM indicator. The DS1307 has a built-in power-sense circuit that detects power failures and automatically switches to the battery supply.” The DS1307 does not support the Daylight Savings Time option".

Ds1307.png
Figure: DS1307 Pins

X1–X2: These are input pins that allow the DS1307 connection to an external crystal oscillator to provide the clock source to the chip. We must use a 32.768 kHz quartz crystal. The accuracy of the clock depends on the quality of this crystal oscillator.

Vbat: Pin 3 can be connected to an external +3 V lithium battery, thereby providing the power source to the chip when the external supply voltage is not available. We must connect this pin to ground if it is not used. A 48mAh lithium battery can provide the power needed for more than 10 years to power the chip.

GND: Pin 4 is the ground.

SDA: (Serial Data): Pin 5 is the SDA pin and must be connected to the SDA line of the I2C bus.

SCL: (Serial Clock): Pin 6 is the SCL pin and must be connected to the SCL line of the I2C bus.

SWQ/OUT: Pin 7 is an output pin providing 1 kHz, 4 kHz, 8 kHz, or 32 kHz frequency if enabled. This pin needs an external pull-up resistor to generate the frequency because it is open drain. If you do not want to use this pin you can omit the external pull-up resistor. We will see shortly how to control this pin.

VCC: Pin 8 is used as the primary voltage supply to the chip. This primary voltage source is generally set to +5 V. When Vcc falls below the Vbat level, the DS1307 switches to Vbat and the external lithium battery provides power to the RTC. When the system is powered on, the DS1307 automatically switches to the power supply from battery power.

Address map of the DS1307

The DS1307 has a total of 64 bytes of RAM space with addresses 00–3FH. The first seven locations, 00–06, are set aside for RTC values of time and date. The next byte is used for the control register. It is located at address 07 in hex. That leaves 56 bytes, from addresses 07H to 3FH, available for general-purpose data storage. That means the entire 64 bytes of RAM are accessible directly for read or write. The following table shows the address map of the DS1307.

Address Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Function Range
00H CH
10 Seconds
Seconds
Seconds 00-59
01H 0
10 Minutes
Minutes
Minutes 00-59
02H 0 12
24
10 Hour
PM/AM
10 Hour
Hours
Hours 1-12
+AM/PM
0-23
03H 0 0 0 0 0
Day
Day 01-07
04H 0 0
10 Date
Date
Date 01-31
05H 0 0 0 10 Month
Month
Month 1-12
06H
10 Year
Year
Year 00-99
07H OUT 0 0 SQWE 0 0 RS1 RS0 Control -
08H-3FH RAM 56x8 00h-FFH
Table: DS1307 Address Map

The DS1307 control register

As shown in the above table, the control register has an address of 07H. In the DS1307 control register, the bits control the function of the SQW/OUT pin. If the square wave output is disabled, setting the OUT bit to 1 will make the SQW/OUT pin low, and clearing the OUT bit to zero will make the SQW/OUT pin high. If SQWE (square wave enable) bit is set HIGH, the oscillator output is enabled, otherwise, it is disabled. RS1-RS0 (rate select) bits select the output frequency of the oscillator output according to the following table:

RS1 RS0 Output Frequency
0 0 1 Hz
0 1 4.096 kHz
1 0 8.192 kHz
1 1 32.768 kHz
Table: DS1307 RS Bits

CH bit in address 00

One of the most important bits in the Seconds (address location 00) in the DS1307 is the CH (Clock Halt) bit. It is the seventh bit of address location 00. Setting the CH bit to one disables the oscillator, while setting CH to zero enables the oscillator. The CH bit is undefined upon reset. In order to enable the oscillator, we must clear the CH during initial configuration.

Time and date address locations and modes

The byte addresses 0–6 are set aside for the time and date, as shown in the above table. The DS1307 provides data in BCD format only. Notice the data range for the hour mode. We can select 12-hour or 24-hour mode with bit 6 of Hours register at location 02. When bit 6 is 1, the 12-hour mode is selected, and bit 6 = 0 provides us the 24-hour mode. In the 12-hour mode, bit 5 indicates whether it is AM or PM. If bit 5 = 0, it is AM and if bit 5 = 1 it is PM. See the following example.

What value should be placed at location 02 to set the hour to: (a) 21, (b) 11AM, (c) 12 PM

Solution:
(a) For 24-hour mode, we have D6 = 0. Therefore, we place 0010 0001 (or 0x21) at location 02, which is 21 in BCD.
(b) For 12-hour mode, we have D6 = 1. Also, we have D5 = 0 for AM. Therefore, we place 0101 0001 at location 02, which is 51 in BCD.
(c) For 12-hour mode, we have D6 = 1. Also, we have D5 = 1 for PM. Therefore, we place 0111 0010 at location 02, which is 72 in BCD.

Register pointer

In DS1307, there is a register pointer that specifies the byte that will be accessed in the next read or write command. The first read or write operation sets the value of the pointer. After each read or write operation, the content of the register pointer is automatically incremented to point to the next location. It is useful in multi-byte read or write.

Writing to DS1307

To set the value of the register pointer and write one or more bytes of data to DS1307, you can use the following steps:

  1. To access the DS1307 for a write operation, after sending a START condition, you should transmit the address of DS1307 (1101 000) followed by 0 to indicate a write operation.
  2. The first byte of data in the write operation will set the register pointer. For example, if you want to write to the control register you should send 0x07.
  3. Check the acknowledge bit to be sure that DS1307 responded.
  4. If you want to write one or more bytes of data, you should transmit them one byte at a time and check the acknowledge bit at the end of each byte sent. Remember that the register pointer is automatically incremented and you can simply transmit bytes of data to consecutive locations in a multi-byte burst write.
  5. Transmit a STOP bit condition.

Reading from DS1307

Notice that before reading a byte you should load the address of the byte to the register pointer by doing a write operation as mentioned before.

To read one or more bytes of data from the DS1307 we should do the following steps:

  1. To access the DS1307 for a read operation, you need to set the register pointer first. After sending a START condition, you should transmit the address of DS1307 (1101 000) followed by 0 to indicate a write operation (writing the register pointer).
  2. Check the acknowledge bit to be sure that DS1307 responded.
  3. The byte of data in the write operation will set the register pointer. For example, if you want to read from the control register you should send 0x07. Check the acknowledge bit to be sure that DS1307 responded.
  4. Now you need to change the bus direction from a transmit to receive. Send a START condition (a REPEATED START), then transmit the address of DS1307 (1101 000) followed by 1 to indicate a read operation. Check the acknowledge bit to be sure that DS1307 responded.
  5. You can read one or more bytes of data. Remember that the register pointer indicates which location will be read. The ACK bit in the I2CMCS register should be set for the master to acknowledge the data received. Also notice that the register pointer is automatically incremented and you can simply receive consecutive bytes of data in a multi-byte burst read.
  6. Before reading the last byte, clear the ACK bit in the I2CMCS register. The last byte read will have a NACK to signal the DS1307 that the burst read is complete.
  7. Transmit a STOP bit condition.


RTC DS1307 on EduARM4 Trainer Board

Ds1307 tm4c connections.png
Figure:DS1307 Connectons

Note

Source Code

  1. /*
  2. * I2C to DS1307 single byte writes
  3.  
  4. * This program communicates with the DS1307 Real-time Clock via I2C.
  5. *
  6. * The DS1307 has a total of 64 bytes of RAM space with addresses 00–3FH.
  7. * The first seven locations, 00–06, are set aside for RTC values of Time and Date.
  8. * The next byte is used for the control register. It is located at address 0x07.
  9. * That leaves 56 bytes, from addresses 0x08 to 0x3F, available for general-purpose data storage.
  10. * That means the entire 64 bytes of RAM are accessible directly for read or write
  11. *
  12. */
  13.  
  14. /* DS1307 parameters:  fmax = 100 kHz
  15.  *                     I2C1SCL PA6
  16.  *                     I2C1SDA PA7
  17.  *
  18. */
  19.  
  20. #include <stdint.h>
  21. #include "inc/tm4c123gh6pm.h"
  22.  
  23. #define MAX_BUF_SIZE                56
  24.  
  25. #define SLAVE_ADDR 0x68             /* 1100 1000 */
  26. #define I2C_START_ADDR              0x08
  27.  
  28. void I2C1_init(void);
  29. char I2C1_byteWrite(int slaveAddr, char memAddr, char data);
  30. char I2C1_read(int slaveAddr, char memAddr, int byteCount, char* data);
  31.  
  32. char readBuf[MAX_BUF_SIZE];
  33.  
  34. int main(void)
  35. {
  36.     char err, i;
  37.  
  38.     char writeBuf;
  39.  
  40.     writeBuf = I2C_START_ADDR;
  41.     I2C1_init();
  42.  
  43.     /* write 56 bytes with single byte writes */
  44.     for( i = 0; i < MAX_BUF_SIZE; i++) {
  45.         err = I2C1_byteWrite(SLAVE_ADDR, writeBuf++, i);
  46.         if(err) break;
  47.     }
  48.  
  49.     if( err ) {
  50.         ;                       /* Handle error condition */
  51.     } else {
  52.         /* uses burst read to read 56 bytes of data */
  53.         err = I2C1_read(SLAVE_ADDR, I2C_START_ADDR, MAX_BUF_SIZE, readBuf);
  54.     }
  55.  
  56.     for(;;) {
  57.         ;
  58.     }
  59. }
  60.  
  61. /* initialize I2C1 as master and the port pins */
  62. void I2C1_init(void)
  63. {
  64.     SYSCTL_RCGCI2C_R |= 0x02;       /* enable clock to I2C1 */
  65.     SYSCTL_RCGCGPIO_R |= 0x01;      /* enable clock to GPIOA */
  66.  
  67.     /* PORTA 7, 6 for I2C1 */
  68.     GPIO_PORTA_AFSEL_R |= 0xC0;     /* PORTA 7, 6 for I2C1 */
  69.     GPIO_PORTA_PCTL_R &= ~0xFF000000; /* PORTA 7, 6 for I2C1 */
  70.     GPIO_PORTA_PCTL_R |= 0x33000000;
  71.     GPIO_PORTA_DEN_R |= 0xC0;       /* PORTA 7, 6 as digital pins */
  72.     GPIO_PORTA_ODR_R |= 0x80;       /* PORTA 7 as open drain */
  73.  
  74.     I2C1_MCR_R = 0x10;              /* master mode */
  75.     I2C1_MTPR_R = 7;                /* 100 kHz @ 16 MHz */
  76. }
  77.  
  78.  
  79. /* Wait until I2C master is not busy and return error code */
  80. /* If there is no error, return 0 */
  81. static int I2C_wait_till_done(void)
  82. {
  83.     while(I2C1_MCS_R & 1)
  84.         ;                           /* wait until I2C master is not busy */
  85.     return I2C1_MCS_R & 0xE;        /* return I2C error code */
  86. }
  87.  
  88. /* Write one byte only */
  89. /* byte write: S-(saddr+w)-ACK-maddr-ACK-data-ACK-P */
  90. char I2C1_byteWrite(int slaveAddr, char memAddr, char data)
  91. {
  92.     char error;
  93.  
  94.     /* send slave address and starting address */
  95.     I2C1_MSA_R = slaveAddr << 1;
  96.     I2C1_MDR_R = memAddr;
  97.     I2C1_MCS_R = 3;                 /* S-(saddr+w)-ACK-maddr-ACK */
  98.  
  99.     error = I2C_wait_till_done();   /* wait until write is complete */
  100.     if(error) return error;
  101.  
  102.     /* send data */
  103.     I2C1_MDR_R = data;
  104.     I2C1_MCS_R = 5;                 /* -data-ACK-P */
  105.     error = I2C_wait_till_done();   /* wait until write is complete */
  106.     while( I2C1_MCS_R & 0x40 )
  107.         ;                           /* wait until bus is not busy */
  108.     error = I2C1_MCS_R & 0xE;
  109.     if(error) return error;
  110.  
  111.     return 0;                       /* no error */
  112. }
  113.  
  114. /* Read memory */
  115. /* read: S-(saddr+w)-ACK-maddr-ACK-R-(saddr+r)-ACK-data-ACK-data-ACK-...-data-NACK-P */
  116. char I2C1_read(int slaveAddr, char memAddr, int byteCount, char* data)
  117. {
  118.     char error;
  119.  
  120.     if (byteCount <= 0)
  121.         return -1;                  /* no read was performed */
  122.  
  123.     /* send slave address and starting address */
  124.     I2C1_MSA_R = slaveAddr << 1;
  125.     I2C1_MDR_R = memAddr;
  126.     I2C1_MCS_R = 3;                 /* S-(saddr+w)-ACK-maddr-ACK */
  127.     error = I2C_wait_till_done();
  128.     if(error)
  129.         return error;
  130.  
  131.     /* to change bus from write to read, send restart with slave addr */
  132.     I2C1_MSA_R = (slaveAddr << 1) + 1;   /* restart: -R-(saddr+r)-ACK */
  133.  
  134.     if( byteCount == 1 )            /* if last byte, don't ack */
  135.         I2C1_MCS_R = 7;             /* -data-NACK-P */
  136.     else                            /* else ack */
  137.         I2C1_MCS_R = 0xB;           /* -data-ACK- */
  138.     error = I2C_wait_till_done();
  139.     if(error) return error;
  140.  
  141.     *data++ = I2C1_MDR_R;           /* store the data received */
  142.  
  143.     if( --byteCount == 0 ) {        /* if single byte read, done */
  144.         while( I2C1_MCS_R & 0x40 )
  145.             ;                       /* wait until bus is not busy */
  146.         return 0;                   /* no error */
  147.     }
  148.  
  149.     /* read the rest of the bytes */
  150.     while( byteCount > 1 ) {
  151.         I2C1_MCS_R = 9;              /* -data-ACK- */
  152.         error = I2C_wait_till_done();
  153.         if(error) return error;
  154.         byteCount--;
  155.         *data++ = I2C1_MDR_R;        /* store data received */
  156.     }
  157.  
  158.     I2C1_MCS_R = 5;                 /* -data-NACK-P */
  159.     error = I2C_wait_till_done();
  160.     *data = I2C1_MDR_R;             /* store data received */
  161.     while( I2C1_MCS_R & 0x40 )
  162.         ;                           /* wait until bus is not busy */
  163.  
  164.     return 0;                       /* no error */
  165. }


   # Implement Date & Time and Display it on 2x16 LCD ( 1st line = Date, 2nd line = Time), update it every second.
  1. Date format should be : dd/mm/yyyy, and
    Time format should be : hh:mm:ss

Caution:

  • Don't use I2C1SCL(PA6) and I2C1SDA(PA7).
  • PA6(RS) and PA7(E) are used by LCD Interface.
  • PB0 to PB7 are used as LCD Data Bus.
  • So, we are left with I2C3SCL(PD0) and I2C3SDA(PD1).
  • Connect PD0 to pin No. 2 of J6
  • Connect PD1 to Pin No. 4 of J6
  • Please look at the Schematic