Xenomai RT SPI Driver for Blackfin
April 14th, 2007I am working on a Blackfin uClinux hardware project that requires a real-time SPI driver. I know that the RTDM community is looking to create profiles for various devices, but to my knowledge, nothing has been done for SPI controllers yet.
This driver is targeted at the Blackfin 537 running Xenomai 2.3.1. I have made some effort to separate the hardware specific bits from the main interface, but that was not my primary purpose. Also, the driver is targeted at custom embedded hardware where the definitions for each slave select are known at build time and is driven by actual board layout. The driver could be extended to allow configuration of each SPI slave via ioctls, but this was not a priority for me. The slave select configuration is in the device_config.inc file which defines an array of structures specifying the characteristics for each supported slave select. Slave selects are numbered from zero in the same order as listed in the device_config.inc file. Each slave select is exposed through RTDM as a device with a name of “rtspi%d”, where %d is the slave select number as listed in device_config.inc.
For the Blackfin, two types of slave selects are supported: “Native” and “GPIO”. Native slave selects map to bits in the SPI_FLG register and are listed on the spec sheets as SLAVE SELECTS. GPIO slave selects allow any available GPIO port to be controlled by the driver as a slave select. Since all native slave selects can also be GPIO pins, the entire driver could be configured solely with GPIO slave selects if desired.
SPI is a bidirectional protocol where sending and receiving is done simultaneously. The driver supports this by requiring that a call to “write” will initiate a SPI transaction and will also store the received results in a buffer which can subsequently be accessed through a call to “read”. On the Blackfin a single DMA channel is available for the SPI controller. The DMA can operate in either send or receive mode but not both. Because of this disparity, the driver does not use the DMA, which can be appropriate for typical, small SPI transactions. The driver could conceivably be extended with IOCTLS that could activate the DMA when a large one-way transfer is to take place.
This code is GPL’d and is freely available for anyone to use and extend. The current version at the time of this writing can be downloaded from http://www.rcode.net/blogattach/dist/rtspi-20070414.tgz. If there is any interest, I can post the code in a public SVN repository. Otherwise, recent versions can be obtained by emailing me.
In addition, I typically use the following program “rttran” to work with the SPI peripherals interactively:
/** rttran - Real-time copy.
* Provides a non-realtime -> realtime bridge to write data
* to real-time devices.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <native/task.h>
#include <rtdm/rtdm.h>
int process(int rt_fd, char** args) {
unsigned char *tx_buffer, *rx_buffer;
int buffer_size=0;
int loc, len;
int written;
char** cur;
// Count
cur=args;
while (*cur) {
buffer_size++;
cur++;
}
// Allocate buffers
tx_buffer=malloc(buffer_size);
rx_buffer=malloc(buffer_size);
cur=args;
loc=0;
while (*cur) {
tx_buffer[loc++]=(unsigned char)strtoul(*cur, NULL, 16);
cur++;
}
printf("Writing %d bytes: ", buffer_size);
for (loc=0; loc<buffer_size; loc++) {
printf(" %x", (unsigned int)tx_buffer[loc]);
}
printf("n");
written=rt_dev_write(rt_fd, tx_buffer, buffer_size);
printf("rt_dev_write wrote %d bytesn", written);
// Now read
len=rt_dev_read(rt_fd, rx_buffer, buffer_size);
printf("rt_dev_read read %d bytes: ", len);
for (loc=0; loc<len; loc++) {
printf(" %x", (unsigned int)rx_buffer[loc]);
}
printf("n");
return 0;
}
RT_TASK task;
int rt_fd=-1;
static void catch_interrupt(int sig_num)
{
fprintf(stderr, "CTRL-C caughtn");
if (rt_fd!=-1) rt_dev_close(rt_fd);
exit(0);
}
int main(int argc, char** argv) {
int ret=-1;
char* device_name;
if (argc<2) {
printf("Usage: rttran target_dev [hex bytes] n");
return -1;
}
signal(SIGINT, catch_interrupt);
device_name=argv[1];
ret=rt_task_shadow(&task, "rttran", 1, 0);
if (ret) {
fprintf(stderr, "Cannot shadow task: %dn", ret);
return -3;
}
/* Open the real-time device */
rt_fd=rt_dev_open(argv[1], 0);
if (rt_fd<0) {
fprintf(stderr, "Cannot open target device %sn", argv[1]);
return -3;
}
/* Do the processing */
ret=process(rt_fd, argv+2);
if (ret) {
fprintf(stderr, "Error writing to device: %dn", ret);
}
if (rt_fd>=0) rt_dev_close(rt_fd);
return 0;
}
An example interaction with this program and a TMP124 temperature sensor connected to slave select 0 in the driver follows:
% rttran rtspi0 0x0 0x0
Writing 2 bytes: 0 0
rt_dev_write wrote 2 bytes
rt_dev_read read 2 bytes: d 7
Xenomai 2.3.0 on Ubuntu 6.10
March 3rd, 2007I have been developing with Xenomai on a real-time embedded project and just got around to getting my desktop system rigged up with Adeos and Xenomai. I had been primarily doing driver development on the embedded platform, so I didn’t need Xenomai on my PC. However, I am now working on more of the application layer and would really like to build first on the PC and then port to the embedded platform.
The first step is getting a Ubuntu kernel built with Xenomai/Adeos. I had some trouble getting the patched Ubuntu sources to take the Adeos patch without freezing during the boot process, so I went with vanilla 2.6.19 sources from Kernel.org and rolled them into a Debian package.
Step 1. Get pre-requisite packages
apt-get install build-essential
apt-get install kernel-package
apt-get install gcc
apt-get install libncurses5
apt-get install libncurses5-dev
(additional packages may be needed, but most will be available on a developer’s workstation)
Step 2. Download Sources
Download Xenomai 2.3.0, the Linux Kernel 2.6.19.7. Also grab the up-to-date Adeos patch for 2.6.19 from the Adeos site.
Step 3. Extract Files
Create a directory (as root) /usr/src/xenomai-kernel and extract the kernel and xenomai sources into this directory.
Step 4. Apply the Adeos Patch
cd /usr/src/xenomai-kernel/linux-2.6.19.7
patch -p1 < ../adeos-ipipe-2.6.19-i386-1.7-03.patch
Step 5. Add Xenomai to the Kernel
cd /usr/src/xenomai-kernel/xenomai-2.3.0
scripts/prepare-kernel.sh --linux=../linux-2.6.19.7 --arch=i386
Step 6. Configure Kernel
There are a number of configuration gotchas that are not all documented very well. What documentation does exist can be found in the Xenomai FAQ.
I found the following issues particular troublesome:
- Disable Frequency Scaling
- Disable HPET (there are two settings for this on different screens - grep the .config file for HPET if in doubt)
- Select a processor that is Pentium Classic or greater to get TSC support (user-land programs will complain loudly if not present). In particular, 586/K5/5×86/6×86/6×86MX is a less than ideal processor choice since it does not provide TSC.
The config that I used can be downloaded here. Copy it into the Linux source directory as “.config” and then run make menuconfig to customize.
Step 7. Build Debian Kernel Packages
From the Linux kernel directory run:
make-kpkg --initrd kernel_image kernel_headers
The two debs (one for the headers and one for the image) will be deposited in the parent directory.
Step 8. Install Packages
dpkg -i linux-image-2.6.19.7-adeos-xenomai2.3.0_2.6.19.7-adeos-xenomai2.3.0-10.00.Custom_i386.deb
dpkg -i linux-headers-2.6.19.7-adeos-xenomai2.3.0_2.6.19.7-adeos-xenomai2.3.0-10.00.Custom_i386.deb
I then edited /boot/grub/menu.lst to remove the quiet and splashscreen parameters from this kernel (so that I could see console output).
Step 9. Install Third Party Modules
After booting into the new kernel, I had to re-install the NVidia drivers. For this I use Albert Milone’s Envy script. If you have that installed and need to update the ATI or NVidia drivers, just run “envy” after your X Server fails to start. Follow the prompts (elect to NOT have it modify the Xorg.conf file) and you’ll be set to go.
I’m not sure that I would install Kernel Debs from an unknown source, but for the less scrupulous out there, here are the debs that I created from following this procedure:
-
linux-headers-2.6.19.7-adeos-xenomai2.3.0_2.6.19.7-adeos-xenomai2.3.0-10.00.Custom_i386.deb
-
linux-image-2.6.19.7-adeos-xenomai2.3.0_2.6.19.7-adeos-xenomai2.3.0-10.00.Custom_i386.deb
These debs were built with the previously referenced configuration and could be considered a kitchen-sink build. The kernel targets an SMP Pentium Classic with lots of drivers available as modules.