资讯详情

firefly-rk3288j开发板--linux spi实验之flash驱动

firefly-rk3288j开发板–linux spi实验之flash驱动

1 准备工作

开发板:aio-rk3288j SDK版本:rk3288_linux_release_20210304 下载工具:Linux_Upgrade_Tool_v2.1 内核版本:4.4.194 文件系统:buildroot Ubuntu版本:18.04 交叉编译工具:gcc version 6.3.1 20170404

2 硬件原理图

2.1 开发板SPI接口

在这里插入图片描述

2.2 w25q64模块原理图

3 SPI使用

SPI 它是一种连接微控制器、传感器、存储设备等的高速、全双工、同步串行通信接口。W25Q例如,简要介绍64模块 SPI 使用。

SPI 这种模式通常有一个主设备和一个或多个从设备,至少需要 4 根线分别为: CS 片选信号 SCLK 时钟信号 MOSI 主设备数据输出,设备数据输入 MISO 输入主设备数据,输出设备数据

Linux 内核用 CPOL 和 CPHA 组合表示当前 SPI 四种工作模式: 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

SPI的CPOL,表示当SCLK空闲idle电平值为低电平0或高电平1:CPOL=0,时钟空闲idle当时的电平是低电平,所以当SCLK有效时,即高电平,即所谓active-high。 CPOL=1,时钟空闲idle当时的电平是高电平,所以当SCLK有效时,即低电平,即所谓active-low。

CPHA=0,表示第一个边缘: 对于CPOL=0,idle当时是低电平,第一边是从低到高,所以是上升边; 对于CPOL=1,idle当时是高电平,第一个边缘是从高到低,所以是下降边缘; CPHA=1.表示第二个边缘: 对于CPOL=0,idle当时是低电平,第二个边缘是从高到低,所以是下降边缘; 对于CPOL=1,idle当时是高电平,第一边是从低到高,所以是上升边;

4 API函数

spi_alloc_master 应用函数 spi_master struct spi_master *spi_alloc_master(struct device *dev, unsigned size) 注册函数 int spi_register_master(struct spi_master *master) 注销函数 void spi_unregister_master(struct spi_master *master)

5 DTS配置

设备树文件位于核心kernel/arch/arm/boot/dts目录下,我们需要打开rk3288.dtsi、rk3288-linux.dtsi、rk3288-firefly-port.dtsi、rk3288-firefly-aio.dtsi.d打开rk3288-firefly-aio.dtsi文件,添加spi设备节点:

&spi2 { 
          status = "okay";  w25q64: w25q64@0{ 
           compatible = "firefly,w25q64";   spi-max-frequency = <24000000>;   reg = <0>;   wp-gpio = <&gpio7 2 GPIO_ACTIVE_HIGH>;   /*cs-gpios = <&gpio8 7 GPIO_ACTIVE_LOW>;*/    pinctrl-names = "default";   pinctrl-0 = <&w25q64_wp>; /*<&w25q64_cs>; */   spi-cpha;   spi-cpol;  }; };  &pinctrl { 
          w25q64 { 
           /*w25q64_cs: w25q64-cs { rockchip,pins = <8 7 RK_FUNC_GPIO &pcfg_pull_up>; };*/   w25q64_wp: w25q64-wp { 
        
			rockchip,pins = <7 2 RK_FUNC_GPIO &pcfg_pull_up>;
		};
	};
}

编译内核,输入如下命令 ./build.sh kernel ./build.sh updateimg

6 W25Q64驱动编写

#include <linux/module.h>//模块加载卸载函数
#include <linux/kernel.h>//内核头文件
#include <linux/types.h>//数据类型定义
#include <linux/fs.h>//file_operations结构体
#include <linux/device.h>//class_create等函数
#include <linux/ioctl.h>
#include <linux/kernel.h>/*包含printk等操作函数*/
#include <linux/of.h>/*设备树操作相关的函数*/
#include <linux/gpio.h>/*gpio接口函数*/
#include <linux/of_gpio.h>
#include <linux/platform_device.h>/*platform device*/
#include <linux/spi/spi.h> /*spi相关api*/
#include <linux/delay.h> /*内核延时函数*/
#include <linux/slab.h> /*kmalloc、kfree函数*/
#include <linux/cdev.h>/*cdev_init cdev_add等函数*/
#include <asm/gpio.h>/*gpio接口函数*/
#include <asm/uaccess.h>/*__copy_from_user 接口函数*/

#define DEVICE_NAME "spi"
#define W25Qxx_PAGE_SIZE 256 /*页 大小256字节*/
#define W25QXX_SECTOR_SIZE 4096 /*扇区 大小4096*/

/*W25Qxx 指令*/
#define W25X_WriteEnable 0x06 
#define W25X_WriteDisable 0x04 
#define W25X_ReadStatusReg 0x05 
#define W25X_WriteStatusReg 0x01 
#define W25X_ReadData 0x03 
#define W25X_FastReadData 0x0B 
#define W25X_FastReadDual 0x3B 
#define W25X_PageProgram 0x02 
#define W25X_BlockErase 0xD8 
#define W25X_SectorErase 0x20 
#define W25X_ChipErase 0xC7 
#define W25X_PowerDown 0xB9 
#define W25X_ReleasePowerDown 0xAB 
#define W25X_DeviceID 0xAB 
#define W25X_ManufactDeviceID 0x90 
#define W25X_JedecDeviceID 0x9F 

