Raspberry Pi 3 Hardware and System Software Reference

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

A Raspberry Pi 3 (model b) is a single circuit computer. Raspberry Pi 3 (rPI3) provides rich support for interfacing external peripherals through a hardware and system software interface. In this article, we will explore various hardware and system software configurations configurations available through rPI3 for the users.

The various hardware features covered in this article are:

  • rPI3 specifications
  • Addressing modes
  • Peripherals supported
  • Operating system installation and its configuration

RPi3 Specification

GPIO pinout diagram
Figure 1: GPIO pinout diagram

  • SoC: Broadcom BCM2837 (roughly 50% faster than the Pi 2)
  • CPU: 1.2 GHZ quad-core ARM Cortex A53 (ARMv8 Instruction Set)
  • GPU: Broadcom VideoCore IV @ 400 MHz
  • Memory: 1 GB LPDDR2-900 SDRAM
  • USB ports: 4
  • Network: 10/100 MBPS Ethernet, 802.11n Wireless LAN, Bluetooth 4.0
  • OS: Raspbian

RPi3 Addressing Mode

Addressing chart
Figure 2: Addressing chart
Image source: BCM2835 ARM Peripherals, page 5.

There are several ways of addressing is handled with rPI3s. These include:

  • Bus addresses
  • Physical addresses
  • Virtual addresses

Bus Addresses

Peripherals registers are available through its device i/o address or bus address. Peripherals are available at the 0x7E00_0000 address. These addresses are higher than the physical memory address. Processors generally provide IN/OUT opcode to access the peripherals. Access peripherals using DMA uses bus addressing or direct device addressing.

Physical Addresses

Physical memory is secondary cache, which is 1GB on a rPI3. All peripheral bus addresses are mapped to physical memory. So, accessing memory mapped addresses in physical memory tends to access the bus addresses. This eliminates using separate op codes usage in bus addressing mode.

Physical addresses start at 0x00000000 for RAM.

Physical addresses range from 0x3F0000000 to 0x3FFFFFFF for peripherals.

The bus addresses for peripherals are set up to map onto the peripheral bus address range starting at 0x7E000000. Thus, a peripheral advertised here at bus address 0x7Ennnnnn is available at physical address 0x3Fnnnnnn. MMU maps bus addresses to physical address.

Virtual Addresses

A program runs in two parts: user mode and kernel mode. User programs and kernel programs does not access physical memory by hard addresses; rather, they use soft or virtual addresses. Typically, each application runs on its own virtual address space where application code in the virtual address takes the lower 3GB, whereas the upper 1GB is occupied by the kernel code (running on behalf of user code). Total 4GB sums to 32 bit complete addressing.

This makes an application independent on real physical (RAM) memory size and all magic is done through a mediator page table.

Virtual addresses in kernel mode ranges between 0xC0000000 and 0xEFFFFFFF.

Virtual addresses in user mode (i.e. seen by processes running in ARM Linux) ranges between 0x00000000 and 0xBFFFFFFF.

Peripherals (at physical address 0x3F000000 on) are mapped into the kernel virtual address space starting at address 0xF2000000. Thus, a peripheral advertised here at bus address 0x7Ennnnnn is available in the ARM kernel at virtual address 0xF2nnnnnn.

To map physical address to virtual address, /dev/mem (physical memory) needs to be opened as a file. For example:

mem_fd = open("/dev/mem", O_RDWR|O_SYNC) );

Then, mem_fd is mapped to virtual address by mmap. For example:

map = mmap(
   NULL,
   BLOCK_SIZE,
   PROT_READ|PROT_WRITE,
   MAP_SHARED,
   // File descriptor to physical memory virtual file '/dev/mem'
   mem_fd,
   // Address in physical map that we want this memory block
   // to expose
   GPIO_BASE
);

map returns the mapped address, which can be used for reading and writing as a pointer.

RPi 3 Peripheral Support

RPi3 provides support for the following peripherals:

  • Timers
  • Interrupt controller
  • GPIO
  • USB
  • PCM/I2S
  • DMA controller
  • I2C master
  • I2C/SPI slave
  • SPI0, SPI1, SPI2
  • PWM
  • UART0, UART1

In this article, we will cover GPIO, I2C Mater/Slave, and SPI peripherals.

GPIO

RPi3 has 54 general purpose i/o (gpio) lines split into 2 banks. gpio has 41 registers and each of 32 bit size (a word). Registers categories are as follows:

Selection register
Figure 3: Selection register

GPIO alternate function select register 0
Figure 4: GPIO alternate function select register 0

  • GPSEL: Selection registers are dedicated to configuring each gpio pin for different functions. Each gpio pin other than input and output can perform up to six different functions. There are six selection registers, where each handles 10 GPIO pins, keeping 3 bits for each pin. 2 MSB bits are unused in each register.
  • GPSET (Set register): RPi3 has 2 SET Registers where each bit is for one

    GPSET chart
    Figure 5: GPSET chart

  • gpio: This only sets the pin to 1 and cannot be used for clearing it (0).

    GPIO chart
    Figure 6: GPIO chart

  • GPCLR (Clear register): This clears the pin to 0.

    GPCLR chart
    Figure 7: GPCLR chart

  • GPLEV (Level register): Returns the actual value (level) of the pin.

