RPI: Add support for RPI4 (#1364)

This commit is contained in:
Olivier
2019-11-23 09:59:14 +01:00
committed by GitHub
parent be4d6a8874
commit 409b079b69
4 changed files with 920 additions and 131 deletions

View File

@@ -26,7 +26,7 @@ GATEWAY_OBJECTS=$(patsubst %.c,$(BUILDDIR)/%.o,$(GATEWAY_C_SOURCES)) $(patsubst
INCLUDES=-I. -I./core -I./hal/architecture/Linux/drivers/core
ifeq ($(SOC),$(filter $(SOC),BCM2835 BCM2836 BCM2837))
ifeq ($(SOC),$(filter $(SOC),BCM2835 BCM2836 BCM2837 BCM2711))
BCM_C_SOURCES=$(wildcard hal/architecture/Linux/drivers/BCM/*.c)
BCM_CPP_SOURCES=$(wildcard hal/architecture/Linux/drivers/BCM/*.cpp)
GATEWAY_OBJECTS+=$(patsubst %.c,$(BUILDDIR)/%.o,$(BCM_C_SOURCES)) $(patsubst %.cpp,$(BUILDDIR)/%.o,$(BCM_CPP_SOURCES))

17
configure vendored
View File

@@ -17,7 +17,7 @@ SPI driver options:
Device path. [/dev/spidev0.0]
Building options:
--soc=[BCM2835|BCM2836|BCM2837|AM33XX|A10|A13|A20|H3]
--soc=[BCM2711|BCM2835|BCM2836|BCM2837|AM33XX|A10|A13|A20|H3]
SoC type to be used. [configure autodetected]
--cpu-flags=<CPUFLAGS> CPU defining/optimizing flags to be used. [configure autodetected]
--extra-cflags=<CFLAGS> Extra C flags passed to C compilation. []
@@ -183,6 +183,10 @@ function detect_machine {
soc="BCM2837"
tp="rpi3"
;;
3)
soc="BCM2711"
tp="rpi4"
;;
esac
fi
elif [[ $hardware == "BCM2708"* ]]; then
@@ -261,6 +265,9 @@ function gcc_cpu_flags {
BCM2837)
flags="-march=armv8-a+crc -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard"
;;
BCM2711)
flags="-march=armv8-a+crc -mtune=cortex-a72 -mfpu=neon-fp-armv8 -mfloat-abi=hard"
;;
AM33XX)
flags="-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard"
;;
@@ -557,7 +564,7 @@ if [ -z "${CPUFLAGS}" ]; then
CPUFLAGS=$(gcc_cpu_flags $SOC)
fi
if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" ]]; then
if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" || $SOC == "BCM2711" ]]; then
CPPFLAGS="-DLINUX_ARCH_RASPBERRYPI $CPPFLAGS"
else
printf "${SECTION} Checking GPIO Sysfs.\n"
@@ -570,7 +577,7 @@ fi
if [ -z "${SPI_DRIVER}" ]; then
printf "${SECTION} Detecting SPI driver.\n"
if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" ]]; then
if [[ $SOC == "BCM2835" || $SOC == "BCM2836" || $SOC == "BCM2837" || $SOC == "BCM2711" ]]; then
SPI_DRIVER=BCM
elif [[ $(eval 'ls /dev/spidev* 2>/dev/null') ]]; then
SPI_DRIVER=SPIDEV
@@ -585,8 +592,8 @@ fi
if [ -n "${SPI_DRIVER}" ]; then
case ${SPI_DRIVER} in
BCM)
if [[ $SOC != "BCM2835" && $SOC != "BCM2836" && $SOC != "BCM2837" ]]; then
die "BCM SPI driver is only supported for SOCs BCM2835, BCM2836 or BCM2837" 5
if [[ $SOC != "BCM2835" && $SOC != "BCM2836" && $SOC != "BCM2837" && $SOC != "BCM2711" ]]; then
die "BCM SPI driver is only supported for SOCs BCM2835, BCM2836, BCM2837 or BCM2711" 5
fi
CPPFLAGS="-DLINUX_SPI_BCM $CPPFLAGS"
;;

View File

@@ -5,9 +5,7 @@
//
// Author: Mike McCauley
// Copyright (C) 2011-2013 Mike McCauley
// $Id: bcm2835.c,v 1.23 2015/03/31 04:55:41 mikem Exp mikem $
//
// Modified September 2016 by Marcelo Aquino <marceloaqno@gmail.org>
// $Id: bcm2835.c,v 1.27 2019/07/22 23:04:24 mikem Exp mikem $
*/
@@ -20,8 +18,6 @@
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <inttypes.h> // For PRIu64
#include "log.h"
#define BCK2835_LIBRARY_BUILD
#include "bcm2835.h"
@@ -59,18 +55,77 @@ volatile uint32_t *bcm2835_spi0 = (uint32_t *)MAP_FAILED;
volatile uint32_t *bcm2835_bsc0 = (uint32_t *)MAP_FAILED;
volatile uint32_t *bcm2835_bsc1 = (uint32_t *)MAP_FAILED;
volatile uint32_t *bcm2835_st = (uint32_t *)MAP_FAILED;
volatile uint32_t *bcm2835_aux = (uint32_t *)MAP_FAILED;
volatile uint32_t *bcm2835_spi1 = (uint32_t *)MAP_FAILED;
/* This variable 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;
/* RPI 4 has different pullup registers - we need to know if we have that type */
static uint8_t pud_type_rpi4 = 0;
/* RPI 4 has different pullup operation - make backwards compat */
static uint8_t pud_compat_setting = BCM2835_GPIO_PUD_OFF;
/* I2C The time needed to transmit one byte. In microseconds.
*/
static int i2c_byte_wait_us = 0;
/* SPI bit order. BCM2835 SPI0 only supports MSBFIRST, so we instead
* have a software based bit reversal, based on a contribution by Damiano Benedetti
*/
static uint8_t bcm2835_spi_bit_order = BCM2835_SPI_BIT_ORDER_MSBFIRST;
static uint8_t bcm2835_byte_reverse_table[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
static uint8_t bcm2835_correct_order(uint8_t b)
{
if (bcm2835_spi_bit_order == BCM2835_SPI_BIT_ORDER_LSBFIRST) {
return bcm2835_byte_reverse_table[b];
} else {
return b;
}
}
/*
// Low level register access functions
*/
@@ -95,6 +150,11 @@ uint32_t* bcm2835_regbase(uint8_t regbase)
return (uint32_t *)bcm2835_bsc0;
case BCM2835_REGBASE_BSC1:
return (uint32_t *)bcm2835_st;
case BCM2835_REGBASE_AUX:
return (uint32_t *)bcm2835_aux;
case BCM2835_REGBASE_SPI1:
return (uint32_t *)bcm2835_spi1;
}
return (uint32_t *)MAP_FAILED;
}
@@ -114,11 +174,11 @@ unsigned int bcm2835_version(void)
*/
uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
{
uint32_t ret;
if (debug) {
printf("bcm2835_peri_read paddr %08X\n", (unsigned) paddr);
printf("bcm2835_peri_read paddr %p\n", (void *) paddr);
return 0;
} else {
uint32_t ret;
__sync_synchronize();
ret = *paddr;
__sync_synchronize();
@@ -135,7 +195,7 @@ uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
{
if (debug) {
printf("bcm2835_peri_read_nb paddr %08X\n", (unsigned) paddr);
printf("bcm2835_peri_read_nb paddr %p\n", paddr);
return 0;
} else {
return *paddr;
@@ -148,7 +208,7 @@ uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
{
if (debug) {
printf("bcm2835_peri_write paddr %08X, value %08X\n", (unsigned) paddr, value);
printf("bcm2835_peri_write paddr %p, value %08X\n", paddr, value);
} else {
__sync_synchronize();
*paddr = value;
@@ -160,8 +220,8 @@ void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value)
{
if (debug) {
printf("bcm2835_peri_write_nb paddr %08X, value %08X\n",
(unsigned) paddr, value);
printf("bcm2835_peri_write_nb paddr %p, value %08X\n",
paddr, value);
} else {
*paddr = value;
}
@@ -380,8 +440,12 @@ void bcm2835_gpio_clr_afen(uint8_t pin)
/* Set pullup/down */
void bcm2835_gpio_pud(uint8_t pud)
{
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4;
bcm2835_peri_write(paddr, pud);
if( pud_type_rpi4 ) {
pud_compat_setting = pud;
} else {
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4;
bcm2835_peri_write(paddr, pud);
}
}
/* Pullup/down clock
@@ -389,9 +453,15 @@ void bcm2835_gpio_pud(uint8_t pud)
*/
void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on)
{
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32;
uint8_t shift = pin % 32;
bcm2835_peri_write(paddr, (on ? 1 : 0) << shift);
if( pud_type_rpi4 ) {
if( on ) {
bcm2835_gpio_set_pud( pin, pud_compat_setting);
}
} else {
volatile uint32_t* paddr = bcm2835_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 */
@@ -439,7 +509,7 @@ void bcm2835_delayMicroseconds(uint64_t micros)
if (debug) {
/* Cant access sytem timers in debug mode */
printf("bcm2835_delayMicroseconds %" PRIu64 "\n", micros);
printf("bcm2835_delayMicroseconds %lld\n", (long long int) micros);
return;
}
@@ -448,6 +518,14 @@ void bcm2835_delayMicroseconds(uint64_t micros)
*/
start = bcm2835_st_read();
/* Not allowed to access timer registers (result is not as precise)*/
if (start==0) {
t1.tv_sec = 0;
t1.tv_nsec = 1000 * (long)(micros);
nanosleep(&t1, NULL);
return;
}
if (micros > 450) {
t1.tv_sec = 0;
t1.tv_nsec = 1000 * (long)(micros - 200);
@@ -505,17 +583,79 @@ void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask)
// 6. Write to GPPUDCLK0/1 to remove the clock
//
// RPi has P1-03 and P1-05 with 1k8 pullup resistor
//
// RPI 4 uses a different PUD method - no clock
*/
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);
if( pud_type_rpi4 ) {
int shiftbits = (pin & 0xf) << 1;
uint32_t bits;
uint32_t pull;
switch (pud) {
case BCM2835_GPIO_PUD_OFF:
pull = 0;
break;
case BCM2835_GPIO_PUD_UP:
pull = 1;
break;
case BCM2835_GPIO_PUD_DOWN:
pull = 2;
break;
default:
return;
}
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4);
bits = bcm2835_peri_read_nb( paddr );
bits &= ~(3 << shiftbits);
bits |= (pull << shiftbits);
bcm2835_peri_write_nb( paddr, bits );
} else {
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);
}
}
uint8_t bcm2835_gpio_get_pud(uint8_t pin)
{
uint8_t ret = BCM2835_GPIO_PUD_ERROR;
if( pud_type_rpi4 ) {
uint32_t bits;
volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4);
bits = (bcm2835_peri_read_nb( paddr ) >> ((pin & 0xf)<<1)) & 0x3;
switch (bits) {
case 0:
ret = BCM2835_GPIO_PUD_OFF;
break;
case 1:
ret = BCM2835_GPIO_PUD_UP;
break;
case 2:
ret = BCM2835_GPIO_PUD_DOWN;
break;
default:
ret = BCM2835_GPIO_PUD_ERROR;
}
}
return ret;
}
int bcm2835_spi_begin(void)
{
volatile uint32_t* paddr;
@@ -551,9 +691,9 @@ void bcm2835_spi_end(void)
bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); /* CLK */
}
void bcm2835_spi_setBitOrder(uint8_t __attribute__((unused)) order)
void bcm2835_spi_setBitOrder(uint8_t order)
{
/* BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one supported by SPI0 */
bcm2835_spi_bit_order = order;
}
/* defaults to 0, which means a divider of 65536.
@@ -567,6 +707,13 @@ void bcm2835_spi_setClockDivider(uint16_t divider)
bcm2835_peri_write(paddr, divider);
}
void bcm2835_spi_set_speed_hz(uint32_t speed_hz)
{
uint16_t divider = (uint16_t) ((uint32_t) BCM2835_CORE_CLK_HZ / speed_hz);
divider &= 0xFFFE;
bcm2835_spi_setClockDivider(divider);
}
void bcm2835_spi_setDataMode(uint8_t mode)
{
volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
@@ -596,14 +743,14 @@ uint8_t bcm2835_spi_transfer(uint8_t value)
;
/* Write to FIFO, no barrier */
bcm2835_peri_write_nb(fifo, value);
bcm2835_peri_write_nb(fifo, bcm2835_correct_order(value));
/* Wait for DONE to be set */
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
;
/* Read any byte that was sent back by the slave while we sere sending to it */
ret = bcm2835_peri_read_nb(fifo);
ret = bcm2835_correct_order(bcm2835_peri_read_nb(fifo));
/* Set TA = 0, and also set the barrier */
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
@@ -634,12 +781,12 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len)
while((TXCnt < len)||(RXCnt < len)) {
/* TX fifo not full, so add some more bytes */
while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len )) {
bcm2835_peri_write_nb(fifo, tbuf[TXCnt]);
bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[TXCnt]));
TXCnt++;
}
/* Rx fifo not empty, so get the next received bytes */
while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len )) {
rbuf[RXCnt] = bcm2835_peri_read_nb(fifo);
rbuf[RXCnt] = bcm2835_correct_order(bcm2835_peri_read_nb(fifo));
RXCnt++;
}
}
@@ -652,7 +799,7 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len)
}
/* Writes an number of bytes to SPI */
void bcm2835_spi_writenb(char* tbuf, uint32_t len)
void bcm2835_spi_writenb(const char* tbuf, uint32_t len)
{
volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
@@ -676,7 +823,7 @@ void bcm2835_spi_writenb(char* tbuf, uint32_t len)
;
/* Write to FIFO, no barrier */
bcm2835_peri_write_nb(fifo, tbuf[i]);
bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[i]));
/* Read from FIFO to prevent stalling */
while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD) {
@@ -718,6 +865,279 @@ void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active)
bcm2835_peri_set_bits(paddr, active << shift, 1 << shift);
}
void bcm2835_spi_write(uint16_t data)
{
#if 0
char buf[2];
buf[0] = data >> 8;
buf[1] = data & 0xFF;
bcm2835_spi_transfern(buf, 2);
#else
volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
/* 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))
;
/* Write to FIFO */
bcm2835_peri_write_nb(fifo, (uint32_t) data >> 8);
bcm2835_peri_write_nb(fifo, data & 0xFF);
/* Wait for DONE to be set */
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
;
/* Set TA = 0, and also set the barrier */
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
#endif
}
int bcm2835_aux_spi_begin(void)
{
volatile uint32_t* enable = bcm2835_aux + BCM2835_AUX_ENABLE/4;
volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
if (bcm2835_spi1 == MAP_FAILED) {
return 0; /* bcm2835_init() failed, or not root */
}
/* Set the SPI pins to the Alt 4 function to enable SPI1 access on them */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_ALT4); /* SPI1_CE2_N */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MISO */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_ALT4); /* SPI1_MOSI */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_ALT4); /* SPI1_SCLK */
bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(1000000)); // Default 1MHz SPI
bcm2835_peri_write(enable, BCM2835_AUX_ENABLE_SPI0);
bcm2835_peri_write(cntl1, 0);
bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO);
return 1; /* OK */
}
void bcm2835_aux_spi_end(void)
{
/* Set all the SPI1 pins back to input */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_INPT); /* SPI1_CE2_N */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_INPT); /* SPI1_MISO */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_INPT); /* SPI1_MOSI */
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_INPT); /* SPI1_SCLK */
}
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz)
{
uint16_t divider;
if (speed_hz < (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN) {
speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN;
} else if (speed_hz > (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX) {
speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX;
}
divider = (uint16_t) DIV_ROUND_UP(BCM2835_CORE_CLK_HZ, 2 * speed_hz) - 1;
if (divider > (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX) {
return (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX;
}
return divider;
}
static uint32_t spi1_speed;
void bcm2835_aux_spi_setClockDivider(uint16_t divider)
{
spi1_speed = (uint32_t) divider;
}
void bcm2835_aux_spi_write(uint16_t data)
{
volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
_cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
_cntl0 |= 16; // Shift length
bcm2835_peri_write(cntl0, _cntl0);
bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL)
;
bcm2835_peri_write(io, (uint32_t) data << 16);
}
void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len)
{
volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4;
volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
char *tx = (char *) tbuf;
uint32_t tx_len = len;
uint32_t count;
uint32_t data;
uint32_t i;
uint8_t byte;
uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
_cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH;
bcm2835_peri_write(cntl0, _cntl0);
bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
while (tx_len > 0) {
while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL)
;
count = MIN(tx_len, 3);
data = 0;
for (i = 0; i < count; i++) {
byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0;
data |= byte << (8 * (2 - i));
}
data |= (count * 8) << 24;
tx_len -= count;
if (tx_len != 0) {
bcm2835_peri_write(txhold, data);
} else {
bcm2835_peri_write(io, data);
}
while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY)
;
(void) bcm2835_peri_read(io);
}
}
void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len)
{
volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4;
volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
char *tx = (char *)tbuf;
char *rx = (char *)rbuf;
uint32_t tx_len = len;
uint32_t rx_len = len;
uint32_t count;
uint32_t data;
uint32_t i;
uint8_t byte;
uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
_cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
_cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH;
bcm2835_peri_write(cntl0, _cntl0);
bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
while ((tx_len > 0) || (rx_len > 0)) {
while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0)) {
count = MIN(tx_len, 3);
data = 0;
for (i = 0; i < count; i++) {
byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0;
data |= byte << (8 * (2 - i));
}
data |= (count * 8) << 24;
tx_len -= count;
if (tx_len != 0) {
bcm2835_peri_write(txhold, data);
} else {
bcm2835_peri_write(io, data);
}
}
while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) {
count = MIN(rx_len, 3);
data = bcm2835_peri_read(io);
if (rbuf != NULL) {
switch (count) {
case 3:
*rx++ = (char)((data >> 16) & 0xFF);
/*@fallthrough@*/
/* no break */
case 2:
*rx++ = (char)((data >> 8) & 0xFF);
/*@fallthrough@*/
/* no break */
case 1:
*rx++ = (char)((data >> 0) & 0xFF);
}
}
rx_len -= count;
}
while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0)) {
count = MIN(rx_len, 3);
data = bcm2835_peri_read(io);
if (rbuf != NULL) {
switch (count) {
case 3:
*rx++ = (char)((data >> 16) & 0xFF);
/*@fallthrough@*/
/* no break */
case 2:
*rx++ = (char)((data >> 8) & 0xFF);
/*@fallthrough@*/
/* no break */
case 1:
*rx++ = (char)((data >> 0) & 0xFF);
}
}
rx_len -= count;
}
}
}
void bcm2835_aux_spi_transfern(char *buf, uint32_t len)
{
bcm2835_aux_spi_transfernb(buf, buf, len);
}
int bcm2835_i2c_begin(void)
{
uint16_t cdiv;
@@ -898,7 +1318,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len)
/* wait for transfer to complete */
while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)) {
/* we must empty the FIFO as it is populated and not use any delay */
while (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) {
while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD) {
/* Read from FIFO, no barrier */
buf[i] = bcm2835_peri_read_nb(fifo);
i++;
@@ -1122,6 +1542,11 @@ uint64_t bcm2835_st_read(void)
volatile uint32_t* paddr;
uint32_t hi, lo;
uint64_t st;
if (bcm2835_st==MAP_FAILED) {
return 0;
}
paddr = bcm2835_st + BCM2835_ST_CHI/4;
hi = bcm2835_peri_read(paddr);
@@ -1259,7 +1684,7 @@ static void *mapmem(const char *msg, size_t size, int fd, off_t off)
{
void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off);
if (map == MAP_FAILED) {
logError("bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno));
fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno));
}
return map;
}
@@ -1291,22 +1716,58 @@ int bcm2835_init(void)
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4;
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4;
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4;
bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4;
bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4;
return 1; /* Success */
}
/* Figure out the base and size of the peripheral address block
// using the device-tree. Required for RPi2, optional for RPi 1
// using the device-tree. Required for RPi2/3/4, optional for RPi 1
*/
if ((fp = fopen(BMC2835_RPI2_DT_FILENAME, "rb"))) {
unsigned char buf[4];
fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) {
bcm2835_peripherals_base = (uint32_t *)(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
}
fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf)) {
bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
unsigned char buf[16];
uint32_t base_address;
uint32_t peri_size;
if (fread(buf, 1, sizeof(buf), fp) >= 8) {
base_address = (buf[4] << 24) |
(buf[5] << 16) |
(buf[6] << 8) |
(buf[7] << 0);
peri_size = (buf[8] << 24) |
(buf[9] << 16) |
(buf[10] << 8) |
(buf[11] << 0);
if (!base_address) {
/* looks like RPI 4 */
base_address = (buf[8] << 24) |
(buf[9] << 16) |
(buf[10] << 8) |
(buf[11] << 0);
peri_size = (buf[12] << 24) |
(buf[13] << 16) |
(buf[14] << 8) |
(buf[15] << 0);
}
/* check for valid known range formats */
if ((buf[0] == 0x7e) &&
(buf[1] == 0x00) &&
(buf[2] == 0x00) &&
(buf[3] == 0x00) &&
((base_address == BCM2835_PERI_BASE) || (base_address == BCM2835_RPI2_PERI_BASE) ||
(base_address == BCM2835_RPI4_PERI_BASE))) {
bcm2835_peripherals_base = (uint32_t *)base_address;
bcm2835_peripherals_size = peri_size;
if( base_address == BCM2835_RPI4_PERI_BASE ) {
pud_type_rpi4 = 1;
}
}
}
fclose(fp);
}
/* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */
@@ -1321,14 +1782,14 @@ int bcm2835_init(void)
if (geteuid() == 0) {
/* Open the master /dev/mem device */
if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) {
logError("bcm2835_init: Unable to open /dev/mem: %s\n",
strerror(errno)) ;
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
strerror(errno)) ;
goto exit;
}
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd,
(uint32_t)bcm2835_peripherals_base);
(off_t)bcm2835_peripherals_base);
if (bcm2835_peripherals == MAP_FAILED) {
goto exit;
}
@@ -1345,21 +1806,23 @@ int bcm2835_init(void)
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4;
bcm2835_aux = bcm2835_peripherals + BCM2835_AUX_BASE/4;
bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4;
ok = 1;
} else {
/* Not root, try /dev/gpiomem */
/* Open the master /dev/mem device */
if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0) {
logError("bcm2835_init: Unable to open /dev/gpiomem: %s\n",
strerror(errno)) ;
fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n",
strerror(errno)) ;
goto exit;
}
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals_base = 0;
bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd,
(uint32_t)bcm2835_peripherals_base);
(off_t)bcm2835_peripherals_base);
if (bcm2835_peripherals == MAP_FAILED) {
goto exit;
}
@@ -1396,6 +1859,8 @@ int bcm2835_close(void)
bcm2835_bsc0 = MAP_FAILED;
bcm2835_bsc1 = MAP_FAILED;
bcm2835_st = MAP_FAILED;
bcm2835_aux = MAP_FAILED;
bcm2835_spi1 = MAP_FAILED;
return 1; /* Success */
}
@@ -1478,3 +1943,6 @@ int main(int argc, char **argv)
return 0;
}
#endif

