SPI has more speeds


14 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: 598
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: 598
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: 21
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: 598
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: 21
Joined: Mon Jun 03, 2013 12:55 am
by Chinafreak » Fri May 01, 2015 8:58 pm
Hello,

Code: Select all
wget http://tronnes.org/downloads/2013-05-10-no-power-of-two-spi-bcm2708.zip


throw me a error "ERROR 404"

Does anybody has this file? I'd glad if someone could upload a new file! :)

- China

[EDIT]

is this included on fbtft already or not?
Posts: 7
Joined: Sun Dec 21, 2014 12:38 am
by notro » Fri May 01, 2015 9:21 pm
This will give you a kernel that has this change:
Code: Select all
sudo REPO_URI=https://github.com/notro/rpi-firmware rpi-update
https://github.com/notro/fbtft/wiki#install

This is the driver used in that kernel: https://github.com/notro/spi-bcm2708
Posts: 598
Joined: Tue Oct 16, 2012 6:21 pm
Location: Norway
by Chinafreak » Sat May 02, 2015 10:55 am
@notro oh okay, then:

Then can you tell me why the display show me as glitch / not working on higher speed?
https://www.raspberrypi.org/forums/view ... 54#p750554
I'd glad if you answer this topic, because you've more experience. :)
Posts: 7
Joined: Sun Dec 21, 2014 12:38 am
by rew » Thu May 14, 2015 9:47 am
Just FYI: I think the datasheet has a typo. It says: "CDIV has to be a power of two", while it intended to say: "CDIV has to be a multiple of two". This suspicion is strengthened by: "Odd numbers rounded down.".

There is also a hardware-explanation for this: The SPI and I2C modules in the BCM2835 (and 2708 and 2836) DO things at twice the clock frequency. They "sleep" for CDIV/2 cycles, and then do something like toggle the clock. So, to give you an easy way of calculating the resulting clock, (base_clock/CDIV) they implemented just 15 bits.
Check out our raspberry pi addons: http://www.bitwizard.nl/catalog/
User avatar
Posts: 397
Joined: Fri Aug 26, 2011 3:25 pm
by msperl » Thu May 14, 2015 10:19 am
note that this is is already in the spi-bcm2835 driver for 3.18.y kernels by the foundation.

All you have to do is switch by adding:
Code: Select all
dtoverlay=spi-bcm2835-overlay

to /boot/config.txt and then load spi-bcm2835 instead of spi-bcm2708.

And then you get this plus some more optimizations/features out of the box.
For all the patches please look at: https://github.com/raspberrypi/linux/issues/930

But note that the next release (4.0) makes further improvements on the spi framework side itself, which make responses for
short transfers quicker again by reducing the number of context-switches.

Finally DMA-mode is submitted for upstream, has been merged into for-next and so should go into 4.2, so that will help with the framebuffer devices...
As soon as it has been merged upstream by linus we will try to push that patch downstream as well to improve responsiveness for tft displays and other devices that produce long spi transfers.
Posts: 310
Joined: Thu Sep 20, 2012 3:40 pm
by mntmst » Thu May 14, 2015 9:00 pm
msperl wrote:note that this is is already in the spi-bcm2835 driver for 3.18.y kernels by the foundation.

All you have to do is switch by adding:
Code: Select all
dtoverlay=spi-bcm2835-overlay

to /boot/config.txt and then load spi-bcm2835 instead of spi-bcm2708.



Quick question about device trees. I currently unhooking (with a hack) the spi-bcm2708 driver from spidev to autoload a SPI protocol driver I'm writing for the comedi daq system using the gertboard.

Code: Select all
diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
index 762e17c..329f978 100644
--- a/arch/arm/mach-bcm2708/bcm2708.c
+++ b/arch/arm/mach-bcm2708/bcm2708.c
@@ -563,16 +563,29 @@ static struct spi_board_info bcm2708_spi_devices[] = {
        {
                .modalias = "spidev",
                .max_speed_hz = 500000,
-               .bus_num = 0,
+               .bus_num = 1,
                .chip_select = 0,
                .mode = SPI_MODE_0,
        }, {
                .modalias = "spidev",
                .max_speed_hz = 500000,
-               .bus_num = 0,
+               .bus_num = 1,
                .chip_select = 1,
                .mode = SPI_MODE_0,
-       }
+       },
+        {
+                .modalias = "spigert",
+                .max_speed_hz = 500000,
+                .bus_num = 0,
+                .chip_select = 0,
+                .mode = SPI_MODE_0,
+        }, {
+                .modalias = "spigert",
+                .max_speed_hz = 500000,
+                .bus_num = 0,
+                .chip_select = 1,
+                .mode = SPI_MODE_0,
+        }
 #endif
 };
 #endif


Is there a better way to access the SPI master with both chip selects as devices from a kernel driver for low-level access?
Posts: 20
Joined: Thu Sep 27, 2012 11:26 pm
by msperl » Sat May 16, 2015 3:54 pm
It is fairly simple to achieve that with a device-tree with a 3.18 foundation kernel (or self-built).

Look at the mcp2515-can0-overlay as an example of how it can get done:
https://github.com/raspberrypi/linux/bl ... verlay.dts
Posts: 310
Joined: Thu Sep 20, 2012 3:40 pm