Firefly-RK3128 Buy Specs

Quad-core Cortex-A7 open source board,frequency up to 1.3GHz, integrated Mali-400 MP2 graphics processors, support OpenGL ES1.1/2.0, embedded high-performance 2D hardware acceleration, and can achieve 1080P H.265 video decoding and H.264 video coding.

¬¬

SPI

Update time:2018-04-17 Views:2961

SPI is a high-speed, full-duplex, synchronous serial communication interface for connection to a microcontroller, sensors, storage devices, etc. In this paper, let's take the ingerprint recognition module for example to introduce the use of SPI.

    SPI working mode

    SPI work in master-slave mode, This usually has a master device and one or more slave devices. They need at least four lines, which are:

    CS		Chip select signals
    SCLK		Clock signal
    MOSI		Master data output, and Slave data input
    MISO		Master data input, and Slave data output

    Linux kernel using a combination of CPOL and CPHA to represent the SPI's four working modes:

    CPOL=0,CPHA=0		SPI_MODE_0
    CPOL=0,CPHA=1		SPI_MODE_1
    CPOL=1,CPHA=0		SPI_MODE_2
    CPOL=1,CPHA=1		SPI_MODE_3

    CPOL:Represents the initial level of the state of the clock signal,0 is low,1 is high.
    CPHA:Represents the sampling clock edge, 0 is leading edge, 1 is trailing edge.
    SPI waveforms of four working modes as follows:
    SPI work en.jpg

    Add your own driver file in kernel

    Create a new file in the kernel source directory: kernel/drivers/spi/ for example spi-rockchip-firefly.c
    Add the corresponding driver configuration file In the directory where the driver files Kconfig:

    @@ -525,6 +525,10 @@ config SPI_ROCKCHIP_TEST
           bool "ROCKCHIP spi test code"
           depends on SPI_ROCKCHIP
    
    +config SPI_ROCKCHIP_FIREFLY
    +       bool "ROCKCHIP spi firefly code"
    +       depends on SPI_ROCKCHIP
    +
    #
    # There are lots of SPI device types, with sensors and memory
    # being probably the most widely used ones.

    Add the driver file name in the Makefile such as:

    +obj-$(CONFIG_SPI_ROCKCHIP_FIREFLY) += spi-rockchip-firefly.o

    Select the driver files to add to the kernel options using make menuconfig

    There is no help available for this option.                                                                                             
     │ Symbol: SPI_ROCKCHIP_FIREFLY [=y]                                                                                                       
     │ Type  : boolean                                                                                                                         
     │ Prompt: ROCKCHIP spi firefly code                                                                                                                
     │   Location:                                                                                                                             
     │     -> Device Drivers                                                                                                                   
     │       -> SPI support (SPI [=y])                                                                                                         
     │         -> ROCKCHIP SPI controller core support (SPI_ROCKCHIP_CORE [=y])                                                                
     │           -> ROCKCHIP SPI interface driver (SPI_ROCKCHIP [=y])                                                                          
     │   Defined at drivers/spi/Kconfig:528                                                                                                    
     │   Depends on: SPI [=y] && SPI_MASTER [=y] && SPI_ROCKCHIP [=y]

    Define and register SPI devices

    Add SPI driver node description in DTS, such as: kernel/arch/arm/boot/dts/rk3128-fireprime.dts

    &spi0 {
             status = "okay";                                                                                                    
             max-freq = <24000000>;
             spidev@00 {                                                                                                         
                     compatible = "rockchip,spi_firefly";
                     reg = <0x00>;
                     spi-max-frequency = <14000000>;
                     spi-cpha = <1>;
                     //spi-cpol = <1>;
             };
    };

    Status: If you want to enable SPI, then set "okay", if not enabled, set "disable".
    spidev@00:Because this example uses the SPI0, and the use of CS0, so here is set to 00, if you use CS1, is set to 01.
    Compatible:This property must be consistent with the structure of_device_id members.
    Reg: Make this 0x00, consistent with spidev@00.
    spi-max-frequency: the highest frequency of SPI.
    spi-cpha,spi-cpol: Set the SPI work mode here, The SPI module in this example work in SPI_MODE_1, so we set spi-cpha = <1>. You should mask this two, if your SPI module work in SPI_MODE0. You should set spi-cpha = <1>;spi-cpol = <1>, if your SPI module work in SPI_MODE3.

    Define and register SPI driver

    Define SPI driver

    Before defining the SPI driver, you need to define the of_device_id first.
    of_device_id is defined to match the corresponding node in the dts file:

    static const struct of_device_id spidev_dt_ids[] = {                                                                                       
            { .compatible = "rockchip,spi_firefly" },
            {},
    };

    Define the spi_driver:

    static struct spi_driver spidev_spi_driver = {
              .driver = {
                    .name =         "silead_fp",
                    .owner =        THIS_MODULE,
                    .of_match_table = of_match_ptr(spidev_dt_ids),
            },
            .probe =        spi_gsl_probe,                                                                                                     
            .remove =       spi_gsl_remove,
    };

    Register SPI driver

    Create a character device in initialization function spidev_init(void):

    alloc_chrdev_region(&devno, 0,255, "sileadfp");

    Add the device to kernel:

    spidev_major = MAJOR(devno);                                                                                                        
    cdev_init(&spicdev, &spidev_fops);
    spicdev.owner = THIS_MODULE;
    status = cdev_add(&spicdev,MKDEV(spidev_major, 0),N_SPI_MINORS);

    Create a device class:

    class_create(THIS_MODULE, "spidev");

    Register SPI driver to the kernel:

    spi_register_driver(&spidev_spi_driver);

    If it match Successful when kernel start up, it will call the driver's probe function.Probe function as following:

    static int spi_gsl_probe(struct spi_device *spi)
    {
    	struct spidev_data	*spidev;
    	int					status;
    	unsigned long		minor;
    	struct gsl_fp_data  *fp_data;
    
    	printk("===============spi_gsl_probe ==============\n");
    	if(!spi)	
    		return -ENOMEM;
    	
    	/* Allocate driver data */
    	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
    	if (!spidev)
    		return -ENOMEM;
    		
    	/* Initialize the driver data */
    	spidev->spi = spi;
    	spin_lock_init(&spidev->spi_lock);//Initialize spin lock
    	mutex_init(&spidev->buf_lock);//Initialize the mutex
    	INIT_LIST_HEAD(&spidev->device_entry);//Initialize the device l ist
    
    	//init fp_data
    	fp_data = kzalloc(sizeof(struct gsl_fp_data), GFP_KERNEL);
    	if(fp_data == NULL){
    		status = -ENOMEM;
    		return status;
    	}
    	//set fp_data struct value
    	fp_data->spidev = spidev;
    	
    	mutex_lock(&device_list_lock);//open the mutex
    	minor = find_first_zero_bit(minors, N_SPI_MINORS);//Find the first value of 0 in the memory area
    	if (minor < N_SPI_MINORS) {
    		struct device *dev;
    		spidev->devt = MKDEV(spidev_major, minor);
    		dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "silead_fp_dev");//Create device nodes in /dev/
    		status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
    	} else {
    		dev_dbg(&spi->dev, "no minor number available!\n");
    		status = -ENODEV;
    	}
    	if (status == 0) {
    		set_bit(minor, minors);
    		list_add(&spidev->device_entry, &device_list);//Added to the device list
    	}
    	mutex_unlock(&device_list_lock);//close the mutex
    	if (status == 0)
    		spi_set_drvdata(spi, spidev);
    	else
    		kfree(spidev);
    
    	printk("%s:name=%s,bus_num=%d,cs=%d,mode=%d,speed=%d\n",__func__,spi->modalias, spi->master->bus_num, spi->chip_select, spi->mode,
    spi->max_speed_hz);//print SPI message
    
    	return status;
    }

    You can see your driver name in the / dev / directory, and to see your driver device class in sys / class / directory, if you register SPI drive successfully.

    SPI read and write data process

    SPI write data

    static ssize_t spidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
    {
            struct spidev_data      *spidev;
            ssize_t                 status = 0;
            unsigned long           missing;
            if (count > bufsiz)
                    return -EMSGSIZE;
    
            spidev = filp->private_data;
    
            mutex_lock(&spidev->buf_lock);
            missing = copy_from_user(spidev->buffer, buf, count);//copying data from user space to kernel space
            if (missing == 0) {
                    status = spidev_sync_write(spidev, count);//Write synchronization function calls
            } else
                    status = -EFAULT;
            mutex_unlock(&spidev->buf_lock);
    
            return status;                                                                                                                      
    }

    Write synchronization function:

    spidev_sync_write(struct spidev_data *spidev, size_t len)                                                                                  
    {
            struct spi_transfer     t = {
                            .tx_buf         = spidev->buffer,//Send buffer
                            .len            = len,//Send data length
                    };
            struct spi_message      m;
            spi_message_init(&m);//initialize spi_message
            spi_message_add_tail(&t, &m);//add the new spi_transfer to the tail of spi_message
            return spidev_sync(spidev, &m);//Synchronous
    }

    SPI read data

    The process of reading data in this example SPI module is:

    1. Host write register address and read the instructions to the module

    2. SPI module send the data to host in the form of page, after it receive the reading instruction

    3. Host Set the read mode

    4. The host reads data and save

      static ssize_t
    spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    {
    	struct spidev_data	*spidev;
    	int			status = 0;
    	int 		i = 0;
    
    	spidev = filp->private_data;
    	mutex_lock(&spidev->buf_lock);
    	
    	gsl_fp_write(spidev, 0x00, 0xf0);//Host write register address and read the instructions to the module
    	while(1){ 
    		for(i=0;i <= 110*118/128/read_pages;i++){
    			status = gsl_fp_getOneFrame(spidev,0x00);//read data
    		}
    		pos = 0;
    		break;
    	}
    	
    	if(status > 0){
    		printk("gsl read data success!!!\n");
    	}else{
    		printk("gsl read data failed!!!");
    	}
    	
    	mutex_unlock(&spidev->buf_lock);
    	return status;
    }
    static inline unsigned int
    gsl_fp_getOneFrame(struct spidev_data *spidev,unsigned char reg_8b)
    {
    	int status,i;
    	unsigned char buf_d[128*1+3] = {0x00,0x00};
    	struct spi_transfer t;
    	t.tx_buf = buf_d;
    	t.rx_buf = buf_d;
    	t.len = 131;
    
    	status = gsl_spidev_sync_read(spidev, &t);
    	
    	if (status > 0){
    		
    		for(i=0;i<128*read_pages;i++)
    			kmalloc_area[pos++] = buf_d[i+3];
    	}
    	
    	return status;
    }
    static inline ssize_t 
    gsl_spidev_sync_read(struct spidev_data *spidev,struct spi_transfer *t)
    {
    	struct spi_message m;
    	spi_message_init(&m);
    
    	t->bits_per_word = 8;//read data length is 8
    	t->delay_usecs = 1;//Read delay
    	t->speed_hz = 14*1000*1000;//Read rate
    	t->cs_change = 1;//CS pin change
    	
    	spi_message_add_tail(t, &m);
    	return spidev_sync(spidev, &m);
    }

    WARNING:Firefly's SPI driver is generic driver under Linux, you can refer to the source kernel/drivers/spi/spidev.c