SPI has more speeds


7 posts
by notro » Sat May 11, 2013 11:00 am
The SPI Controller driver spi_bcm2708 support speeds in doubles from ~8kHz to ~125MHz. This leaves large gaps, escpecially at the higher speeds (8, 16, 32, 64MHz).
Removing this constraint in the driver, gives at least 32000 speeds to choose from. I haven't tried them all, but these are my findings.

Using the Bus Pirate I have measured some low speeds

This table shows which speed was asked for, what divisor was used, the resulting theoretical speed and the measured speed.
Code: Select all
 asked     cdiv       given      measured
-----------------------------------------
   4000     62500      4000         4760
   5000     50000      5000         5940
   6000     41667      5999         7130
   7000     35715      6999         8310
   8000     31250      8000         9510
   9000     27778      8999        10700
  10000     25000     10000        11880
  80000      3125     80000        94590
 100000      2500    100000       118640

The Bus Pirate is too slow for high speeds, so I have used the sainsmart18fb FBTFT driver to measure the time it takes to update the display at different speeds.

This table shows which speed was asked for, the resulting theoretical speed and the measured frames per second.
Code: Select all
 asked    given    fps
-----------------------
  12       11.9     31
  16       15.6     38
  20       19.2     48
  24       22.7     59
  32       31.2     65
So it certainly looks like it's working.

Techincal information
The datasheet has this to say about the CDIV (Clock Divider) field of the CLK register (page 156):
SCLK = Core Clock / CDIV
If CDIV is set to 0, the divisor is 65536. The divisor must be a power of 2. Odd numbers rounded down. The maximum SPI clock rate is of the APB clock.

This is the codesnippet from the driver that sets the divisor.
From bcm2708_setup_state()
Code: Select all
   bus_hz = clk_get_rate(bs->clk);

   if (hz >= bus_hz) {
      cdiv = 2; /* bus_hz / 2 is as fast as we can go */
   } else if (hz) {
      cdiv = DIV_ROUND_UP(bus_hz, hz);

      /* CDIV must be a power of 2, so round up */
      cdiv = roundup_pow_of_two(cdiv);

      if (cdiv > 65536) {
         dev_dbg(dev,
            "setup: %d Hz too slow, cdiv %u; min %ld Hz\n",
            hz, cdiv, bus_hz / 65536);
         return -EINVAL;
      } else if (cdiv == 65536) {
         cdiv = 0;
      } else if (cdiv == 1) {
         cdiv = 2; /* 1 gets rounded down to 0; == 65536 */
      }
   } else {
      cdiv = 0;
   }
By default bus_hz = 250MHz which is the Core clock.

What I have done, is to remove the roundup_pow_of_two() statement.
I don't know if this has some unknown side effects, but I was hoping that someone could help test this more thoroughly.
For instance I don't have any equipment to test SPI reading.

For those that use 2013-02-09-wheezy-raspbian.img, I have made a binary (with DEBUG enabled) for download:
Code: Select all
uname -a
Linux raspberrypi 3.6.11+ #371 PREEMPT Thu Feb 7 16:31:35 GMT 2013 armv6l GNU/Linux

sudo rmmod spi_bcm2708
wget http://tronnes.org/downloads/2013-05-10-no-power-of-two-spi-bcm2708.zip
unzip 2013-05-10-no-power-of-two-spi-bcm2708.zip
dmesg -C
sudo insmod spi-bcm2708.ko

dmesg
bcm2708_spi bcm2708_spi.0: master is unqueued, this is deprecated
spi spi0.0: setup: want 500000 Hz; bus_hz=250000000 / cdiv=500 == 500000 Hz; mode 0: cs 0x00000000
spi spi0.0: setup: cd 0: 500000 Hz, bpw 8, mode 0x0 -> CS=00000000 CDIV=01f4
spi spi0.1: setup: want 500000 Hz; bus_hz=250000000 / cdiv=500 == 500000 Hz; mode 0: cs 0x00000001
spi spi0.1: setup: cd 1: 500000 Hz, bpw 8, mode 0x0 -> CS=00000001 CDIV=01f4
bcm2708_spi bcm2708_spi.0: SPI Controller at 0x20204000 (irq 80)

# I used this to test different speeds with the Bus Pirate
wget https://raw.github.com/torvalds/linux/master/Documentation/spi/spidev_test.c
gcc -o spidev_test spidev_test.c
sudo ./spidev_test -D /dev/spidev0.0 -s 80000

dmesg
spidev spi0.0: setup: want 80000 Hz; bus_hz=250000000 / cdiv=3125 == 80000 Hz; mode 0: cs 0x00000000
spidev spi0.0: setup: cd 0: 80000 Hz, bpw 8, mode 0x0 -> CS=00000000 CDIV=0c35
spidev spi0.0: setup: want 80000 Hz; bus_hz=250000000 / cdiv=3125 == 80000 Hz; mode 0: cs 0x00000000

References
* Datasheet: http://www.raspberrypi.org/wp-content/u ... herals.pdf
* SPI driver source: https://github.com/raspberrypi/linux/bl ... -bcm2708.c
* Bus Pirate: http://dangerousprototypes.com/docs/Bus_Pirate
* Bus Pirate Logic Analyzer mode: http://dangerousprototypes.com/docs/Logic_analyzer_mode
Posts: 530
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by swisslizard » Tue May 21, 2013 8:42 am
Just tried this. It is working fine! :D

