From 409b079b6952f6f19ae742b505a02f80f4fa595e Mon Sep 17 00:00:00 2001 From: Olivier Date: Sat, 23 Nov 2019 09:59:14 +0100 Subject: [PATCH] RPI: Add support for RPI4 (#1364) --- Makefile | 2 +- configure | 17 +- hal/architecture/Linux/drivers/BCM/bcm2835.c | 566 +++++++++++++++++-- hal/architecture/Linux/drivers/BCM/bcm2835.h | 466 ++++++++++++--- 4 files changed, 920 insertions(+), 131 deletions(-) diff --git a/Makefile b/Makefile index cdc34a7c..5b2ef8bd 100644 --- a/Makefile +++ b/Makefile @@ -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)) diff --git a/configure b/configure index c04675bd..125af49a 100755 --- a/configure +++ b/configure @@ -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= CPU defining/optimizing flags to be used. [configure autodetected] --extra-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" ;; diff --git a/hal/architecture/Linux/drivers/BCM/bcm2835.c b/hal/architecture/Linux/drivers/BCM/bcm2835.c index 4235b134..425d1e01 100644 --- a/hal/architecture/Linux/drivers/BCM/bcm2835.c +++ b/hal/architecture/Linux/drivers/BCM/bcm2835.c @@ -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 +// $Id: bcm2835.c,v 1.27 2019/07/22 23:04:24 mikem Exp mikem $ */ @@ -20,8 +18,6 @@ #include #include #include -#include // 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 + + + diff --git a/hal/architecture/Linux/drivers/BCM/bcm2835.h b/hal/architecture/Linux/drivers/BCM/bcm2835.h index 4dc08162..3a8f78ce 100644 --- a/hal/architecture/Linux/drivers/BCM/bcm2835.h +++ b/hal/architecture/Linux/drivers/BCM/bcm2835.h @@ -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 + #define + 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.
- Unneccessary 'volatile' qualifiers removed from all variables and signatures.
+ Unnecessary 'volatile' qualifiers removed from all variables and signatures.
Removed unsupportable PWM dividers, based on a report from Christophe Cecillon.
Minor improvements to spi.c example.
@@ -425,13 +480,13 @@ Testing on RPI 2, with ArchLinuxARM-rpi-2-latest and 2015-02-16-raspbian-wheezy.
\version 1.44 Added documention about the need for device tree to be enabled on RPI2.
- Improvements to detection of availablity of DMB instruction based on value of __ARM_ARCH macro.
+ Improvements to detection of availability of DMB instruction based on value of __ARM_ARCH macro.
\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.
+ Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2.
\version 1.47 2015-11-18 Fixed possibly incorrect reads in bcm2835_i2c_read_register_rs, patch from Eckhardt Ulrich.
@@ -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 */ @@ -462,7 +552,7 @@ #include -#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 +*/