0. Introduction
Since the official stable kernel linux-3.4 for Cubieboard2 doesn’t include native SPI driver support, we need to modify the kernel source code, compile and put it into NAND to replace the original uImage, and add the new kernel module files. Additionally, we need to modify system files to fully support full-duplex communication.
1. Flash Debian System to NAND (Windows)
First, download the customized Debian system for Cubieboard2 from: http://dl.cubieboard.org/software/a20-cubieboard/debian/nand/debian-nand.img.gz
Download Phoenixsuit and open the downloaded image through it. With the Cubieboard2 unpowered, hold down the FEL button while connecting it to the computer via an OTG cable. In Device Manager, install the driver for this unrecognized device, specifying the Phoenixsuit installation directory as the driver location, and confirm installation.
Then format and flash the image.
2. Log in to the System
Connect the Cubieboard2 to an Ethernet cable. The Debian system is set to a static IP address 192.168.1.124 by default, which you can access via SSH. You can also log in through a TTL line from UART, change to DHCP mode, then check the IP address from the router before logging in. The router username and password are root/cubieboard.
3. Modify the Kernel (Ubuntu)
The current Linux kernel doesn’t support SPI drivers for Cubieboard2. First, download the linux-3.4 branch kernel source: https://github.com/linux-sunxi/linux-sunxi. Then add SPI support to the kernel (not recommended); for details, see http://blog.csdn.net/u010352603/article/details/51657265#. Instead, directly download a pre-modified version with SPI support from https://github.com/linanwx/cubieboard2-spi-support. After downloading, delete the drivers/spi directory in the kernel directory and place the downloaded spi folder in its place.
4. Compile the Kernel (Ubuntu)
Prepare an arm-linux-gnueabihf-gcc compiler, version not higher than 5.0.
In the kernel directory, using the terminal, enter:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun7i_defconfig
Enter:
gedit .config
Find the following options and change them to:
CONFIG_SPI=y
CONFIG_SUNXI_NAND_PARTITION=y
CONFIG_SUNXI_NAND=y
Enter the following command to start compiling the kernel:
make -j4ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage modules
Since CONFIG_SPI and CONFIG_SUNXI_NAND support are enabled, a series of settings are required. When you encounter spi support, CONFIG_SPI_SUN7I, CONFIG_SUN7I_SPI_NDMA, enter y; in other cases, press enter.
Check if the uImage file exists in the path arch/arm/boot
Enter the following command to generate kernel modules:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=output modules_install
Check if the output/lib/modules/3.4.104
directory has been created.
5. Replace the Kernel (Cubieboard2)
On Cubieboard2, open the terminal and enter:
mkdir /media/nanda
mount /dev/nanda /media/nanda
Copy the uImage to the /media/nanda
directory of the Cubieboard2 Debian system, overwriting the original kernel.
Use the tar command to pack the output/lib/modules/3.4.104
directory, transfer this file to Cubieboard2’s /lib/modules
directory, then extract it.
6. Check Compilation Success
Restart. If the system enters normally, it was successful.
7. Modify System Files to Enable SPI and Support Full-Duplex SPI (Cubieboard2)
Remount /dev/nanda
to the /media/nanda/
directory and enter it.
Enter the following commands:
bin2fex script.bin script.fex
nano script.fex
Find [spi0_para], change the line with “used” to 1, and add the following code:
[spi_devices]
spi_dev_num =1
[spi_board0]
modalias ="spidev"
max_speed_hz =100000
bus_num =0
chip_select =0
mode =0
full_duplex =1
manual_cs =0
Enter the following commands:
fex2bin script.fex script.bin
nano /usr/include/linux/spi/spidev.h
Add the following line above __u16 delay_usecs;
:
__u16 interbyte_usecs;
8. Test SPI
Restart Cubieboard2. Check if the spidev device exists in the /dev
path. Short-circuit pins 46 and 48; pin diagram available at http://docs.cubieboard.org/products/a10_cubieboard/expansion_ports
.
Create a new text file with the following code:
/*
* SPI testing utility (using spidev driver)
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev0.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;
static void transfer(int fd)
{
int ret;
uint8_t tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE(tx)] = {
0,
};
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret == 1)
pabort("can't send spi message");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++)
{
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-DsbdlHOLC3]\n", prog);
puts(" -D --device device to use (default /dev/spidev0.0)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -b --bpw bits per word \n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -3 --3wire SI/SO signals shared\n");
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1)
{
static const struct option lopts[] = {
{"device", 1, 0, 'D'},
{"speed", 1, 0, 's'},
{"delay", 1, 0, 'd'},
{"bpw", 1, 0, 'b'},
{"loop", 0, 0, 'l'},
{"cpha", 0, 0, 'H'},
{"cpol", 0, 0, 'O'},
{"lsb", 0, 0, 'L'},
{"cs-high", 0, 0, 'C'},
{"3wire", 0, 0, '3'},
{"no-cs", 0, 0, 'N'},
{"ready", 0, 0, 'R'},
{NULL, 0, 0, 0},
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
if (c == -1)
break;
switch (c)
{
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'b':
bits = atoi(optarg);
break;
case 'l':
mode |= SPI_LOOP;
break;
case 'H':
mode |= SPI_CPHA;
break;
case 'O':
mode |= SPI_CPOL;
break;
case 'L':
mode |= SPI_LSB_FIRST;
break;
case 'C':
mode |= SPI_CS_HIGH;
break;
case '3':
mode |= SPI_3WIRE;
break;
case 'N':
mode |= SPI_NO_CS;
break;
case 'R':
mode |= SPI_READY;
break;
default:
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
parse_opts(argc, argv);
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
printf("spi mode: %d\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
transfer(fd);
close(fd);
return ret;
}
Compile this file and run it. If the output looks like the following, the modification was successful:
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D