CCPIL, a CC1110 hardware loader for Raspberry Pi - to solve the initial bootloader problem.
This commit is contained in:
1 parent
eb39b745cf
commit
aee06d9e1e
10 files changed
+2260
-2
No files matched your search
+4
-2
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef HEX_H
|
||||
#define HEX_H 1
|
||||
|
||||
int read_hexfile(uint8_t *buf, size_t buflen, const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user