SPI驱动从模式初步实现
SPI接口技术是一种高速,高效的串行接口技术,因而SPI设备在数据通信中应用十分方便。设备驱动程序作为操作系统内核和硬件之间的接口,是嵌入式开发的重要组成部分,针对TI的davinci芯片DM6467和嵌入式Linux操作系统构建的开发平台,根据DM6467的SPI接口特性,及接口电路的连接特点说明了SPI设备驱动程序的基本开发方法和动态模块加载实现过程。
硬件连线采用最简单的3线模式,即只使用3根线:SIMO,SOMI,clock。
SPI设置为从模式,模拟从设备。根据主设备的请求进行相应的回馈即可。
本例程实现了一次返回一字节的程序,演示了SPI从模式的驱动过程,及相应应用程序的访问过程。
////////////////////////////////////////////////////////////////////////////////////////
//SPI从模式驱动程序源码:
//先包括必要的头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//设置SPI的设备名与主设备号,注意避开那些已经使用的主设备号
#define DEVICE_NAME "SPI" //设备名
#define SPI_MAJOR 240 //主设备号
#define SPI_MINOR 1 //次设备号
//指定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
");
return 0;
}
static int davinci_spi_open(struct inode *inode,struct file *filp)
{ int config;
int value;
int i;
printk("spi open begin..
");
// 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
| ( 0 << 1 )
| ( 0 << 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
");
return 0;
}
static ssize_t davinci_spi_read(struct file *filp, char __user *buf,size_t count,loff_t *f_ops)
{
int spcon;
char i=0;
char len=0;
char data_next=0;
//将用户空间的数据搬移到内核空间
copy_from_user(dataTx,buf,count);//sizeof(dataTx));
/* Clear any old data */
// spi_reg_davinci->SPIBUF;
/* SPI access cycle */
/* Wait for receive data ready */
while ( spi_reg_davinci->SPIBUF & 0x80000000 );
/* Read 1 byte */
dataRx[0] = spi_reg_davinci->SPIBUF;
/* Wait for transmit ready */
//仅仅是测试,将收到的数据加2,再送至发送缓冲中
data_next = dataRx[0] + 2;
spi_reg_davinci->SPIDAT1 = spidat1 | (int)(data_next);//(dataTx[i]);
copy_to_user(buf,dataRx,count);//sizeof(dataTx));
return 0;
}
//填充file_operations结构体
static struct file_operations davinci_z_spi_fops =
{
.owner = THIS_MODULE,
.open = davinci_spi_open,
.read = davinci_spi_read,
.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
");
return ret;
}
devfs_mk_cdev(MKDEV(SPI_MAJOR, SPI_MINOR), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
printk (DEVICE_NAME" initialized
");
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!
");
}
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_slave_mod.o
#echo "else"
endif
///////////////////// end ////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
//SPI 从模式通讯应用程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 slave start:
");
fd = open("/dev/SPI_slave", O_RDWR);
if (fd<0)
{
perror("Can't Open SPI Port");
return -1;
}
sleep(1);
while(1)
{
if(send_num%10==0)
printf("before read is %d
",data_buf[0]);
read(fd, data_buf, 1);//sizeof(data_buf));
if(send_num%10==0)
printf("after read is %d
",data_buf[0]);
usleep(2000);
}
return 0;
}
////////////////////// end /////////////////////////////////////////////////////////////
1,分别编译
配置好内核路径,以及交叉编译器路径,几次make即可。
2,加载模块
insmod spi_slave_mod.ko
创建设备节点
mknod /dev/SPI_slave c 200 1
3,open设备,操作
进入运行路径,执行应用程序,例如:./spi_slave
注意,本程序需要与上一篇的主设备程序配合才能正常运行。
本文的从模式程序,与上一篇的主模式程序,分别在两颗DM6467上运行。两颗DM6467,其硬件连线采用最简单的3线模式,即只使用3根线:SIMO,SOMI,clock。
当一起配合运行时,就能实现基本的spi通讯了。