SPI驱动从模式初步实现

2019-07-13 04:46发布

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通讯了。