CCPIL, a CC1110 hardware loader for Raspberry Pi - to solve the initial bootloader problem.

This commit is contained in:
Toby Jaffey committed 2012-08-07 21:13:15 +00:00
1 parent eb39b745cf
commit aee06d9e1e
10 files changed
+2260 -2

No files matched your search

+4 -2
View File
@@ -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 <jrt-cctl@hodgepig.org>
Toby Jaffey (c) 2012 <toby-cctl@hodgepig.org>
Portions originally from CC Bootloader, Fergus Noble (c) 2011
+13
View File
@@ -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)
+16
View File
@@ -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/
+776
View File
@@ -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 <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
+760
View File
@@ -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 <stdint.h>
/// \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
+179
View File
@@ -0,0 +1,179 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <getopt.h>
#include <sys/time.h>
#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 <toby-ccpl@hodgepig.org>\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<FLASH_SIZE;i+=1024)
{
bool skip = true;
int j;
for (j=i;j<i+1024;j++)
{
if (buf[j] != 0xFF)
{
skip = false;
break;
}
}
if (skip)
{
printf("Skipping blank page %d\n", i/1024);
continue;
}
printf("Programming and verifying page %d\n", i/1024);
if (0 != program_verify_page(buf + i, i/1024))
{
fprintf(stderr, "FAILED\n");
return 1;
}
}
printf("Programming complete\n");
dbg_reset();
}
return 0;
}
+296
View File
@@ -0,0 +1,296 @@
/*
* Derived from:
* Open IMME https://github.com/jkerdels/open_imme
* Jochen kerdels
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
//#define DEBUG 1
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#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);
}
+11
View File
@@ -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
+198
View File
@@ -0,0 +1,198 @@
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
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<hexstrlen;i+=2)
{
int8_t a, b;
if (-1 == (a = parseHexDigit(hexstr[i])))
{
fprintf(stderr, "hexstring_parse: bad digit 0x%02X\n", hexstr[i]);
return 1;
}
if (-1 == (b = parseHexDigit(hexstr[i+1])))
{
fprintf(stderr, "hexstring_parse: bad digit 0x%02X\n", hexstr[i+1]);
return 1;
}
*buf++ = (a << 4) | b;
}
*buflen = hexstrlen/2;
return 0;
}
static int read_record(uint8_t *buf, size_t buflen, const char *line, bool *eof)
{
size_t len;
uint8_t sum = 0;
uint8_t record_sum;
uint8_t record_len;
uint8_t type;
uint16_t addr;
uint8_t data[256];
int i;
*eof = false;
if (line[0] != ':')
{
fprintf(stderr, "bad hexfile: no start\n");
return 1;
}
line+=1;
len = 1;
if (0 != hexstring_parse(line, &record_len, &len))
{
fprintf(stderr, "bad hexfile: no len '%s'\n", line);
return 1;
}
line+=len * 2;
len = 2;
if (0 != hexstring_parse(line, (uint8_t *)&addr, &len))
{
fprintf(stderr, "bad hexfile: no addr\n");
return 1;
}
addr = ntohs(addr);
line+=len * 2;
len = 1;
if (0 != hexstring_parse(line, &type, &len))
{
fprintf(stderr, "bad hexfile: no type\n");
return 1;
}
line+=len * 2;
len = record_len;
if (0 != hexstring_parse(line, data, &len))
{
fprintf(stderr, "bad hexfile: no data\n");
return 1;
}
line+=len * 2;
len = 1;
if (0 != hexstring_parse(line, &record_sum, &len))
{
fprintf(stderr, "bad hexfile: no sum\n");
return 1;
}
line+=len * 2;
if (type == 0)
{
sum += record_len;
sum += addr >> 8;
sum += addr & 0xFF;
for (i=0;i<record_len;i++)
sum += data[i];
sum = (sum ^ 0xFF) + 1;
if (sum != record_sum)
{
fprintf(stderr, "bad hexfile, checksum mismatch\n");
return 1;
}
if (addr + record_len > 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;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef HEX_H
#define HEX_H 1
int read_hexfile(uint8_t *buf, size_t buflen, const char *filename);
#endif