最近在弄PowerPC平台上的spi flash的驱动程序,总体比较简单,在借鉴了U-Boot中的相关源码后,花了两周左右的时间搞定了,对于spi总线之前一直都有了解,但未能实际接触,这次在vxWorks上尝试了一下,确实有不小的收获。由于网上关于vxWorks平台的资料稀少,就写下此文,以备查阅。

这次驱动的对象是Spansion的S25FLXX系列的,扇区64K,相对比较低端,但原理都是相通的。核心板的SPI总线提供时钟和MOSI、MISO,用GPIO实现片选。要实现对flash的驱动无非“三步走”:初始化SPI总线、完成spi读写驱动、在spi总线基础上完成flash读写驱动。

先看第一步,这个比较简单,对于mpc8xxx系列,主要需要配置的就是模式寄存器SPMODE了(当然,像Fsl的另一款处理器P2020ds采用的eSPI,就不止这一个寄存器,还有Command要复杂配置,有兴趣的可以看下)如下图:

首位LOOP指是否开启LoopBack模式,用于测试SPI传输的,一般不建议打开;CI、CP一起用于设置SPI时钟,这个要根据对应flash的数据手册来,像我的这款说了支持00和11两种模式,这里设置为00;DIV16用于为SPI BRG设置时钟源,这个不是很懂,就设置为0了;M/S设置SPI工作模式,CPU要控制flash,这里当然是master模式了;还有个PM,用于设置时钟分频的,以u-boot为准,设置为1,即SYSCLK/8。详细的配置代码如下:

/*SPI模式寄存器配置位*/
#define SPI_LOOP        (0x01<<30)/*开启Loopback模式,此处不开启*/
#define CI_CP           (0x00<<28)/*时钟模式为00(还是11),与SPI Flash时序相对应*/
#define SPI_CLK         (0x0<<27)   /*此位设置为0,即原始时钟频率*/
#define REV_DATA        (0x1<<26)   /*设置数据模式为MSB先收发*/
#define MS              (0x1<<25)   /*设置为master模式*/
#define PM              (0x0001<<16)/*设置SYSCLK/8为时钟*/
#define SPI_ENA         (0x1<<24)   /*打开SPI*/
#define CH_LEN          (0x0000<<20) /*设置数据长为32位,即一次可传输4个字节*/
#define SPIMODE_INIT      (CI_CP | SPI_CLK | REV_DATA | MS | PM | CH_LEN)
/*SPI初始化*/
STATUS init_spi()
{
/*一开始要禁止片选(此处为拉高GPIO)*/
spi_cs_assert();
/*设置SPI Mode并打开*/
WRITE_ADDR_INT32(SPMODE,SPIMODE_INIT);
/*先清空事件*/
WRITE_ADDR_INT32(SPIE,SPI_EV_CLEAR);
/*再使能*/
WRITE_ADDR_INT32(SPMODE,(READ_ADDR_INT32(SPMODE)) | SPI_ENA);
/*屏蔽所有中断*/
WRITE_ADDR_INT32(SPIM,0x0);
return OK;
}

好了,到这里第一步的工作就做完了,比较简单,只要弄清楚flash的时序配置起来就会方便不少了。

然后就开始第二步,这一步是最关键的,但其中在这一步中搞清楚两点也不会很困难,1、注意片选信号和flash读写时序的关系;2、SPI总线的全双工特性。这里特别声明一下,由于SPI总线是全双工的,所以在编写驱动时最好把读写放在一起实现,写完即读缓冲区,方便有效。在U-boot中,实现这个功能的函数是spi_xfer函数,这里以FreeScale的Mpc8xxx系列为例简单的解释下:

/* spi_xfer为spi总线读写驱动函数
* 参数1:spi_salve在只有一个设备时可以无视,用于表示spi从设备,这里为spi flash
* 参数2、3、4为输入和输出的数据及长度,若不想要数据则设为NULL
* 参数5为传输开始或结束的标示,以此控制片选信号*/
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi;
unsigned int tmpdout, tmpdin, event;
int numBlks = bitlen / 32 + (bitlen % 32 ? 1 : 0);/*此处这样设置是因为模式寄存器中将传输长度设为32位*/
int tm, isRead = 0;
unsigned char charSize = 32;
debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen);
/*判断若为开始则激活片选,在mpc8xxx系列中为拉低对应的GPIO信号*/
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
spi->event = 0xffffffff;    /* 清空SPI 事件 */
/* handle data in 32-bit chunks */
while (numBlks--) {
tmpdout = 0;
charSize = (bitlen >= 32 ? 32 : bitlen);
/* 调整数据传送模式为MSB在前*/
tmpdout = *(u32 *) dout >> (32 - charSize);
/* 这里做的很精妙,根据剩余数据位数调整模式
* 寄存器中的数据位
*/
if (bitlen <= 16) {
if (bitlen <= 4)
spi->mode = (spi->mode & 0xff0fffff) |
(3 << 20);
else
spi->mode = (spi->mode & 0xff0fffff) |
((bitlen - 1) << 20);
} else {
spi->mode = (spi->mode & 0xff0fffff);
/* Set up the next iteration if sending > 32 bits */
bitlen -= 32;
dout += 4;
}
spi->tx = tmpdout;  /* Write the data out */
debug("*** spi_xfer: ... %08x written\n", tmpdout);
/* 等待SPI传输超时,之后清空事件寄存器*/
for (tm = 0, isRead = 0; tm < SPI_TIMEOUT; ++tm) {
event = spi->event;
if (event & SPI_EV_NE) {
tmpdin = spi->rx;
spi->event |= SPI_EV_NE;
isRead = 1;
*(u32 *) din = (tmpdin << (32 - charSize));
if (charSize == 32) {
/* Advance output buffer by 32 bits */
din += 4;
}
}
/*
* Only bail when we've had both NE and NF events.
* This will cause timeouts on RO devices, so maybe
* in the future put an arbitrary delay after writing
* the device.  Arbitrary delays suck, though...
*/
if (isRead && (event & SPI_EV_NF))
break;
}
if (tm >= SPI_TIMEOUT)
puts("*** spi_xfer: Time out during SPI transfer");
debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin);
}
/*传输完成后关闭片选,对应的为拉高GPIO信号*/
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return 0;
}

我的代码就是从上面的程序演变过来的,改动很小就可以使用在vxWorks平台了,感谢u-boot,感谢W.Denk大师啊!!

做完第二步之后,第三步就简单多了,只是在读写操作的时候要加上一个操作码和操作地址(组成4个字节的帧发送),写操作之前还要有写使能等操作。对于Spansion的S25FLXX系列Spi Flash,操作码都是一样的,下图为各操作码定义:

发送完了flash操作码后,再下读写指令就可以了,下面是一个页编程的函数代码,有误请指正:

/*整页写*/

STATUS flash_PagePro

(

UINT32 destAddr,  //目的地址

UINT8  *data,    //传输的数据

UINT32 dataLen   //数据长度(字节数)

)

{

UINT32 cmd32;

flash_WrEnable();  //写使能

if((destAddr > 0xffffff) || ((destAddr + dataLen) > 0xffffff))

return ERROR;

cmd32 = (FLASH_PP <<24) | (destAddr & 0xffffff);

trans_data((UINT8 *)&cmd32,CMD_LEN + ADDR_LEN);

trans_data(data,dataLen);

/*检查flash扇区擦除进度*/

UINT32 i;

i = 0;

while((flash_ReadStat() & WIP) && (i < FLASH_ERASE_TIMEOUT))

{

taskDelay (1);

i++;

}

if (i >= FLASH_ERASE_TIMEOUT)

return ERROR;

return OK;

}

