0

I'm trying to interface with a Winbond W25Q64BV flash chip via SPI.

I started off by testing it on Arduino (target is a Raspberry) as I found a few implementations
before I got started. Most notably this instructable and this project on Google code.

The instructable gets the 'jedec' information like this:

Looking at the code from the instructable, the implementation to get the jedec information

void _get_jedec_id(byte *b1, byte *b2, byte *b3) {
  digitalWrite(SS, HIGH);
  digitalWrite(SS, LOW);
  SPI.transfer(WB_JEDEC_ID);
  *b1 = SPI.transfer(0); // manufacturer id
  *b2 = SPI.transfer(0); // memory type
  *b3 = SPI.transfer(0); // capacity
  digitalWrite(SS, HIGH);
  not_busy();
}  

The project on Google code defined multiple requests for each piece of information,
but the idea is basically the same, for example getting the manufacturer id:

uint8_t winbondFlashClass::readManufacturer()
{
  uint8_t c;
  select();
  transfer(R_JEDEC_ID);
  c = transfer(0x00);
  transfer(0x00);
  transfer(0x00);
  deselect();
  return c;
}

After confirming this worked with the Arduino I wanted to port it to run on a Raspberry.
I enabled the SPI and confirmed the interface was available (method described here).

I installed the BCM2835 C library on the Raspberry (wheezy) as described here
Which is basically:

tar zxvf bcm2835-1.xx.tar.gz
cd bcm2835-1.xx
./configure
make 
sudo make check
sudo make install

I wrote a little test application in C:

#include <bcm2835.h>
#include <stdio.h>


void  main()
{
   printf("START");
   bcm2835_spi_begin();

   //This all the code required to explain my problem
}

It compiled, was able to execute it (as root of course)
and it ran and completed without throwing any exceptions.

It did not however output anything, not even the text "START".
But if I remove the line bcm2835_spi_begin(); it does output "START".

I can't explain it and I'm not a C guy so I started looking at Python.

Using py-spidev, which is 'somewhat' documented here,
I started porting the code from the Arduino samples:

import spidev
import time

WB_ZERO = 0x00
WB_WRITE_ENABLE = 0x06
WB_WRITE_DISABLE = 0x04
WB_CHIP_ERASE = 0xC7
WB_READ_STATUS_REGISTER_1 = 0x05
WB_READ_STATUS_REGISTER_2 = 0x35
WB_READ_DATA = 0x03
WB_READ_PAGE_PROGRAM = 0x02
WB_JEDEC_ID = 0x9F

def connect(bus = 0, device = 0):
        spi = spidev.SpiDev(bus, device)
        spi.open(bus, device)
        return spi

def configure(spi, max_speed_hz = 1953125, mode = 0, bits_per_word = 8):
        spi.max_speed_hz = max_speed_hz
        spi.mode = mode
        spi.bits_per_word = bits_per_word

def print_configuration(spi):
        print "max_speed_hz: %s" % spi.max_speed_hz
        print "mode: %s" % spi.mode
        print "bits_per_word: %s" % spi.bits_per_word

def cs_toggle_high_low(spi):
        spi.cshigh = True;
        spi.cshigh = False;

def cs_toggle_low_high(spi):
        spi.cshigh = False;
        spi.cshigh = True;

def cs_low(spi):
        spi.cshigh = False;

def cs_high(spi):
        spi.cshigh = True;

def transfer(spi, byte):
        return spi.xfer([byte])[0]

def wait_for_device(spi):
        transfer(spi, WB_READ_STATUS_REGISTER_1)
        while(transfer(spi, WB_ZERO) == 1):
                print ".",

def get_jedec_id(spi):
        cs_toggle_high_low(spi)
        transfer(spi, WB_JEDEC_ID)
        manufacturer_id = transfer(spi, WB_ZERO)
        memory_type = transfer(spi, WB_ZERO)
        capacity = transfer(spi, WB_ZERO)
        cs_high(spi);
        wait_for_device(spi)
        return (manufacturer_id, memory_type, capacity)

And then I used it as follows:

spi = connect()
configure(spi)
print_configuration(spi)

print get_jedec_id(spi)

With the cs_toggle_high_low function I'm trying to simulate the calls
to digitalWrite in the original code, but I honestly do not know if it's the same,
although if I don't call cs_toggle_high_low I always get 0x00 data.