typedef struct
{ 
        
   void *tx_buf;
   void *rx_buf;
   unsigned char cmd;            //w25q64指令
   unsigned int address;         //写入或者读取的地址
   unsigned int tx_len;          //需要写入的字节数
   unsigned int rx_len;          //需要读取的字节数

}w25qxx_data_def;

typedef struct
{ 
        
      struct device_node *node;//设备树节点
      struct cdev cdev;       //定义一个cdev结构体
      struct class *class;    //创建一个w25q64类
      struct device *device;  //创建一个w25q64设备 该设备是需要挂在w25q64类下面的
      int major;              //主设备号
      dev_t  dev_id;
      struct spi_device *spi; /*spi设备*/
      int cspin;              /*片选脚*/
      int wppin;
      struct mutex lock;
      w25qxx_data_def data;
}w25qxx_typdef;

static w25qxx_typdef w25qxx_dev;//定义一个w25q64设备

/*函数声明*/
static int w25qxx_read_bytes(w25qxx_typdef *w25q64,unsigned int address,unsigned char* buf,int count);

static int w25q64_spi_read_write(w25qxx_typdef *w25q64)
{ 
        
   struct spi_device *spi = w25q64->spi;
   struct spi_transfer xfer[2];
   struct spi_message msg;
   int ret = 0;
   unsigned char *buf,*readbuf;

   memset(&xfer, 0, sizeof(xfer));/*必须清0 否则无法spi_sync函数无法发送数据*/

   xfer[0].tx_buf = w25q64->data.tx_buf;
   xfer[0].len = w25q64->data.tx_len;
   
   buf = (unsigned char *)(w25q64->data.tx_buf);

   xfer[1].rx_buf = w25q64->data.rx_buf;
   xfer[1].len = w25q64->data.rx_len;

   spi_message_init(&msg);
   spi_message_add_tail(&xfer[0], &msg);

   if(w25q64->data.rx_len)
   { 
        
      spi_message_add_tail(&xfer[1], &msg);
   }

   ret = spi_sync(spi, &msg); 
   if(ret != 0)
   { 
          
      printk("spi_sync failed %d\n", ret);
   }
   
   readbuf = (unsigned char *)(w25q64->data.rx_buf);
   return ret;
}

static void spi_wp_enable(void)
{ 
        
    gpio_set_value(w25qxx_dev.wppin, 1); 
}

static void spi_wp_disable(void)
{ 
        
   gpio_set_value(w25qxx_dev.wppin, 0); 
}

static void spi_cs_enable(void)
{ 
        
   //gpio_set_value(w25qxx_dev.cspin, 0); /* cs = 0 */
}

static void spi_cs_disable(void)
{ 
        
   //gpio_set_value(w25qxx_dev.cspin, 1); /* cs = 1 */
}

static void spi_write_enable(void)
{ 
        
   int ret;
   unsigned char tx_buf[1];

   spi_cs_enable();
    
   tx_buf[0] = W25X_WriteEnable;/*写使能指令*/
   w25qxx_dev.data.tx_buf= tx_buf; 
   w25qxx_dev.data.tx_len = 1;
   w25qxx_dev.data.rx_len = 0;

   ret = w25q64_spi_read_write(&w25qxx_dev);

   spi_cs_disable();
}

static void spi_write_disable(void)
{ 
        

   int ret;
   unsigned char tx_buf[1];
   spi_cs_enable();
    
   tx_buf[0] = W25X_WriteDisable;/*写失能指令*/
   
   w25qxx_dev.data.tx_buf= tx_buf; 
   w25qxx_dev.data.tx_len = 1;
   w25qxx_dev.data.rx_len = 0;

   ret = w25q64_spi_read_write(&w25qxx_dev);

   spi_cs_disable();
}

static int w25qxx_get_sr(w25qxx_typdef *w25q64)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[1];
   unsigned char rx_buf[1];
   
   spi_cs_enable();

   tx_buf[0] = W25X_ReadStatusReg;
   
   w25q64->data.tx_buf= tx_buf; 
   w25q64->data.tx_len = 1;

   w25q64->data.rx_buf = rx_buf;
   w25q64->data.rx_len = 1;
 
   ret = w25q64_spi_read_write(w25q64);

   spi_cs_disable();

   if(ret < 0)
   { 
        
      printk("w25qxx_get_sr failed \n");
      return ret;
   }

   return rx_buf[0];
}

// static int w25qxx_set_sr(w25qxx_typdef *w25q64,unsigned char value)
// { 
        
// int ret = -EINVAL;
// unsigned char tx_buf[2];//
   
// spi_cs_enable();

// tx_buf[0] = W25X_ReadStatusReg;
// tx_buf[1] = value;

