Linux Framebuffer fbtft with SPI DMA for Intel Edison

When you want to connect a display to the Intel Edison module, you should utilise the existing Linux infrastructure and use a kernel framebuffer driver instead of writing your own screen functions based on Arduino libraries.

The schematics and more technical information about my display and board are in my previous post: Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322.

At the time of writing the article, the Linux kernel SPI support was broken in the original Intel kernel. So, I used Primiano’s kernel that fixed the SPI support. It works perfectly for me, it may not contain the latest Intel’s modifications. Use it on your own risk.

First, I downloaded the Intel sources from I extracted them into ~/edison in my home directory and followed the instructions for compiling it.

cd ~/edison/poky/
source oe-init-build-env ../build_edison/
bitbake edison-image u-boot
../poky/meta-intel-edison/utils/flash/ .

Then I replaced the kernel in ~/edison/poky/linux-kernel by my own, but I kept the directories .git and .meta intact.
I increased the revision number in the variable PR = “r3″ in the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/
I added my new configuration parameters for fbtft into the file ~/edison/poky/linux-kernel/arch/x86/configs/i386_edison_defconfig

I downloaded the fbtft sources. I created the folder ~/edison/poky/linux-kernel/drivers/video/fbtft and extracted the fbtft sources into the folder.

I added the line in ~/edison/poky/linux-kernel/drivers/video/Kconfig before the line “endmenu”.

source "drivers/video/fbtft/Kconfig"

I added the line in ~/edison/poky/linux-kernel/drivers/video/Makefile

obj-$(CONFIG_FB_TFT)    += fbtft/

Then I edited the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft_device.c

I added the header

#include <linux/spi/intel_mid_ssp_spi.h>

Then I added the description of my display into the list of displays.
Where “reset” uses the number 49 of the GP49 pin, “dc” is GP15.
The pin “led” is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c