The problem is, that I receive 0xFF for all 3 values when the flash chip is connected to the SPI bus
(whereas I receive 0x00, 0x00, 0x00 if it is not connected or I don't toggle).

So I'm wondering why the code using the BCM2835 library doesn't work (but doesn't throw errors either)

And what I might be doing wrong in the Python version.

Or basically, how I can interface the Raspberry with the flash chip successfully.

1

i've managed to read/write a flash using the built-in spi port on an odroid u3 however the principle is the same,

the thing to note is that the xfer method already takes care of the cs toggling, you probably dont need to touch the cshigh attribute (i didnt need to), also, to make transactions you need to use the xfer2 method (it toggles cs, tranfers all the bytes and then toogles again) (the xfer method toggles cs for every byte, not good)

i was trying to flash a chip using flashrom, however it is bugged, long story short, i wrote a tiny python class, i attach my code, it reads, writes, erases and verify the flash, it's a class that you instantiate to your own bus, (regarding the bus and cs parameters, given /dev/spidevX.Y, bus = X, cs = Y)

just take a look at the end are examples (my own succesful tests)

WREN = 6
WRDI = 4
RDSR = 5
RDSR2 = 0x35
WRSR = 1
READ = 3
WRITE = 2
SECTOR_ERASE = 0x20
CHIP_ERASE = 0xC7

from time import sleep
import spidev
from datetime import datetime

def sleep_ms(msecs):
    sleep(float(msecs) / 1000.0)

class spiflash(object):

    def __init__(self, bus, cs, mode = 0, max_speed_hz = 1000000):
        self.spi = spidev.SpiDev()
        self.spi.open(bus,cs)       
        self.spi.max_speed_hz = max_speed_hz
        self.spi.mode = mode

    def __del__(self):
        try:
            self.spi.close()
        except:
            pass

    #reads ----------------------------------------------------------------------------------
    def read_status(self):
        statreg = self.spi.xfer2([RDSR,RDSR])[1]
        statreg2 = self.spi.xfer2([RDSR2,RDSR2])[1]
        return statreg, statreg2

    def read_page(self, adr1, adr2):
        xfer = [READ, adr1, adr2, 0] + [255 for _ in range(256)] # command + 256 dummies
        return self.spi.xfer2(xfer)[4:] #skip 4 first bytes (dummies)

    #writes ----------------------------------------------------------------------------------
    def write_enable(self):
        self.spi.xfer2([WREN])
        sleep_ms(5)

    def write_disable(self):
        self.spi.xfer2([WRDI])
        sleep_ms(5)

    def write_status(self,s1,s2):
        self.write_enable()

        spi.xfer2([WRSR,s1,s2])
        sleep_ms(10)

        self.wait_until_not_busy()

    def write_page(self, addr1, addr2, page):
        self.write_enable()

        xfer = [WRITE, addr1, addr2, 0] + page[:256]
        self.spi.xfer2(xfer)
        sleep_ms(10)

        self.wait_until_not_busy()

    def write_and_verify_page(self, addr1, addr2, page):
        self.write_page(addr1, addr2, page)
        return self.read_page(addr1, addr2)[:256] == page[:256]

    #erases ----------------------------------------------------------------------------------
    def erase_sector(self,addr1, addr2):
        self.write_enable()

        xfer = [SECTOR_ERASE, addr1, addr2, 0]
        self.spi.xfer2(xfer)
        sleep_ms(10)

        self.wait_until_not_busy()

    def erase_all(self):
        self.write_enable()

        self.spi.xfer2([CHIP_ERASE])
        sleep_ms(10)

        self.wait_until_not_busy()

    #misc ----------------------------------------------------------------------------------
    def wait_until_not_busy(self):
        statreg = 0x1;
        while (statreg & 0x1) == 0x1:
            #Wait for the chip.
            statreg = self.spi.xfer2([RDSR,RDSR])[1]
            #print "%r \tRead %X" % (datetime.now(), statreg)
            sleep_ms(5)

    #helpers -------------------------------------------------------------------------------
    def print_status(self,status):
        print "status %s %s" % (bin(status[1])[2:].zfill(8), bin(status[0])[2:].zfill(8))   

    def print_page(self, page):
        s = ""
        for row in range(16):
            for col in range(15):
                s += "%02X " % page[row * 16 + col]
            s += "\n"
        print s 


#TESTS -------------------------------------------------------------------
#TESTS -------------------------------------------------------------------

chip = spiflash(bus = 0, cs = 0)

#print_status(read_status())
#write_disable()
#print_status(read_status())

p = chip.read_page(0,0)

#print "erasing chip"
#chip.erase_all()
#print "chip erased"

for i in range(256):
    p[i] = (i + 2) % 256
chip.print_page(p)
#write_status(0,0)
#print_status(read_status())
print chip.write_and_verify_page(0,0,p)

chip.print_page(chip.read_page(0,0))

#self.wait_until_not_busy()
#print_status(read_status())
#write_status(0,0)
#print_status(read_status())

cheers

0

I haven't looked at the code carefully but spidev and the BCM2835 library are both known to work.

Are you sure the connections are correct?

You need to connect MOSI/MISO/SCLK and one of CE0/CE1 plus ground at the Pi end.

Unlike most Arduinos the Pi's gpios are 3.3V. Feeding anything over 3.3V to a gpio will destroy it sooner or later

There are other libraries such as wiringPi (C but there is an associated Python module) which also have SPI wrappers.

My pigpio library also has C and Python wrappers. It also lets you talk to a SPI device from the command line using the pigs utility (pigs spio, spix, spic)

  • I'll review all of these, see if it helps. And to make sure I'll probably do it in C# on a netduino as well. I'll let you know the results – TimothyP Oct 11 '14 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.