Thanks for sharing the code and knowledge.
Posts: 2
Joined: Tue May 21, 2013 8:40 am
by swisslizard » Tue May 21, 2013 2:40 pm
2 Questions:

When I reboot the Raspi, the update for the SPI speed is not active anymore. How do I have to install it, so it stays active after a reboot?

Just for curiosity. Why has the orginal limitation on different SPI speeds been put into the system? Are there any problems to be expected with other SPI speeds?
Posts: 2
Joined: Tue May 21, 2013 8:40 am
by notro » Tue May 21, 2013 4:10 pm
swisslizard wrote:When I reboot the Raspi, the update for the SPI speed is not active anymore. How do I have to install it, so it stays active after a reboot?
Replace this file: /lib/modules/3.6.11+/kernel/drivers/spi/spi-bcm2708.ko

swisslizard wrote:Why has the orginal limitation on different SPI speeds been put into the system?
I don't know.

swisslizard wrote:Are there any problems to be expected with other SPI speeds?
I don't know. If not someone with access to the full datasheet can shed some light on this, testing is the only way.

I have put together a page with the knowledge I have aquired: http://elinux.org/index.php?title=RPi_SPI
Posts: 530
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by mike808 » Sat Nov 09, 2013 8:41 pm
Hello,

I'm a complete newbie to SPI, but I was trying to learn a bit. I have a TFT display that does support SPI (ILI9430 chip) and I was trying to have my PI interface with it. However, I ran into issues and from googling and asking others for help, it might be due to a 9-bits per word on this chip, but I'm not sure. I've tried using 8-bit and 9-bit with spidev, but I was never able to successfully communicate with the TFT display.

I saw a post here http://lallafa.de/blog/2013/03/watterott-mi0283qt-9a-display-for-the-rasbperry-pi/ which does mention that it might be 9-bit, an this person used notro's driver framework to get his working. I tried looking at the code and since i'm new to embedded coding, didn't get it to work on my box.

Essentially here is the code i tried:
Code: Select all
void SPI::test(unsigned short *data)
{
   struct spi_ioc_transfer spi;
   spi.tx_buf = (unsigned long)&data;
   spi.rx_buf = (unsigned long)NULL;
   spi.len = 2;
   spi.delay_usecs = 0;
   spi.speed_hz = 1000000;
   spi.bits_per_word = 9;

   int ret = ioctl(this->spifd, SPI_IOC_MESSAGE(1), &spi);
   if (ret < 0)
   {
      perror("Problem transmitting spi data...ioctl");
      exit(1);
   }
}


I never got an error about SPI communication, but then again my display didn't do what I expected it to. According to Adafruit support, I am to set the D/C pin to LOW before I write the command to the SPI bus. However, they also told me that the bits_per_word should be 8 so I'm not quite sure what to believe.

Code: Select all
void TFT_ILI9340::writeCommand(unsigned short cmd)
{
   digitalWrite(dcPin_, LOW);
   spi_->spiWriteRead(&cmd, 1);
   digitalWrite(dcPin_, HIGH);
}


My main
Code: Select all
int main ()
{
    TFT_ILI9340 tft(const_cast<char*>("/dev/spidev0.0"), 7);
    cout << "tft test program" << endl;
    tft.writeCommand(0x28);  // turn display off
    return 0;
}


can anyone please offer some advice to what i am doing incorrectly? i have made sure spidev is enabled on the PI and it has rw permission. Does spidev support 9 bits_per_word out of the box?

Here is what i'm running (uname -a)
Code: Select all
Linux raspberrypi 3.6.11+ #474 PREEMPT Thu Jun 13 17:14:42 BST 2013 armv6l GNU/Linux
User avatar
Posts: 20
Joined: Mon Jun 03, 2013 12:55 am
by notro » Sun Nov 10, 2013 10:53 am
From the Adafruit website http://www.adafruit.com/products/1480:
* ILI9340 (datasheet) controller with built in pixel-addressable video RAM buffer
* 4 wire SPI digital interface - this display talks in '8-bit' SPI check the Arduino library source for how to communicate

The Watterott display, uses an ILI9341, which is a different controller.

Many display controllers can do both 8 and 9-bit SPI. With 9-bit SPI the D/C signal is embedded as the 9nth bit. Most often the display breakout board hardwire this interface choice.
D/C specifies whether the SPI data sent is Data (D/C=1) or Command (D/C=0). In the datasheet, this is shown in the D/CX column in the Command list.

When I made my first framebuffer driver for the previous Adafruit 2.2" display, I first drove it from userspace using info from the Arduino example.

FYI: I just received that same display, and will hopefully add FBTFT support for it in the coming months.
Posts: 530
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by mike808 » Sun Nov 10, 2013 4:12 pm
@notro - thank you for your reply. I did try using 8-bit first and that didn't work for me. clearly, i must be doing something wrong, but I can't figure out what. After you get it working and integrated into your framework, do you think you can show me an example of a simple userspace program to drive it? Looking at the arduino code, it's mostly avr-gcc which i'm not familiar with but have been reading about to try to get that port to the PI working.

thanks again!
mike
User avatar
Posts: 20
Joined: Mon Jun 03, 2013 12:55 am