It seems that the chip SSD1322 supports speeds only up to 12.5MHz, but the Edison must be able to support speeds up to 25MHz.

   .name = "er_oled028",
   .spi = &(struct spi_board_info) {
       .modalias = "fb_ssd1322",
       .max_speed_hz = 12500000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = true,
        .platform_data = &(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           .gpios = (const struct fbtft_gpio []) {
               { "reset", 49 },
               { "dc", 15},
               { "led", 14},

I modified the file ~/edison/poky/linux-kernel/drivers/video/fbtft/fbtft-core.c

--- fbtft-core.c	2015-03-05 02:54:01.000000000 -0800
+++	2016-05-30 19:19:21.000000000 -0700
@@ -863,7 +863,7 @@
 	if (txbuflen > 0) {
 		if (dma) {
 			dev->coherent_dma_mask = ~0;
-			txbuf = dmam_alloc_coherent(dev, txbuflen, &par->txbuf.dma, GFP_DMA);
+			txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_DMA | GFP_ATOMIC);
 		} else {
 			txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL);

I added the lines to the file ~/edison/poky/meta-intel-edison/meta-intel-edison-bsp/conf/machine/edison.conf to autoload the fbtft module.
Where busnum=5 is the SPI bus number on the Edison.,
name=er_oled028 defines my oled display from fbtft_device.c,
debug=7 shows more debugging information. It’s optional.

KERNEL_MODULE_AUTOLOAD += "fbtft_device"
module_conf_fbtft_device = "options fbtft_device name=er_oled028 busnum=5 debug=7"

Then I recompiled the kernel and flashed my Edison.

I compiled mplayer on the Edison and played a sample video. I couldn’t find a video with the same dimensions as my display, but it should work in the full-screen mode too.

/usr/local/bin/mplayer -vo fbdev ultra.mp4

Linux kernel for Edison with SPI DMA fixes and fbtft framebuffer

Framebuffer fbtft Installation on Intel Edison for OLED Display SSD1322

Update: the version with working SPI DMA

The Intel Edison doesn’t have a video interface; so, you connect an OLED or TFT LCD display via the SPI interface using the Linux kernel video module framebuffer.

I used fbtft – Linux Framebuffer drivers for small TFT LCD display modules by Noralf Tronnes.

My display is 2.8″ OLED Display 256×64 Graphic Module SSD1322.

After reading the documentation for the display, I connected it to the Edison via the 4-wire SPI interface, which requires an additional GPIO pin for the D/C signal (Write Data/Write Command). The 3-wire SPI interface uses the 9 bit SPI interface and should be avoided.

I compared the timing diagrams for the SSD1322 chip with the SPI Transfer Modes and found that it uses the SPI Mode 3.


Here is the schematic of my SSD1322 shield for the Intel Edison.

SSD1322 Shield for Intel Edison
The SSD1322 Display Shield for the Intel Edison
Connections between the Edison and the Display:

GP49 -> RES(Reset Signal Input)
GP15 -> D/C(Data/Command Control)
GP110_SPI_2_FS0(CS0) -> CS(Chip Select Input)
GP109_SPI_2_CLK -> D0/SCLK(Serial Clock)
GP115_SPI_2_TXD -> D1/SDIN(Serial Data Input)
I also connected GP14 to the SHDN pin of the +12V voltage regulator.

fbtft download and configuration

The latest code from the notro/fbtft repository didn’t work for me.
I used the older code presslab-us/fbtft, which also included support for my chip SSD1322.

I prepared the Edison directory with the Linux sources as described in my article Building Yocto Linux for Intel Edison.

After I compiled the Edison image for the first time, I copied these two hidden directories into another folder (I placed the Edison sources into the “edison” directory in my home directory); so, I can return the sources to the original state by copying these hidden directories back at any moment.


I created the directory “fbtft” and copied all fbtft files there.

mkdir ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft

I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Kconfig before the line “endmenu”.

source "drivers/video/fbtft/Kconfig"

I added the line in ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/Makefile

obj-$(CONFIG_FB_TFT)    += fbtft/

Then I edited the file ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video/fbtft/fbtft_device.c

I added the header

#include <linux/spi/intel_mid_ssp_spi.h>

Then I added the description of my display into the list of displays.
Where “reset” uses the number 49 of the GP49 pin, “dc” is GP15.
The pin “led” is GP14 and I use it in my custom initialization code in the file fb_ssd1322.c

DMA has to be disabled because the DMA code in intel_mid_ssp_spi.h is broken.
If you enable it, you can see errors like this: “intel_mid_ssp_spi_unified 0000:00:07.1: ERROR : DMA buffers already mapped”

   .name = "er_oled028",
   .spi = &(struct spi_board_info) {
       .modalias = "fb_ssd1322",
       .max_speed_hz = 12000000,
       .mode = SPI_MODE_3,
       .bus_num = 5,
       .chip_select = 0,
       .controller_data = &(struct intel_mid_ssp_spi_chip) {
           .burst_size = DFLT_FIFO_BURST_SIZE,
           .timeout = DFLT_TIMEOUT_VAL,
           .dma_enabled = false,
        .platform_data = &(struct fbtft_platform_data) {
           .display = {
               .buswidth = 8,
               .backlight = 0,
               .width = 256,
               .height = 64,
           .gpios = (const struct fbtft_gpio []) {
               { "reset", 49 },
               { "dc", 15},
               { "led", 14},

I wrote my custom initialization code for the display ER_OLED028 in fb_ssd1322.c following the documentation for my display.

static int init_display(struct fbtft_par *par)
   fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

   //reset the chip
   fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
   gpio_set_value(par->gpio.reset, 0);
   gpio_set_value(par->gpio.reset, 1);

   write_reg(par, 0xfd, 0x12); /* Unlock OLED driver IC */
   write_reg(par, 0xae); /* Display Off */
   write_reg(par, 0xb3, 0x91); /* Display divide clockratio/frequency */
   write_reg(par, 0xca, 0x3f); /* Multiplex ratio, 1/64, 64 COMS enabled */
   write_reg(par, 0xa2, 0x00); /* Set offset, the display map starting line is COM0 */
   write_reg(par, 0xa1, 0x00); /* Set start line position */
   write_reg(par, 0xa0, 0x14, 0x11); /* Set remap, horiz address increment, disable colum address remap, */
				                      /*  enable nibble remap, scan from com[N-1] to COM0, disable COM split odd even */
   write_reg(par, 0xb5, 0x00); /* Set GPIO */
   write_reg(par, 0xab, 0x01); /* Select internal VDD */
   write_reg(par, 0xb4, 0xa0, 0xfd); /* Display enhancement A, external VSL, enhanced low GS display quality */
   write_reg(par, 0xc1, 0xff); /* Contrast current, 256 steps, default is 0x7F */
   write_reg(par, 0xc7, 0x0f); /* Master contrast current, 16 steps, default is 0x0F */
   write_reg(par, 0xb1, 0xe2); /* Phase Length */
   write_reg(par, 0xd1, 0x82, 0x20); /* Display enhancement B */
   write_reg(par, 0xbb, 0x1f); /* Pre-charge voltage */
   write_reg(par, 0xb6, 0x08); /* Set Second Pre-Charge Period */
   write_reg(par, 0xbe, 0x07); /* Set VCOMH */
   write_reg(par, 0xa6); /* Normal display */

   //enable VCC
   gpio_set_value(par->gpio.led[0], 1);
   write_reg(par, 0xaf); /* Display ON */

   return 0;

After all modifications, I created a patch file.
While committing to git, I added the comment line “fbtft_ssd1322″.

cd ~/edison/edison-src/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux/drivers/video
git add .
git commit
git format-patch -1

It created the patch file “0001-fbtft_ssd1322.patch”. I renamed it to “fbtft_ssd1322.patch” and copied it to:


I created the kernel configuration file fbtft.cfg in the same directory “recipes-kernel/linux/files/”:

# CONFIG_FB_TFT_GU39XX is not set
# CONFIG_FB_TFT_HX8340BN is not set
# CONFIG_FB_TFT_HX8347D is not set
# CONFIG_FB_TFT_ILI9320 is not set
# CONFIG_FB_TFT_ILI9325 is not set
# CONFIG_FB_TFT_ILI9341 is not set
# CONFIG_FB_TFT_PCD8544 is not set
# CONFIG_FB_TFT_SSD1289 is not set
# CONFIG_FB_TFT_SSD1351 is not set
# CONFIG_FB_TFT_ST7735R is not set
# CONFIG_FB_FLEX is not set

I commented the line in the file board.c for the device ads7955 that occupies the same SPI busnum=5, cs=0 that we use for the display.
Then I ran the same git sequence to generate the patch file “platform_ssd1322.patch” and copied it into the same “recipes-kernel/linux/files” directory.

From c83f14b37bc2dcfa97b36214f02b15ddaad51a47 Mon Sep 17 00:00:00 2001
From: Farit <>
Date: Sat, 9 Apr 2016 22:02:19 -0700
Subject: [PATCH] platform_ssd1322

 arch/x86/platform/intel-mid/board.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/platform/intel-mid/board.c b/arch/x86/platform/intel-mid/board.c
index eb3d8a4..fca6440 100644
--- a/arch/x86/platform/intel-mid/board.c
+++ b/arch/x86/platform/intel-mid/board.c
@@ -111,7 +111,7 @@ struct devs_id __initconst device_ids[] = {
 	/* SPI devices */
 	{"spidev", SFI_DEV_TYPE_SPI, 0, &spidev_platform_data, NULL},
-	{"ads7955", SFI_DEV_TYPE_SPI, 0, &ads7955_platform_data, NULL},
+//	{"ads7955", SFI_DEV_TYPE_SPI, 0, &ads7955_platform_data, NULL},
 	{"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data, NULL},
 	{"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data, NULL},
 	{"pmic_gpio", SFI_DEV_TYPE_IPC, 1, &pmic_gpio_platform_data,

When all patch and configuration files were ready, I added links to them into the file “~/edison/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/linux-yocto_3.10.bbappend”.

SRC_URI += "file://defconfig"
SRC_URI += "file://upstream_to_edison.patch"

SRC_URI += "file://fbtft.cfg"
SRC_URI += "file://fbtft_ssd1322.patch"
SRC_URI += "file://platform_ssd1322.patch"
fbtft Module Compilation

Then I recompiled the Linux image and installed it.

cd ~/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image
Loading and Testing the Framebuffer Module

I loaded the fbtft module supplying the name of the display and the SPI bus number (it’s 5 on the Edison).

modprobe fbtft_device name=er_oled028 busnum=5

Sometimes, it’s not loaded. I need to investigate the problems.
When it loads, the “dmesg” command shows:

[   32.254181] fbtft_device:  GPIOS used by 'er_oled028':
[   32.254205] fbtft_device:    'reset' = GPIO49
[   32.254221] fbtft_device:    'dc' = GPIO15
[   32.254234] fbtft_device:    'led' = GPIO14
[   32.254246] fbtft_device:  SPI devices registered:
[   32.254264] fbtft_device:      spidev spi5.1 25000kHz 8 bits mode=0x00
[   32.254281] fbtft_device:      fb_ssd1322 spi5.0 12500kHz 8 bits mode=0x03
[   32.394493] graphics fb0: fb_ssd1322 frame buffer, 256x64, 32 KiB video memory, 8 KiB buffer memory, fps=20, spi5.0 at 12 MHz

Then I can test the module by sending random values to the framebuffer socket “/dev/fb0″.

cat /dev/urandom > /dev/fb0

Here is the result.

Sending Random Data to the Framebuffer
Sending Random Data to the Framebuffer

I can also load the fbcon module to display the console on the display.

modprobe fbcon
Edison Console via the Framebuffer
Edison Console via the Framebuffer

There are patches on the Edison forum that may help to fix the DMA issue. Otherwise, the display will be relatively slow.
Check this link stewart maguire’s patches for DMA and FBTFT support.
And this Intel MID SSP SPI driver getting stuck in kernel space.


Configuration and patch files for fbtft on Intel Edison

Building Yocto Linux for Intel Edison

The Intel Edison board is shipped with the Yocto Linux distribution installed. Yocto allows an experienced developer to compile a small-size Linux image with only the selected packages. Then the image is flashed into the board. It’s not as convenient as Ubuntu as there are no pre-compiled packages that are easy to install.

I wanted to add the program mc (Midnight Commander); so, I had to recompile the Yocto image. My computer OS is Linux Ubuntu 14.04. I experienced a few problems while compiling; so, I’ll describe them here.

The main document is Board Support Package (BSP) User Guide. I followed it.

Installed the prerequisite packages with the following command:

sudo apt-get install build-essential git diffstat gawk chrpath texinfo libtool \
gcc-multilib dfu-util u-boot-tools

Set up my git name and email:

git config --global "YOUR NAME"
git config --global "YOUR EMAIL ADDRESS"

Downloaded the package Linux source files from the Intel Edison Software Downloads page. At the time of writing, it was in the section Intel Edison® Board Firmware Software Release 2.1.

I install everything related to the Intel Edison into the directory /home/farit/edison. The Linux sources consume 11G in that directory.

Decompressed the source package:

tar xzf edison-src-ww25.5-15.tgz  -C /home/farit/edison/
cd /home/farit/edison/edison-src/

Moved my download and build cache (also called sstate) directories from the default location under the build directory, using the –dl_dir and –sstate_dir options. To create download and sstate directory, used the mkdir command:

mkdir /home/farit/edison/bitbake_download_dir
mkdir /home/farit/edison/bitbake_sstate_dir

Used the script to initialize the build environment for Intel Edison.

/home/farit/edison/edison-src/meta-intel-edison/ \
--dl_dir=/home/farit/edison/bitbake_download_dir \
--sstate_dir=/home/farit/edison/bitbake_sstate_dir \

After the setup, I ran these commands recommended by the setup script:

cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image

The compilation took 5 or so hours. I should’ve used the SSD disk. But most files have been downloaded and cached now; so, it doesn’t take much time the second time.

Then I decided to compile in Midnight Commander. I checked the name of the package on the Yocto recipes directory. It was “mc”.
I opened the file /home/farit/edison/edison-src/meta-intel-edison/meta-intel-edison-distro/recipes-core/images/ and added the lines at the bottom:

#Midnight Commander

Then ran again:

cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake edison-image

Then I created a new Linux image:


At first, the command complained about the non-existing mkimage program, which is in the package u-boot-tools:

Error : ota_update.scr creation failed, mkimage tool not found

I added a symlink to mkimage and ran the script again:

cd /home/farit/edison/edison-src
mkdir -p u-boot/tools
cd u-boot/tools
ln -s /usr/bin/mkimage mkimage

When there were no errors, I looked in the directory /home/farit/edison/edison-src/build/toFlash. It contained the same files as in the Intel’s firmware image.
I ran the command in it as root. I added myself into the group “dialout”; so, I should be able to connect to a USB port as a regular user. I’ll check it again the next time.

Midnight Commander on Intel Edison
Midnight Commander on Intel Edison

By default, the subshell (Ctrl-O) doesn’t work in Midnight Commander as it requires bash. Change your user shell from the default “/bin/sh” to “/bin/bash” in /etc/passwd.

When I want to quickly recompile just the kernel after changes, I run these commands:

cd /home/farit/edison/edison-src
source poky/oe-init-build-env
bitbake -f linux-yocto

Using Atmega1284 with Arduino

I downloaded an archive for my selected branch of Arduino (version 1.6.3 at the time of writing).

Mighty 1284P: An Arduino core for the ATmega1284P

I connected my programmer AVRISP mkII to the ISP header of the board to upload the Arduino bootloader. I also connected the FTDI device to provide the 5V power to the board; otherwise, programming doesn’t work.


I use the variant of the microcontroller pin definitions “avr_developers”, also known as “sanguino” for my project.

You can find the definitions in the file ~/arduino/hardware/arduino/avr/variants/avr_developers/pins_arduino.h

// ATMEL ATmega1284P (should also work for SANGUINO/ATmega644P)
//                       +---\/---+
//           (D 0) PB0  1|        |40  PA0 (AI 0 / D31)
//           (D 1) PB1  2|        |39  PA1 (AI 1 / D30)
//      INT2 (D 2) PB2  3|        |38  PA2 (AI 2 / D29)
//       PWM (D 3) PB3  4|        |37  PA3 (AI 3 / D28)
//    PWM/SS (D 4) PB4  5|        |36  PA4 (AI 4 / D27)
//      MOSI (D 5) PB5  6|        |35  PA5 (AI 5 / D26)
//  PWM/MISO (D 6) PB6  7|        |34  PA6 (AI 6 / D25)
//   PWM/SCK (D 7) PB7  8|        |33  PA7 (AI 7 / D24)
//                 RST  9|        |32  AREF
//                 VCC 10|        |31  GND
//                 GND 11|        |30  AVCC
//               XTAL2 12|        |29  PC7 (D 23)
//               XTAL1 13|        |28  PC6 (D 22)
//      RX0 (D 8)  PD0 14|        |27  PC5 (D 21) TDI
//      TX0 (D 9)  PD1 15|        |26  PC4 (D 20) TDO
// INT0 RX1 (D 10) PD2 16|        |25  PC3 (D 19) TMS
// INT1 TX1 (D 11) PD3 17|        |24  PC2 (D 18) TCK
//      PWM (D 12) PD4 18|        |23  PC1 (D 17) SDA
//      PWM (D 13) PD5 19|        |22  PC0 (D 16) SCL
//      PWM (D 14) PD6 20|        |21  PD7 (D 15) PWM
//                       +--------+

I use my AVRISP mkII programmer to load the bootloader into the microcontroller. I use Atmel AVR Studio (version is 4.19).

I go to Tools -> Program AVR -> Connect. Then I select my programmer.

Selecting a programmer, AVRISP mkII in my case
Selecting a programmer, AVRISP mkII in my case
Reading the signature from Atmega1284p
Reading the signature from Atmega1284p
Selecting the bootloader hex file, optiboot_atmega1284p.hex in my case.
Selecting the bootloader hex file, optiboot_atmega1284p.hex in my case. Click on Program to upload the bootloader into the chip.
Program the fuses into the chip.
Program the fuses into the chip.

Then I copy the files from the downloaded archive into the hardware/arduino/avr/ subdirectory of the installed Arduino directory and add the settings for the avr_developers board into boards.txt.

############################################################## pinouts 16MHz using Optiboot

After that I select this board in the Arduino IDE: Tools -> Board

Arduino Clock Atmega1284p with MIDI player and Internet

The Arduino Clock
  • uses the C++ object-oriented code written for the Arduino framework
  • is powered by the 8-bit Atmel’s microcontroller ATmega1284P
  • is installed on the PCB from Wise Clock 4
  • displays information on the Sure Electronic’s 32X16 RG Dual Color LED Dot Matrix Unit Board (display colors: red, green, orange)
  • adjusts time from the Internet using the NTP protocol via the Wi-Fi RN-XV WiFly Module
  • shows the weather forecast from the Internet
  • has alarms that can be configured to activate every day or once on a specific date or tomorrow or on different week days
  • plays music in the RTTTL format that was popular in mobile phones
  • plays music in the MIDI format via the Fluxamasynth Shield that uses the Atmel’s and Dream’s ATSAM2195 MIDI chip
  • changes the brightness of the display depending on the light level in a room detected by the light-dependent resistor
A video with the description of the clock menus
Here are the internal devices of the clock
Download the code for the clock

From Github: