diff --git a/package-lock.json b/package-lock.json index 5e94b08..5789889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "node-dht-sensor", - "version": "0.4.5", + "version": "0.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "node-dht-sensor", - "version": "0.4.5", + "version": "0.5.1", "hasInstallScript": true, "license": "LGPL-3.0", "dependencies": { diff --git a/src/bcm2835/bcm2835.c b/src/bcm2835/bcm2835.c old mode 100755 new mode 100644 index fdc91b7..3d58123 --- a/src/bcm2835/bcm2835.c +++ b/src/bcm2835/bcm2835.c @@ -5,9 +5,13 @@ // // Author: Mike McCauley // Copyright (C) 2011-2013 Mike McCauley -// $Id: bcm2835.c,v 1.27 2019/07/22 23:04:24 mikem Exp mikem $ +// $Id: bcm2835.c,v 1.28 2020/01/11 05:07:13 mikem Exp mikem $ +// +// S.Peacock: modified bcm2835_i2c_write() to add a fail safe timer. */ +// Needed to compile with gcc -std=c99, as reported by John Blaiklock. +#define _POSIX_C_SOURCE 200809L #include #include @@ -38,8 +42,8 @@ /* Physical address and size of the peripherals block // May be overridden on RPi2 */ -uint32_t *bcm2835_peripherals_base = (uint32_t *)BCM2835_PERI_BASE; -uint32_t bcm2835_peripherals_size = BCM2835_PERI_SIZE; +off_t bcm2835_peripherals_base = BCM2835_PERI_BASE; +size_t bcm2835_peripherals_size = BCM2835_PERI_SIZE; /* Virtual memory address of the mapped peripherals block */ @@ -57,6 +61,8 @@ 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; +/* BEB*/ +volatile uint32_t *bcm2835_smi = (uint32_t *)MAP_FAILED; @@ -126,6 +132,23 @@ static uint8_t bcm2835_correct_order(uint8_t b) return b; } +#ifdef BCM2835_HAVE_LIBCAP +#include +static int bcm2835_has_capability(cap_value_t capability) +{ + int ok = 0; + cap_t cap = cap_get_proc(); + if (cap) + { + cap_flag_value_t value; + if (cap_get_flag(cap,capability,CAP_EFFECTIVE,&value) == 0 && value == CAP_SET) + ok = 1; + cap_free(cap); + } + return ok; +} +#endif + /* // Low level register access functions */ @@ -155,6 +178,10 @@ uint32_t* bcm2835_regbase(uint8_t regbase) return (uint32_t *)bcm2835_aux; case BCM2835_REGBASE_SPI1: return (uint32_t *)bcm2835_spi1; + /* BEB */ + case BCM2835_REGBASE_SMI: + return (uint32_t *)bcm2835_smi; + } return (uint32_t *)MAP_FAILED; @@ -442,6 +469,7 @@ void bcm2835_gpio_afen(uint8_t pin) uint32_t value = 1 << shift; bcm2835_peri_set_bits(paddr, value, value); } + void bcm2835_gpio_clr_afen(uint8_t pin) { volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32; @@ -453,14 +481,15 @@ void bcm2835_gpio_clr_afen(uint8_t pin) /* Set pullup/down */ void bcm2835_gpio_pud(uint8_t pud) { - if( pud_type_rpi4 ) + if (pud_type_rpi4) { pud_compat_setting = pud; } - else { - volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; - bcm2835_peri_write(paddr, pud); -} + else + { + volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4; + bcm2835_peri_write(paddr, pud); + } } /* Pullup/down clock @@ -468,25 +497,24 @@ void bcm2835_gpio_pud(uint8_t pud) */ void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on) { - if( pud_type_rpi4 ) + if (pud_type_rpi4) { - if( on ) - bcm2835_gpio_set_pud( pin, pud_compat_setting); - } + 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); -} + { + 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 */ uint32_t bcm2835_gpio_pad(uint8_t group) { - if (bcm2835_pads == MAP_FAILED) { - return 0; - } + if (bcm2835_pads == MAP_FAILED) + return 0; volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; return bcm2835_peri_read(paddr); @@ -498,9 +526,8 @@ uint32_t bcm2835_gpio_pad(uint8_t group) */ void bcm2835_gpio_set_pad(uint8_t group, uint32_t control) { - if (bcm2835_pads == MAP_FAILED) { - return; - } + if (bcm2835_pads == MAP_FAILED) + return; volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group; bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD); @@ -537,7 +564,7 @@ void bcm2835_delayMicroseconds(uint64_t micros) start = bcm2835_st_read(); /* Not allowed to access timer registers (result is not as precise)*/ - if (start==0) + if (start == 0) { t1.tv_sec = 0; t1.tv_nsec = 1000 * (long)(micros); @@ -607,7 +634,7 @@ void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask) */ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) { - if( pud_type_rpi4 ) + if (pud_type_rpi4) { int shiftbits = (pin & 0xf) << 1; uint32_t bits; @@ -629,16 +656,16 @@ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud) bcm2835_peri_write_nb( paddr, bits ); - } else + } + 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); -} - + 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); + } } @@ -646,7 +673,7 @@ uint8_t bcm2835_gpio_get_pud(uint8_t pin) { uint8_t ret = BCM2835_GPIO_PUD_ERROR; - if( pud_type_rpi4 ) + if (pud_type_rpi4) { uint32_t bits; volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4); @@ -664,6 +691,14 @@ uint8_t bcm2835_gpio_get_pud(uint8_t pin) return ret; } +static void bcm2835_aux_spi_reset(void) + { + volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4; + volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; + + bcm2835_peri_write(cntl1, 0); + bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO); +} int bcm2835_spi_begin(void) { @@ -717,9 +752,9 @@ void bcm2835_spi_setClockDivider(uint16_t 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); + 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) @@ -919,58 +954,58 @@ int bcm2835_aux_spi_begin(void) volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4; if (bcm2835_spi1 == MAP_FAILED) - return 0; /* bcm2835_init() failed, or not root */ + 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_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_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); + 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 */ + /* 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; + 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; - } + 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; + 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; - } + if (divider > (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX) { + return (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX; + } - return divider; + return divider; } static uint32_t spi1_speed; void bcm2835_aux_spi_setClockDivider(uint16_t divider) { - spi1_speed = (uint32_t) divider; + spi1_speed = (uint32_t) divider; } void bcm2835_aux_spi_write(uint16_t data) @@ -980,19 +1015,19 @@ void bcm2835_aux_spi_write(uint16_t data) 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 + 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); + 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) - ; + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) + ; - bcm2835_peri_write(io, (uint32_t) data << 16); + bcm2835_peri_write(io, (uint32_t) data << 16); } void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len) { @@ -1002,49 +1037,49 @@ void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len) { 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; + 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; + 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); + bcm2835_peri_write(cntl0, _cntl0); + bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN); - while (tx_len > 0) { + while (tx_len > 0) { - while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) - ; + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) + ; - count = MIN(tx_len, 3); - data = 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)); - } + 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; + data |= (count * 8) << 24; + tx_len -= count; - if (tx_len != 0) { - bcm2835_peri_write(txhold, data); - } else { - bcm2835_peri_write(io, data); - } + 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) - ; + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) + ; - (void) bcm2835_peri_read(io); - } + (void) bcm2835_peri_read(io); + } } void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) { @@ -1074,7 +1109,8 @@ void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) { while ((tx_len > 0) || (rx_len > 0)) { - while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0)) { + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0) && (tx_len + BCM2835_AUX_SPI_FIFO_SIZE * 3 > rx_len)) + { count = MIN(tx_len, 3); data = 0; @@ -1094,7 +1130,8 @@ void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) { } - while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) { + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) + { count = MIN(rx_len, 3); data = bcm2835_peri_read(io); @@ -1116,7 +1153,8 @@ void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) { rx_len -= count; } - while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0)) { + while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0) && (tx_len == 0)) + { count = MIN(rx_len, 3); data = bcm2835_peri_read(io); @@ -1144,6 +1182,41 @@ void bcm2835_aux_spi_transfern(char *buf, uint32_t len) { bcm2835_aux_spi_transfernb(buf, buf, len); } +/* Writes (and reads) a single byte to AUX SPI */ +uint8_t bcm2835_aux_spi_transfer(uint8_t value) +{ + 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 data; + + 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_CPHA_IN; + _cntl0 |= 8; // Shift length. + + uint32_t _cntl1 = BCM2835_AUX_SPI_CNTL1_MSBF_IN; + + bcm2835_peri_write(cntl1, _cntl1); + bcm2835_peri_write(cntl0, _cntl0); + + bcm2835_peri_write(io, (uint32_t) bcm2835_correct_order(value) << 24); + + while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) + ; + + data = bcm2835_correct_order(bcm2835_peri_read(io) & 0xff); + + bcm2835_aux_spi_reset(); + + return data; +} + + int bcm2835_i2c_begin(void) { uint16_t cdiv; @@ -1242,10 +1315,12 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4; #endif - uint32_t remaining = len; - uint32_t i = 0; - uint8_t reason = BCM2835_I2C_REASON_OK; - + uint32_t remaining = len; + uint32_t i = 0; + uint8_t reason = BCM2835_I2C_REASON_OK; + unsigned long Failsafe = len * 10000; + int Timeout = 0; + /* Clear FIFO */ bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 ); /* Clear Status */ @@ -1264,29 +1339,45 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len) bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST); /* Transfer is over when BCM2835_BSC_S_DONE */ - while(!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE )) + while(!Timeout && !(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE )) { - while ( remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_TXD )) + while (!Timeout && remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_TXD )) { /* Write to FIFO */ bcm2835_peri_write(fifo, buf[i]); i++; remaining--; + /* Make sure we don't loop forever! */ + if (--Failsafe == 0) + { + Timeout = 1; + break; + } } + /* Make sure we don't loop forever! */ + if (--Failsafe == 0) + { + Timeout = 1; + break; + } + /* printf("Failsafe %d %d\n", len, (len * 1000) - Failsafe); */ } - /* Received a NACK */ - if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) + if (Timeout) + { + /* We had a timeout */ + reason = BCM2835_I2C_REASON_ERROR_TIMEOUT; + } + else if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR) { + /* Received a NACK */ reason = BCM2835_I2C_REASON_ERROR_NACK; } - /* Received Clock Stretch Timeout */ else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT) { reason = BCM2835_I2C_REASON_ERROR_CLKT; } - /* Not all data is sent */ else if (remaining) { @@ -1366,7 +1457,7 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len) reason = BCM2835_I2C_REASON_ERROR_DATA; } - bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); + bcm2835_peri_set_bits(status, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE); return reason; } @@ -1566,6 +1657,201 @@ uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint return reason; } +/* SMI support courtesy Benoit Bouchez */ +int bcm2835_smi_begin(void) +{ + volatile uint32_t* paddr; + uint32_t defConfig; + + if (bcm2835_smi == MAP_FAILED) + return 0; /* bcm2835_smi_init() failed, or not root */ + + /* Set the SMI pins to the Alt 1 function to enable SMI access on them */ + bcm2835_gpio_fsel(2, BCM2835_GPIO_FSEL_ALT1); /* SA3 */ + bcm2835_gpio_fsel(3, BCM2835_GPIO_FSEL_ALT1); /* SA2 */ + bcm2835_gpio_fsel(4, BCM2835_GPIO_FSEL_ALT1); /* SA1 */ + bcm2835_gpio_fsel(5, BCM2835_GPIO_FSEL_ALT1); /* SA0 */ + bcm2835_gpio_fsel(6, BCM2835_GPIO_FSEL_ALT1); /* SOE_N / SE */ + bcm2835_gpio_fsel(7, BCM2835_GPIO_FSEL_ALT1); /* SWE_N / SRW_N */ + bcm2835_gpio_fsel(8, BCM2835_GPIO_FSEL_ALT1); /* SD0 */ + bcm2835_gpio_fsel(9, BCM2835_GPIO_FSEL_ALT1); /* SD1 */ + bcm2835_gpio_fsel(10, BCM2835_GPIO_FSEL_ALT1); /* SD2 */ + bcm2835_gpio_fsel(11, BCM2835_GPIO_FSEL_ALT1); /* SD3 */ + bcm2835_gpio_fsel(12, BCM2835_GPIO_FSEL_ALT1); /* SD4 */ + bcm2835_gpio_fsel(13, BCM2835_GPIO_FSEL_ALT1); /* SD5 */ + bcm2835_gpio_fsel(14, BCM2835_GPIO_FSEL_ALT1); /* SD6 */ + bcm2835_gpio_fsel(15, BCM2835_GPIO_FSEL_ALT1); /* SD7 */ + + /* Set the SMI clock to 125MHz -> one clock period lasts 8 ns */ + paddr = bcm2835_clk + SMICLK_CNTL; + bcm2835_peri_write (paddr, 0x5A000000); /* Disable SMI clock */ + paddr = bcm2835_clk + SMICLK_DIV; + bcm2835_peri_write (paddr, 0x5A004000); /* Set clock divider to 4 */ + paddr = bcm2835_clk + SMICLK_CNTL; + bcm2835_peri_write (paddr, 0x5A000016); /* Enable SMI clock with PLLD */ + + /* Set a default useable configuration for the four SMI config slots */ + /* 8 bits, Intel mode, always pace, 10 clocks for R/W setup = 80 ns + 20 clocks for R/W strobe = 160 ns, 20 clocks for R/W hold = 160 ns, + 1 clock for pace control (not used but a value is needed) */ + defConfig = BCM2835_SMI_RW_WID8 | BCM2835_SMI_RW_MODE80 | + BCM2835_SMI_RW_PACEALL | (10<3) return; + + /* clear done bit if set */ + if (bcm2835_peri_read(psmics) & BCM2835_SMI_DIRCS_DONE) + { + bcm2835_peri_write (psmics, BCM2835_SMI_DIRCS_DONE); + } + + /* Start write transfer */ + bcm2835_peri_write (psmiaddr, (smichannel<3) return 0; + + /* clear done bit if set */ + if (bcm2835_peri_read(psmics) & BCM2835_SMI_DIRCS_DONE) + { + bcm2835_peri_write (psmics, BCM2835_SMI_DIRCS_DONE); + } + + /* Start read transfer */ + bcm2835_peri_write (psmiaddr, (smichannel< -#define BCM2835_VERSION 10060 /* Version 1.60 */ +/* Some compilers need this, as reported by Sam James */ +#include +/* Needed to compile with gcc -std=c99, as reported by John Blaiklock.*/ +#include + +#define BCM2835_VERSION 10075 /* Version 1.75 */ + +// Define this if you want to use libcap2 to determine if you have the cap_sys_rawio capability +// and therefore the capability of opening /dev/mem, even if you are not root. +// See the comments above in the documentation for 'Running As Root' +//#define BCM2835_HAVE_LIBCAP /* 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. - However, not all versions of gcc in all distros support the dmb assembler instruction even on conmpatible processors. + However, not all versions of gcc in all distros support the dmb assembler instruction even on compatible processors. This test is so any ARMv7 or higher processors with suitable GCC will use DMB. */ #if __ARM_ARCH >= 7 @@ -626,13 +739,18 @@ /*! Base Address of the BSC1 registers */ #define BCM2835_BSC1_BASE 0x804000 +/* BEB */ +/*! Base address of the SMI registers */ +#define BCM2835_SMI_BASE 0x600000 + +#include /*! Physical address and size of the peripherals block May be overridden on RPi2 */ -extern uint32_t *bcm2835_peripherals_base; +extern off_t bcm2835_peripherals_base; /*! Size of the peripherals block to be mapped */ -extern uint32_t bcm2835_peripherals_size; +extern size_t bcm2835_peripherals_size; /*! Virtual memory address of the mapped peripherals block */ extern uint32_t *bcm2835_peripherals; @@ -687,6 +805,11 @@ extern volatile uint32_t *bcm2835_aux; */ extern volatile uint32_t *bcm2835_spi1; +/* BEB */ +/*! Base of SMI registers. + Available after bcm2835_init has been called (as root) +*/ +extern volatile uint32_t *bcm2835_smi; /*! \brief bcm2835RegisterBase Register bases for bcm2835_regbase() @@ -700,9 +823,12 @@ 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_AUX = 9, /*!< Base of the AUX registers. */ - BCM2835_REGBASE_SPI1 = 10 /*!< Base of the SPI1 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. */ + /* BEB */ + BCM2835_REGBASE_SMI = 11 /*!< Base of the SMI registers. */ + } bcm2835RegisterBase; /*! Size of memory page on RPi */ @@ -955,8 +1081,8 @@ typedef enum #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_LVL 0xF0000000 /*!< */ +#define BCM2835_AUX_SPI_STAT_RX_LVL 0x00F00000 /*!< */ #define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 /*!< */ #define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 /*!< */ #define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 /*!< */ @@ -1073,7 +1199,7 @@ typedef enum GPIO register offsets from BCM2835_BSC*_BASE. Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map */ -#define BCM2835_BSC_C 0x0000 /*!< BSC Master Control */ +#define BCM2835_BSC_C 0x0000 /*!< BSC Master Control */ #define BCM2835_BSC_S 0x0004 /*!< BSC Master Status */ #define BCM2835_BSC_DLEN 0x0008 /*!< BSC Master Data Length */ #define BCM2835_BSC_A 0x000c /*!< BSC Master Slave Address */ @@ -1105,6 +1231,7 @@ typedef enum #define BCM2835_BSC_S_TA 0x00000001 /*!< Transfer Active */ #define BCM2835_BSC_FIFO_SIZE 16 /*!< BSC FIFO size */ +#define BCM2835_AUX_SPI_FIFO_SIZE 4 /*! \brief bcm2835I2CClockDivider Specifies the divider used to generate the I2C clock from the system clock. @@ -1126,9 +1253,71 @@ typedef enum BCM2835_I2C_REASON_OK = 0x00, /*!< Success */ BCM2835_I2C_REASON_ERROR_NACK = 0x01, /*!< Received a NACK */ BCM2835_I2C_REASON_ERROR_CLKT = 0x02, /*!< Received Clock Stretch Timeout */ - BCM2835_I2C_REASON_ERROR_DATA = 0x04 /*!< Not all data is sent / received */ + BCM2835_I2C_REASON_ERROR_DATA = 0x04, /*!< Not all data is sent / received */ + BCM2835_I2C_REASON_ERROR_TIMEOUT = 0x08 /*!< Time out occurred during sending */ + } bcm2835I2CReasonCodes; +/* Registers offets from BCM2835_SMI_BASE */ +#define BCM2835_SMI_CS 0 /*! < Control and status register > */ +#define BCM2835_SMI_LENGTH 1 /*! < Transfer length register > */ +#define BCM2835_SMI_ADRS 2 /*! < Transfer address register > */ +#define BCM2835_SMI_DATA 3 /*! < Transfer data register > */ +#define BCM2835_SMI_READ0 4 /*! < Read settings 0 register > */ +#define BCM2835_SMI_WRITE0 5 /*! < Write settings 0 register > */ +#define BCM2835_SMI_READ1 6 /*! < Read settings 1 register > */ +#define BCM2835_SMI_WRITE1 7 /*! < Write settings 1 register > */ +#define BCM2835_SMI_READ2 8 /*! < Read settings 2 register > */ +#define BCM2835_SMI_WRITE2 9 /*! < Write settings 2 register > */ +#define BCM2835_SMI_READ3 10 /*! < Read settings 3 register > */ +#define BCM2835_SMI_WRITE3 11 /*! < Write settings 3 register > */ +#define BCM2835_SMI_DMAC 12 /*! < DMA control register > */ +#define BCM2835_SMI_DIRCS 13 /*! < Direct control register > */ +#define BCM2835_SMI_DIRADDR 14 /*! < Direct access address register > */ +#define BCM2835_SMI_DIRDATA 15 /*! < Direct access data register > */ + +/* Register masks for SMI_READ and SMI_WRITE */ +#define BCM2835_SMI_RW_WIDTH_MSK 0xC0000000 /*! < Data width mask > */ +#define BCM2835_SMI_RW_WID8 0x00000000 /*! < Data width 8 bits > */ +#define BCM2835_SMI_RW_WID16 0x40000000 /*! < Data width 16 bits > */ +#define BCM2835_SMI_RW_WID18 0x80000000 /*! < Data width 18 bits > */ +#define BCM2835_SMI_RW_WID9 0xC0000000 /*! < Data width 9 bits > */ +#define BCM2835_SMI_RW_SETUP_MSK 0x3F000000 /*! < Setup cycles (6 bits) > */ +#define BCM2835_SMI_RW_SETUP_LS 24 /*! < Shift for setup cycles > */ +#define BCM2835_SMI_RW_MODE68 0x00800000 /*! < Run cycle motorola mode > */ +#define BCM2835_SMI_RW_MODE80 0x00000000 /*! < Run cycle intel mode > */ +#define BCM2835_SMI_READ_FSETUP 0x00400000 /*! < Read : Setup only for first cycle > */ +#define BCM2835_SMI_WRITE_SWAP 0x00400000 /*! < Write : swap pixel data > */ +#define BCM2835_SMI_RW_HOLD_MSK 0x003F0000 /*! < Hold cycles (6 bits) > */ +#define BCM2835_SMI_RW_HOLD_LS 16 /*! < Shift for hold cycles > */ +#define BCM2835_SMI_RW_PACEALL 0x00008000 /*! < Apply pacing always > */ +#define BCM2835_SMI_RW_PACE_MSK 0x00007F00 /*! < Pace cycles (7 bits) > */ +#define BCM2835_SMI_RW_PACE_LS 8 /*! < Shift for pace cycles > */ +#define BCM2835_SMI_RW_DREQ 0x00000080 /*! < Use DMA req on read/write > */ +#define BCM2835_SMI_RW_STROBE_MSK 0x0000007F /*! < Strobe cycles (7 bits) > */ +#define BCM2835_SMI_RW_STROBE_LS 0 /*! < Shift for strobe cycles > */ + +/* Registers masks for Direct Access control register */ +#define BCM2835_SMI_DIRCS_ENABLE 0x00000001 /*! < Set to enable SMI. 0 = Read from ext. devices > */ +#define BCM2835_SMI_DIRCS_START 0x00000002 /*! < Initiate SMI transfer > */ +#define BCM2835_SMI_DIRCS_DONE 0x00000004 /*! < Set if transfer has finished / Write to clear flag > */ +#define BCM2835_SMI_DIRCS_WRITE 0x00000008 /*! < 1 = Write to ext. devices > */ + +/* Registers masks for Direct Access address register */ +#define BCM2835_SMI_DIRADRS_DEV_MSK 0x00000300 /*! < Timing configuration slot > */ +#define BCM2835_SMI_DIRADRS_DEV_LS 8 /*! < Shift for configuration slot > */ +#define BCM2835_SMI_DIRADRS_DEV0 0x00000000 /*! < Use timing config slot 0 > */ +#define BCM2835_SMI_DIRADRS_DEV1 0x00000100 /*! < Use timing config slot 1 > */ +#define BCM2835_SMI_DIRADRS_DEV2 0x00000200 /*! < Use timing config slot 2 > */ +#define BCM2835_SMI_DIRADRS_DEV3 0x00000300 /*! < Use timing config slot 3 > */ +#define BCM2835_SMI_DIRADRS_MSK 0x0000003F /*! < Adress bits SA5..SA0 > */ +#define BCM2835_SMI_DIRADRS_LS 0 /*! < Shift for address bits > */ + +/* SMI clock control registers : defined as offset from bcm2835_clk */ +#define SMICLK_CNTL (44) /* = 0xB0 */ +#define SMICLK_DIV (45) /* = 0xB4 */ + + /* Defines for ST GPIO register offsets from BCM2835_ST_BASE. Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers @@ -1700,11 +1889,10 @@ extern "C" { */ extern void bcm2835_spi_writenb(const char* buf, uint32_t len); - /*! Transfers half-word to and from the currently selected SPI slave. + /*! Transfers half-word to 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() @@ -1712,14 +1900,14 @@ extern "C" { 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) + Forces RPi AUX SPI pins P1-38 (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) + SPI1 pins P1-38 (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); @@ -1736,10 +1924,10 @@ extern "C" { */ extern uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz); - /*! Transfers half-word to and from the AUX SPI slave. + /*! Transfers half-word to 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 + \return The 16 bit byte simultaneously read from MISO \sa bcm2835_spi_transfern() */ extern void bcm2835_aux_spi_write(uint16_t data); @@ -1755,7 +1943,7 @@ extern "C" { 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 + \param[in] len Number of bytes in the 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); @@ -1770,6 +1958,15 @@ extern "C" { */ extern void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len); + /*! Transfers one byte to and from the AUX SPI slave. + Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + Returns the read data byte from the slave. + \param[in] value The 8 bit data byte to write to MOSI + \return The 8 bit byte simultaneously read from MISO + \sa bcm2835_aux_spi_transfern() + */ + extern uint8_t bcm2835_aux_spi_transfer(uint8_t value); + /*! @} */ /*! \defgroup i2c I2C access @@ -1859,6 +2056,66 @@ extern "C" { /*! @} */ + /*! \defgroup smi SMI bus support + Allows access to SMI bus + @{ + */ + /*! Start SMI operations. + Forces RPi SMI pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + to alternate function ALT1, which enables those pins for SMI interface. + You should call bcm2835_smi_end() when all SMI functions are complete to return the pins to + their default functions. + Only address bits SA0 to SA3 are available as RPi uses GPIO0 (SA5) and GPIO1 (SA4) for I2C + HAT identification EEPROM access + \sa bcm2835_smi_end() + \return 1 if successful, 0 otherwise (perhaps because you are not running as root) + */ + extern int bcm2835_smi_begin (void); + + /*! End SMI operations. + SMI pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + are returned to their default INPUT behaviour. + */ + extern void bcm2835_smi_end (void); + + /*! Setup SMI bus cycle timing parameters + There are four SMI channels for read operation and four channels for write operation + Cycles are expressed as multiple of 8ns + Note that Pace cycles are not used (no effect on hardware) but they are required for + configuration. It is recommended to set this value to 1 (and not 0) to have the + smallest cycle in case the hardware would recognize it + \param[in] smichannel SMI configuration slot to setup (0 to 3) + \param[in] readchannel Set to 1 to configure the read channel (0 = configure write channel) + \param[in] setupcycles Time between address assertion on bus and OE/WR signal assertion + \param[in] strobecycles Duration of OE/WR signal assertion + \param[in] holdcycles Time after OE/WR deassertion before address is deasserted + \param[in] pacecycles Time before next SMI bus cycle + */ + extern void bcm2835_smi_set_timing(uint32_t smichannel, uint32_t readchannel, + uint32_t setupcycles, uint32_t strobecycles, + uint32_t holdcycles, uint32_t pacecycles); + + /*! Transfers one byte to SMI bus. + Uses polled transfer as described in BCM 2835 ARM Peripherals manual + \param[in] timingslot SMI configuration slot to use (0 to 3) + \param[in] data The data byte to write + \param[in] address The address to write to + \sa bcm2835_smi_writenb() + */ + extern void bcm2835_smi_write (uint32_t smichannel, uint8_t data, uint32_t address); + + /*! Reads one byte from SMI bus. + Uses polled transfer as described in BCM 2835 ARM Peripherals manual + \param[in] smichannel SMI configuration slot to use (0 to 3) + \param[in] address The address to read from + \return value read from SMI bus + \sa bcm2835_smi_readnb() + */ + extern uint32_t bcm2835_smi_read (uint32_t smichannel, uint32_t address); + + /*! @} */ + + /*! \defgroup st System Timer access Allows access to and delays using the System Timer Counter. @{ @@ -1960,3 +2217,7 @@ Broadcom bcm2835. Contributed by Shahrooz Shahparnia. 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 */ + +/*! example smi.c + Shows how to use the SMI bus calls. Courtesy Benoit Bouchez. +*/