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 ,不过这样一来,应用程序也需要相应改动一下了,相信对读者朋友来说,这都是小菜一碟了。

 

Logo

更多推荐