Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 144 additions & 27 deletions software/src/bcm2835.c
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//
// Author: Mike McCauley
// Copyright (C) 2011-2013 Mike McCauley
// $Id: bcm2835.c,v 1.10 2013/03/18 05:57:36 mikem Exp mikem $
// $Id: bcm2835.c,v 1.12 2013/09/01 00:56:56 mikem Exp mikem $

#include <stdlib.h>
#include <stdio.h>
Expand All @@ -24,6 +24,11 @@
// ./a.out
//#define BCM2835_TEST

// Uncommenting this define compiles alternative I2C code for the version 1 RPi
// The P1 header I2C pins are connected to SDA0 and SCL0 on V1.
// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected.
// #define I2C_V1

// Pointers to the hardware register bases
volatile uint32_t *bcm2835_gpio = MAP_FAILED;
volatile uint32_t *bcm2835_pwm = MAP_FAILED;
Expand Down Expand Up @@ -65,7 +70,7 @@ uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
// Make sure we dont return the _last_ read which might get lost
// if subsequent code changes to a different peripheral
uint32_t ret = *paddr;
uint32_t dummy = *paddr;
*paddr; // Read without assigneing to an unused variable
return ret;
}
}
Expand All @@ -87,11 +92,11 @@ uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
// safe write to peripheral
void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
{
if (debug)
if (debug)
{
printf("bcm2835_peri_write paddr %08X, value %08X\n", (unsigned) paddr, value);
}
else
else
{
// Make sure we don't rely on the first write, which may get
// lost if the previous access was to a different peripheral.
Expand Down Expand Up @@ -336,7 +341,7 @@ uint32_t bcm2835_gpio_pad(uint8_t group)
void bcm2835_gpio_set_pad(uint8_t group, uint32_t control)
{
volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2;
bcm2835_peri_write(paddr, control);
bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD);
}

// Some convenient arduino-like functions
Expand Down Expand Up @@ -492,14 +497,14 @@ uint8_t bcm2835_spi_transfer(uint8_t value)

// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
delayMicroseconds(10);
;

// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, value);

// Wait for DONE to be set
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
delayMicroseconds(10);
;

// Read any byte that was sent back by the slave while we sere sending to it
uint32_t ret = bcm2835_peri_read_nb(fifo);
Expand Down Expand Up @@ -531,21 +536,21 @@ void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len)
{
// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
delayMicroseconds(10);
;

// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, tbuf[i]);

// Wait for RXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))
delayMicroseconds(10);
;

// then read the data byte
rbuf[i] = bcm2835_peri_read_nb(fifo);
}
// Wait for DONE to be set
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
delayMicroseconds(10);
;

// Set TA = 0, and also set the barrier
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
Expand All @@ -568,19 +573,25 @@ void bcm2835_spi_writenb(char* tbuf, uint32_t len)
bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);

uint32_t i;
for (i = 0; i < len; i++)
{
// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
;

// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, tbuf[i]);
}

for (i = 0; i < len; i++)
{
// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
;

// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, tbuf[i]);

// Read from FIFO to prevent stalling
while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD)
(void) bcm2835_peri_read_nb(fifo);
}

// Wait for DONE to be set
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
;
while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) {
while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD)
(void) bcm2835_peri_read_nb(fifo);
};

// Set TA = 0, and also set the barrier
bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
Expand Down Expand Up @@ -610,11 +621,17 @@ void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active)

void bcm2835_i2c_begin(void)
{
volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4;

#ifdef I2C_V1
volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4;
// Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them
bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA
bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL
#else
volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4;
// Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); // SDA
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); // SCL
#endif

// Read the clock divider register
uint16_t cdiv = bcm2835_peri_read(paddr);
Expand All @@ -626,15 +643,25 @@ void bcm2835_i2c_begin(void)

void bcm2835_i2c_end(void)
{
#ifdef I2C_V1
// Set all the I2C/BSC0 pins back to input
bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA
bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL
#else
// Set all the I2C/BSC1 pins back to input
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); // SDA
bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); // SCL
#endif
}

void bcm2835_i2c_setSlaveAddress(uint8_t addr)
{
// Set I2C Device Address
#ifdef I2C_V1
volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4;
#else
volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4;
#endif
bcm2835_peri_write(paddr, addr);
}

