Difference between revisions of "Embedded System Tutorial SPI"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Assignment)
(Replaced content with "Socialledge is moving to two portals. * The Wiki will remain here for general references about the SJ-One board, and to document student reports. * The bookstack will...")
 
Line 1: Line 1:
== SPI BUS ==
+
Socialledge is moving to two portals.
SPI stands for '''S'''erial '''P'''eripheral '''B'''us.  It is a high-speed, full-duplex bus that uses minimum of 3 wires to exchange data.  The popularity of this bus rose when SD cards (and its variants ie: micro-sd) officially supported this bus according to the SD specificationsFurthermore, unlike UART in which you can only have one transmitter and a receiver, SPI bus can have one master and multiple slave devices.
+
* The Wiki will remain here for general references about the SJ-One board, and to document student reports.
 +
* The bookstack will now be used for SJSU assignments
  
=== SPI Bus Signals ===
+
[http://books.socialledge.com/books/embedded-drivers-real-time-operating-systems/chapter/lesson-spi This article has been moved here]
{|
 
|
 
*  <code>MOSI --> </code>Master Out Slave In  (driven by master)
 
*  <code>MISO --> </code>Master In  Slave Out (driven by slave)
 
*  <code>SCK&nbsp; --> </code>Clock (driven by master)
 
*  <code>CS&nbsp;&nbsp;  --> </code>Chip-select signal, one per slave
 
| |[[File:spi_tutorial_conns.jpg|center|frame|SPI BUS Connections]]
 
|}
 
 
 
The '''CS''' signal selects one slave, and the slave takes over the MISO pin.  If a slave is not selected, then it shall leave the MISO pin in hi-z state.  If multiple slaves have their CS signal asserted, they will try to take control of the MISO pin and damage their MISO pins.  For example, if one slave drives the signal high (connect to 3.3v) and the other drives it low (connect to ground), then short-circuit will occur damaging this pin.
 
 
 
The SCK signal can reach speed of 24Mhz and beyond, however, SD cards are usually limited to 24Mhz according to the specifications.  Furthermore, any signal over 24Mhz on a PCB requires special design consideration to make sure it will not deteriorate, thus 24Mhz is the usual maximum.  Furthermore, you need a CPU twice as fast as the speed you wish to run to support it.  For example, to run at 24Mhz SPI, we need 48Mhz CPU or higher.  Because each wire is driven directly (rather than open-collector), higher speeds can be attained compared to 400Khz I2C bus.
 
 
 
<BR/>
 
 
 
== Hardware ==
 
Suppose that you wanted to interface a single SPI bus to three SD cards, the following will need to be done :
 
*  Connect all MOSI, MISO, and SCK lines together
 
*  Connect individual CS lines of three SD cards to SPI master (your processor)
 
 
 
It is also recommended to provide a weak pull-up resistor on each of the SPI wires otherwise some devices like an SD card may not work.  50K resistor should work, however, lower resistor value can acheive higher SPI speeds.
 
 
 
<BR/>
 
== Software Driver ==
 
Unlike UART, the SPI driver is incredibly easy. The SPI is labeled as SSP on LPC17xx datasheet due to historic reasons, and this chapter in the datasheet shows the software setup very well. After the SPI is initialized on the hardware pins, the next steps is to write an spi function that will exchange a byte.  Note that if the master wants to receive data, it must send a data byte out to get a data byte back.  The moment we write to the '''DR''' (data register) of the SPI peripheral, the MOSI will begin to send out the data.  At the same time, the MISO will capture the data byte back to '''the same DR register'''.  In other words, SPI bus is a forced full-duplex bus.
 
 
 
{|
 
|<syntaxhighlight lang="c">
 
void spi1_Init()
 
{
 
    LPC_SC->PCONP |= (1 << 10);    // SPI1 Power Enable
 
    LPC_SC->PCLKSEL0 &= ~(3 << 20); // Clear clock Bits
 
    LPC_SC->PCLKSEL0 |=  (1 << 20); // CLK / 1
 
 
 
    // Select MISO, MOSI, and SCK pin-select functionality
 
    LPC_PINCON->PINSEL0 &= ~( (3 << 14) | (3 << 16) | (3 << 18) );
 
    LPC_PINCON->PINSEL0 |=  ( (2 << 14) | (2 << 16) | (2 << 18) );
 
 
 
    LPC_SSP1->CR0 = 7;          // 8-bit mode
 
    LPC_SSP1->CR1 = (1 << 1);  // Enable SSP as Master
 
    LPC_SSP1->CPSR = 8;        // SCK speed = CPU / 8
 
}
 
 
 
char spi1_ExchangeByte(char out)
 
{
 
    LPC_SSP1->DR = out;
 
    while(LPC_SSP1->SR & (1 << 4)); // Wait until SSP is busy
 
    return LPC_SSP1->DR;
 
}
 
</syntaxhighlight>
 
|[[File:spi_tutorial_summary.jpg|right|frame|SPI Driver from LPC17xx datasheet]]
 
|}
 
 
 
<BR/>
 
=== Multitasking Warnings ===
 
If your software runs multiple tasks, and these tasks can access SPI, care needs to be taken because if two CS signals are asserted at the same time, hardware damage will occur.  This leads to the topic of using a mutex (semaphore) under FreeRTOS and you can read the [[FreeRTOS_Tutorial | FreeRTOS tutorial]] to learn more.
 
 
 
<BR/>
 
== SJ-One Board Driver Instructions ==
 
Preparation for the SPI driver:
 
* Note that when we refer to "SPI", it also means "SSP" in the LPC user manual
 
* Study the schematic, and take a note of which pins have the SSP1 or SPI#1 peripheral pin-out.  Note this down or draw this out.
 
* Study and read the SSP1 LPC user manual chapter a few times
 
* Study the schematic, and locate the CS pin for the SPI flash attached to SSP1, then write a simple GPIO driver for this to select and deslect this pin
 
* Read the SPI flash datasheet that shows the SPI transactions for read/write, signature read etc.
 
*:  Rev4 board has Adesto flash, and previous revisions have Atmel flash.
 
 
 
Writing the driver:
 
* First, initialize the pin MUX using the PINSEL register.  I believe the PINSEL register is part of the LPC_PINCON struct
 
* Read the SSP1 chapter, and follow the steps on the first page to power on the SSP peripheral, and then initialize the CR0 and the CR1 register to perform SPI master initializationaa
 
* Now, write the spi exchange byte function using the DR and the SR (status) register.
 
 
 
== Assignment ==
 
*  <b>Things to do:</b>
 
*: Study the SSP chapter of the LPC user manual
 
*: Write an SSP(SPI) driver to send and receive a data byte
 
*: Write a GPIO driver to CS/DS the SPI flash
 
*: Read the SPI flash memory signature and verify that it matches the expectation
 
 
 
*  Write a driver for SSP#1
 
*:  This SPI is interfaced to SD card and SPI Flash memory
 
*:  You need just an <b><code>init()</code></b> routine along with <B><code>char byte_transfer(char)</code></b> function.
 
*  Identify the pin for SPI flash memory's chip-select signal (CS)
 
*  Read the SPI flash memory datasheet and read the '''signature''' of this device and display it using printf()
 
*:  This should be at <b><code>SJSU_Dev\ref_and_datasheets\datasheets</code></b>
 
*  Read the '''SPI Flash memory's status register''' and print information about '''each bit''' in a nicely formatted output.
 
*:  This isn't the SPI peripheral status register, read the SPI flash memory datasheet.
 
*  Read the SPI flash memory datasheet and print each byte of the manufacturer ID and device ID
 
*  <b>Extra credit:</b>
 
*:  Read page zero (first 512 bytes), and print the following:
 
*::  Number of bytes per sector, number of sectors per cluster, and the total number of sectors.
 
*:  Hint: Use this: [http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html FAT info]
 
*:: Hint#2:  You can use the API from spi_flash.h
 
 
 
*  <b>What to turn in:</b>
 
*: INDIVIDUAL code (relevant code and changes, not the entire main file)
 
*: Make sure you turn in the <b>code and screenshot</b> before the due date.
 
 
 
=== FAT Information ===
 
Information about the FAT boot sector and MBR
 
 
 
The first sector of the flash contains a Master Boot Record (MBR) which has pointers to where the partitions are placed in the storage device. Please have a look at [https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout this link] for the structure of MBR. The first 446 bytes is the bootstrap code area. After that there is a partition table with four entries and then a boot signature (0x55AA). Each entry is 16 bytes long and you can find the layout of each [https://en.wikipedia.org/wiki/Master_boot_record#PTE entry here].
 
 
 
One of the fields in the partition entry is [https://en.wikipedia.org/wiki/Partition_type#List_of_partition_IDs "partition type"]. This should tell us what type of filesystem is resident on the partition. In our case it should be FAT12 (0x01). The last 8 bytes in the partition entry will tell us the Logical Block Address (LBA) of the first absolute sector in the partition and the total number of sectors in the partition. Essentially, we want to read the sector pointed to by this LBA for the FAT boot sector. That sector will give us the required information (no of clusters, total sectors, etc.. ).
 

Latest revision as of 17:57, 2 March 2018

Socialledge is moving to two portals.

  • The Wiki will remain here for general references about the SJ-One board, and to document student reports.
  • The bookstack will now be used for SJSU assignments

This article has been moved here