Other gpio registers are GPEDS (Pin event detection), GPREN (Pin rising edge detect), GPFEN (Pin falling edge detect), GPHEN (Pin High detect enable), GPLEN (Pin low detect enable), GPAREN (Pin async rise edge detect), GPAFEN (Pin async fall edge detect), GPPUD (Pin pullup down enable), and GPPUDCLK (Pin pull up/down enable clock.

I2C

I2C chart
Figure 8: I2C chart

I2c interfacing is supported through a broadcom serial controller(BSC) master setup. It supports 7 and 10 bit addressing modes. There are three bsc master available:

  • BSC0: 0x7E20_5000
  • BSC1: 0x7E80_4000
  • BSC2: 0x7E80_5000

In rpi3 mostly it is bsc1 (SPI1) that are in use.

bsc/SPI master has 6 registers, each 32 bit wide.

I2VC address map
Figure 9: I2VC address map

Protocol has START, STOP, RESTART, and ACK/NACK conditions. These are controlled through a TA bit in the control register (START, RESTART, STOP), and ERR bit in status register (ACK/NACK).

Protocol works in the following fashion:

Condition protocols
Figure 10: Condition protocols

  • Master sends START condition.
  • Master sets address of the device on bus along with w/r bit, slave sends ACK.
  • Master sets register address of the device (among various registers in the device) on bus, slave sends ACK.
  • If write bit is set master write the data and slave ACKs.
  • To read the data, master sends RESTART condition followed by the address with read bit checked. Slave writes the data and master ACKs.
  • STOP condition set for ending the transmission.

In rPI3, the program has to do the following things:

  • Clear Control and Status register; it makes write/read bit 0 (write).
  • Write slave address to the Address register.
  • Set data length register as 1 (byte).
  • Write byte to the FIFO register.
  • Set I2CEN (I2C enable) and ST (start transfer bit) in Control register.
  • Check for Status register TA bit and DONE bit for transfer complete.
  • Clear Control register again, set READ bit in it and start transfer again making junk character to read.
  • To receive valid data from the slave, again clear the control and status register.
  • Set the READ flag in Control register and start the transfer with ST enable. Read the data from FIFO.
int i2c::read(void *buf){
   peri_set_bits(i2c1addr+I2C_C/4,I2C_C_CLEAR_1, I2C_C_CLEAR_1 );

   /* Clear Status */
   *(i2c1addr+I2C_S/4)=I2C_S_CLKT | I2C_S_ERR | I2C_S_DONE;

   /* Set Data Length */
   *(i2c1addr+I2C_DLEN/4)=1;

   /* pre populate FIFO with max buffer */
   *(i2c1addr+I2C_FIFO/4)=((char*)buf)[0];

   /* Enable device and start transfer */
   *(i2c1addr+I2C_C/4)= I2C_C_I2CEN | I2C_C_ST;

   /* poll for transfer has started (way to do repeated start,
   * from BCM2835 datasheet) */
   while ( !( *(i2c1addr+I2C_S/4) & I2C_S_TA ) &&
      !(*(i2c1addr+I2C_S/4) & I2C_S_DONE));
   /* Send a repeated start with read bit set in address */
   *(i2c1addr+I2C_DLEN/4)=1;
   *(i2c1addr+I2C_C/4)=I2C_C_I2CEN | I2C_C_ST  | I2C_C_READ;

   // Wait for write to complete and get rubbish first byte back.
   usleep(timetowait*(1+1));

   // Wait for transfer to complete
   while (!(*(i2c1addr+I2C_S/4) & I2C_S_DONE))
   {
      // We must empty the FIFO as it is populated and not use
      // any delay
      while ((*(i2c1addr+I2C_S/4) & I2C_S_RXD))
      {
         // Read from FIFO, no barrier
         ((char*)buf)[0] = *(i2c1addr+I2C_FIFO/4);
      }
   }
   /* Read the data*/
   /* Clear FIFO */
   peri_set_bits(i2c1addr+I2C_C/4, I2C_C_CLEAR_1 , I2C_C_CLEAR_1 );
   *(i2c1addr+I2C_S/4)=I2C_S_CLKT | I2C_S_ERR | I2C_S_DONE;
   *(i2c1addr+I2C_DLEN/4)=1;
   *(i2c1addr+I2C_C/4)=I2C_C_I2CEN | I2C_C_ST | I2C_C_READ;

   /* Wait for transfer to complete */
   while (!(*(i2c1addr+I2C_S/4) & I2C_S_DONE))
   {
      /* We must empty the FIFO as it is populated and not use
      * any delay */
      while (*(i2c1addr+I2C_S/4) & I2C_S_RXD)
      {
         /* Read from FIFO, no barrier */
         ((char*)buf)[0] = *(i2c1addr+I2C_FIFO/4);
      }
   }
   peri_set_bits(i2c1addr+I2C_C/4, I2C_S_DONE , I2C_S_DONE);
   return 1;
}

SPI

SPI pin connectivity
Figure 11: SPI pin connectivity

Communication protocol with MCP3008 slave
Figure 12: Communication protocol with MCP3008 slave

rPI3 has only one SPI interface type, SPI0. The base address of this SPI0 interface is 0x7E204000.

SPI address map
Figure 13: SPI address map

Serial Peripheral Interfacing works in the following fashion:

  1. Chip selects the slave device to communicate.
  2. 1 is written on the bus to give the device a hint for start of the communication.
  3. Channel number is written for selecting the channel for input.
  4. Junk character (for example, 0x0) is written for getting the output from the slave.

In rPI3, the program has to do the following things:

  • Set CPHA and CSPOl to 00 (mode0)
  • Set CS0 as chip select
  • Set CSPOL0 polarity to low
  • Set TA bit is the the control register to start the transfer
  • Set 0x01 as data to FIFO, wait for transfer checking TXD bit in Control/Status register
  • Set 0x80 as data to FIFO for selecting the channel 0 for input
  • Wait for TXD bit in control/status register
  • Set 0x00 data to FIFO and let data comes in as response.
int jstk::process(void*){
   // Trace("><jstk::process")
   static int xvalue=0,yvalue=0;
   volatile uint32_t *addr_cs=spi0addr+SPI0_CS/4;
   volatile uint32_t *addr_fifo=spi0addr+SPI0_FIFO/4;
   uint8_t mosi[2][3]={{0x01, 0x80, 0x00},{0x01,0x90,0x00}};
   uint8_t miso[2][3]={0};
   uint32_t txcnt=0,rxcnt=0;
   int i=0;
   for(i=8;i<12;i++){
      INP_GPIO(i);
      SET_GPIO_ALT(i,0);
   }
   for(i=0;i<2;i++){
      txcnt=rxcnt=0;
      peri_set_bits(addr_cs,SPI0_CS_CLEAR,SPI0_CS_CLEAR);
      peri_set_bits(addr_cs,SPI0_CS_TA,SPI0_CS_TA);
      while(txcnt<3 || rxcnt<3){
         while(((*addr_cs) & SPI0_CS_TXD)&&(txcnt<3))
         *(addr_fifo)=mosi[i][txcnt++];
         while(((*addr_cs) & SPI0_CS_RXD)&&(rxcnt<3))
            miso[i][rxcnt++]=*(addr_fifo);
      }
      while(!((*addr_cs)& SPI0_CS_DONE)) ;
      peri_set_bits(addr_cs,0,SPI0_CS_TA);
   }
   if((xvalue+yvalue -miso[0][2]-((miso[0][1]&3)<<8)
         - miso[1][2]-((miso[1][1]&3)<<8))>10 ||
         (miso[0][2]+((miso[0][1]&3)<<8) + miso[1][2]+
         ((miso[1][1]&3)<<8)-xvalue-yvalue)>10) {
      _ct.y1=xvalue=miso[0][2]+((miso[0][1]&3)<<8);
      _ct.y2=yvalue=miso[1][2]+((miso[1][1]&3)<<8);
      return 1;
   }
   // Trace("<>jstk::process")
   return 0;
}

Operating System Installation and Configuration

  1. Get a Micro Sd card 8GB or more. Download the latest raspibian from https://www.raspberrypi.org/downloads. Untar it and copy it to the SD card through WIN32DiskImager on Windows or running the following command on Linux.
    ' dd if=*.img of=/dev/sdb bs=4M'

    Note that /dev/sdb is a device file for the SD card reader; choose it accordingly.

  2. Insert SD card in rPI3, connect monitor to rPI3, and power it on.
  3. Bring up the config menu through 'sudo raspi-config' and select it to expand the file system.
  4. Update the rPI3 with 'sudo apt-get update'.

Summary

This article briefly discussed the specification of a rPI3. It also described various addressing modes and communication protocols. Parallel port protocol GPIO and Serial port protocol is I2C and SPI were discussed adequately. Mapping of device address to physical address and then physical address to the virtual address were also discussed. Open and mmap system calls were discussed for mapping physical address to virtual address space. Minimum installation instructions were also discussed.

References

  • BCM2835 ARM Peripherals by Broadcom Corporation
  • Mastering the Raspberry Pi by Warren Gay


About the Author

Pravin Kumar Sinha

Pravin Kumar Sinha is embedded C++ developer and training consultant with 18 years of experience in Object Oriented Technology mostly in C++, Qt, Python. Pravin has publications in Algorithms (Stack-based breadth-first search tree traversal), Design Patterns (Perl), High volume C++ generic logger, Qt:Chain of Responsibility and Qt:3D OpenGL drawing on Google Maps.

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The hunger for IIoT-enabled solutions is driving companies to seek out reliable, secure IIoT platforms that can handle industrial-grade IoT capabilities. What features and capabilities should companies expect in an IIoT platform? Until now, developing an IIoT solution has required the costly, time-intensive effort of platform building, as developers create technology stacks from scratch to handle the hardware, firmware, software, edge computing, analytics, business systems integration, and more. This …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date