Raspberry Pi Expansion Board

Simple expansion board to log the room temperature.

Parts

Schematic

Schematic

Photo

Reference

Software

AVR side software

  • main.c

    #include <avr/io.h>
    #include <avr/boot.h>
    #include <util/delay.h>
    #include <avr/signature.h>
    #include <avr/pgmspace.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <avr/eeprom.h>
    
    #include <usart.h>
    #include "ad.h"
    
    union {
        uint16_t data[2];
        uint8_t bytes[8]; // buffer
    } ad_data;
    
    uint8_t spi_count;
    
    int main(void)
    {
        usart_init_38400();
        ad_init();
    
        DDRD  = _BV(2);
        DDRB = _BV(4);  // MOSI
    
        SPCR = _BV(SPIE) | _BV(SPE); // enable SPI, Interrupt and slave mode
    
        PCMSK0 = _BV(PCINT2);
        PCICR = _BV(PCIE0);
    
        sei();
    
        // usart_putstr_P(PSTR("Small Test\r\n"));
    
        while(1) {
            _delay_ms(1000);
    
            uint16_t temp_ad = 0;
            uint8_t i = 0, j = 0;
            for (i = 0; i < 8; i++) {
                temp_ad += ad_get(0);
            }
    
            float temp = ((temp_ad/8.0)*3.3*1000/(1 << 10)-600)/10.0;
    
            printf_P(PSTR("%f %3u %02x\n"), temp, ad_get(1), PINB & _BV(2));
    
            for (j = 0; j < 2; j++) {
                uint16_t ad_temporary = 0;
                for (i = 0; i < 0x10; i++) {
                    ad_temporary += ad_get(j);
                }
                ad_data.data[j] = ad_temporary;
            }
        }
    }
    
    ISR(SPI_STC_vect)
    {
        //uint8_t a = SPDR;
        //SPDR = a;
        //printf_P(PSTR("%02x!"), a);
        SPDR = ad_data.bytes[++spi_count];
        usart_putc('.');
    }
    
    ISR(PCINT0_vect)
    {
        if (PINB & _BV(2)) {
            PORTD |= _BV(2);
        } else {
            PORTD &= ~_BV(2);
            spi_count = 0;
            SPDR = ad_data.bytes[0];
        }
    }
    
  • ad.c

    #include "ad.h"
    
    void ad_init(void)
    {
      ADCSRA = _BV(ADEN) | _BV(ADPS2);
    }
    
    uint16_t ad_get(uint8_t num)
    {
      ADMUX = (num & 0xf) | _BV(REFS0);
      ADCSRA |= _BV(ADSC);
      while(!(ADCSRA & _BV(ADIF)));
      uint16_t data = ADC;
      ADCSRA &= ~_BV(ADIF);
      return data;
    }
    
  • usart.c / usart.h

    Please download from Github

Linux side software

/*
 * Copyright (c) 2012 Y.Okamura
 *
 * Based on http://elinux.org/Rpi_Low-level_peripherals
 *          How to access GPIO registers from C-code on the Raspberry-Pi
 *          15-January-2012
 *          Dom and Gert
 *
 * Based on http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob_plain;f=Documentation/spi/spidev_test.c
 *          SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 */


// Access from ARM Running Linux

#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <stdint.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s)
{
    perror(s);
    abort();
}


#include <unistd.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int  mem_fd;
char *gpio_mem, *gpio_map;
char *spi0_mem, *spi0_map;


// I/O access
volatile unsigned *gpio;
int spi_fd;

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

#define SPEED 10000
#define BITS 8

void setup_io();
void setup_spi();
static void transfer(int fd);

int main(int argc, char **argv)
{ 
    int g,rep;

    // Set up gpi pointer for direct register access
    setup_io();

    INP_GPIO(25); // Slave Select
    OUT_GPIO(25);

    GPIO_CLR = 1 << 25;
    setup_spi();

    transfer(spi_fd);

    GPIO_SET = 1 << 25;


    close(spi_fd);
    return 0;

} // main


//
// Set up a memory regions to access GPIO
//
void setup_io()
{
    /* open /dev/mem */
    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
        printf("can't open /dev/mem \n");
        exit (-1);
    }

    /* mmap GPIO */

    // Allocate MAP block
    if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
        printf("allocation error \n");
        exit (-1);
    }

    // Make sure pointer is on 4K boundary
    if ((unsigned long)gpio_mem % PAGE_SIZE)
        gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);

    // Now map it
    gpio_map = (unsigned char *)mmap(
        (caddr_t)gpio_mem,
        BLOCK_SIZE,
        PROT_READ|PROT_WRITE,
        MAP_SHARED|MAP_FIXED,
        mem_fd,
      GPIO_BASE
        );

    if ((long)gpio_map < 0) {
        printf("mmap error %d\n", (int)gpio_map);
        exit (-1);
    }

    // Always use volatile pointer!
    gpio = (volatile unsigned *)gpio_map;
} // setup_io

void setup_spi(void)
{
    int ret = 0;
    spi_fd = open("/dev/spidev0.0", O_RDWR);

    /*
     * bits per word
     */
    int bits = 8;
    ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't set bits per word");

    ret = ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't get bits per word");

    /*
     * max speed hz
     */
    int speed = 10000;
    ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't set max speed hz");

    ret = ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't get max speed hz");
}

static void transfer(int fd)
{
    int ret;
    uint8_t tx[] = {
        0xff, 0x00, 0x00, 0x00
    };

    union {
        uint16_t data[2];
        uint8_t bytes[8]; // buffer
    } ad_data;

    struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)ad_data.bytes,
        .len = ARRAY_SIZE(tx),
        .delay_usecs = 0,
        .speed_hz = SPEED,
        .bits_per_word = BITS,
    };

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 1)
        pabort("can't send spi message");

    /* for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { */
    /*  if (!(ret % 6)) */
    /*      puts(""); */
    /*  printf("%.2X ", rx[ret]); */
    /* } */
    /* puts(""); */

    double temp = ((ad_data.data[0]/16.0)*3.3*1000/(1 << 10)-600)/10.0;
    printf ("%lf\n", temp);
}

Reference