777 lines
21 KiB
C
777 lines
21 KiB
C
// bcm2835.c
|
||
// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi
|
||
// http://elinux.org/RPi_Low-level_peripherals
|
||
// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
|
||
//
|
||
// Author: Mike McCauley (mikem@open.com.au)
|
||
// Copyright (C) 2011 Mike McCauley
|
||
// $Id: bcm2835.c,v 1.4 2012/07/16 23:57:59 mikem Exp mikem $
|
||
|
||
#include "bcm2835.h"
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <sys/mman.h>
|
||
#include <unistd.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
|
||
// This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11)
|
||
// You can do some safe, non-destructive testing on any platform with:
|
||
// gcc bcm2835.c -D BCM2835_TEST
|
||
// ./a.out
|
||
//#define BCM2835_TEST
|
||
|
||
// Locals to hold pointers to the hardware
|
||
static volatile uint32_t *gpio = MAP_FAILED;
|
||
static volatile uint32_t *pwm = MAP_FAILED;
|
||
static volatile uint32_t *clk = MAP_FAILED;
|
||
static volatile uint32_t *pads = MAP_FAILED;
|
||
static volatile uint32_t *spi0 = MAP_FAILED;
|
||
|
||
static int fd = -1;
|
||
static uint8_t *gpioMem = NULL;
|
||
static uint8_t *pwmMem = NULL;
|
||
static uint8_t *clkMem = NULL;
|
||
static uint8_t *padsMem = NULL;
|
||
static uint8_t *spi0Mem = NULL;
|
||
|
||
// This define allows us to test on hardware other than RPi.
|
||
// It prevents access to the kernel memory, and does not do any peripheral access
|
||
// Instead it prints out what it _would_ do if debug were 0
|
||
static uint8_t debug = 0;
|
||
|
||
|
||
//
|
||
// Low level register access functions
|
||
//
|
||
|
||
void bcm2835_set_debug(uint8_t d)
|
||
{
|
||
debug = d;
|
||
}
|
||
|
||
// safe read from peripheral
|
||
uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
|
||
{
|
||
if (debug)
|
||
{
|
||
printf("bcm2835_peri_read paddr %p\n", paddr);
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
uint32_t ret = *paddr;
|
||
ret = *paddr;
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
// read from peripheral without the read barrier
|
||
uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
|
||
{
|
||
if (debug)
|
||
{
|
||
printf("bcm2835_peri_read_nb paddr %p\n", paddr);
|
||
return 0;
|
||
}
|
||
else
|
||
return *paddr;
|
||
}
|
||
|
||
// safe write to peripheral
|
||
void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
|
||
{
|
||
if (debug)
|
||
{
|
||
printf("bcm2835_peri_write paddr %p, value %08X\n", paddr, value);
|
||
}
|
||
else
|
||
{
|
||
*paddr = value;
|
||
*paddr = value;
|
||
}
|
||
}
|
||
|
||
// write to peripheral without the write barrier
|
||
void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value)
|
||
{
|
||
if (debug)
|
||
printf("bcm2835_peri_write_nb paddr %p, value %08X\n", paddr, value);
|
||
else
|
||
*paddr = value;
|
||
}
|
||
|
||
// Set/clear only the bits in value covered by the mask
|
||
void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask)
|
||
{
|
||
uint32_t v = bcm2835_peri_read(paddr);
|
||
v = (v & ~mask) | (value & mask);
|
||
bcm2835_peri_write(paddr, v);
|
||
}
|
||
|
||
//
|
||
// Low level convenience functions
|
||
//
|
||
|
||
// Function select
|
||
// pin is a BCM2835 GPIO pin number NOT RPi pin number
|
||
// There are 6 control registers, each control the functions of a block
|
||
// of 10 pins.
|
||
// Each control register has 10 sets of 3 bits per GPIO pin:
|
||
//
|
||
// 000 = GPIO Pin X is an input
|
||
// 001 = GPIO Pin X is an output
|
||
// 100 = GPIO Pin X takes alternate function 0
|
||
// 101 = GPIO Pin X takes alternate function 1
|
||
// 110 = GPIO Pin X takes alternate function 2
|
||
// 111 = GPIO Pin X takes alternate function 3
|
||
// 011 = GPIO Pin X takes alternate function 4
|
||
// 010 = GPIO Pin X takes alternate function 5
|
||
//
|
||
// So the 3 bits for port X are:
|
||
// X / 10 + ((X % 10) * 3)
|
||
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
|
||
{
|
||
// Function selects are 10 pins per 32 bit word, 3 bits per pin
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPFSEL0/4 + (pin/10);
|
||
uint8_t shift = (pin % 10) * 3;
|
||
uint32_t mask = BCM2835_GPIO_FSEL_MASK << shift;
|
||
uint32_t value = mode << shift;
|
||
bcm2835_peri_set_bits(paddr, value, mask);
|
||
}
|
||
|
||
// Set putput pin
|
||
void bcm2835_gpio_set(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPSET0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
bcm2835_peri_write(paddr, 1 << shift);
|
||
}
|
||
|
||
// Clear output pin
|
||
void bcm2835_gpio_clr(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPCLR0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
bcm2835_peri_write(paddr, 1 << shift);
|
||
}
|
||
|
||
// Read input pin
|
||
uint8_t bcm2835_gpio_lev(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPLEV0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = bcm2835_peri_read(paddr);
|
||
return (value & (1 << shift)) ? HIGH : LOW;
|
||
}
|
||
|
||
// See if an event detection bit is set
|
||
// Sigh cant support interrupts yet
|
||
uint8_t bcm2835_gpio_eds(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPEDS0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = bcm2835_peri_read(paddr);
|
||
return (value & (1 << shift)) ? HIGH : LOW;
|
||
}
|
||
|
||
// Write a 1 to clear the bit in EDS
|
||
void bcm2835_gpio_set_eds(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPEDS0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_write(paddr, value);
|
||
}
|
||
|
||
// Rising edge detect enable
|
||
void bcm2835_gpio_ren(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPREN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_ren(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPREN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// Falling edge detect enable
|
||
void bcm2835_gpio_fen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPFEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_fen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPFEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// High detect enable
|
||
void bcm2835_gpio_hen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPHEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_hen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPHEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// Low detect enable
|
||
void bcm2835_gpio_len(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPLEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_len(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPLEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// Async rising edge detect enable
|
||
void bcm2835_gpio_aren(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPAREN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_aren(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPAREN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// Async falling edge detect enable
|
||
void bcm2835_gpio_afen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPAFEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, value, value);
|
||
}
|
||
void bcm2835_gpio_clr_afen(uint8_t pin)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPAFEN0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
uint32_t value = 1 << shift;
|
||
bcm2835_peri_set_bits(paddr, 0, value);
|
||
}
|
||
|
||
// Set pullup/down
|
||
void bcm2835_gpio_pud(uint8_t pud)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPPUD/4;
|
||
bcm2835_peri_write(paddr, pud);
|
||
}
|
||
|
||
// Pullup/down clock
|
||
// Clocks the value of pud into the GPIO pin
|
||
void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on)
|
||
{
|
||
volatile uint32_t* paddr = gpio + BCM2835_GPPUDCLK0/4 + pin/32;
|
||
uint8_t shift = pin % 32;
|
||
bcm2835_peri_write(paddr, (on ? 1 : 0) << shift);
|
||
}
|
||
|
||
// Read GPIO pad behaviour for groups of GPIOs
|
||
uint32_t bcm2835_gpio_pad(uint8_t group)
|
||
{
|
||
volatile uint32_t* paddr = pads + BCM2835_PADS_GPIO_0_27/4 + group*2;
|
||
return bcm2835_peri_read(paddr);
|
||
}
|
||
|
||
// Set GPIO pad behaviour for groups of GPIOs
|
||
// powerup value for al pads is
|
||
// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA
|
||
void bcm2835_gpio_set_pad(uint8_t group, uint32_t control)
|
||
{
|
||
volatile uint32_t* paddr = pads + BCM2835_PADS_GPIO_0_27/4 + group*2;
|
||
bcm2835_peri_write(paddr, control);
|
||
}
|
||
|
||
// Some convenient arduino like functions
|
||
// milliseconds
|
||
void delay (unsigned int millis)
|
||
{
|
||
struct timespec sleeper, dummy ;
|
||
|
||
sleeper.tv_sec = (time_t)(millis / 1000) ;
|
||
sleeper.tv_nsec = (long)(millis % 1000) * 1000000 ;
|
||
nanosleep (&sleeper, &dummy) ;
|
||
}
|
||
|
||
// microseconds
|
||
void delayMicroseconds (unsigned int micros)
|
||
{
|
||
struct timespec sleeper, dummy ;
|
||
|
||
sleeper.tv_sec = 0 ;
|
||
sleeper.tv_nsec = (long)(micros * 1000) ;
|
||
nanosleep (&sleeper, &dummy) ;
|
||
}
|
||
|
||
//
|
||
// Higher level convenience functions
|
||
//
|
||
|
||
// Set the state of an output
|
||
void bcm2835_gpio_write(uint8_t pin, uint8_t on)
|
||
{
|
||
if (on)
|
||
bcm2835_gpio_set(pin);
|
||
else
|
||
bcm2835_gpio_clr(pin);
|
||
}
|
||
|
||
// Set the pullup/down resistor for a pin
|
||
//
|
||
// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on
|
||
// the respective GPIO pins. These registers must be used in conjunction with the GPPUD
|
||
// register to effect GPIO Pull-up/down changes. The following sequence of events is
|
||
// required:
|
||
// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither
|
||
// to remove the current Pull-up/down)
|
||
// 2. Wait 150 cycles – this provides the required set-up time for the control signal
|
||
// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to
|
||
// modify – NOTE only the pads which receive a clock will be modified, all others will
|
||
// retain their previous state.
|
||
// 4. Wait 150 cycles – this provides the required hold time for the control signal
|
||
// 5. Write to GPPUD to remove the control signal
|
||
// 6. Write to GPPUDCLK0/1 to remove the clock
|
||
//
|
||
// RPi has P1-03 and P1-05 with 1k8 pullup resistor
|
||
void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud)
|
||
{
|
||
bcm2835_gpio_pud(pud);
|
||
delayMicroseconds(10);
|
||
bcm2835_gpio_pudclk(pin, 1);
|
||
delayMicroseconds(10);
|
||
bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF);
|
||
bcm2835_gpio_pudclk(pin, 0);
|
||
}
|
||
|
||
void bcm2835_spi_begin()
|
||
{
|
||
// Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); // CE1
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); // CE0
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); // MISO
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); // MOSI
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); // CLK
|
||
|
||
// Set the SPI CS register to the some sensible defaults
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
bcm2835_peri_write(paddr, 0); // All 0s
|
||
|
||
// Clear TX and RX fifos
|
||
bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR);
|
||
}
|
||
|
||
void bcm2835_spi_end()
|
||
{
|
||
// Set all the SPI0 pins back to input
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); // CE1
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); // CE0
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); // MISO
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); // MOSI
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); // CLK
|
||
}
|
||
|
||
void bcm2835_spi_setBitOrder(uint8_t order)
|
||
{
|
||
// BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0
|
||
(void)order;
|
||
}
|
||
|
||
// defaults to 0, which means a divider of 65536.
|
||
// The divisor must be a power of 2. Odd numbers
|
||
// rounded down. The maximum SPI clock rate is
|
||
// of the APB clock
|
||
void bcm2835_spi_setClockDivider(uint16_t divider)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CLK/4;
|
||
bcm2835_peri_write(paddr, divider);
|
||
}
|
||
|
||
void bcm2835_spi_setDataMode(uint8_t mode)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
// Mask in the CPO and CPHA bits of CS
|
||
bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA);
|
||
}
|
||
|
||
// Writes (and reads) a single byte to SPI
|
||
uint8_t bcm2835_spi_transfer(uint8_t value)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
volatile uint32_t* fifo = spi0 + BCM2835_SPI0_FIFO/4;
|
||
|
||
// This is Polled transfer as per section 10.6.1
|
||
// BUG ALERT: what happens if we get interupted in this section, and someone else
|
||
// accesses a different peripheral?
|
||
// Clear TX and RX fifos
|
||
bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
|
||
|
||
// Set TA = 1
|
||
bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
|
||
|
||
// Maybe wait for TXD
|
||
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
|
||
delayMicroseconds(10);
|
||
|
||
// Write to FIFO, no barrier
|
||
bcm2835_peri_write_nb(fifo, value);
|
||
|
||
// Wait for DONE to be set
|
||
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
|
||
delayMicroseconds(10);
|
||
|
||
// Read any byte that was sent back by the slave while we sere sending to it
|
||
uint32_t ret = bcm2835_peri_read_nb(fifo);
|
||
|
||
// Set TA = 0, and also set the barrier
|
||
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
|
||
|
||
return ret;
|
||
}
|
||
|
||
// Writes (and reads) an number of bytes to SPI
|
||
void bcm2835_spi_transfern(char* buf, uint32_t len)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
volatile uint32_t* fifo = spi0 + BCM2835_SPI0_FIFO/4;
|
||
|
||
// This is Polled transfer as per section 10.6.1
|
||
// BUG ALERT: what happens if we get interupted in this section, and someone else
|
||
// accesses a different peripheral?
|
||
|
||
// Clear TX and RX fifos
|
||
bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
|
||
|
||
// Set TA = 1
|
||
bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
|
||
|
||
uint32_t i;
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
// Maybe wait for TXD
|
||
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
|
||
delayMicroseconds(10);
|
||
|
||
// Write to FIFO, no barrier
|
||
bcm2835_peri_write_nb(fifo, buf[i]);
|
||
|
||
// Wait for RXD
|
||
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))
|
||
delayMicroseconds(10);
|
||
|
||
// then read the data byte
|
||
buf[i] = bcm2835_peri_read_nb(fifo);
|
||
}
|
||
// Wait for DONE to be set
|
||
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
|
||
delayMicroseconds(10);
|
||
|
||
// Set TA = 0, and also set the barrier
|
||
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
|
||
}
|
||
|
||
void bcm2835_spi_chipSelect(uint8_t cs)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
// Mask in the CS bits of CS
|
||
bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS);
|
||
}
|
||
|
||
void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active)
|
||
{
|
||
volatile uint32_t* paddr = spi0 + BCM2835_SPI0_CS/4;
|
||
uint8_t shift = 21 + cs;
|
||
// Mask in the appropriate CSPOLn bit
|
||
bcm2835_peri_set_bits(paddr, active << shift, 1 << shift);
|
||
}
|
||
|
||
|
||
// Initialise this library
|
||
int bcm2835_init()
|
||
{
|
||
if (debug)
|
||
{
|
||
pads = (uint32_t*)BCM2835_GPIO_PADS;
|
||
clk = (uint32_t*)BCM2835_CLOCK_BASE;
|
||
gpio = (uint32_t*)BCM2835_GPIO_BASE;
|
||
pwm = (uint32_t*)BCM2835_GPIO_PWM;
|
||
spi0 = (uint32_t*)BCM2835_SPI0_BASE;
|
||
return 1; // Success
|
||
}
|
||
else
|
||
{
|
||
uint8_t *mapaddr;
|
||
|
||
// Open the master /dev/memory device
|
||
if ((fd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
// GPIO:
|
||
// Allocate 2 pages - 1 ...
|
||
if ((gpioMem = malloc(BCM2835_BLOCK_SIZE + (BCM2835_PAGE_SIZE - 1))) == NULL)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: malloc failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
// ... to make sure we can round it up to a whole page size
|
||
mapaddr = gpioMem;
|
||
if (((uint32_t)mapaddr % BCM2835_PAGE_SIZE) != 0)
|
||
mapaddr += BCM2835_PAGE_SIZE - ((uint32_t)mapaddr % BCM2835_PAGE_SIZE) ;
|
||
|
||
gpio = (uint32_t *)mmap(mapaddr, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, BCM2835_GPIO_BASE) ;
|
||
|
||
if ((int32_t)gpio < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: mmap failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
// PWM
|
||
if ((pwmMem = malloc(BCM2835_BLOCK_SIZE + (BCM2835_PAGE_SIZE - 1))) == NULL)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: pwmMem malloc failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
mapaddr = pwmMem;
|
||
if (((uint32_t)mapaddr % BCM2835_PAGE_SIZE) != 0)
|
||
mapaddr += BCM2835_PAGE_SIZE - ((uint32_t)mapaddr % BCM2835_PAGE_SIZE) ;
|
||
|
||
pwm = (uint32_t *)mmap(mapaddr, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, BCM2835_GPIO_PWM) ;
|
||
|
||
if ((int32_t)pwm < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: mmap failed (pwm): %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
// Clock control (needed for PWM)
|
||
if ((clkMem = malloc(BCM2835_BLOCK_SIZE + (BCM2835_PAGE_SIZE-1))) == NULL)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: clkMem malloc failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
mapaddr = clkMem;
|
||
if (((uint32_t)mapaddr % BCM2835_PAGE_SIZE) != 0)
|
||
mapaddr += BCM2835_PAGE_SIZE - ((uint32_t)mapaddr % BCM2835_PAGE_SIZE) ;
|
||
|
||
clk = (uint32_t *)mmap(mapaddr, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, BCM2835_CLOCK_BASE) ;
|
||
|
||
if ((int32_t)clk < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: mmap failed (clk): %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
if ((padsMem = malloc(BCM2835_BLOCK_SIZE + (BCM2835_PAGE_SIZE - 1))) == NULL)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: padsMem malloc failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
mapaddr = padsMem;
|
||
if (((uint32_t)mapaddr % BCM2835_PAGE_SIZE) != 0)
|
||
mapaddr += BCM2835_PAGE_SIZE - ((uint32_t)mapaddr % BCM2835_PAGE_SIZE) ;
|
||
|
||
pads = (uint32_t *)mmap(mapaddr, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, BCM2835_GPIO_PADS) ;
|
||
|
||
if ((int32_t)pads < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: mmap failed (pads): %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
if ((spi0Mem = malloc(BCM2835_BLOCK_SIZE + (BCM2835_PAGE_SIZE - 1))) == NULL)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: spi0Mem malloc failed: %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
mapaddr = spi0Mem;
|
||
if (((uint32_t)mapaddr % BCM2835_PAGE_SIZE) != 0)
|
||
mapaddr += BCM2835_PAGE_SIZE - ((uint32_t)mapaddr % BCM2835_PAGE_SIZE) ;
|
||
|
||
spi0 = (uint32_t *)mmap(mapaddr, BCM2835_BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, BCM2835_SPI0_BASE) ;
|
||
|
||
if ((int32_t)spi0 < 0)
|
||
{
|
||
fprintf(stderr, "bcm2835_init: mmap failed (spi0): %s\n", strerror(errno)) ;
|
||
return 0;
|
||
}
|
||
|
||
return 1; // Success
|
||
}
|
||
}
|
||
|
||
// Close this library and deallocate everything
|
||
int bcm2835_close()
|
||
{
|
||
if (!debug)
|
||
{
|
||
if (gpio != MAP_FAILED)
|
||
{
|
||
munmap((void*)gpio, BCM2835_BLOCK_SIZE);
|
||
gpio = MAP_FAILED;
|
||
}
|
||
if (gpioMem)
|
||
{
|
||
free(gpioMem);
|
||
gpioMem = NULL;
|
||
}
|
||
|
||
if (pwm != MAP_FAILED)
|
||
{
|
||
munmap((void*)pwm, BCM2835_BLOCK_SIZE);
|
||
pwm = MAP_FAILED;
|
||
}
|
||
if (pwmMem)
|
||
{
|
||
free(pwmMem);
|
||
pwmMem = NULL;
|
||
}
|
||
|
||
if (clk != MAP_FAILED)
|
||
{
|
||
munmap((void*)clk, BCM2835_BLOCK_SIZE);
|
||
clk = MAP_FAILED;
|
||
}
|
||
if (clkMem)
|
||
{
|
||
free(clkMem);
|
||
clkMem = NULL;
|
||
}
|
||
|
||
if (spi0 != MAP_FAILED)
|
||
{
|
||
munmap((void*)spi0, BCM2835_BLOCK_SIZE);
|
||
spi0 = MAP_FAILED;
|
||
}
|
||
if (spi0Mem)
|
||
{
|
||
free(spi0Mem);
|
||
spi0Mem = NULL;
|
||
}
|
||
|
||
if (fd >= 0)
|
||
{
|
||
close(fd);
|
||
fd = -1;
|
||
}
|
||
}
|
||
return 1; // Success
|
||
}
|
||
|
||
#ifdef BCM2835_TEST
|
||
// this is a simple test program that prints out what it will do rather than
|
||
// actually doing it
|
||
int main(int argc, char **argv)
|
||
{
|
||
// Be non-destructive
|
||
bcm2835_set_debug(1);
|
||
|
||
if (!bcm2835_init())
|
||
return 1;
|
||
|
||
// Configure some GPIO pins fo some testing
|
||
// Set RPI pin P1-11 to be an output
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP);
|
||
// Set RPI pin P1-15 to be an input
|
||
bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT);
|
||
// with a pullup
|
||
bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP);
|
||
// And a low detect enable
|
||
bcm2835_gpio_len(RPI_GPIO_P1_15);
|
||
// and input hysteresis disabled on GPIOs 0 to 27
|
||
bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA);
|
||
|
||
#if 1
|
||
// Blink
|
||
while (1)
|
||
{
|
||
// Turn it on
|
||
bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH);
|
||
|
||
// wait a bit
|
||
delay(500);
|
||
|
||
// turn it off
|
||
bcm2835_gpio_write(RPI_GPIO_P1_11, LOW);
|
||
|
||
// wait a bit
|
||
delay(500);
|
||
}
|
||
#endif
|
||
|
||
#if 0
|
||
// Read input
|
||
while (1)
|
||
{
|
||
// Read some data
|
||
uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15);
|
||
printf("read from pin 15: %d\n", value);
|
||
|
||
// wait a bit
|
||
delay(500);
|
||
}
|
||
#endif
|
||
|
||
#if 0
|
||
// Look for a low event detection
|
||
// eds will be set whenever pin 15 goes low
|
||
while (1)
|
||
{
|
||
if (bcm2835_gpio_eds(RPI_GPIO_P1_15))
|
||
{
|
||
// Now clear the eds flag by setting it to 1
|
||
bcm2835_gpio_set_eds(RPI_GPIO_P1_15);
|
||
printf("low event detect for pin 15\n");
|
||
}
|
||
|
||
// wait a bit
|
||
delay(500);
|
||
}
|
||
#endif
|
||
|
||
if (!bcm2835_close())
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
#endif
|