SPI驱动主模式初步实现
SPI接口技术是一种高速,高效的串行接口技术,因而SPI设备在数据通信中应用十分方便。设备驱动程序作为操作系统内核和硬件之间的接口,是嵌入式开发的重要组成部分,针对TI的davinci芯片DM6467和嵌入式Linux操作系统构建的开发平台,根据DM6467的SPI接口特性,及接口电路的连接特点说明了SPI设备驱动程序的基本开发方法和动态模块加载实现过程。
硬件连线采用最简单的3线模式,即只使用3根线:SIMO,SOMI,clock。
SPI设置为主模式,直接访问从设备。不需要写出总线模式,根据从设备的要求直接发送命令即可。
本例程实现了一次发送一字节的程序,演示了SPI主模式的驱动过程,及相应应用程序的访问过程。
//////////////////////////////////////////////////////////////////////////////
//SPI主模式驱动程序源码:
//先包括必要的头文件
#include <linux/config.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>
//设置SPI的设备名与主设备号,注意避开那些已经使用的主设备号
#define DEVICE_NAME "SPI"//设备名
#define SPI_MAJOR 240//主设备号
//指定SPI寄存器的基地址
#define DAVINCI_SPI_BASE (0x01C66800)
//定义SPI寄存器结构体
struct davinci_spi_reg {
volatile __u32 __bitwise SPIGCR0;
volatile __u32 __bitwise SPIGCR1;
volatile __u32 __bitwise SPIINT;
volatile __u32 __bitwise SPILVL;
volatile __u32 __bitwise SPIFLG;
volatile __u32 __bitwise SPIPC0;
volatile __u32 __bitwise SPIPC1;
volatile __u32 __bitwise SPIPC2;
volatile __u32 __bitwise SPIPC3;
volatile __u32 __bitwise SPIPC4;
volatile __u32 __bitwise SPIPC5;
volatile __u32 __bitwise SPIPC6;
volatile __u32 __bitwise SPIPC7;
volatile __u32 __bitwise SPIPC8;
volatile __u32 __bitwise SPIDAT0;
volatile __u32 __bitwise SPIDAT1;
volatile __u32 __bitwise SPIBUF;
volatile __u32 __bitwise SPIEMU;
volatile __u32 __bitwise SPIDELAY;
volatile __u32 __bitwise SPIDEF;
volatile __u32 __bitwise SPIFMT[4];
volatile __u32 __bitwise TGINTVEC[2];
volatile __u8 __bitwise RSVD0[8];
volatile __u32 __bitwise MIBSPIE;
};
struct davinci_spi_reg *spi_reg_davinci;
int spidat1;
static char dataTx[2]="";
static char dataRx[2]="";
static int davinci_spi_close(struct inode *inode,struct file *filp)
{
printk("davinci-spi closed\n");
return 0;
}
static int davinci_spi_open(struct inode *inode,struct file *filp)
{ int config;
int value;
int i;
printk("spi open begin..\n");
// 1,Reset SPI
spi_reg_davinci->SPIGCR0 = 0;
for(i=0;i<1000;i++);//进行必要的等待
// 2,Release SPI
spi_reg_davinci->SPIGCR0 = 1;
// 3,master or slave mode setup
spi_reg_davinci->SPIGCR1 = 0
| ( 0 << 24 )
| ( 0 << 16 )//loopback mode default :0 test: 1 //在最初没有从设备的调试阶段,可以设置为 1 。
| ( 1 << 1 )
| ( 1 << 0 );
//4,Enable the SPI_SIMO, SPI_SOMI, and SPI_CLK pins and the necessary chip select pins
spi_reg_davinci->SPIPC0 = 0
| ( 1 << 11 ) // DI
| ( 1 << 10 ) // DO
| ( 1 << 9 ) // CLK
| ( 0 << 1 ) // EN1
| ( 0 << 0 ); // EN0
//5,Configure the desired data format
spi_reg_davinci->SPIFMT[0] = 0
| ( 0 << 20 ) // SHIFTDIR
| ( 0 << 17 ) // Polarity
| ( 1 << 16 ) // Phase
| ( 50 << 8 ) // Prescale
| ( 8 << 0 ); // Char Len
//6,Select the preconfigured data format
//7,using SPI in 4-pin mode with SPI_CS,configue hold time,default chip select pin value
//8,4-pin mode with spi_en
//9,5-pin mode
spidat1 = 0
| ( 1 << 28 ) // CSHOLD
| ( 0 << 24 ) // Format [0]
| ( 3 << 16 ) // CSNR [0 both, 1 CS1, 2 only CS0 enbled , 3 none]
| ( 0 << 0 ); //
spi_reg_davinci->SPIDAT1 = spidat1;
spi_reg_davinci->SPIDELAY = 0
| ( 8 << 24 ) // C2TDELAY
| ( 8 << 16 ); // T2CDELAY
spi_reg_davinci->SPIDEF = 0
| ( 1 << 1 ) // EN1 inactive high
| ( 1 << 0 ); // EN0 inactive high
//10. Enable the desired interrupts
spi_reg_davinci->SPIINT = 0
| ( 0 << 16 ) //
| ( 0 << 8 ) //
| ( 0 << 6 ) //
| ( 0 << 4 ); //
//11,select interrupt level
spi_reg_davinci->SPILVL = 0
| ( 0 << 8 ) // EN0
| ( 0 << 6 ) // EN0
| ( 0 << 4 ); // EN0
//12, Enable SPI
spi_reg_davinci->SPIGCR1 |= ( 1 << 24 );
//13. If using the EDMA to perform the transfers, setup and enable the EDMA channels for transmit or receive and then set the DMAREQEN bit in SPIINT.
//14. Data is ready to be transferred using the CPU or EDMA by writing to SPIDAT1.
printk("Open spi successfully\n");
return 0;
}
static ssize_t davinci_spi_write(struct file *filp, char __user *buf,size_t count,loff_t *f_ops)
{
int spcon;
char i=0;
char len=0;
//printk("in davinci_spi_write: count is %d\n",count);
//将用户空间的数据搬移到内核空间
copy_from_user(dataTx,buf,count);//sizeof(dataTx));
//dataTx[0]=0x85;//just for test
/* Clear any old data */
spi_reg_davinci->SPIBUF;
/* SPI access cycle */
/* Wait for transmit ready */
while ( spi_reg_davinci->SPIBUF & 0x10000000 );
//发送一字节
spi_reg_davinci->SPIDAT1 = spidat1 | (int)(dataTx[i]);
/* Wait for receive data ready */
while ( spi_reg_davinci->SPIBUF & 0x80000000 );
/* Read 1 byte */
dataRx[i] = (spi_reg_davinci->SPIBUF)&0xff;
copy_to_user(buf,dataRx,count);//sizeof(dataTx));
//printk("copy_to_user is %d\n",dataRx[0]);
return 0;
}
//填充file_operations结构体
static struct file_operations davinci_z_spi_fops =
{
.owner = THIS_MODULE,
.open = davinci_spi_open,
.write = davinci_spi_write,
.release = davinci_spi_close,
};
static int __init dev_init(void)
{
int ret;
spi_reg_davinci = (struct davinci_spi_reg *)ioremap( DAVINCI_SPI_BASE ,200);
ret = register_chrdev(SPI_MAJOR,DEVICE_NAME,&davinci_z_spi_fops);//设备注册
if(ret < 0)
{
printk(DEVICE_NAME "can't get major number\n");
return ret;
}
devfs_mk_cdev(MKDEV(SPI_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
printk (DEVICE_NAME"\tinitialized\n");
return 0;
}
static void __exit dev_exit(void)
{
devfs_remove(DEVICE_NAME);
unregister_chrdev(SPI_MAJOR, DEVICE_NAME);
printk("Good-bye, SPI module was removed!\n");
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lintax ");
///////////////////// end ////////////////////////////////////////////////////
配置Makefile
//////////////////////////////////////////////////////////////////////////////
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /opt/ti-davinci_bt1120/linux-2.6.10_mvl401_LSP_01_30_00_082
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
echo "before modules"
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
echo "modules"
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
echo "modules_install"
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
echo "clean"
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := spi_master_mod.o
#echo "else"
endif
///////////////////// end ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//SPI 主模式通讯应用程序
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
int main()
{
int fd;
char data_buf[10];
int send_num,recv_num;
send_num = recv_num = 0;
memset(data_buf, 0, sizeof(data_buf));
printf("spi master start:\n");
fd = open("/dev/SPI", O_RDWR);
if (fd<0)
{
perror("Can't Open SPI Port");
return -1;
}
sleep(1);
while(1)
{
if(send_num%10==0)
printf("before write is %d\n",data_buf[0]);
write(fd, data_buf, 1);//sizeof(data_buf));
if(send_num%10==0)
printf("after write is %d\n",data_buf[0]);
usleep(2000);
if(data_buf[0] == data_buf[1]+1)//0x55)//此处注意,本次收到的数,是否应该是当前值加2。spi的交互机制,需要多想一想,是否真的实现了同步通讯。
{
recv_num++;
printf("send num is %d, recv %d\n",send_num,recv_num);
}
if(send_num<5000000)
send_num++;
else
break;
data_buf[0] = send_num%128;
data_buf[1] = data_buf[0];
}
return 0;
}
//////////////////////////// end //////////////////////////////////////////////////
1,分别编译
配置好内核路径,以及交叉编译器路径,几次make即可。
2,加载模块
insmod spi_master_mod.ko
3,创建设备节点
mknod /dev/SPI c 200 0
4,open设备,操作
进入运行路径,执行应用程序,例如:./spi_master
注意,程序运行需要从设备的配合,从设备驱动例程将在下一篇文章中给出。
在没有从设备时要进行初步的测试,可以启动spi的环路测试模式,即自发自收,这样发出的数据就是接收的数据,对应的寄存器是 spi_reg_davinci->SPIGCR1 ,不过这样一来,应用程序也需要相应改动一下了,相信对读者朋友来说,这都是小菜一碟了。