Newer
Older
WH1080 / bcm2835.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>

#include "bcm2835.h"

struct bcm2835_peripheral gpio = {GPIO_BASE, 0};
struct bcm2835_peripheral bsc0 = {BSC0_BASE, 0};
struct bcm2835_peripheral bsc1 = {BSC1_BASE, 0};
struct bcm2835_peripheral timer_arm = {TIMER_ARM_BASE, 0};

// Exposes the physical address defined in the passed structure using mmap on /dev/mem
int map_peripheral(struct bcm2835_peripheral *p)
{
	if(p->init_count > 0) {
		p->init_count++;
		return 0;
	}

	// Open /dev/mem 
	if ((p->mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
		printf("Failed to open /dev/mem, try checking permissions.\n");
		return -1;
	}

	p->map = mmap(
	NULL,
	BLOCK_SIZE,
	PROT_READ|PROT_WRITE,
	MAP_SHARED,
	p->mem_fd,  // File descriptor to physical memory virtual file '/dev/mem'
	p->addr_p	  // Address in physical map that we want this memory block to expose
	);

	if (p->map == MAP_FAILED) {
		perror("mmap");
		return -1;
	}

	p->addr = (volatile unsigned int *)p->map;
	p->init_count++;

	return 0;
}


void unmap_peripheral(struct bcm2835_peripheral *p) {

	p->init_count--;
	if(p->init_count == 0) {
		munmap(p->map, BLOCK_SIZE);
		close(p->mem_fd);
	}
}

// Function to wait for the I2C transaction to complete
void wait_i2c_done() {

		while((!((BSC1_S) & BSC_S_DONE))) {
			usleep(100);
		}
}

// Function to write data to an I2C device via the FIFO.  This doesn't refill the FIFO, so writes are limited to 16 bytes
// including the register address. len specifies the number of bytes in the buffer.
void i2c_write(char dev_addr, char reg_addr, char *buf, unsigned short len) {

		int idx;
		
		BSC1_A = dev_addr;
		BSC1_DLEN = len + 1;	// one byte for the register address, plus the buffer length
		
		BSC1_FIFO = reg_addr;	// start register address
		for(idx=0; idx < len; idx++)
			BSC1_FIFO = buf[idx];
			
		BSC1_S = CLEAR_STATUS; // Reset status bits (see #define)
		BSC1_C = START_WRITE;	// Start Write (see #define)

		wait_i2c_done();
		
}

// Function to read a number of bytes into a  buffer from the FIFO of the I2C controller
void i2c_read(char dev_addr, char reg_addr, char *buf, unsigned short len) {

		i2c_write(dev_addr, reg_addr, NULL, 0);
		
		unsigned short bufidx = 0;

		memset(buf, 0, len); // clear the buffer

		BSC1_DLEN = len;
		BSC1_S = CLEAR_STATUS; // Reset status bits (see #define)
		BSC1_C = START_READ;	// Start Read after clearing FIFO (see #define)

		do {
			// Wait for some data to appear in the FIFO
			while((BSC1_S & BSC_S_TA) && !(BSC1_S & BSC_S_RXD));

			// Consume the FIFO
			while((BSC1_S & BSC_S_RXD) && (bufidx < len)) {
				buf[bufidx++] = BSC1_FIFO;
			}
		} while((!(BSC1_S & BSC_S_DONE)));
}

void dump_bsc_status() {
	
	unsigned int s = BSC1_S;
	
	printf("BSC0_S: ERR=%d  RXF=%d  TXE=%d  RXD=%d  TXD=%d  RXR=%d  TXW=%d  DONE=%d  TA=%d\n",
		(s & BSC_S_ERR) != 0, 
		(s & BSC_S_RXF) != 0, 
		(s & BSC_S_TXE) != 0, 
		(s & BSC_S_RXD) != 0, 
		(s & BSC_S_TXD) != 0, 
		(s & BSC_S_RXR) != 0, 
		(s & BSC_S_TXW) != 0, 
		(s & BSC_S_DONE) != 0, 
		(s & BSC_S_TA) != 0 );
}