加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 06:12:53 所属栏目:百科 来源:网络整理
导读:? 最近在弄PowerPC平台上的spi flash的驱动程序,总体比较简单,在借鉴了U-Boot中的相关源码后,花了两周左右的时间搞定了,对于spi总线之前一直都有了解,但未能实际接触,这次在vxWorks上尝试了一下,确实有不小的收获。由于网上关于vxWorks平台的资料稀少

? 最近在弄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。详细的配置代码如下:

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

?

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

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

copy
    /*?spi_xfer为spi总线读写驱动函数?
  1. ?*?参数1:spi_salve在只有一个设备时可以无视,用于表示spi从设备,这里为spi?flash?
  2. ?*?参数2、3、4为输入和输出的数据及长度,若不想要数据则设为NULL?
  3. ?*?参数5为传输开始或结束的标示,以此控制片选信号*/??
  4. int?spi_xfer(struct?spi_slave?*slave,?unsigned?int?bitlen,?const?void?*dout,??
  5. ????????void?*din,?unsigned?long?flags)??
  6. ????volatile?spi8xxx_t?*spi?=?&((immap_t?*)?(CONFIG_SYS_IMMR))->spi;??
  7. ????unsigned?int?tmpdout,?tmpdin,?event;??
  8. ????int?numBlks?=?bitlen?/?32?+?(bitlen?%?32???1?:?0);/*此处这样设置是因为模式寄存器中将传输长度设为32位*/??
  9. ????int?tm,?isRead?=?0;??
  10. ????unsigned?char?charSize?=?32;??
  11. ??
  12. ????debug("spi_xfer:?slave?%u:%u?dout?%08X?din?%08X?bitlen?%un",??
  13. ??????????slave->bus,?slave->cs,?*(uint?*)?dout,?*(uint?*)?din,?bitlen);??
  14. ??????/*判断若为开始则激活片选,在mpc8xxx系列中为拉低对应的GPIO信号*/??
  15. ????if?(flags?&?SPI_XFER_BEGIN)??
  16. ????????spi_cs_activate(slave);??
  17. ????spi->event?=?0xffffffff;?/*?清空SPI?事件?*/??
  18. ????/*?handle?data?in?32-bit?chunks?*/??
  19. ????while?(numBlks--)?{??
  20. ????????tmpdout?=?0;??
  21. ????????charSize?=?(bitlen?>=?32???32?:?bitlen);??
  22. ????????/*?调整数据传送模式为MSB在前*/??
  23. ????????tmpdout?=?*(u32?*)?dout?>>?(32?-?charSize);??
  24. ????????/*?这里做的很精妙,根据剩余数据位数调整模式?
  25. ?????????*?寄存器中的数据位?
  26. ?????????*/??
  27. ????????if?(bitlen?<=?16)?{??
  28. ????????????if?(bitlen?<=?4)??
  29. ????????????????spi->mode?=?(spi->mode?&?0xff0fffff)?|??
  30. ????????????????????????????(3?<<?20);??
  31. ????????????else??
  32. ????????????????spi->mode?=?(spi->mode?&?0xff0fffff)?|??
  33. ????????????????????????????((bitlen?-?1)?<<?20);??
  34. ????????}?else?{??
  35. ????????????spi->mode?=?(spi->mode?&?0xff0fffff);??
  36. ????????????/*?Set?up?the?next?iteration?if?sending?>?32?bits?*/??
  37. ????????????bitlen?-=?32;??
  38. ????????????dout?+=?4;??
  39. ????????}??
  40. ????????spi->tx?=?tmpdout;???/*?Write?the?data?out?*/??
  41. ????????debug("***?spi_xfer:?...?%08x?writtenn",?tmpdout);??
  42. ????????/*?等待SPI传输超时,之后清空事件寄存器*/??
  43. ????????for?(tm?=?0,?isRead?=?0;?tm?<?SPI_TIMEOUT;?++tm)?{??
  44. ????????????event?=?spi->event;??
  45. ????????????if?(event?&?SPI_EV_NE)?{??
  46. ????????????????tmpdin?=?spi->rx;??
  47. ????????????????spi->event?|=?SPI_EV_NE;??
  48. ????????????????isRead?=?1;??
  49. ????????????????*(u32?*)?din?=?(tmpdin?<<?(32?-?charSize));??
  50. ????????????????if?(charSize?==?32)?{??
  51. ????????????????????/*?Advance?output?buffer?by?32?bits?*/??
  52. ????????????????????din?+=?4;??
  53. ????????????????}??
  54. ????????????}??
  55. ????????????/*?
  56. ?????????????*?Only?bail?when?we've?had?both?NE?and?NF?events.?
  57. ?????????????*?This?will?cause?timeouts?on?RO?devices,?so?maybe?
  58. ?????????????*?in?the?future?put?an?arbitrary?delay?after?writing?
  59. ?????????????*?the?device.??Arbitrary?delays?suck,?though...?
  60. ?????????????*/??
  61. ????????????if?(isRead?&&?(event?&?SPI_EV_NF))??
  62. ????????????????break;??
  63. ????????}??
  64. ????????if?(tm?>=?SPI_TIMEOUT)??
  65. ????????????puts("***?spi_xfer:?Time?out?during?SPI?transfer");??
  66. ????????debug("***?spi_xfer:?transfer?ended.?Value=%08xn",?tmpdin);??
  67. ????}??
  68. ????/*传输完成后关闭片选,对应的为拉高GPIO信号*/??
  69. ????if?(flags?&?SPI_XFER_END)??
  70. ????????spi_cs_deactivate(slave);??
  71. ????return?0;??
  72. ??? 我的代码就是从上面的程序演变过来的,改动很小就可以使用在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 OK;

    }

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读