Skip to content

Latest commit

 

History

History
226 lines (168 loc) · 6.34 KB

2012-08-spi-from-python.markdown

File metadata and controls

226 lines (168 loc) · 6.34 KB

Testing SPI hardware, spidev driver

  • Recompiled kernel from 4ffb8311bfb54e44c992b959b3849960fc663feb (had some extra wifi drivers enabled)

Appeared in dmesg:

atmel_spi atmel_spi.1: Atmel SPI Controller at 0xfffcc000 (irq 13)

Also, boot errors due to conflict with GPIO driver:

/etc/init.d/rc: /etc/rc5.d/S60rascal-gpio.sh: line 5: can't create /sys/class/gpio/gpio55/direction: nonexistent directory
/etc/init.d/rc: /etc/rc5.d/S60rascal-gpio.sh: line 5: can't create /sys/class/gpio/gpio56/direction: nonexistent directory
/etc/init.d/rc: /etc/rc5.d/S60rascal-gpio.sh: line 5: can't create /sys/class/gpio/gpio67/direction: nonexistent directory
/etc/init.d/rc: /etc/rc5.d/S60rascal-gpio.sh: line 5: can't create /sys/class/gpio/gpio100/direction: nonexistent directory
/etc/init.d/rc: /etc/rc5.d/S60rascal-gpio.sh: line 5: can't create /sys/class/gpio/gpio101/direction: nonexistent directory

New files that exist with the new kernel

/dev/spidev1.0
/dev/spidev1.1
/dev/spidev1.2
/sys/devices/platform/atmel_spi.1
/sys/class/spidev/
/sys/class/spidev/spidev1.0
/sys/class/spidev/spidev1.1
/sys/class/spidev/spidev1.2

Testing SPI

Sends data out MOSI and pulses clock at ~15 MHz, but chip select stays low. Expect SS0 on PB3, AKA M16. (Checked SS0, SS1, SS2, SS3.)

echo "Aaron Burr" > /dev/spidev1.0

Testing with an Arduino SPI slave using code from http://www.gammon.com.au/forum/?id=10892

With the slave code modified to send each character out the serial port immediately, rather than waiting for a newline character, it almost works. Characters are returned, but they're not the right ones. Probably the Rascal max speed of ~15 MHz is too fast for the Arduino. The Arduino can only transmit at 4 MHz. Receive speed might be even slower.

Tried to change driver to slower max speed. Seemed to compile fine.

.max_speed_hz   = 15 * 1000 * 10,

But, error in dmesg when driver was loaded.

atmel_spi atmel_spi.1: Atmel SPI Controller at 0xfffcc000 (irq 13)
atmel_spi atmel_spi.1: can't setup spi1.0, status -22
atmel_spi atmel_spi.1: can't setup spi1.1, status -22
atmel_spi atmel_spi.1: can't setup spi1.2, status -22

Also, could be problem with 5 V Arduino not registering 3.3 V signals.

(Later discovered it worked fine, but the chip select started way early and ended way late, so it appeared to be eternally low at first glance.)

Changing SPI speed

Master clock is 132 MHZ; CPU is 396 MHZ (3x MCK); crystal is 18.432 MHZ (MCK/7.16?).

SPI clock is MCK divided by 8 bit value stored in the SCBR field, which is part of the SPI_CSR0-SPI_CSR3 registers.

132/9 = 14.67 MHz, appears to be what is produced by 15 * 1000 * 1000 in driver code.

Minimum speed is 132/255 = 518 kHz.

// Written by Nick Gammon, slightly modified by Brandon
// February 2011

#include <SPI.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
  Serial.begin (115200);   // debugging

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
  
  // get ready for an interrupt 
  pos = 0;   // buffer empty
  process_it = false;

  // now turn on interrupts
  SPI.attachInterrupt();

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
  
  // add to buffer if room
  if (pos < sizeof buf)
    {
    buf [pos++] = c;

    if (c == '!') \\ This line is weird. Requires two !! to trigger. Whatever.
      process_it = true;
      
    }  // end of room available
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;  
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // end of flag set
    
}  // end of loop

New version of spiWrite in Python

def spiWrite(val, cs='0'):
    with open('/dev/spidev1.' + str(cs), 'w') as f:
        f.write(val)
        f.close()

Also, from Bash

echo -n "Text goes here" > > /dev/spidev1.0

The -n option prevents the emission of a newline character.

More SPI speed stuff

#define SPI_IOC_RD_MAX_SPEED_HZ         _IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ         _IOW(SPI_IOC_MAGIC, 4, __u32)

#define _IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),sizeof(size))

#define _IOC(dir,type,nr,size) \
         (((dir)  << _IOC_DIRSHIFT) | \
          ((type) << _IOC_TYPESHIFT) | \
          ((nr)   << _IOC_NRSHIFT) | \
          ((size) << _IOC_SIZESHIFT))

SPI_IOC_MAGIC is defined as 'k', which is 0x6B or 107.

From include/asm-generic/ioctl.h

#define _IOC_NRBITS     8
#define _IOC_TYPEBITS   8
#define _IOC_SIZEBITS   14 // This can be overridden, but appears not to be for ARM arch
#define _IOC_READ       2U // U means unsigned int
#define _IOC_WRITE      1U // U means unsigned int
#define _IOC_NRSHIFT    0
#define _IOC_TYPESHIFT  (_IOC_NRSHIFT+_IOC_NRBITS)      // 8 for ARM
#define _IOC_SIZESHIFT  (_IOC_TYPESHIFT+_IOC_TYPEBITS)  // 16 for ARM
#define _IOC_DIRSHIFT   (_IOC_SIZESHIFT+_IOC_SIZEBITS)  // 30 for ARM
SPI_IOC_RD_MAX_SPEED_HZ
_IOR(SPI_IOC_MAGIC, 4, __u32)
_IOR(0x6B, 4 __u32)
_IOR(type,nr,size)      _IOC(_IOC_READ,(type),(nr),sizeof(size))
_IOC(_IOC_READ, 0x6B, 4, sizeof(__u32))
(((dir)  << _IOC_DIRSHIFT) | ((type) << _IOC_TYPESHIFT) | ((nr)   << _IOC_NRSHIFT) | ((size) << _IOC_SIZESHIFT))
(((_IOC_READ)  << _IOC_DIRSHIFT) | ((0x6B) << _IOC_TYPESHIFT) | ((4)   << _IOC_NRSHIFT) | ((4) << _IOC_SIZESHIFT))
((2 << 30) | (0x6B << 8) | (4 << 0) | (4 << 16))
((2 << 30) | (0x6B << 8) | (0x04 << 0) | (0x04 << 16))
0x80046B04

Reading maximum speed

>>> import array, fcntl
>>> speed = array.array('i', [0])
>>> fcntl.ioctl(f, 0x80046B04, speed)
0
>>> print speed[0]
528000

This is the result of setting the max speed to 528 kbps in arch/arm/mach-at91/board-rascal.c

.max_speed_hz   = 528 * 1000

Get/set code

def spiGetSpeed(channel='0'):
    import array, fcntl
    with open('/dev/spidev1.' + str(channel), 'w') as f:
        speed = array.array('i', [0])
        fcntl.ioctl(f, 0x80046B04, speed)
        return speed[0]

def spiSetSpeed(speed, channel='0'):
    import array, fcntl
    with open('/dev/spidev1.' + str(channel), 'w') as f:
        return fcntl.ioctl(f, 0x40046B04, array.array('i', [int(speed)]))