diff --git a/README.markdown b/README.markdown index 679b1cb..8f2c36c 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,9 @@ CC Tiny Loader CCTL is a serial bootloader for the Chipcon CC1110/CC1111 using only one 1KB page of flash. It allows update of the the microcontroller firmware over its serial port. -Included is CCHL, ChipCon Hardware Loader, an application which runs on the CC111x and can program a slave device over the ChipCon debug interface (http://focus.ti.com/lit/ug/swra124/swra124.pdf) +Included is CCPIL, a ChipCon Hardware Loader which runs on the Raspberry Pi, which can be used to program the initial bootloader. + +Included is also CCHL, a ChipCon Hardware Loader which runs on the CC111x and can program a slave device over the ChipCon debug interface (http://focus.ti.com/lit/ug/swra124/swra124.pdf) The bootloader consists of two components, a piece of firmware that is flashed onto the device and a utility for downloading code and manipulating the @@ -13,7 +15,7 @@ flash memory. The client program, `cctl-prog` runs on Linux, OSX/Darwin and Wind Authors ------- -Joby Taffey (c) 2012 +Toby Jaffey (c) 2012 Portions originally from CC Bootloader, Fergus Noble (c) 2011 diff --git a/ccpil/Makefile b/ccpil/Makefile new file mode 100644 index 0000000..bef733a --- /dev/null +++ b/ccpil/Makefile @@ -0,0 +1,13 @@ +CC = gcc +CFLAGS += -Wall -Wextra -O2 -I. +TARGET = ccpil + +all: $(TARGET) + +OBJS = ccpil.o dbg.o hex.o bcm2835.o + +$(TARGET): $(OBJS) + +clean: + rm -f $(TARGET) $(OBJS) + diff --git a/ccpil/README.markdown b/ccpil/README.markdown new file mode 100644 index 0000000..f31aaea --- /dev/null +++ b/ccpil/README.markdown @@ -0,0 +1,16 @@ +Raspberry Pi ChipCon Loader +=========================== + +A bit banging hardware flasher for CC1110 to run on Raspberry Pi. + +Wire: + +* BCM2835 P1.11/GP0 to CC1110 P2.1/DD (XRF pin 17) +* BCM2835 P1.12/GP1 to CC1110 P2.2/DC (XRF pin 18) +* BCM2835 P1.13/GP2 to CC1110 Reset (XRF pin 5) + +I use a Ciseco "Slice of Pi" prototyping board for this, http://openmicros.org/index.php/articles/88-ciseco-product-documentation/160-slice-of-pi-for-the-raspberry-pi + +This board also connects the CC1110's serial port to the Raspberry Pi. +To use /dev/ttyAMA0 from the Pi, you will need to disable the serial console - http://www.irrational.net/2012/04/19/using-the-raspberry-pis-serial-port/ + diff --git a/ccpil/bcm2835.c b/ccpil/bcm2835.c new file mode 100644 index 0000000..3f237a2 --- /dev/null +++ b/ccpil/bcm2835.c @@ -0,0 +1,776 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +// 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 diff --git a/ccpil/bcm2835.h b/ccpil/bcm2835.h new file mode 100644 index 0000000..2fb5009 --- /dev/null +++ b/ccpil/bcm2835.h @@ -0,0 +1,760 @@ +// bcm2835.h +// +// 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.h,v 1.4 2012/07/16 23:57:59 mikem Exp mikem $ +// +/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi +/// +/// This is a C library for Raspberry Pi (RPi). It provides access to +/// GPIO and other IO functions on the Broadcom BCM 2835 chip, +/// allowing access to the GPIO pins on the +/// 26 pin IDE plug on the RPi board so you can control and interface with various external devices. +/// +/// It provides functions for reading digital inputs and setting digital outputs. +/// Pin event detection is supported by polling (interrupts not supported). +/// +/// It is C++ compatible, and installs as a header file and non-shared library on +/// any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with +/// BCM 2835). +/// +/// The latest version of this documentation can be downloaded from +/// http://www.open.com.au/mikem/bcm2835 +/// +/// The version of the package that this documentation refers to can be downloaded +/// from http://www.open.com.au/mikem/bcm2835/bcm2835-1.6.tar.gz +/// You can find the latest version at http://www.open.com.au/mikem/bcm2835 +/// +/// Several example programs are provided. +/// +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +/// +/// You can also find online help and discussion at http://groups.google.com/group/bcm2835 +/// Please use that group for all questions and discussions on this topic. +/// Do not contact the author directly, unless it is to discuss commercial licensing. +/// +/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// are used and the pin is pulled LOW +/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian and Occidentalisv01. +/// Reason for this is not yet determined, but suspect that an interrupt handler is +/// hitting a hard loop on those OSs. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_cler_len() and friends after use. +/// +/// \par Installation +/// +/// This library consists of a single non-shared library and header file, which will be +/// installed in the usual places by make install +/// +/// tar zxvf bcm2835-1.0.tar.gz +/// cd bcm2835-1.0 +/// ./configure +/// make +/// # as root: +/// make check +/// make install +/// +/// \par Physical Addresses +/// +/// The functions bcm2845_peri_read(), bcm2845_peri_write() and bcm2845_peri_set_bits() +/// are low level peripheral register access functions. They are designed to use +/// physical addresses as described in section 1.2.3 ARM physical addresses +/// of the BCM2835 ARM Peripherals manual. +/// Physical addresses range from 0x20000000 to 0x20FFFFFF 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 in the manual at bus address 0x7Ennnnnn is available at +/// physical address 0x20nnnnnn. +/// +/// \par Pin Numbering +/// +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs +/// +/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// IDE header. +/// +/// The functions in this librray are disgned to be passed the BCM 2835 GPIO pin number and _not_ +/// the RPi pin number. There are symbolic definitions for each of the available pins +/// that you should use for convenience. See \ref RPiGPIOPin. +/// +/// \par SPI Pins +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// allowing you to send and received data by SPI (Serial Peripheral Interface). +/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus +/// +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be configured and controled with the +/// usual bcm2835_gpio_* calls +/// +/// The Raspberry Pi GPIO pins used for SPI are: +/// +/// - P1-19 (MOSI) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) +/// - P1-26 (CE1) +/// +/// \par Open Source Licensing GPL V2 +/// +/// This is the appropriate option if you want to share the source code of your +/// application with everyone you distribute it to, and you also want to give them +/// the right to share who uses it. If you wish to use this software under Open +/// Source Licensing, you must contribute all your source code to the open source +/// community in accordance with the GPL Version 2 when your application is +/// distributed. See http://www.gnu.org/copyleft/gpl.html and COPYING +/// +/// \par Acknowledgements +/// +/// Some of this code has been inspired by Dom and Gert. +/// +/// \par Revision History +/// +/// \version 1.0 Initial release +/// \version 1.1 Minor bug fixes +/// \version 1.2 Added support for SPI +/// \version 1.3 Added bcm2835_spi_transfern() +/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. +/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by César Ortiz +/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// changes only the pin specified. Other pins that were already previoulsy +/// enabled stay enabled. +/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// to clear the enable for individual pins, suggested by Andreas Sundstrom. +/// +/// \author Mike McCauley (mikem@open.com.au) + + + +// Defines for BCM2835 +#ifndef BCM2835_H +#define BCM2835_H + +#include + +/// \defgroup constants Constants for passing to and from library functions +/// The values here are designed to be passed to various functions in the bcm2835 library. +/// @{ + + +/// This means pin HIGH, true, 3.3volts on a pin. +#define HIGH 0x1 +/// This means pin LOW, false, 0volts on a pin. +#define LOW 0x0 + +// Physical addresses for various peripheral regiser sets +/// Base Physical Address of the BCM 2835 peripheral registers +#define BCM2835_PERI_BASE 0x20000000 +/// Base Physical Address of the Pads registers +#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) +/// Base Physical Address of the Clock/timer registers +#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) +/// Base Physical Address of the GPIO registers +#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) +/// Base Physical Address of the SPI0 registers +#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) +/// Base Physical Address of the PWM registers +#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) + +/// Size of memory page on RPi +#define BCM2835_PAGE_SIZE (4*1024) +/// Size of memory block on RPi +#define BCM2835_BLOCK_SIZE (4*1024) + + +// Defines for GPIO +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 +#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 +#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 +#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 +#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 +#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 +#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 +#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 +#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 +#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 +#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 +#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 +#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 +#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 +#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 +#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 +#define BCM2835_GPFEN0 0x0048 ///< GPIO Pin Falling Edge Detect Enable 0 +#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 +#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 +#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 +#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 +#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 +#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 +#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 +#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 +#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 +#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable +#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 +#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 + +/// \brief bcm2835PortFunction +/// Port function select modes for bcm2845_gpio_fsel() +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input + BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output + BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 + BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 + BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 + BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 + BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 + BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 + BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask +} bcm2835FunctionSelect; + +/// \brief bcm2835PUDControl +/// Pullup/Pulldown defines for bcm2845_gpio_pud() +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0b00, ///< Off – disable pull-up/down + BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control + BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control +} bcm2835PUDControl; + +/// Pad control register offsets from BCM2835_GPIO_PADS +#define BCM2835_PADS_GPIO_0_27 0x002c ///< Pad control register for pads 0 to 27 +#define BCM2835_PADS_GPIO_28_45 0x0030 ///< Pad control register for pads 28 to 45 +#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 + +/// Pad Control masks +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x04 ///< Hysteresis enabled +#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current +#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current +#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current +#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current +#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current +#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current +#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current +#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current + +/// \brief bcm2835PadGroup +/// Pad group specification for bcm2845_gpio_pad() +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 + BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 + BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 +} bcm2835PadGroup; + +/// \brief RPiGPIOPin +/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. +/// These can be passed as a pin number to any function requiring a pin. +/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins +/// and some can adopt an alternate function. +/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively +/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI +/// and cant be controlled independently +typedef enum +{ + RPI_GPIO_P1_03 = 0, ///< Pin P1-03 + RPI_GPIO_P1_05 = 1, ///< Pin P1-05 + RPI_GPIO_P1_07 = 4, ///< Pin P1-07 + RPI_GPIO_P1_08 = 14, ///< Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_GPIO_P1_10 = 15, ///< Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_GPIO_P1_11 = 17, ///< Pin P1-11 + RPI_GPIO_P1_12 = 18, ///< Pin P1-12 + RPI_GPIO_P1_13 = 21, ///< Pin P1-13 + RPI_GPIO_P1_15 = 22, ///< Pin P1-15 + RPI_GPIO_P1_16 = 23, ///< Pin P1-16 + RPI_GPIO_P1_18 = 24, ///< Pin P1-18 + RPI_GPIO_P1_19 = 10, ///< Pin P1-19, MOSI when SPI0 in use + RPI_GPIO_P1_21 = 9, ///< Pin P1-21, MISO when SPI0 in use + RPI_GPIO_P1_22 = 25, ///< Pin P1-22 + RPI_GPIO_P1_23 = 11, ///< Pin P1-23, CLK when SPI0 in use + RPI_GPIO_P1_24 = 8, ///< Pin P1-24, CE0 when SPI0 in use + RPI_GPIO_P1_26 = 7 ///< Pin P1-26, CE1 when SPI0 in use +} RPiGPIOPin; + +/// Defines for SPI +/// GPIO register offsets from BCM2835_SPI0_BASE. +/// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status +#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs +#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider +#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length +#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH +#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls + +// Register masks for SPI0_CS +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity +#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full +#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) +#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done +#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused +#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused +#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable +#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable +#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR +#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done +#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable +#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active +#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity +#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select + +/// \brief bcm2835SPIBitOrder +/// Specifies the SPI data bit ordering +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First +}bcm2835SPIBitOrder; + +/// \brief bcm2835SPIMode +/// Specify the SPI data mode +typedef enum +{ + BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 + BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 + BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 +}bcm2835SPIMode; + +/// \brief bcm2835SPIChipSelect +/// Specify the SPI chip select pin(s) +typedef enum +{ + BCM2835_SPI_CS0 = 0, ///< Chip Select 0 + BCM2835_SPI_CS1 = 1, ///< Chip Select 1 + BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) + BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself +} bcm2835SPIChipSelect; + +/// \brief bcm2835SPIClockDivider +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +typedef enum +{ + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, ///< 65536 = 256us = 4kHz + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 126us = 8kHz + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 64us = 15.625kHz + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 32us = 31.25kHz + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 16us = 62.5kHz + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 8us = 125kHz + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 4us = 250kHz + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, ///< 512 = 2us = 500kHz + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, ///< 256 = 1us = 1MHz + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, ///< 128 = 500ns = = 2MHz + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, ///< 64 = 250ns = 4MHz + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, ///< 32 = 125ns = 8MHz + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, ///< 16 = 50ns = 20MHz + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, ///< 8 = 25ns = 40MHz + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, ///< 4 = 12.5ns 80MHz + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, ///< 2 = 6.25ns = 160MHz + BCM2835_SPI_CLOCK_DIVIDER_1 = 1, ///< 0 = 256us = 4kHz +} bcm2835SPIClockDivider; + + +/// @} + + +// Defines for PWM +#define BCM2835_PWM_CONTROL 0 +#define BCM2835_PWM_STATUS 1 +#define BCM2835_PWM0_RANGE 4 +#define BCM2835_PWM0_DATA 5 +#define BCM2835_PWM1_RANGE 8 +#define BCM2835_PWM1_DATA 9 + +#define BCM2835_PWMCLK_CNTL 40 +#define BCM2835_PWMCLK_DIV 41 + +#define BCM2835_PWM1_MS_MODE 0x8000 /// Run in MS mode +#define BCM2835_PWM1_USEFIFO 0x2000 /// Data from FIFO +#define BCM2835_PWM1_REVPOLAR 0x1000 /// Reverse polarity +#define BCM2835_PWM1_OFFSTATE 0x0800 /// Ouput Off state +#define BCM2835_PWM1_REPEATFF 0x0400 /// Repeat last value if FIFO empty +#define BCM2835_PWM1_SERIAL 0x0200 /// Run in serial mode +#define BCM2835_PWM1_ENABLE 0x0100 /// Channel Enable + +#define BCM2835_PWM0_MS_MODE 0x0080 /// Run in MS mode +#define BCM2835_PWM0_USEFIFO 0x0020 /// Data from FIFO +#define BCM2835_PWM0_REVPOLAR 0x0010 /// Reverse polarity +#define BCM2835_PWM0_OFFSTATE 0x0008 /// Ouput Off state +#define BCM2835_PWM0_REPEATFF 0x0004 /// Repeat last value if FIFO empty +#define BCM2835_PWM0_SERIAL 0x0002 /// Run in serial mode +#define BCM2835_PWM0_ENABLE 0x0001 /// Channel Enable + +#ifdef __cplusplus +extern "C" { +#endif + + /// \defgroup init Library initialisation and management + /// These functions allow you to intialise and control the bcm2835 library + /// @{ + + /// Initialise the library by opening /dev/mem and getting pointers to the + /// internal memory for BCM 2835 device registers. You must call this (successfully) + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, + /// calling any other function may result in crashes or other failures. + /// Prints messages to stderr in case of errors. + /// \return 1 if successful else 0 + extern int bcm2835_init(); + + /// Close the library, deallocating any allocaterd memory and closing /dev/mem + /// \return 1 if successful else 0 + extern int bcm2835_close(); + + /// Sets the debug level of the library. + /// A value of 1 prevents mapping to /dev/mem, and makes the library print out + /// what it would do, rather than accessing the GPIO registers. + /// A value of 0, the default, causes normal operation. + /// Call this before calling bcm2835_init(); + /// \param[in] debug The new debug level. 1 means debug + extern void bcm2835_set_debug(uint8_t debug); + + /// @} // end of init + + /// \defgroup lowlevel Low level register access + /// These functions provide low level register access, and should not generally + /// need to be used + /// + /// @{ + + /// Reads 32 bit value from a peripheral address + /// The read is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + + /// Reads 32 bit value from a peripheral address without the read barrier + /// You should only use this when your code has previously called bcm2835_peri_read() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /// Writes 32 bit value from a peripheral address + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /// Writes 32 bit value from a peripheral address without the write barrier + /// You should only use this when your code has previously called bcm2835_peri_write() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /// Alters a number of bits in a 32 peripheral regsiter. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. + /// All other bits that are 0 in the mask are unaffected. + /// Use this to alter a subset of the bits in a register. + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write, masked in by mask. + /// \param[in] mask Bitmask that defines the bits that will be altered in the register. + /// \sa Physical Addresses + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /// @} // end of lowlevel + + /// \defgroup gpio GPIO register access + /// These functions allow you to control the GPIO interface. You can set the + /// function of each GPIO pin, read the input state and set the output state. + /// @{ + + /// Sets the Function Select register for the given pin, which configures + /// the pin as Input, Output or one of the 6 alternate functions. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from RPiGPIOPin. + /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /// Sets the specified pin output to + /// HIGH. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_set(uint8_t pin); + + /// Sets the specified pin output to + /// LOW. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_clr(uint8_t pin); + + /// Reads the current level on the specified + /// pin and returns either HIGH or LOW. Works whether or not the pin + /// is an input or an output. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return the current level either HIGH or LOW + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /// Event Detect Status. + /// Tests whether the specified pin has detected a level or edge + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return HIGH if the event detect status for th given pin is true. + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /// Sets the Event Detect Status register for a given pin to 1, + /// which has the effect of clearing the flag. Use this afer seeing + /// an Event Detect Status on the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /// Enable Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a “011” pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_ren(uint8_t pin); + + /// Disable Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /// Enable Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a “100” pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_fen(uint8_t pin); + + /// Disable Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /// Enable High Detect Enable for the specified pin. + /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_hen(uint8_t pin); + + /// Disable High Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /// Enable Low Detect Enable for the specified pin. + /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_len(uint8_t pin); + + /// Disable Low Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// rising edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_aren(uint8_t pin); + + /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// falling edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_afen(uint8_t pin); + + /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /// Sets the Pull-up/down register for the given pin. This is + /// used with bcm2835_gpio_pudclk() to set the Pull-up/down resistor for the given pin. + /// However, it is usually more convenient to use bcm2835_gpio_set_pud(). + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pud(uint8_t pud); + + /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. + /// LOW to remove the clock. + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); + + /// Reads and returns the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + extern uint32_t bcm2835_gpio_pad(uint8_t group); + + /// Sets the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /// Delays for the specified number of milliseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// \param[in] millis Delay in milliseconds + extern void delay (unsigned int millis); + + /// Delays for the specified number of microseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep: + /// If the interval specified in req is not an exact multiple of the granu- + /// larity underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep com- + /// pletes, there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact + /// result in a dleay of about 80 microseconds. Your mileage may vary. + /// \param[in] micros Delay in microseconds + extern void delayMicroseconds (unsigned int micros); + + /// Sets the output state of the specified pin + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /// Sets the Pull-up/down mode for the specified pin. This is more convenient than + /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /// @} + + /// \defgroup spi SPI access + /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// interface with an external SPI device. + /// @{ + + /// Start SPI operations. + /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// to alternate function ALT0, which enables those pins for SPI interface. + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// their default functions + /// \sa bcm2835_spi_end() + extern void bcm2835_spi_begin(); + + /// End SPI operations. + /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// are returned to their default INPUT behaviour. + extern void bcm2835_spi_end(); + + /// Sets the SPI bit order + /// NOTE: has no effect. Not supported by SPI0. + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// see \ref bcm2835SPIBitOrder + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// see \ref bcm2835SPIClockDivider + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /// Sets the SPI data mode + /// Sets the clock polariy and phase + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// see \ref bcm2835SPIMode + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /// Sets the chip select pin(s) + /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + /// transfer. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /// Sets the chip select pin polarity for a given pin + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) + /// return to the complement (inactive) value. + /// \param[in] cs The chip select pin to affect + /// \param[in] active Whether the chip select pin is to be active HIGH + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /// Transfers one byte to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + /// Returns the read data byte from the slave. + /// Uses polled transfer as per section 10.6.1 of teh BCM 2835 ARM Peripherls manual + /// \param[in] value The 8 bit data byte to write to MOSI + /// \return The 8 bit byte simultaneously read from MISO + /// \sa bcm2835_spi_transfern() + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /// Transfers any number of bytes to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + /// The returned data from the slave replaces the transmitted data in the buffer. + /// Uses polled transfer as per section 10.6.1 of teh BCM 2835 ARM Peripherls manual + /// \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents + /// \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + + /// @} + +#ifdef __cplusplus +} +#endif + +#endif // BCM2835_H + +/// @example blink.c +/// Blinks RPi GPIO pin 11 on and off + +/// @example input.c +/// Reads the state of an RPi input pin + +/// @example event.c +/// Shows how to use event detection on an input pin + +/// @example spi.c +/// Shows how to use SPI interface to transfer a byte to and from an SPI device + +/// @example spin.c +/// Shows how to use SPI interface to transfer a number of bytes to and from an SPI device + diff --git a/ccpil/ccpil.c b/ccpil/ccpil.c new file mode 100644 index 0000000..92c4648 --- /dev/null +++ b/ccpil/ccpil.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbg.h" +#include "hex.h" + +#define FLASH_SIZE (32*1024) + +static bool opt_flash = false; +static char *flash_filename = NULL; + +static struct option long_options[] = +{ + {"help", no_argument, 0, 'h'}, + {"flash", required_argument, 0, 'f'}, + {0, 0, 0, 0} +}; + +static void usage(void) +{ + fprintf(stderr, "ChipCon Pi Loader, Toby Jaffey \n"); + fprintf(stderr, "cctl-prog [-f file.hex]\n"); + fprintf(stderr, " --help -h This help\n"); + fprintf(stderr, " --flash=file.hex -f file.hex Reflash device with intel hex file\n"); +} + +static int parse_options(int argc, char **argv) +{ + int c; + int option_index; + + while(1) + { + c = getopt_long (argc, argv, "hf:", long_options, &option_index); + if (c == -1) + break; + switch(c) + { + case 'h': + return 1; + break; + case 'f': + opt_flash = true; + flash_filename = strdup(optarg); + break; + default: + return 1; + break; + } + } + + return 0; +} + +static void dump(const uint8_t *p, size_t len) +{ + while(len--) + printf("%02X", *p++); + printf("\n"); +} + +static int program_verify_page(const uint8_t *data, uint8_t page) +{ + uint8_t verbuf[1024]; + + if (0 != dbg_writepage(page, data)) + { + fprintf(stderr, "program_page failed\n"); + return 1; + } + + if (0 != dbg_readpage(page, verbuf)) + { + fprintf(stderr, "read_page failed\n"); + return 1; + } + + if (0!=memcmp(verbuf, data, 1024)) + { + fprintf(stderr, "verify failed\n"); + + printf("verbuf = "); + dump(verbuf, 1024); + printf("expected = "); + dump(data, 1024); + + return 1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + uint8_t *buf; + int i; + + if (NULL == (buf=malloc(FLASH_SIZE))) + { + fprintf(stderr, "out of ram\n"); + return 1; + } + + if (0 != parse_options(argc, argv)) + { + usage(); + return 1; + } + + if (!opt_flash) + { + usage(); + return 1; + } + + if (opt_flash) + { + if (0 != dbg_init()) + { + fprintf(stderr, "Failed to initialise (run as root for /dev/mem access)\n"); + return 1; + } + + memset(buf, 0xFF, FLASH_SIZE); + if (0 != read_hexfile(buf, FLASH_SIZE, flash_filename)) + { + fprintf(stderr, "Failed to read %s\n", flash_filename); + return 1; + } + + if (0 != dbg_mass_erase()) + { + fprintf(stderr, "CC1110 mass erase failed\n"); + return 1; + } + + for (i=0;i +#include +#include +#include +#include +#include + +#include "bcm2835.h" + +#define NUM_ATTEMPTS 100 + +#define PIN_DD RPI_GPIO_P1_11 +#define PIN_DC RPI_GPIO_P1_12 +#define PIN_RST RPI_GPIO_P1_13 + + +#define ST_CHIP_ERASE_DONE 0x80 +#define ST_PCON_IDLE 0x40 +#define ST_CPU_HALTED 0x20 +#define ST_POWER_MODE_0 0x10 +#define ST_HALT_STATUS 0x08 +#define ST_DEBUG_LOCKED 0x04 +#define ST_OSCILLATOR_STABLE 0x02 +#define ST_STACK_OVERFLOW 0x01 + +#define FLASHPAGE_SIZE 1024 +#define FLASH_WORD_SIZE 2 +#define WORDS_PER_FLASH_PAGE 512 + +void critical_error(const char *msg) +{ + fprintf(stderr, "Critical error: %s\n", msg); + exit(1); +} + +static void delay_ns(unsigned int ns) +{ + struct timespec sleeper, dummy; + sleeper.tv_sec = 0; + sleeper.tv_nsec = ns ; + nanosleep (&sleeper, &dummy) ; +} + +static void send_byte(uint8_t ch) +{ + int8_t i; + bcm2835_gpio_fsel(PIN_DD, BCM2835_GPIO_FSEL_OUTP); + + for (i = 7; i >= 0; i--) + { + if (ch & (1 << i)) + bcm2835_gpio_write(PIN_DD, HIGH); + else + bcm2835_gpio_write(PIN_DD, LOW); + + bcm2835_gpio_write(PIN_DC, HIGH); + delay_ns(1); + bcm2835_gpio_write(PIN_DC, LOW); + delay_ns(1); + } +} + +static uint8_t recv_byte(void) +{ + uint8_t ch = 0; + int8_t i; + + bcm2835_gpio_fsel(PIN_DD, BCM2835_GPIO_FSEL_INPT); + bcm2835_gpio_set_pud(PIN_DD, BCM2835_GPIO_PUD_DOWN); + + for (i = 7; i >= 0; i--) + { + bcm2835_gpio_write(PIN_DC, HIGH); + delay_ns(1); + if (bcm2835_gpio_lev(PIN_DD)) + ch |= (1 << i); + bcm2835_gpio_write(PIN_DC, LOW); + } +#ifdef DEBUG + fprintf(stderr, "RX: %02X\n", ch); +#endif + return ch; +} + + +int dbg_init(void) +{ + if (!bcm2835_init()) + return 1; + + bcm2835_gpio_fsel(PIN_RST, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(PIN_DD, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_fsel(PIN_DC, BCM2835_GPIO_FSEL_OUTP); + + bcm2835_gpio_write(PIN_DD, LOW); + + // send debug init sequence + bcm2835_gpio_write(PIN_RST, LOW); + delay(1); + bcm2835_gpio_write(PIN_DC, LOW); + delay(1); + bcm2835_gpio_write(PIN_DC, HIGH); + delay(1); + bcm2835_gpio_write(PIN_DC, LOW); + delay(1); + bcm2835_gpio_write(PIN_DC, HIGH); + delay(1); + bcm2835_gpio_write(PIN_DC, LOW); + delay(1); + bcm2835_gpio_write(PIN_RST, HIGH); + delay(1); + + return 0; +} + +void dbg_reset(void) +{ + bcm2835_gpio_write(PIN_RST, LOW); + delay(100); + bcm2835_gpio_write(PIN_RST, HIGH); +} + +static uint8_t read_status(void) +{ + send_byte(0x34); + return recv_byte(); +} + +int dbg_mass_erase(void) +{ + int attempts = NUM_ATTEMPTS; + send_byte(0x14); + recv_byte(); + while (attempts && !(read_status() & ST_CHIP_ERASE_DONE)) + attempts--; + + if (0 == attempts) + return 1; + return 0; +} + +static uint8_t debug_instr_1(uint8_t in0) +{ + send_byte(0x55); + send_byte(in0); + return recv_byte(); +} + +static uint8_t debug_instr_2(uint8_t in0, uint8_t in1) +{ + send_byte(0x56); + send_byte(in0); + send_byte(in1); + return recv_byte(); +} + +static uint8_t debug_instr_3(uint8_t in0, uint8_t in1, uint8_t in2) +{ + send_byte(0x57); + send_byte(in0); + send_byte(in1); + send_byte(in2); + return recv_byte(); +} + +static void write_xdata_memory(uint16_t address, uint16_t count, const uint8_t *buf) +{ + int i; + debug_instr_3(0x90,address >> 8,address); + for (i = 0; i < count; ++i) { + debug_instr_2(0x74, buf[i]); + debug_instr_1(0xF0); + debug_instr_1(0xA3); + } +} + +static void set_pc(uint16_t address) +{ + debug_instr_3(0x02,address >> 8,address); +} + +static void cpu_resume(void) +{ + send_byte(0x4C); + recv_byte(); // ignore sent value +} + + + +static void read_code_memory(uint16_t address, + uint8_t bank, + uint16_t count, + uint8_t *outputData) +{ + int i; + if (address >= 0x8000) + address = (address & 0x7FFF) + (bank * 0x8000); + + debug_instr_3(0x75,0xC7,(bank * 16) + 1); + debug_instr_3(0x90,address >> 8,address); + for (i = 0; i < count; ++i) { + debug_instr_1(0xE4); + outputData[i] = debug_instr_1(0x93); + debug_instr_1(0xA3); + } +} + +static uint8_t updProc[] = +{ + 0x75, 0xAD, /*ADDRESS*/0x00, + 0x75, 0xAC, 0x00, + 0x75, 0xAB, 0x23, 0x00, + 0x75, 0xAE, 0x01, // ------ + 0xE5, 0xAE, // erase code + 0x20, 0xE7, 0xFB, // ------ + 0x90, 0xF0, 0x00, + 0x7F, WORDS_PER_FLASH_PAGE >> 8, + 0x7E, WORDS_PER_FLASH_PAGE & 0xFF, + 0x75, 0xAE, 0x02, + 0x7D, FLASH_WORD_SIZE, + 0xE0, + 0xA3, + 0xF5, 0xAF, + 0xDD, 0xFA, + 0xE5, 0xAE, + 0x20, 0xE6, 0xFB, + 0xDE, 0xF1, + 0xDF, 0xEF, + 0xA5 +}; + + +static int write_flash_page(uint32_t address, const uint8_t *buf) +{ + int attempts = NUM_ATTEMPTS; + uint8_t updProcSize = sizeof(updProc); + + updProc[2] = ((address >> 8) / FLASH_WORD_SIZE) & 0x7E; + + write_xdata_memory(0xF000, FLASHPAGE_SIZE, buf); + + write_xdata_memory(0xF000 + FLASHPAGE_SIZE, updProcSize, updProc); + debug_instr_3(0x75, 0xC7, 0x51); + + set_pc(0xF000 + FLASHPAGE_SIZE); + cpu_resume(); + while (attempts && !(read_status() & ST_CPU_HALTED)) + attempts--; + + if (0 == attempts) + return 1; + return 0; +} + + + +static int read_flash_page(uint32_t address, uint8_t *outputData) +{ + read_code_memory(address & 0xFFFF, + (address >> 15) & 0x03, FLASHPAGE_SIZE, outputData); + return 0; +} + + +int dbg_readpage(uint8_t page, uint8_t *buf) +{ + return read_flash_page(page * 1024, buf); +} + +int dbg_writepage(uint8_t page, const uint8_t *buf) +{ + uint32_t addr = page*1024; + return write_flash_page(addr, buf); +} + diff --git a/ccpil/dbg.h b/ccpil/dbg.h new file mode 100644 index 0000000..f571125 --- /dev/null +++ b/ccpil/dbg.h @@ -0,0 +1,11 @@ +#ifndef DBG_H +#define DBG_H 1 + +extern int dbg_init(void); +extern int dbg_mass_erase(void); +extern int dbg_writepage(uint8_t page, const uint8_t *buf); +extern int dbg_readpage(uint8_t page, const uint8_t *buf); +extern void dbg_reset(void); + +#endif + diff --git a/ccpil/hex.c b/ccpil/hex.c new file mode 100644 index 0000000..0c5c336 --- /dev/null +++ b/ccpil/hex.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t tolowercase(uint8_t ch) +{ + if ((ch >= 'A') && (ch <= 'Z')) + return ch + 0x20; // Convert uppercase to lowercase + return ch; +} + +static int8_t parseHexDigit(uint8_t digit) +{ + digit = tolowercase(digit); + if (isdigit(digit)) + return (int8_t)digit - '0'; + if ((digit >= 'a') && (digit <= 'f')) + return (int8_t)digit + 0xA - 'a'; + return -1; // Error case - input wasn't a valid hex digit +} + + +static int hexstring_parse(const char *hexstr, uint8_t *buf, size_t *buflen) +{ + size_t hexstrlen = *buflen * 2; + size_t i; + + if (hexstrlen & 0x1) + { + fprintf(stderr, "hexstring_parse: not even\n"); + return 1; + } + + if (*buflen < hexstrlen/2) + { + fprintf(stderr, "hexstring_parse: buffer too small %zu < %zu\n", *buflen, hexstrlen/2); + return 1; + } + + for (i=0;i> 8; + sum += addr & 0xFF; + + for (i=0;i buflen) + { + fprintf(stderr, "bad hexfile, too big\n"); + return 1; + } + memcpy(buf + addr, data, record_len); + } + else + if (type == 1) + { + *eof = true; + } + else + { + fprintf(stderr, "bad hexfile: unknown record type %02X\n", type); + } + + return 0; +} + +int read_hexfile(uint8_t *buf, size_t buflen, const char *filename) +{ + FILE *fp; + char line[1024]; + bool eof; + + if (NULL == (fp = fopen(filename, "ro"))) + return 1; + + while (NULL != fgets(line, sizeof(line), fp)) + { + char *p = (line + strlen(line)) - 1; + + while(p > line) + { + if (isspace((int)(*p))) + *p = 0; + p--; + } + + if (0 != read_record(buf, buflen, line, &eof)) + goto fail; + + if (eof) + break; + } + + fclose(fp); + return 0; +fail: + fclose(fp); + return 1; +} + + diff --git a/ccpil/hex.h b/ccpil/hex.h new file mode 100644 index 0000000..1293874 --- /dev/null +++ b/ccpil/hex.h @@ -0,0 +1,7 @@ +#ifndef HEX_H +#define HEX_H 1 + +int read_hexfile(uint8_t *buf, size_t buflen, const char *filename); + +#endif +