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传感器