View File

@@ -4,14 +4,13 @@
Author: Mike McCauley
Copyright (C) 2011-2013 Mike McCauley
$Id: bcm2835.h,v 1.20 2015/03/31 04:55:41 mikem Exp mikem $
$Id: bcm2835.h,v 1.25 2019/07/22 23:04:13 mikem Exp $
*/
/*! \defgroup BCM2835grp C library for Broadcom BCM 2835 as used in Raspberry Pi
\ingroup internals
This is a C library for Raspberry Pi (RPi). It provides access to
GPIO and other IO functions on the Broadcom BCM 2835 chip,
GPIO and other IO functions on the Broadcom BCM 2835 chip, as used in the RaspberryPi,
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.
@@ -19,12 +18,15 @@
and for accessing the system timers.
Pin event detection is supported by polling (interrupts are not supported).
Works on all versions upt to and including RPI 4.
Works with all versions of Debian up to and including Debian Buster 10.
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 version of the package that this documentation refers to can be downloaded
from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.50.tar.gz
from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.60.tar.gz
You can find the latest version at http://www.airspayce.com/mikem/bcm2835
Several example programs are provided.
@@ -36,7 +38,10 @@
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.
Before asking a question or reporting a bug, please read http://www.catb.org/esr/faqs/smart-questions.html
Before asking a question or reporting a bug, please read
- http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
- http://www.catb.org/esr/faqs/smart-questions.html
- http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian
and Occidentalisv01, 2016-02-09 Raspbian Jessie.
@@ -107,6 +112,8 @@
bcm2835_st
bcm2835_bsc0
bcm2835_bsc1
bcm2835_aux
bcm2835_spi1
\par Raspberry Pi 2 (RPI2)
@@ -161,6 +168,22 @@
- P1-24 (CE0)
- P1-26 (CE1)
Although it is possible to select high speeds for the SPI interface, up to 125MHz (see bcm2835_spi_setClockDivider())
you should not expect to actually achieve those sorts of speeds with the RPi wiring. Our tests on RPi 2 show that the
SPI CLK line when unloaded has a resonant frequency of about 40MHz, and when loaded, the MOSI and MISO lines
ring at an even lower frequency. Measurements show that SPI waveforms are very poor and unusable at 62 and 125MHz.
Dont expect any speed faster than 31MHz to work reliably.
The bcm2835_aux_spi_* functions allow you to control the BCM 2835 SPI1 interface,
allowing you to send and received data by SPI (Serial Peripheral Interface).
The Raspberry Pi GPIO pins used for AUX SPI (SPI1) are:
- P1-38 (MOSI)
- P1-35 (MISO)
- P1-40 (CLK)
- P1-36 (CE2)
\par I2C Pins
The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface,
@@ -216,7 +239,7 @@
clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be
1.2MHz/1024 = 1171.875Hz.
\par SPI
\par Interactions with other systems
In order for bcm2835 library SPI to work, you may need to disable the SPI kernel module using:
@@ -227,6 +250,20 @@
Reboot.
\endcode
Since bcm2835 accesses the lowest level hardware interfaces (in eh intererests of speed and flexibility)
there can be intercations with other low level software trying to do similar things.
It seems that with "latest" 8.0 Jessie 4.9.24-v7+ kernel PWM just won't
work unless you disable audio. There's a line
\code
dtparam=audio=on
\endcode
in the /boot/config.txt.
Comment it out like this:
\code
#dtparam=audio=on
\endcode
\par Real Time performance constraints
The bcm2835 is a library for user programs (i.e. they run in 'userland').
@@ -241,6 +278,9 @@
Arjan reports that you can prevent swapping on Linux with the following code fragment:
\code
#define <sched.h>
#define <sys/mman.h>
struct sched_param sp;
memset(&sp, 0, sizeof(sp));
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
@@ -248,6 +288,15 @@
mlockall(MCL_CURRENT | MCL_FUTURE);
\endcode
\par Crashing on some versions of Raspbian
Some people have reported that various versions of Rasbian will crash or hang
if certain GPIO pins are toggled: https://github.com/raspberrypi/linux/issues/2550
when using bcm2835.
A workaround is to add this line to your /boot/config.txt:
\code
dtoverlay=gpio-no-irq
\endcode
\par Bindings to other languages
mikem has made Perl bindings available at CPAN:
@@ -266,7 +315,13 @@
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
distributed. See https://www.gnu.org/licenses/gpl-2.0.html and COPYING
\par Commercial Licensing
This is the appropriate option if you are creating proprietary applications
and you are not prepared to distribute and share the source code of your
application. To purchase a commercial license, contact info@airspayce.com
\par Acknowledgements
@@ -368,7 +423,7 @@
\version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is
now automatically included.
Added suport for PWM mode with bcm2835_pwm_* functions.
Added support for PWM mode with bcm2835_pwm_* functions.
\version 1.28 Fixed a problem where bcm2835_spi_writenb() would have problems with transfers of more than
64 bytes dues to read buffer filling. Patched by Peter Würtz.
@@ -403,7 +458,7 @@
\version 1.39 Beta version of RPi2 compatibility. Not tested here on RPi2 hardware.
Testers please confirm correct operation on RPi2.<br>
Unneccessary 'volatile' qualifiers removed from all variables and signatures.<br>
Unnecessary 'volatile' qualifiers removed from all variables and signatures.<br>
Removed unsupportable PWM dividers, based on a report from Christophe Cecillon.<br>
Minor improvements to spi.c example.<br>
@@ -425,13 +480,13 @@
Testing on RPI 2, with ArchLinuxARM-rpi-2-latest and 2015-02-16-raspbian-wheezy.<br>
\version 1.44 Added documention about the need for device tree to be enabled on RPI2.<br>
Improvements to detection of availablity of DMB instruction based on value of __ARM_ARCH macro.<br>
Improvements to detection of availability of DMB instruction based on value of __ARM_ARCH macro.<br>
\version 1.45 Fixed an error in the pad group offsets that would prevent bcm2835_gpio_set_pad()
and bcm2835_gpio_pad() working correctly with non-0 pad groups. Reported by Guido.
\version 1.46 2015-09-18
Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2. <br>
Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2. <br>
\version 1.47 2015-11-18
Fixed possibly incorrect reads in bcm2835_i2c_read_register_rs, patch from Eckhardt Ulrich.<br>
@@ -449,10 +504,45 @@
(which prevents access to the SPI and I2C peripherals, amongst others).
Testing on Raspbian Jessie.
\version 1.51 2016-11-03
Added documentation about SPI clock divider and resulting SPI speeds on RPi3.
Fixed a problem where seg fault could occur in bcm2835_delayMicroseconds() if not running as root. Patch from Pok.
\version 1.52 2017-02-03
Added link to commercial license purchasing.
\version 1.53 2018-01-14
Added support for AUX SPI (SPI1)
Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/)
\version 1.54 2018-01-17
Fixed compile errors in new AUX spi code under some circumstances.
\version 1.55 2018-01-20
Fixed version numbers.
Fixed some warnings.
\version 1.56 2018-06-10
Supports bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST), after which SPI bytes are reversed on read or write.
Based on a suggestion by Damiano Benedetti.
\version 1.57 2018-08-28
Added SPI function bcm2835_spi_set_speed_hz(uint32_t speed_hz);
Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/)
\version 1.58 2018-11-29
Added examples/spiram, which shows how to use the included little library (spiram.c and spiram.h)
to read and write SPI RAM chips such as 23K256-I/P
\version 1.59 2019-05-22
Fixed a bug in bcm2835_i2c_read reported by Charles Hayward where a noisy I2C line cold cause a seg fault by
reading too many characters.
\version 1.60 2019-07-23
Applied patch from Mark Dootson for RPi 4 compatibility. Thanks Mark. Not tested here on RPi4, but others report it works.
Tested as still working correctly on earlier RPi models. Tested with Debian Buster on earlier models
\author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
Modified September 2016 by Marcelo Aquino <marceloaqno@gmail.org>
*/
@@ -462,7 +552,7 @@
#include <stdint.h>
#define BCM2835_VERSION 10050 /* Version 1.50 */
#define BCM2835_VERSION 10060 /* Version 1.60 */
/* RPi 2 is ARM v7, and has DMB instruction for memory barriers.
Older RPis are ARM v6 and don't, so a coprocessor instruction must be used instead.
@@ -473,8 +563,7 @@
#define BCM2835_HAVE_DMB
#endif
/*! \defgroup BCM2835constantsgrp Constants for passing to and from library functions
\ingroup BCM2835grp
/*! \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.
@{
*/
@@ -484,15 +573,16 @@
/*! This means pin LOW, false, 0volts on a pin. */
#define LOW 0x0
/*! Return the minimum of 2 numbers */
#ifndef MIN
#define MIN(a, b) (a < b ? a : b)
#endif
/*! Speed of the core clock core_clk */
#define BCM2835_CORE_CLK_HZ 250000000 /*!< 250 MHz */
/*! On RPi2 with BCM2836, and all recent OSs, the base of the peripherals is read from a /proc file */
/*! On all recent OSs, the base of the peripherals is read from a /proc file */
#define BMC2835_RPI2_DT_FILENAME "/proc/device-tree/soc/ranges"
/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals base address */
#define BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET 4
/*! Offset into BMC2835_RPI2_DT_FILENAME for the peripherals size address */
#define BMC2835_RPI2_DT_PERI_SIZE_OFFSET 8
/*! Physical addresses for various peripheral register sets
Base Physical Address of the BCM 2835 peripheral registers
@@ -504,11 +594,17 @@
#define BCM2835_PERI_BASE 0x20000000
/*! Size of the peripherals block on RPi 1 */
#define BCM2835_PERI_SIZE 0x01000000
/*! Alternate base address for RPI 2 / 3 */
#define BCM2835_RPI2_PERI_BASE 0x3F000000
/*! Alternate base address for RPI 4 */
#define BCM2835_RPI4_PERI_BASE 0xFE000000
/*! Alternate size for RPI 4 */
#define BCM2835_RPI4_PERI_SIZE 0x01800000
/*! Offsets for the bases of various peripherals within the peripherals block
/ Base Address of the System Timer registers
*/
#define BCM2835_ST_BASE 0x3000
#define BCM2835_ST_BASE 0x3000
/*! Base Address of the Pads registers */
#define BCM2835_GPIO_PADS 0x100000
/*! Base Address of the Clock/timer registers */
@@ -518,11 +614,18 @@
/*! Base Address of the SPI0 registers */
#define BCM2835_SPI0_BASE 0x204000
/*! Base Address of the BSC0 registers */
#define BCM2835_BSC0_BASE 0x205000
#define BCM2835_BSC0_BASE 0x205000
/*! Base Address of the PWM registers */
#define BCM2835_GPIO_PWM 0x20C000
/*! Base Address of the AUX registers */
#define BCM2835_AUX_BASE 0x215000
/*! Base Address of the AUX_SPI1 registers */
#define BCM2835_SPI1_BASE 0x215080
/*! Base Address of the AUX_SPI2 registers */
#define BCM2835_SPI2_BASE 0x2150C0
/*! Base Address of the BSC1 registers */
#define BCM2835_BSC1_BASE 0x804000
#define BCM2835_BSC1_BASE 0x804000
/*! Physical address and size of the peripherals block
May be overridden on RPi2
@@ -574,6 +677,17 @@ extern volatile uint32_t *bcm2835_bsc0;
*/
extern volatile uint32_t *bcm2835_bsc1;
/*! Base of the AUX registers.
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_aux;
/*! Base of the SPI1 registers.
Available after bcm2835_init has been called (as root)
*/
extern volatile uint32_t *bcm2835_spi1;
/*! \brief bcm2835RegisterBase
Register bases for bcm2835_regbase()
*/
@@ -585,7 +699,9 @@ typedef enum {
BCM2835_REGBASE_PADS = 5, /*!< Base of the PADS registers. */
BCM2835_REGBASE_SPI0 = 6, /*!< Base of the SPI0 registers. */
BCM2835_REGBASE_BSC0 = 7, /*!< Base of the BSC0 registers. */
BCM2835_REGBASE_BSC1 = 8 /*!< Base of the BSC1 registers. */
BCM2835_REGBASE_BSC1 = 8, /*!< Base of the BSC1 registers. */
BCM2835_REGBASE_AUX = 9, /*!< Base of the AUX registers. */
BCM2835_REGBASE_SPI1 = 10 /*!< Base of the SPI1 registers. */
} bcm2835RegisterBase;
/*! Size of memory page on RPi */
@@ -631,6 +747,12 @@ typedef enum {
#define BCM2835_GPPUDCLK0 0x0098 /*!< GPIO Pin Pull-up/down Enable Clock 0 */
#define BCM2835_GPPUDCLK1 0x009c /*!< GPIO Pin Pull-up/down Enable Clock 1 */
/* 2711 has a different method for pin pull-up/down/enable */
#define BCM2835_GPPUPPDN0 0x00e4 /* Pin pull-up/down for pins 15:0 */
#define BCM2835_GPPUPPDN1 0x00e8 /* Pin pull-up/down for pins 31:16 */
#define BCM2835_GPPUPPDN2 0x00ec /* Pin pull-up/down for pins 47:32 */
#define BCM2835_GPPUPPDN3 0x00f0 /* Pin pull-up/down for pins 57:48 */
/*! \brief bcm2835PortFunction
Port function select modes for bcm2835_gpio_fsel()
*/
@@ -655,6 +777,9 @@ typedef enum {
BCM2835_GPIO_PUD_UP = 0x02 /*!< Enable Pull Up control 0b10 */
} bcm2835PUDControl;
/* need a value for pud functions that can't work unless RPI 4 */
#define BCM2835_GPIO_PUD_ERROR 0x08
/*! 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 */
@@ -737,7 +862,7 @@ typedef enum {
RPI_V2_GPIO_P1_31 = 6, /*!< Version 2, Pin P1-31 */
RPI_V2_GPIO_P1_32 = 12, /*!< Version 2, Pin P1-32 */
RPI_V2_GPIO_P1_33 = 13, /*!< Version 2, Pin P1-33 */
RPI_V2_GPIO_P1_35 = 19, /*!< Version 2, Pin P1-35 */
RPI_V2_GPIO_P1_35 = 19, /*!< Version 2, Pin P1-35, can be PWM channel 1 in ALT FUN 5 */
RPI_V2_GPIO_P1_36 = 16, /*!< Version 2, Pin P1-36 */
RPI_V2_GPIO_P1_37 = 26, /*!< Version 2, Pin P1-37 */
RPI_V2_GPIO_P1_38 = 20, /*!< Version 2, Pin P1-38 */
@@ -771,13 +896,69 @@ typedef enum {
RPI_BPLUS_GPIO_J8_31 = 6, /*!< B+, Pin J8-31, */
RPI_BPLUS_GPIO_J8_32 = 12, /*!< B+, Pin J8-32, */
RPI_BPLUS_GPIO_J8_33 = 13, /*!< B+, Pin J8-33, */
RPI_BPLUS_GPIO_J8_35 = 19, /*!< B+, Pin J8-35, */
RPI_BPLUS_GPIO_J8_35 = 19, /*!< B+, Pin J8-35, can be PWM channel 1 in ALT FUN 5 */
RPI_BPLUS_GPIO_J8_36 = 16, /*!< B+, Pin J8-36, */
RPI_BPLUS_GPIO_J8_37 = 26, /*!< B+, Pin J8-37, */
RPI_BPLUS_GPIO_J8_38 = 20, /*!< B+, Pin J8-38, */
RPI_BPLUS_GPIO_J8_40 = 21 /*!< B+, Pin J8-40, */
} RPiGPIOPin;
/* Defines for AUX
GPIO register offsets from BCM2835_AUX_BASE.
*/
#define BCM2835_AUX_IRQ 0x0000 /*!< xxx */
#define BCM2835_AUX_ENABLE 0x0004 /*!< */
#define BCM2835_AUX_ENABLE_UART1 0x01 /*!< */
#define BCM2835_AUX_ENABLE_SPI0 0x02 /*!< SPI0 (SPI1 in the device) */
#define BCM2835_AUX_ENABLE_SPI1 0x04 /*!< SPI1 (SPI2 in the device) */
#define BCM2835_AUX_SPI_CNTL0 0x0000 /*!< */
#define BCM2835_AUX_SPI_CNTL1 0x0004 /*!< */
#define BCM2835_AUX_SPI_STAT 0x0008 /*!< */
#define BCM2835_AUX_SPI_PEEK 0x000C /*!< Read but do not take from FF */
#define BCM2835_AUX_SPI_IO 0x0020 /*!< Write = TX, read=RX */
#define BCM2835_AUX_SPI_TXHOLD 0x0030 /*!< Write = TX keep CS, read=RX */
#define BCM2835_AUX_SPI_CLOCK_MIN 30500 /*!< 30,5kHz */
#define BCM2835_AUX_SPI_CLOCK_MAX 125000000 /*!< 125Mhz */
#define BCM2835_AUX_SPI_CNTL0_SPEED 0xFFF00000 /*!< */
#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX 0xFFF /*!< */
#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20 /*!< */
#define BCM2835_AUX_SPI_CNTL0_CS0_N 0x000C0000 /*!< CS 0 low */
#define BCM2835_AUX_SPI_CNTL0_CS1_N 0x000A0000 /*!< CS 1 low */
#define BCM2835_AUX_SPI_CNTL0_CS2_N 0x00060000 /*!< CS 2 low */
#define BCM2835_AUX_SPI_CNTL0_POSTINPUT 0x00010000 /*!< */
#define BCM2835_AUX_SPI_CNTL0_VAR_CS 0x00008000 /*!< */
#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH 0x00004000 /*!< */
#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000 /*!< */
#define BCM2835_AUX_SPI_CNTL0_ENABLE 0x00000800 /*!< */
#define BCM2835_AUX_SPI_CNTL0_CPHA_IN 0x00000400 /*!< */
#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO 0x00000200 /*!< */
#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT 0x00000100 /*!< */
#define BCM2835_AUX_SPI_CNTL0_CPOL 0x00000080 /*!< */
#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040 /*!< */
#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F /*!< */
#define BCM2835_AUX_SPI_CNTL1_CSHIGH 0x00000700 /*!< */
#define BCM2835_AUX_SPI_CNTL1_IDLE 0x00000080 /*!< */
#define BCM2835_AUX_SPI_CNTL1_TXEMPTY 0x00000040 /*!< */
#define BCM2835_AUX_SPI_CNTL1_MSBF_IN 0x00000002 /*!< */
#define BCM2835_AUX_SPI_CNTL1_KEEP_IN 0x00000001 /*!< */
#define BCM2835_AUX_SPI_STAT_TX_LVL 0xFF000000 /*!< */
#define BCM2835_AUX_SPI_STAT_RX_LVL 0x00FF0000 /*!< */
#define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 /*!< */
#define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 /*!< */
#define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 /*!< */
#define BCM2835_AUX_SPI_STAT_RX_EMPTY 0x00000080 /*!< */
#define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 /*!< */
#define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F /*!< */
/* Defines for SPI
GPIO register offsets from BCM2835_SPI0_BASE.
Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map
@@ -848,28 +1029,35 @@ typedef enum {
/*! \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.
Clock divided is based on nominal base clock rate of 250MHz
Clock divided is based on nominal core clock rate of 250MHz on RPi1 and RPi2, and 400MHz on RPi3.
It is reported that (contrary to the documentation) any even divider may used.
The frequencies shown for each divider have been confirmed by measurement.
The frequencies shown for each divider have been confirmed by measurement on RPi1 and RPi2.
The system clock frequency on RPi3 is different, so the frequency you get from a given divider will be different.
See comments in 'SPI Pins' for information about reliable SPI speeds.
Note: it is possible to change the core clock rate of the RPi 3 back to 250MHz, by putting
\code
core_freq=250
\endcode
in the config.txt
*/
typedef enum {
BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, /*!< 65536 = 262.144us = 3.814697260kHz */
BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, /*!< 32768 = 131.072us = 7.629394531kHz */
BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, /*!< 16384 = 65.536us = 15.25878906kHz */
BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, /*!< 8192 = 32.768us = 30/51757813kHz */
BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, /*!< 4096 = 16.384us = 61.03515625kHz */
BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 8.192us = 122.0703125kHz */
BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 4.096us = 244.140625kHz */
BCM2835_SPI_CLOCK_DIVIDER_512 = 512, /*!< 512 = 2.048us = 488.28125kHz */
BCM2835_SPI_CLOCK_DIVIDER_256 = 256, /*!< 256 = 1.024us = 976.5625kHz */
BCM2835_SPI_CLOCK_DIVIDER_128 = 128, /*!< 128 = 512ns = = 1.953125MHz */
BCM2835_SPI_CLOCK_DIVIDER_64 = 64, /*!< 64 = 256ns = 3.90625MHz */
BCM2835_SPI_CLOCK_DIVIDER_32 = 32, /*!< 32 = 128ns = 7.8125MHz */
BCM2835_SPI_CLOCK_DIVIDER_16 = 16, /*!< 16 = 64ns = 15.625MHz */
BCM2835_SPI_CLOCK_DIVIDER_8 = 8, /*!< 8 = 32ns = 31.25MHz */
BCM2835_SPI_CLOCK_DIVIDER_4 = 4, /*!< 4 = 16ns = 62.5MHz */
BCM2835_SPI_CLOCK_DIVIDER_2 = 2, /*!< 2 = 8ns = 125MHz, fastest you can get */
BCM2835_SPI_CLOCK_DIVIDER_1 = 1 /*!< 1 = 262.144us = 3.814697260kHz, same as 0/65536 */
BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, /*!< 65536 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, /*!< 32768 = 7.629394531kHz on Rpi2, 12.20703125kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, /*!< 16384 = 15.25878906kHz on Rpi2, 24.4140625kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, /*!< 8192 = 30.51757813kHz on Rpi2, 48.828125kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, /*!< 4096 = 61.03515625kHz on Rpi2, 97.65625kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, /*!< 2048 = 122.0703125kHz on Rpi2, 195.3125kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, /*!< 1024 = 244.140625kHz on Rpi2, 390.625kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_512 = 512, /*!< 512 = 488.28125kHz on Rpi2, 781.25kHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_256 = 256, /*!< 256 = 976.5625kHz on Rpi2, 1.5625MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_128 = 128, /*!< 128 = 1.953125MHz on Rpi2, 3.125MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_64 = 64, /*!< 64 = 3.90625MHz on Rpi2, 6.250MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_32 = 32, /*!< 32 = 7.8125MHz on Rpi2, 12.5MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_16 = 16, /*!< 16 = 15.625MHz on Rpi2, 25MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_8 = 8, /*!< 8 = 31.25MHz on Rpi2, 50MHz on RPI3 */
BCM2835_SPI_CLOCK_DIVIDER_4 = 4, /*!< 4 = 62.5MHz on Rpi2, 100MHz on RPI3. Dont expect this speed to work reliably. */
BCM2835_SPI_CLOCK_DIVIDER_2 = 2, /*!< 2 = 125MHz on Rpi2, 200MHz on RPI3, fastest you can get. Dont expect this speed to work reliably.*/
BCM2835_SPI_CLOCK_DIVIDER_1 = 1 /*!< 1 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3, same as 0/65536 */
} bcm2835SPIClockDivider;
/* Defines for I2C
@@ -945,19 +1133,22 @@ typedef enum {
#define BCM2835_ST_CLO 0x0004 /*!< System Timer Counter Lower 32 bits */
#define BCM2835_ST_CHI 0x0008 /*!< System Timer Counter Upper 32 bits */
/*! @} */
/* Defines for PWM, word offsets (ie 4 byte multiples) */
#define BCM2835_PWM_CONTROL 0 /*!< PWM Control register */
#define BCM2835_PWM_STATUS 1 /*!< PWM Status register */
#define BCM2835_PWM_DMAC 2 /*!< PWM DMA Configuration */
#define BCM2835_PWM0_RANGE 4 /*!< PWM Channel 0 Range */
#define BCM2835_PWM0_DATA 5 /*!< PWM Channel 0 Data */
#define BCM2835_PWM_FIF1 6 /*!< PWM FIFO Input */
#define BCM2835_PWM1_RANGE 8 /*!< PWM Channel 1 Range */
#define BCM2835_PWM1_DATA 9 /*!< PWM Channel 1 Data */
#define BCM2835_PWM_CONTROL 0
#define BCM2835_PWM_STATUS 1
#define BCM2835_PWM_DMAC 2
#define BCM2835_PWM0_RANGE 4
#define BCM2835_PWM0_DATA 5
#define BCM2835_PWM_FIF1 6
#define BCM2835_PWM1_RANGE 8
#define BCM2835_PWM1_DATA 9
/* Defines for PWM Clock, word offsets (ie 4 byte multiples) */
#define BCM2835_PWMCLK_CNTL 40 /*!< PWM Clock Control */
#define BCM2835_PWMCLK_DIV 41 /*!< PWM Clock Divider */
#define BCM2835_PWMCLK_CNTL 40
#define BCM2835_PWMCLK_DIV 41
#define BCM2835_PWM_PASSWRD (0x5A << 24) /*!< Password to enable setting PWM clock */
#define BCM2835_PWM1_MS_MODE 0x8000 /*!< Run in Mark/Space mode */
@@ -998,8 +1189,6 @@ typedef enum {
BCM2835_PWM_CLOCK_DIVIDER_1 = 1 /*!< 1 = 4.6875kHz, same as divider 4096 */
} bcm2835PWMClockDivider;
/*! @} */
/* Historical name compatibility */
#ifndef BCM2835_NO_DELAY_COMPATIBILITY
#define delay(x) bcm2835_delay(x)
@@ -1010,8 +1199,7 @@ typedef enum {
extern "C" {
#endif
/*! \defgroup BCM2835initgrp Library initialisation and management
\ingroup BCM2835grp
/*! \defgroup init Library initialisation and management
These functions allow you to intialise and control the bcm2835 library
@{
*/
@@ -1052,8 +1240,7 @@ extern unsigned int bcm2835_version(void);
/*! @} */
/*! \defgroup BCM2835lowlevelgrp Low level register access
\ingroup BCM2835grp
/*! \defgroup lowlevel Low level register access
These functions provide low level register access, and should not generally
need to be used
@@ -1133,8 +1320,7 @@ extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value);
extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask);
/*! @} end of lowlevel */
/*! \defgroup BCM2835gpiogrp GPIO register access
\ingroup BCM2835grp
/*! \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.
@{
@@ -1295,6 +1481,8 @@ extern void bcm2835_gpio_clr_afen(uint8_t pin);
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
On the RPI 4, although this function and bcm2835_gpio_pudclk() are supported for backward
compatibility, new code should always use bcm2835_gpio_set_pud().
\sa bcm2835_gpio_set_pud()
*/
extern void bcm2835_gpio_pud(uint8_t pud);
@@ -1303,17 +1491,23 @@ extern void bcm2835_gpio_pud(uint8_t pud);
\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.
On the RPI 4, although this function and bcm2835_gpio_pud() are supported for backward
compatibility, new code should always use bcm2835_gpio_set_pud().
\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.
Caution: requires root access.
\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.
Caution: requires root access.
\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. Note
that it is not necessary to include BCM2835_PAD_PASSWRD in the mask as this
@@ -1373,10 +1567,17 @@ extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask);
*/
extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud);
/*! On the BCM2711 based RPI 4, gets the current Pull-up/down mode for the specified pin.
Returns one of BCM2835_GPIO_PUD_* from bcm2835PUDControl.
On earlier RPI versions not based on the BCM2711, returns BCM2835_GPIO_PUD_ERROR
\param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
*/
extern uint8_t bcm2835_gpio_get_pud(uint8_t pin);
/*! @} */
/*! \defgroup BCM2835spigrp SPI access
\ingroup BCM2835grp
/*! \defgroup spi SPI access
These functions let you use SPI0 (Serial Peripheral Interface) to
interface with an external SPI device.
@{
@@ -1399,8 +1600,9 @@ extern int bcm2835_spi_begin(void);
extern void bcm2835_spi_end(void);
/*! Sets the SPI bit order
NOTE: has no effect. Not supported by SPI0.
Defaults to
Set the bit order to be used for transmit and receive. The bcm2835 SPI0 only supports BCM2835_SPI_BIT_ORDER_MSB,
so if you select BCM2835_SPI_BIT_ORDER_LSB, the bytes will be reversed in software.
The library defaults to BCM2835_SPI_BIT_ORDER_MSB.
\param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*,
see \ref bcm2835SPIBitOrder
*/
@@ -1413,6 +1615,12 @@ extern void bcm2835_spi_setBitOrder(uint8_t order);
*/
extern void bcm2835_spi_setClockDivider(uint16_t divider);
/*! Sets the SPI clock divider by converting the speed parameter to
the equivalent SPI clock divider. ( see \sa bcm2835_spi_setClockDivider)
\param[in] speed_hz The desired SPI clock speed in Hz
*/
extern void bcm2835_spi_set_speed_hz(uint32_t speed_hz);
/*! Sets the SPI data mode
Sets the clock polariy and phase
\param[in] mode The desired data mode, one of BCM2835_SPI_MODE*,
@@ -1476,14 +1684,83 @@ extern void bcm2835_spi_transfern(char* buf, uint32_t len);
Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect)
during the transfer.
\param[in] buf Buffer of bytes to send.
\param[in] len Number of bytes in the buf buffer, and the number of bytes to send
*/
extern void bcm2835_spi_writenb(const char* buf, uint32_t len);
/*! Transfers half-word 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 the BCM 2835 ARM Peripherls manual
\param[in] data The 8 bit data byte to write to MOSI
\sa bcm2835_spi_writenb()
*/
extern void bcm2835_spi_write(uint16_t data);
/*! Start AUX SPI operations.
Forces RPi AUX SPI pins P1-36 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2)
to alternate function ALT4, which enables those pins for SPI interface.
\return 1 if successful, 0 otherwise (perhaps because you are not running as root)
*/
extern int bcm2835_aux_spi_begin(void);
/*! End AUX SPI operations.
SPI1 pins P1-36 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2)
are returned to their default INPUT behaviour.
*/
extern void bcm2835_aux_spi_end(void);
/*! Sets the AUX SPI clock divider and therefore the AUX SPI clock speed.
\param[in] divider The desired AUX SPI clock divider.
*/
extern void bcm2835_aux_spi_setClockDivider(uint16_t divider);
/*!
* Calculates the input for \sa bcm2835_aux_spi_setClockDivider
* @param speed_hz A value between \sa BCM2835_AUX_SPI_CLOCK_MIN and \sa BCM2835_AUX_SPI_CLOCK_MAX
* @return Input for \sa bcm2835_aux_spi_setClockDivider
*/
extern uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz);
/*! Transfers half-word to and from the AUX SPI slave.
Asserts the currently selected CS pins during the transfer.
\param[in] data The 8 bit data byte to write to MOSI
\return The 8 bit byte simultaneously read from MISO
\sa bcm2835_spi_transfern()
*/
extern void bcm2835_aux_spi_write(uint16_t data);
/*! Transfers any number of bytes to the AUX SPI slave.
Asserts the CE2 pin during the transfer.
\param[in] buf Buffer of bytes to send.
\param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send
*/
extern void bcm2835_spi_writenb(char* buf, uint32_t len);
extern void bcm2835_aux_spi_writenb(const char *buf, uint32_t len);
/*! Transfers any number of bytes to and from the AUX SPI slave
using bcm2835_aux_spi_transfernb.
The returned data from the slave replaces the transmitted data in the buffer.
\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_aux_spi_transfer()
*/
extern void bcm2835_aux_spi_transfern(char *buf, uint32_t len);
/*! Transfers any number of bytes to and from the AUX SPI slave.
Asserts the CE2 pin during the transfer.
Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO.
The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long
\param[in] tbuf Buffer of bytes to send.
\param[out] rbuf Received bytes will by put in this buffer
\param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received
*/
extern void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len);
/*! @} */
/*! \defgroup BCM2835i2cgrp I2C access
\ingroup BCM2835grp
/*! \defgroup i2c I2C access
These functions let you use I2C (The Broadcom Serial Control bus with the Philips
I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device.
@{
@@ -1571,8 +1848,7 @@ extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* bu
/*! @} */
/*! \defgroup BCM2835stgrp System Timer access
\ingroup BCM2835grp
/*! \defgroup st System Timer access
Allows access to and delays using the System Timer Counter.
@{
*/
@@ -1590,8 +1866,7 @@ extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros);
/*! @} */
/*! \defgroup BCM2835pwmgrp Pulse Width Modulation
\ingroup BCM2835grp
/*! \defgroup pwm Pulse Width Modulation
Allows control of 2 independent PWM channels. A limited subset of GPIO pins
can be connected to one of these 2 channels, allowing PWM control of GPIO pins.
You have to set the desired pin into a particular Alt Fun to PWM output. See the PWM
@@ -1635,3 +1910,42 @@ extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data);
#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
*/
/*! @example pwm.c
Shows how to use PWM to control GPIO pins
*/
/*! @example i2c.c
Command line utility for executing i2c commands with the
Broadcom bcm2835. Contributed by Shahrooz Shahparnia.
*/
/*! example gpio.c
Command line utility for executing gpio commands with the
Broadcom bcm2835. Contributed by Shahrooz Shahparnia.
*/
/*! example spimem_test.c
Shows how to use the included little library (spiram.c and spiram.h)
to read and write SPI RAM chips such as 23K256-I/P
*/