// w25q64->data.tx_buf= tx_buf; 
// w25q64->data.tx_len = 2;

// w25q64->data.rx_len = 0;/*rx_len设置为0 表示不需要接收数据*/
 
// ret = w25q64_spi_read_write(w25q64);

// spi_cs_disable();

// if(ret != 0)
// { 
        
// printk("w25qxx_set_sr failed\n");
// return ret;
// }

// return 0;
// }

static int w25qxx_get_id(w25qxx_typdef *w25q64)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[4];
   unsigned char rx_buf[5];
   
   spi_cs_enable();

   tx_buf[0] = W25X_ManufactDeviceID;/*读取ID指令*/
   tx_buf[1] = 0x0;
   tx_buf[2] = 0x0;
   tx_buf[3] = 0x0;
   
   w25q64->data.tx_buf= tx_buf; 
   w25q64->data.tx_len = 4;

   w25q64->data.rx_buf = rx_buf;
   w25q64->data.rx_len = 2;
 
   ret = w25q64_spi_read_write(w25q64);

   spi_cs_disable();

   if(ret != 0)
   { 
        
      printk("w25qxx_get_id failed %d\n",ret);
      return ret;
   }
   printk("rx_buf 0x%x 0x%x 0x%x 0x%x\n\r",rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
   return (rx_buf[0] << 8 | rx_buf[1]);
}

static void w25qxx_Reset(w25qxx_typdef *w25q64)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[4];//
   unsigned char rx_buf[5];//

   spi_wp_disable(); 
   spi_cs_enable();

   udelay(2);
   tx_buf[0] = 0x66;/*读取ID指令*/
   tx_buf[1] = 0x99;

   
   w25q64->data.tx_buf= tx_buf; 
   w25q64->data.tx_len = 2;

   w25q64->data.rx_buf = rx_buf;
   w25q64->data.rx_len = 0;
 
   ret = w25q64_spi_read_write(w25q64);

   spi_cs_disable();
   spi_wp_enable();
   udelay(2);

   if(ret < 0)
   { 
        
      printk("w25qxx_get_id failed %d\n",ret);
   }
   else
   { 
        
       printk("w25qxx_ Init Success %d\n",ret); 
   }
}

static int w25qxx_wait_idle(void)
{ 
        
   int ret = -EINVAL; 
   do { 
        
        ret = w25qxx_get_sr(&w25qxx_dev);
        if(ret < 0 )
        { 
        
           return ret;/*通信错误*/
        }
        else
        { 
        
            if(!(ret & 0x01))
            { 
        
               return 0;/*w25q64空闲*/
            }
        }  
        /* REVISIT: at HZ=100, this is sloooow */
        msleep(10);
   } while(1);   
   return 1; 
}

static int w25qxx_erase_sector(w25qxx_typdef *w25q64,unsigned int address)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[4];//
   
   spi_write_enable();/*写保护关闭*/
   spi_cs_enable();

   tx_buf[0] = W25X_SectorErase;/*扇区擦除指令*/
   tx_buf[1] = (unsigned char)((address>>16) & 0xFF);
   tx_buf[2] = (unsigned char)((address>>8) & 0xFF);
   tx_buf[3] = (unsigned char)(address & 0xFF);
   
   w25q64->data.tx_buf= tx_buf; 
   w25q64->data.tx_len = 4;

   w25q64->data.rx_len = 0;
 
   ret = w25q64_spi_read_write(w25q64);

   spi_cs_disable();

   if(ret != 0)
   { 
        
      printk("erase sector@%d failed %d\n",address,ret);
      return ret;
   }
   ret = w25qxx_wait_idle();/*等待flash内部操作完成*/
   spi_write_disable();/*写保护打开*/

   return ret;
}

static int w25qxx_erase_chip(w25qxx_typdef *w25q64)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[1];//
   
   spi_write_enable();/*写保护关闭*/
   spi_cs_enable();

   tx_buf[0] = W25X_ChipErase;/*扇区擦除指令*/

  
   w25q64->data.tx_buf= tx_buf; 
   w25q64->data.tx_len = 1;

   w25q64->data.rx_len = 0;
 
   ret = w25q64_spi_read_write(w25q64);

   spi_cs_disable();

   if(ret != 0)
   { 
        
      printk("erase chip failed %d\n", ret);
      return ret;
   }
   ret = w25qxx_wait_idle();/*等待flash内部操作完成*/
   spi_write_disable();/*写保护打开*/

   return ret;
}

static int w25qxx_need_erase(unsigned char*old,unsigned char*new,int count)
{ 
        
   int i;
   unsigned char p;

   for ( i = 0; i < count; i++)
   { 
        
      p = *old++;
      p = ~p; 
      
      if((p &(*new++))!=0)
      { 
        
         return 1;
      }
   }
   return 0;
}

static int w25qxx_read_bytes(w25qxx_typdef *w25q64,unsigned int address,unsigned char* buf,int count)
{ 
        
   int ret = -EINVAL;
   unsigned char tx_buf[
        标签: w25传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台