Expand All @@ -643,7 +670,11 @@ void bcm2835_i2c_setSlaveAddress(uint8_t addr)
// rounded down.
void bcm2835_i2c_setClockDivider(uint16_t divider)
{
#ifdef I2C_V1
volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4;
#else
volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4;
#endif
bcm2835_peri_write(paddr, divider);
// Calculate time for transmitting one byte
// 1000000 = micros seconds in a second
Expand All @@ -663,10 +694,17 @@ void bcm2835_i2c_set_baudrate(uint32_t baudrate)
// Writes an number of bytes to I2C
uint8_t bcm2835_i2c_write(const char * buf, uint32_t len)
{
#ifdef I2C_V1
volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4;
volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
#else
volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4;
volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
#endif

uint32_t remaining = len;
uint32_t i = 0;
Expand Down Expand Up @@ -727,10 +765,17 @@ uint8_t bcm2835_i2c_write(const char * buf, uint32_t len)
// Read an number of bytes from I2C
uint8_t bcm2835_i2c_read(char* buf, uint32_t len)
{
#ifdef I2C_V1
volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4;
volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
#else
volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4;
volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
#endif

uint32_t remaining = len;
uint32_t i = 0;
Expand Down Expand Up @@ -794,11 +839,17 @@ uint8_t bcm2835_i2c_read(char* buf, uint32_t len)
// the required register. Only works if your device supports this mode
uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len)
{
#ifdef I2C_V1
volatile uint32_t* dlen = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc0 + BCM2835_BSC_S/4;
volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
#else
volatile uint32_t* dlen = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
volatile uint32_t* fifo = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
volatile uint32_t* status = bcm2835_bsc1 + BCM2835_BSC_S/4;
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;
Expand Down Expand Up @@ -896,6 +947,72 @@ void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros)
;
}

// PWM

void bcm2835_pwm_set_clock(uint32_t divisor)
{
// From Gerts code
divisor &= 0xfff;
// Stop PWM clock
bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01);
bcm2835_delay(110); // Prevents clock going slow
// Wait for the clock to be not busy
while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0)
bcm2835_delay(1);
// set the clock divider and enable PWM clock
bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12));
bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); // Source=osc and enable
}

void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
{
uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL);

if (channel == 0)
{
if (markspace)
control |= BCM2835_PWM0_MS_MODE;
else
control &= ~BCM2835_PWM0_MS_MODE;
if (enabled)
control |= BCM2835_PWM0_ENABLE;
else
control &= ~BCM2835_PWM0_ENABLE;
}
else if (channel == 1)
{
if (markspace)
control |= BCM2835_PWM1_MS_MODE;
else
control &= ~BCM2835_PWM1_MS_MODE;
if (enabled)
control |= BCM2835_PWM1_ENABLE;
else
control &= ~BCM2835_PWM1_ENABLE;
}

// If you use the barrier here, wierd things happen, and the commands dont work
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control);
// bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE);

}

void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
{
if (channel == 0)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range);
else if (channel == 1)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range);
}

void bcm2835_pwm_set_data(uint8_t channel, uint32_t data)
{
if (channel == 0)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data);
else if (channel == 1)
bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data);
}

// Allocate page-aligned memory.
void *malloc_aligned(size_t size)
{
Expand Down Expand Up @@ -928,9 +1045,9 @@ int bcm2835_init(void)
if (debug)
{
bcm2835_pads = (uint32_t*)BCM2835_GPIO_PADS;
bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE;
bcm2835_clk = (uint32_t*)BCM2835_CLOCK_BASE;
bcm2835_gpio = (uint32_t*)BCM2835_GPIO_BASE;
bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM;
bcm2835_pwm = (uint32_t*)BCM2835_GPIO_PWM;
bcm2835_spi0 = (uint32_t*)BCM2835_SPI0_BASE;
bcm2835_bsc0 = (uint32_t*)BCM2835_BSC0_BASE;
bcm2835_bsc1 = (uint32_t*)BCM2835_BSC1_BASE;
Expand Down
Loading