FreeScale mpc8xxx + vxWorks平台下spi flash驱动开发三步走相关推荐

  1. 2017年5月问题记录与总结——powerpc p1020 spi flash驱动

    1.SPI基础 SPI是串行外围接口的意思,一般用来接一些低速的外围设备,比如eeprom,flash,ad传感器,rtc等.硬件上,SPI有四根线,SDI/SDO/CS/SCLK,具体的协议可以参看 ...

  2. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的I2C驱动架构分析

    PowerPC + Linux2.6.25平台下的I2C驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

  3. uClinux 平台下的Flash存储技术(转)

    摘要:本文较为详细地介绍了嵌入式操作系统uClinux平台下的Flash的存储技术,并给出了基于三星S3C4510系统下Flash存储器的具体设计实例. 关键字: Flash存储器:uClinux:S ...

  4. FPGA接口_N25Q128型号的spi flash驱动verilog代码编写

    # N25Q128型号的spi flash驱动verilog代码编写 提示:使用正点原子达芬奇pro做的小例子,由于教程中无flash的读写,因此撰写记录 文章目录 # N25Q128型号的spi f ...

  5. imx6ul spi 设备驱动开发

    imx6ul spi 设备驱动开发 spi设备树格式 spi设备树配置 spi 驱动 设备树解析 spi设备驱动使用 spi通用设备驱动 spi测试工具 spi时序对比 spi api 接口 spi设 ...

  6. Android应用程序访问linux驱动第三步:实现并向系统注册Service

    在学习Android应用程序访问linux驱动时,原博主在第一.二步写得具体详细,但我学到第三步实现并向系统注册Service时,发觉内迷惑和发现几处错误,这里我将我的理解和修改记录下来和大家分享.希 ...

  7. 一个x86平台的spi flash驱动移植笔记

    最近一个项目,要用到flash存放一些数据,要内核支持.于是又要去移植. 其实,这个项目的合作方式我一直不喜欢,但言语轻微.起初我也不断地提意见,但看了几次的结果后,觉得不提也罢.对于开发人员来说,合 ...

  8. ESP8266_RTOS_SDK 之spi flash驱动小窥

    学习时间到,来看一下人家的flash驱动是咋写的吧,用文字记录不容易有所遗漏,也更加方便想起. 先从驱动测试代码开始,进而切入调用关系底层,以下是sdk中测试flash read的测试代码: stat ...

  9. VxWorks嵌入式操作系统的TrueFFS文件系统驱动开发

    嵌入式系统对执行速度和系统可靠性的要求,决定了嵌入式系统需要一种安全.快速的存储设备,这种设备备同时还需要体积小.容量大.掉电数据不丢失等特点.而Flash存储器恰恰能够满足上述要求.这也使得Flas ...

最新文章

  1. android 网络编程实现,Android开发使用HttpURLConnection进行网络编程详解【附源码下载】...
  2. 瀚思首发三款产品 推动大数据安全战略布局
  3. 《C#精彩实例教程》小组阅读11 -- C#结构与类
  4. 前端有啥好用的手机模拟软件吗_隐藏应用,软件双开,一个APP就解决了
  5. iOS 向下取整、向上取整、四舍五入
  6. mysql 排序去重复_php mysql 过滤重复记录并排序
  7. Linux oracle(常用命令)启动、停止、监听
  8. 单调有界定理适用于函数吗_第二百零二夜:导数与三角函数
  9. 【设计过程】.NET ORM FreeSql WhereDynamicFilter 动态表格查询功能
  10. 加密解密之 crypto-js 知识
  11. CDA LEVEL I 数据分析认证考试模拟题库(一)
  12. Pycharm导入已有的本地安装包
  13. C语言实现的个人信息管理系统
  14. Survey on Human pose estimation
  15. Git 使用的一些命令以及Git commit 注释格式
  16. 基于JSF框架的在线棋牌游戏平台
  17. 任务管理器--内存篇(上)
  18. 迅捷音频转换器如何提取音频文件教程
  19. 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解
  20. Hexo主题插入音乐之aplayer音乐播放器

热门文章

  1. Dockerfile 中的 VOLUME 与 docker -v 区别
  2. 2022年茶艺师(中级)报名考试及茶艺师(中级)作业考试题库
  3. Leetcode PHP题解--D70 784. Letter Case Permutation
  4. U盘启动盘制作工具哪个好用?你怕是没用过Rufus
  5. 阿里云共享和独享云虚拟主机该怎么选?
  6. 如何把pdf转换成excel转换器免费版
  7. MMLAB学习-Gard-CAM可视化方法
  8. mongoose之bulkWrite
  9. [转]《Python编程金典》读书笔记
  10. 达梦数据库-8 错误代码查询