Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SPI
[新手试水] LPC1768_Nano3_SPI_FLASH
发布于 2018-03-26 18:52:08 浏览:3104
订阅该版
* 本帖最后由 wlof 于 2018-3-26 18:52 编辑 * **第六章 SPI_FLASH**[align=center]Wlof摘要:本文主要介绍在LPC1768上基于RTT_Nano3使用SPI接口的Flash,参考STM32F107bsp实现代码,使用现成的sst25vfxx驱动,并针对其不适用于实际项目的地方进行了修改,主要是要解决多个Flash挂载在同一总线上的问题,希望对大家有所帮助。**6.1 起源**数据存储是嵌入式开发的基本功能,少量数据直接写到EEPROM中去,但是数据量一旦比较大,且不用经常更改的时候,我们会选择容量大一点的Flash。由于Flash写入数据时,会要求对扇区进行擦除,所以操作起来比较麻烦,值得注意的是擦写是寿命的,因此,只有在写入扇区首地址时,才进行擦除操作。写这个文章的目的是为了告诉大家,在使用现成代码的同时,也要仔细思考自己的应用是否允许这样搞。本人在实际使用时,也没有考虑那些问题,直接就用了,后来发生灾难性故障啦。又在群里发问,结果大神们一下就定位了,设备复用重复初始化了,这也都归结于没有基础的盲目使用。 【提示:LPC176x bsp下的SPI适用于SD卡,写法不一样,要用现成的sst25vfxx驱动,就要按它的驱动模式去搞,先初始化总线设备,然后再绑上去。】 参考网文 [RT-thread设备驱动组件之SPI设备](http://www.cnblogs.com/King-Gentleman/p/4657844.html)这个文章写得不错,请基于**STM32F107的bsp** 来看。 **6.2 SPI内核驱动复制**由于我们是在nano上搞的工程,很多组件都没有,所以要自己动手去复制,当然啦,如果有用env的话,很多工作都不用我们动手去做了。【基本原则】凡是内核相关的,尽量不动,如果有重名的,但改和自己板子有关的东西,如板子上也有SPI,参考代码也有spi.h,那就将我们自己的spi.h改为“lpc_spi.h”,表示是lpc下的驱动。复制文件spi_core.c,spi_dev.c,spi.h到自己的目录下,然后添加到工程中,3个文件所在源目录如表所示。表1 SPI内核驱动文件位置[table] [tr][td=47] 序号 [/td][td=104]文件名[/td][td=498]路径[/td][/tr] [tr][td=47]1[/td][td=104][align=right]spi_core.c[/td][td=498][align=right]rt-thread-master\components\drivers\spi\[/td][/tr] [tr][td=47]2[/td][td=104][align=right]spi_dev.c[/td][td=498][align=right]rt-thread-master\components\drivers\spi\[/td][/tr] [tr][td=47]3[/td][td=104][align=right]spi.h[/td][td=498][align=right]rt-thread-master\components\drivers\include\drivers\[/td][/tr] [/table] 由于文件是复制过来的,包含路径就和之前不一样的,我们将文件放在一起,所以.c的包含文件路径要改一下。 ```#include "spi.h"//#include
``` **6.3 各文件的功能****spi_core.c**文件实现了spi的抽象操作,如**注册spi总线**(spi_bus),向SPI总线**添加设备**函数等。注: 这里将MCU的一路spi外设**虚拟成spi总线**,然后总线上可以**挂很多spi设备(spi_device)**,一个spi_device有**一个片选cs**。spi总线和spi设备要在RTT中可以生效就必须先向RTT注册,因此就需要使用上面的注册SPI总线函数和向SPI总线中添加SPI设备。**spi_core.c**还包含了**配置SPI**函数,**发送和接收**等通信函数,占用和释放SPI总线函数及选择SPI设备函数。这些函数都是抽象出来的,反映出SPI总线上的一些常规操作。真正执行这些操作的过程并不在spi_core.c源文件中,实际上,这些操作信息都是通过注册SPI总线和向总线添加SPI设备时这些操作集就已经"注册"下来了,真正操作时是通过注册信息内的操作函数去实现,也**可以说是一种回调操作**。spi_core.c中实现的函数主要有:rt_spi_bus_register(); rt_spi_bus_attach_device();rt_spi_configure(); rt_spi_send_then_send(); rt_spi_send_then_recv();rt_spi_transfer(); rt_spi_transfer_message(); rt_spi_take_bus();rt_spi_release_bus(); rt_spi_take(); rt_spi_release()。而**spi_dev.c**实现了SPI设备的一些抽象操作,比如**读,写,打开,关闭,初始化**等,当然当MCU操作SPI设备的时候,是需要通过SPI总线与SPI设备进行通信的,既然通信就必然会有SPI通信协议,但是通信协议并不在这里具体,spi_dev.c这里还只是SPI设备的抽象操作而已,它只是简单地调用spi_core.c源文件中的抽象通信而已,具体实现还是要靠上层通过SPI总线或SPI设备注册下来的信息而实现的。spi_device.c中实现的函数主要有:_spi_bus_device_read(); _spi_bus_device_write(); _spi_bus_device_control();t_spi_bus_device_init();_spidev_device_read(); _spidev_device_write();_spidev_device_control(); rt_spidev_device_init()。 以上文字来自网文,讲得很细了,大概归纳一下,其主功能如表2-3所示。[align=center]** 表2 spi_core.c函数功能**[align=center][table=517] [tr][td=72] [align=center]序号 [/td][td=257][align=center]函数名[/td][td=188][align=center]功能描述[/td][/tr] [tr][td=72][align=center]1[/td][td=257][align=right]rt_spi_bus_register[/td][td=188][align=right]总线注册[/td][/tr] [tr][td=72][align=center]2[/td][td=257][align=right]rt_spi_bus_attach_device[/td][td=188][align=right]片选绑定并注册为新总线[/td][/tr] [tr][td=72][align=center]3[/td][td=257][align=right]rt_spi_configure[/td][td=188][align=right]总线配置函数[/td][/tr] [tr][td=72][align=center]4[/td][td=257][align=right]rt_spi_send_then_send[/td][td=188][align=right]先后发送两个buf数据[/td][/tr] [tr][td=72][align=center]5[/td][td=257][align=right]rt_spi_send_then_recv[/td][td=188][align=right]发完后接收数据[/td][/tr] [tr][td=72][align=center]6[/td][td=257][align=right]rt_spi_transfer[/td][td=188][align=right]收发数据同时进行[/td][/tr] [tr][td=72][align=center]7[/td][td=257][align=right]rt_spi_transfer_message[/td][td=188][align=right]发送一则消息数据[/td][/tr] [tr][td=72][align=center]8[/td][td=257][align=right]rt_spi_take_bus[/td][td=188][align=right]总线上锁占用[/td][/tr] [tr][td=72][align=center]9[/td][td=257][align=right]rt_spi_release_bus[/td][td=188][align=right]总线释放[/td][/tr] [tr][td=72][align=center]10[/td][td=257][align=right]rt_spi_take[/td][td=188][align=right]CS片选[/td][/tr] [tr][td=72][align=center]11[/td][td=257][align=right]rt_spi_release[/td][td=188][align=right]CS释放[/td][/tr] [/table] [align=center][align=center]**表3 spi_dev.c 函数功能(不用管)**[align=center][table=517] [tr][td=72] [align=center]序号 [/td][td=257]函数名[/td][td=188]功能 [/td][/tr] [tr][td=72][align=center]1[/td][td=257][align=right]_spi_bus_device_read[/td][td=188][align=right]总线读[/td][/tr] [tr][td=72][align=center]2[/td][td=257][align=right]_spi_bus_device_write[/td][td=188][align=right]总线写[/td][/tr] [tr][td=72][align=center]3[/td][td=257][align=right]_spi_bus_device_control[/td][td=188][align=right]空函数[/td][/tr] [tr][td=72][align=center]4[/td][td=257][align=right]rt_spi_bus_device_init[/td][td=188][align=right]总线初始化[/td][/tr] [tr][td=72][align=center]1[/td][td=257][align=right]_spidev_device_read[/td][td=188][align=right]设备读[/td][/tr] [tr][td=72][align=center]2[/td][td=257][align=right]_spidev_device_write[/td][td=188][align=right]设备写[/td][/tr] [tr][td=72][align=center]3[/td][td=257][align=right]_spidev_device_control[/td][td=188][align=right]空函数[/td][/tr] [tr][td=72][align=center]4[/td][td=257][align=right]rt_spidev_device_init[/td][td=188][align=right]设备初始化[/td][/tr] [/table] [align=center]**6.4 设备驱动复制**spi_flash_sst25vfxx的源文件和头文件都复制过来,添加到工程。由于文件是复制过来的,包含路径就和之前不一样的,我们将文件放在一起,所以.c的包含文件路径要改一下。**#include “spi.h”//#include
**直接编译看一下有没有错误。包含文件改了之后,没有错误了。 在配置之前先来看一下,spi_flash_sst25vfxx都干了什么,如表4所示。[align=center]** 表4 spi_flash_sst25vfxx.c 函数功能**[align=center][table=517] [tr][td=72] [align=center]序号 [/td][td=257]函数名[/td][td=188]功能 [/td][/tr] [tr][td=72][align=center]1[/td][td=257][align=right]sst25vfxx_read_status[/td][td=188][align=right]读状态【内部调用】[/td][/tr] [tr][td=72][align=center]2[/td][td=257][align=right]sst25vfxx_wait_busy[/td][td=188][align=right]等待【内部调用】[/td][/tr] [tr][td=72][align=center]3[/td][td=257][align=right]sst25vfxx_page_write[/td][td=188][align=right]页编程【内部调用】[/td][/tr] [tr][td=72][align=center]4[/td][td=257][align=right]sst25vfxx_flash_init[/td][td=188][align=right]初始化(空)[/td][/tr] [tr][td=72][align=center]5[/td][td=257][align=right]sst25vfxx_flash_open[/td][td=188][align=right]打开(好像也没用?)[/td][/tr] [tr][td=72][align=center]6[/td][td=257][align=right]sst25vfxx_flash_close[/td][td=188][align=right]关闭(空)[/td][/tr] [tr][td=72][align=center]7[/td][td=257][align=right]sst25vfxx_flash_control[/td][td=188][align=right]不知道干用(没用?)[/td][/tr] [tr][td=72][align=center]8[/td][td=257][align=right]sst25vfxx_flash_read[/td][td=188][align=right]读取【有用】[/td][/tr] [tr][td=72][align=center]9[/td][td=257][align=right]sst25vfxx_flash_write[/td][td=188][align=right]写入【有用】[/td][/tr] [tr][td=72][align=center]10[/td][td=257][align=right]sst25vfxx_init[/td][td=188][align=right]注册【外用】[/td][/tr] [/table] 从注册这个函数开始,它先找到总线,然后发送数据读取芯片ID,根据ID识别芯片容量,然后初始化将各函数注册进来。 [align=center]**6.5 sst25vfxx_page_write** ```static uint32_t sst25vfxx_page_write(struct spi_flash_sst25vfxx * spi_flash, uint32_t page, const uint8_t * buffer, uint32_t size) { uint32_t index; uint32_t need_wirte = size; uint8_t send_buffer[6]; page &= ~0xFFF; // page size = 4096byte send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); send_buffer[0] = CMD_ERASE_4K; send_buffer[1] = (page >> 16); send_buffer[2] = (page >> 8); send_buffer[3] = (page); rt_spi_send(spi_flash->rt_spi_device, send_buffer, 4); sst25vfxx_wait_busy(spi_flash); // wait erase done. send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); send_buffer[0] = CMD_AAIP; send_buffer[1] = (uint8_t)(page >> 16); send_buffer[2] = (uint8_t)(page >> 8); send_buffer[3] = (uint8_t)(page); send_buffer[4] = *buffer++; send_buffer[5] = *buffer++; need_wirte -= 2; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 6); sst25vfxx_wait_busy(spi_flash); for(index=0; index < need_wirte/2; index++) { send_buffer[0] = CMD_AAIP; send_buffer[1] = *buffer++; send_buffer[2] = *buffer++; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 3); sst25vfxx_wait_busy(spi_flash); } send_buffer[0] = CMD_WRDI; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); return size; } ```这个函数其头上已说明,写入参数page是4096字节对齐的,也就是说扇区的首地址,会强行进行对齐操作,比如你要写在0x01这个位置开始的话,对不起,这个函数哥,给你从00开始写。函数先对区进行擦除操作,然后再进行数据写入操作,这里还用了自动编程功能,一次可以写入2个字节,所以长度除以2了。 **6.6 sst25vfxx_flash_write** ```buffer, rt_size_t size) { uint32_t i; rt_err_t result; const uint8_t * write_buffer = buffer; struct spi_flash_sst25vfxx * spi_flash = (struct spi_flash_sst25vfxx *)dev; /* lock spi flash */ result = rt_mutex_take(&(spi_flash->lock), RT_WAITING_FOREVER); if(result != RT_EOK) { return 0; } for(i=0; i
geometry.bytes_per_sector, write_buffer, spi_flash->geometry.bytes_per_sector); write_buffer += spi_flash->geometry.bytes_per_sector; } /* release lock */ rt_mutex_release(&(spi_flash->lock)); return size; } ```这个函数调用页编程,一次写一个扇区呢,如果我们要在其内部某个位置写入数据的话怎么办呢?有人说,把数据读出来,然后再写进去,恩,好方法,我是芯片制造商,你这个做法让我很满意。芯片是有写寿命的,怎么经得起你这么搞?又有同志会说,那就满了再写,恩,也挺好,如果数据比较短,多条数据来时,一起写入,还能节省时间呢。但有的时候,可能我们真的要马上就写入去,怎么办呢?芯片经不起这来回擦除呢!我们重定义一个字节写入函数,当前与入位置在区内部时,使用字节写入方法,不进行擦除操作,就可以了。 **6.7 添加函数sst25vfxx_byte_write** ```static uint32_t sst25vfxx_byte_write(struct spi_flash_sst25vfxx * spi_flash, uint32_t offest, const uint8_t * buffer, uint32_t size) { //uint32_t index; uint32_t need_wirte = size; uint8_t send_buffer[6]; if((offest & 0x0fff) == 0) //页开始地址,先擦除它 { send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); sst25vfxx_wait_busy(spi_flash); send_buffer[0] = CMD_ERASE_4K; send_buffer[1] = (offest >> 16); send_buffer[2] = (offest >> 8); send_buffer[3] = (0); rt_spi_send(spi_flash->rt_spi_device, send_buffer, 4); sst25vfxx_wait_busy(spi_flash); } send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); sst25vfxx_wait_busy(spi_flash); #if (FLASH_AAIP_EN == 1) // { uint32_t index; send_buffer[0] = CMD_AAIP; send_buffer[1] = (uint8_t)(offest >> 16); send_buffer[2] = (uint8_t)(offest >> 8); send_buffer[3] = (uint8_t)(offest); send_buffer[4] = *buffer++; send_buffer[5] = *buffer++; need_wirte -= 2; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 6); sst25vfxx_wait_busy(spi_flash); for(index=0; index < need_wirte/2; index++) { send_buffer[0] = CMD_AAIP; send_buffer[1] = *buffer++; send_buffer[2] = *buffer++; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 3); sst25vfxx_wait_busy(spi_flash); } } // #else // { while(need_wirte--) { send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); send_buffer[0] = CMD_BP; send_buffer[1] = (uint8_t)(offest >> 16); send_buffer[2] = (uint8_t)(offest >> 8); send_buffer[3] = (uint8_t)(offest); send_buffer[4] = *buffer++; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 5); offest++; sst25vfxx_wait_busy(spi_flash); if(((offest & 0x0fff) == 0)&&(need_wirte != 0)) { send_buffer[0] = CMD_WREN; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); sst25vfxx_wait_busy(spi_flash); send_buffer[0] = CMD_ERASE_4K; send_buffer[1] = (offest >> 16); send_buffer[2] = (offest >> 8); send_buffer[3] = (0); rt_spi_send(spi_flash->rt_spi_device, send_buffer, 4); sst25vfxx_wait_busy(spi_flash); } } } // #endif send_buffer[0] = CMD_WRDI; rt_spi_send(spi_flash->rt_spi_device, send_buffer, 1); return size; } ```关于FLASH_AAIP_EN主要是考虑到,这个驱动应用在别的芯片上时,有可能没有这个功能,这时就只能一个字一个字地写入了。 **6.8 修改sst25vfxx_flash_write**这里对写入地址进行判断,如果是扇区的首地址,那么就调用page_write,如果不是首地址,那么就先计算一下,扇区内可以写多少字内,先用byte_write写完,然后再调用page_write写剩下的数据。 ```static rt_size_t sst25vfxx_flash_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { //if(pos 是首地址) page_write //else{ //1. 计算偏移量 m= pos & (4096 – 1) //2. 计算本区可写入字节 n = 4096 – m //if(size <= n) byte_write //else{ //1.byte_write[n] //2.写入地址+=n //3.剩余字节 m = size – n //4.page_write [m] //}} return size; } ```**6.9 多个Flash的问题**当我们的芯片容量不够时,有时会使用多片扩展。这种情况下,就不能直接使用sst25vfxx了,因为它只定义了一个设备哦,**会导致重复初始化的问题,为此要自己定义一个数组进去**,根据需要设置数组大小,然后,选择对应的设备对象进行初始化。 注意,它们除了片选不一样,其他都是一样的,也就是说,这个代码完全可以复用,不用担心。因此片选参考是我们注册spi总线attch上去的。 **6.10 SPI到底怎么玩?**前面搞了半天,还没有说到重点,那个SPI到底怎么玩呀?**6.10.1 底层驱动****首先,模仿STM32编写底层驱动**,主要实现的函数如表5所示。要注意的是,STM32的SPI接口类型和LPC的不一样,要进行修改。[align=center]** 表5 lpc_ssp.c 要实现的函数**[align=center][table=517] [tr][td=72] [align=center]序号 [/td][td=257]函数名[/td][td=188]功能 [/td][/tr] [tr][td=72][align=center]1[/td][td=257][align=right]lpc_ssp_register[/td][td=188][align=right]注册[/td][/tr] [tr][td=72][align=center]2[/td][td=257][align=right]rt_ssp_xfer[/td][td=188][align=right]数据发送函数[/td][/tr] [tr][td=72][align=center]3[/td][td=257][align=right]rt_ssp_configure[/td][td=188][align=right]配置函数[/td][/tr] [tr][td=3,1,517]说明:ssp接口初始化函数被rt_ssp_configure所调用,属于它的一部分。[/td][/tr] [/table] 其中最主要的还是那个发送函数:【基本是直接参考着修改的,没有什么难度】 ```//发送数据 static rt_uint32_t rt_ssp_xfer(struct rt_spi_device* device, struct rt_spi_message* message) { struct lpc_ssp_bus * spi_bus = (struct lpc_ssp_bus *)device->bus; struct rt_spi_configuration * config = &device->config; LPC_SSP_TypeDef * SPI = spi_bus->SPI; struct lpc_spi_cs * spi_cs = device->parent.user_data; rt_uint32_t size = message->length; if((SPI != LPC_SSP0)&&(SPI != LPC_SSP1)) { return 0; } /* take CS */ if(message->cs_take && spi_cs) { GPIO_ResetBits(spi_cs->GPIOx, spi_cs->GPIO_Pin); } /* send dat */ // #ifdef SPI_USE_DMA // #endif /*SPI_USE_DMA*/ { if(config->data_width <= 8) { const rt_uint8_t * send_ptr = message->send_buf; rt_uint8_t * recv_ptr = message->recv_buf; while(size--) { rt_uint8_t data = 0xFF; if(send_ptr != RT_NULL) { data = *send_ptr++; } //Wait until the transmit buffer is empty while ( SPI->SR & (1 << SSPSR_BSY) ); /* Wait for transfer to finish */ // Send the byte SPI->DR = data; while ( SPI->SR & (1 << SSPSR_BSY) ); /* Wait for transfer to finish */ //Wait until a data is received while( !( SPI->SR & ( 1 << SSPSR_RNE ) ) ); /* Wait untill the Rx FIFO is not empty */ // Get the received data data = SPI->DR; if(recv_ptr != RT_NULL) { *recv_ptr++ = data; } }//end while } else if(config->data_width <= 16) { const rt_uint16_t * send_ptr = message->send_buf; rt_uint16_t * recv_ptr = message->recv_buf; while(size--) { rt_uint16_t data = 0xFF; if(send_ptr != RT_NULL) { data = *send_ptr++; } //Wait until the transmit buffer is empty while ( SPI->SR & (1 << SSPSR_BSY) ); /* Wait for transfer to finish */ // Send the byte SPI->DR = data; while ( SPI->SR & (1 << SSPSR_BSY) ); /* Wait for transfer to finish */ //Wait until a data is received while( !( SPI->SR & ( 1 << SSPSR_RNE ) ) ); /* Wait untill the Rx FIFO is not empty */ // Get the received data data = SPI->DR; if(recv_ptr != RT_NULL) { *recv_ptr++ = data; } }//end while } }/* send dat end line*/ /* release CS */ if(message->cs_release && spi_cs) { GPIO_SetBits(spi_cs->GPIOx, spi_cs->GPIO_Pin); } return message->length; } ``` **6.10.2 中间接口层**中间层**定义初始化,读函数和写函数**,对上层统一提供接口。中间层使用SPI时,先注SPI总线,然后使用attch一个CS上去,产生新总线,然后再将flash设备注册到这个新的总线上来。 //1.注册spi总线 spi1//2.attch 一个片选 产生spi10//3.注册flash到spi10总线上 **总线初始化**```void hw_ssp0_init(void) { #ifdef RT_USING_LPC_SSP0 /* register spi bus */ { static struct lpc_ssp_bus lpc_bus_ssp0; lpc_ssp_register(LPC_SSP0, &lpc_bus_ssp0, "spi2");//注册真实的总线 } /* attach cs */ { static struct rt_spi_device ssp0_dev1; static struct lpc_spi_cs ssp0_cs1; ssp0_cs1.GPIOx = LPC_GPIO1; ssp0_cs1.GPIO_Pin = 18; rt_spi_bus_attach_device(&ssp0_dev1, "spi20", "spi2", (void*)&ssp0_cs1); //注册虚拟总线 } /* attach cs */ { static struct rt_spi_device ssp0_dev2; static struct lpc_spi_cs ssp0_cs2; ssp0_cs2.GPIOx = LPC_GPIO1; ssp0_cs2.GPIO_Pin = 21; rt_spi_bus_attach_device(&ssp0_dev2, "spi21", "spi2", (void*)&ssp0_cs2); //注册虚拟总线 } #endif /* RT_USING_LCP_SSP0 */ } ```**Flash注册** ```void rt_hw_flash_init(void) { //SPI FLASH SCK0=p1.20,MOSI0=p1.24,MISO0=p1.23,SEL0=p1.21,CS=p1.18, if(RT_EOK == sst25vfxx_init("flash0","spi20",0)) { rt_device_t spi_bus = rt_device_find("flash0");//1.根据名字找到总线 struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; flash_chip_mgr.chip_size = sst_flash->geometry.bytes_per_sector * sst_flash->geometry.sector_count; flash_chip_mgr.ok_num++; } else { FLASH_TRACE("U30 chip init failed!
"); __NOP(); } if(RT_EOK == sst25vfxx_init("flash1","spi21",1)) { rt_device_t spi_bus = rt_device_find("flash0");//1.根据名字找到总线 struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; flash_chip_mgr.chip_size = sst_flash->geometry.bytes_per_sector * sst_flash->geometry.sector_count; flash_chip_mgr.ok_num++; } else { FLASH_TRACE("U28 chip init failed!
"); __NOP(); } if(flash_chip_mgr.ok_num != 0) { flash_chip_mgr.tatle_size = flash_chip_mgr.chip_size * flash_chip_mgr.ok_num; } else { FLASH_TRACE("no flash chip detection
"); __NOP(); } } ``` **读函数** ```rt_err_t rt_flash_read(uint32_t pos, uint8_t *buffer, uint32_t size) { rt_device_t spi_bus; /* get physical spi bus */ if(rt_getcs_chip(pos,size) == 0) { return RT_ERROR;//flash_chip_mgr.select=0 } if(flash_chip_mgr.select == 2) { spi_bus = rt_device_find("flash0"); if (spi_bus != RT_NULL && spi_bus->type == RT_Device_Class_Block) { struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; if (!(sst_flash->flash_device.flag & RT_DEVICE_FLAG_ACTIVATED)) { rt_device_open(&sst_flash->flash_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); } rt_device_read(&sst_flash->flash_device, pos, buffer, flash_chip_mgr.leng); //---------------------------------操作长度变更---------------------- buffer += (flash_chip_mgr.leng); size -= flash_chip_mgr.leng; pos = 0; spi_bus = rt_device_find("flash1"); //接操作第2个flash } else { return RT_ERROR; } } else //1 { if(flash_chip_mgr.start == 0) { spi_bus = rt_device_find("flash0"); } else { spi_bus = rt_device_find("flash1"); } } /* get physical spi bus */ if (spi_bus != RT_NULL && spi_bus->type == RT_Device_Class_Block) { struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; if (!(sst_flash->flash_device.flag & RT_DEVICE_FLAG_ACTIVATED)) { rt_device_open(&sst_flash->flash_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); } //buffer 和 size都是经过计算的 rt_device_read(&sst_flash->flash_device, pos, buffer, size); return RT_EOK; } return RT_ERROR; } ``` **写函数** ```rt_err_t rt_flash_write(uint32_t pos, const uint8_t *buffer, uint32_t size) { rt_device_t spi_bus; /* get physical spi bus */ if(rt_getcs_chip(pos,size) == 0) { return RT_ERROR;//flash_chip_mgr.select=0 } if(flash_chip_mgr.select == 2) { spi_bus = rt_device_find("flash0"); //先写入一定长度 if (spi_bus != RT_NULL && spi_bus->type == RT_Device_Class_Block) { struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; if (!(sst_flash->flash_device.flag & RT_DEVICE_FLAG_ACTIVATED)) { rt_device_open(&sst_flash->flash_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); } //写入的数据长度和要写的长度一致 if(size == rt_device_write(&sst_flash->flash_device, pos, buffer, flash_chip_mgr.leng)) { //---------------------------------操作长度变更---------------------- buffer += (flash_chip_mgr.leng); size -= flash_chip_mgr.leng; pos = 0; //换片子了指向首地址 spi_bus = rt_device_find("flash1"); //接操作第2个flash } else { return RT_EIO; //长度不一样 } } else { return RT_EIO; //长度不一样 } } else if(flash_chip_mgr.start == 0) { spi_bus = rt_device_find("flash0"); } else { spi_bus = rt_device_find("flash1"); } if (spi_bus != RT_NULL && spi_bus->type == RT_Device_Class_Block) { struct spi_flash_sst25vfxx * sst_flash = (struct spi_flash_sst25vfxx *)spi_bus; if (!(sst_flash->flash_device.flag & RT_DEVICE_FLAG_ACTIVATED)) { rt_device_open(&sst_flash->flash_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); } //写入的数据长度和要写的长度一致 if(size == rt_device_write(&sst_flash->flash_device, pos, buffer, size)) { return RT_EOK; } return RT_EIO; //长度不一样 } return RT_ERROR;//没有找到设备 } ```总之呢,这里是将两个Flash合并看成是一个Flash来操作的啦。没有什么技术含量,只要对地址进行计算,得到片选序号和片内偏移地址,就可以进行操作了。 **6.10.3上层应用** //1.调用总线初始化voidhw_ssp0_init**(**void**);**//2.设备初始化void rt_hw_flash_init**(**void**);**//3.读写函数rt_err_trt_flash_write**(**uint32_t pos**,** const uint8_t *****buffer**,** uint32_t size**)****;**rt_err_trt_flash_read**(**uint32_t pos**,** uint8_t *****buffer**,** uint32_t size**);** **6.11 总结**1、对现有的库文件,进行修改,添加了字节写入函数,解决非对齐写入问题;2、修改现有库文件,添加数组,按序号进行初始化,以免重复初始化(否则会卡住);3、复制stm32 spi驱动,对比修改;4、添加中间接口层,为上层应用提供简单的接口; 文档下载: ![RT_Nano_V3初级教程_6 SPI_Flash.pdf](/uploads/201803/26/185103rpczxu9g44gwcz49.attach)
查看更多
2
个回答
默认排序
按发布时间排序
armink
2018-03-26
这家伙很懒,什么也没写!
赞啊,wlof 的文章越来越有深度了。
wlof
2018-03-27
这个家伙不懒,什么也没写
>赞啊,wlof 的文章越来越有深度了。 --- 谢谢鼓励:handshake
撰写答案
登录
注册新账号
关注者
0
被浏览
3.1k
关于作者
wlof
这个家伙不懒,什么也没写
提问
24
回答
64
被采纳
0
关注TA
发私信
相关问题
1
BBB的SPI驱动
2
求个SPI上挂两个或多个设备的使用例子
3
SPI设备有个bug
4
spi flash 的fatfs使用一段时间后读写文件出现故障
5
SPI驱动
6
请教rt_spi_configure函数理解
7
SPI FLASH挂载的问题
8
SPI-FLASH 文件系统 SPIFFS
9
求助一个完整的 spi flash 驱动
10
关于同时使用文件系统与SPI FLASH的问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
CherryUSB的bootuf2配置
2
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
3
GD32F450 片内 flash驱动适配
4
STM32H7R7运行CherryUSB
5
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
12
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
7
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部