这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
cc2640r2f:spi [2017/09/02 11:28] long |
cc2640r2f:spi [2021/06/22 23:14] (当前版本) |
||
---|---|---|---|
行 1: | 行 1: | ||
< | < | ||
- | # SPI驱动 # | + | |
- | 这一节我们详细讲解TI CC13x0/ | + | # SPI 驱动 # |
+ | |||
+ | 这一节详细讲解 TI CC13x0/ | ||
## 概述 ## | ## 概述 ## | ||
- | SPI(Serial Perripheral Interface),串行外设接口,是Motorala公司推出的一种同步串行接口技术,它能够使MCU以全双工(数据能够同时进行双向传输)的同步串行方式与各种外围设备进行高速数据通信。它主要用在EEPROM、Flash、实时时钟(RTC)、数模转换器(ADC)、数字信号处理器(DSP)以及数字信号解码器之间,他只占用芯片的4根管脚来实现控制和数据传输,现在很多芯片上都集成了SPI技术。SPI有时也称为SSI(同步串行接口)。SPI的控制方式采用主-从模式(Master-Slave)。 | + | |
- | SPI驱动程序能够驱动芯片与外围设备在SPI总线上进行数据的传输和接收。SPI驱动程序接口是典型的RTOS应用程序调用接口,它们被重定向到`SPI_FxnTalble`中的指针指定的特定驱动程序实现,增强了程序的移植性。 | + | SPI (Serial Perripheral Interface)串行外设接口,是 Motorala 公司推出的一种同步串行接口技术。它能够使 MCU 以全双工(数据能够同时进行双向传输)的同步串行方式与各种外围设备进行高速数据通信。 |
- | ## SPI驱动的分层实现 ## | + | |
- | 虽然我们在应用层直接调用几个驱动接口就可以在SPI总线上发送数据,但是在驱动程序内部从接口函数到底层硬件操作是通过了多层封装的。如图1所示是SPI驱动程序的分层实现图: | + | SPI 主要用在 EEPROM 、 Flash 、实时时钟( RTC )、数模转换器( ADC )、数字信号处理器( DSP )以及数字信号解码器之间。只占用芯片的 4 根管脚来实现控制和数据传输,现在很多芯片上也集成了 SPI 技术。SPI 有时也称为 SSI(同步串行接口),SPI 的控制方式采用主-从模式( Master - Slave )。 |
+ | |||
+ | SPI 驱动程序能够驱动芯片与外围设备在 SPI 总线上进行数据的传输和接收。SPI 驱动程序接口是典型的 RTOS 应用程序调用接口,它们被重定向到 `SPI_FxnTalble` 中指针指定的特定驱动程序,增强了程序的移植性。 | ||
+ | |||
+ | ## SPI 驱动的分层实现 ## | ||
+ | |||
+ | 虽然在应用层直接调用几个驱动接口就可以在 SPI 总线上发送数据,但驱动程序内部从接口函数到底层硬件的操作是多层封装的。如图 1 所示是SPI驱动程序的分层实现图。 | ||
![](http:// | ![](http:// | ||
- | 图1 SPI驱动程序的分层实现 | + | 图1. SPI 驱动程序的分层实现 |
- | 由图1我们可以看到,应用程序开发者只需要直接调用中间件层的驱动接口(例如:SPI_init,SPI_open等等)就可以实现SPI的驱动功能,这里的中间件层就是我们程序中的SPI.c和SPI.h所在层。这一层规范统一了应用程序的调用接口,也就是说对于TI不同类型的芯片平台它们在这一层给出的接口都是一样的。应用层都是调用相同的接口来实现SPI功能,这样做的好处在于增强了程序的可移植性,不管你的平台怎么换,我的应用程序都是不变的,因为驱动的接口相同。 | + | |
+ | 图 1 表明开发者只需要直接调用中间件层的驱动接口(例如:SPI_init、SPI_open 等等)就可以实现 SPI 的驱动功能。中间件层就是程序中的 SPI.c 和 SPI.h 所在层。 | ||
+ | |||
+ | 中间件层规范统一了应用程序的调用接口,对于 TI 不同类型的芯片平台在这一层给出的接口是一样的,应用层都是调用相同的接口来实现 SPI 功能。这样做的好处是增强了程序的可移植性,不管你的平台怎么换,应用程序都是不变的。 | ||
+ | |||
+ | 中间件层往下就是业务逻辑层,从业务逻辑层开始往下根据不同的芯片平台其接口封装实现就不尽相同了。以CC26XX芯片平台为例,业务逻辑层就位于 SPICC26XXDMA.c 和 SPICC26XXDMA.h 所在的层。 | ||
+ | |||
+ | 业务逻辑层采用 DMA 的数据传输方式,主要操控 DMA 进行数据传输以及调用驱动库中的一些函数实现相应功能。需要注意的是:这一层封装的驱动接口函数被全部放在一个函数指针结构体中。 | ||
+ | |||
+ | 如清单 1 所示,中间件层不直接调用这些驱动接口,而是通过一个配置文件( CC2640R2_LAUNCHXL.c )将装有驱动接口指针的结构体指针注册到 SPI_config 中。 | ||
+ | |||
+ | 如清单 2 所示,这样中间件层通过调用 SPI_config 中的结构体指针就可以指定使用业务逻辑层的驱动接口了。 | ||
+ | |||
+ | 清单1.业务逻辑层驱动接口指针结构体 | ||
- | 中间件层往下就是业务逻辑层,从业务逻辑层开始往下根据不同的芯片平台其接口封装实现就不尽相同了。这里我们以CC26XX芯片平台为例,业务逻辑层就位于SPICC26XXDMA.c和SPICC26XXDMA.h所在的层。这里采用DMA的数据传输方式,所以这一层主要在操控DMA以进行数据传输以及调用驱动库中的一些函数实现相应功能。需要注意的是这一层封装的驱动接口函数被全部放在一个函数指针结构体中,如List1所示,中间件层不直接调用这些驱动接口,而是通过一个配置文件(CC2640R2_LAUNCHXL.c)将装有驱动接口指针的结构体指针注册到SPI_config中,如List2所示,这样中间件层通过调用SPI_config中的结构体指针就可以指定使用业务逻辑层的驱动接口了。 | ||
- | List1: | ||
```C | ```C | ||
const SPI_FxnTable SPICC26XXDMA_fxnTable = { | const SPI_FxnTable SPICC26XXDMA_fxnTable = { | ||
行 25: | 行 45: | ||
}; | }; | ||
``` | ``` | ||
- | List2:SPI_config中的驱动接口结构体指针注册 | + | |
+ | 清单2. | ||
```C | ```C | ||
const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = { | const SPI_Config SPI_config[CC2640R2_LAUNCHXL_SPICOUNT] = { | ||
行 40: | 行 62: | ||
}; | }; | ||
``` | ``` | ||
- | 业务逻辑层再往下就是驱动库层(driver library),业务逻辑层会直接调用这一层的接口函数来来进行相应逻辑操作的。驱动库层位于ssi.c和ssi.h所在的层,这一层就开始与硬件接触,进行相应寄存器操作来实现SPI驱动了。 | + | |
- | ## SPI的驱动配置 ## | + | 业务逻辑层再往下就是驱动库层( driver library ),业务逻辑层直接调用这一层的接口函数进行相应逻辑操作。驱动库层位于 ssi.c 和 ssi.h 所在的层,这一层就开始与硬件接触,进行相应寄存器操作来实现 SPI 驱动了。 |
- | 在上文中我们已经提到SPI的配置数组SPI_config[],它位于相应芯片平台的配置文件中,这里我们以CC26XX芯片平台为例,其配置文件为CC2640R2_LAUNCHXL.c。如List3所示,是CC2640R2_LAUNCHXL.c中关于SPI的配置代码段。 | + | |
- | List3:UART的配置代码段 | + | ## SPI 的驱动配置 ## |
+ | |||
+ | 之前已经提到 SPI 的配置数组 SPI_config[],它位于相应芯片平台的配置文件中。以 CC26XX 芯片平台为例,其配置文件为 CC2640R2_LAUNCHXL.c 。清单 3 是 CC2640R2_LAUNCHXL.c 中关于 SPI 的配置代码段。 | ||
+ | |||
+ | 清单3. | ||
```C | ```C | ||
/* | /* | ||
行 80: | 行 107: | ||
const uint_least8_t SPI_count = CC2640R2_LAUNCHXL_SPICOUNT; | const uint_least8_t SPI_count = CC2640R2_LAUNCHXL_SPICOUNT; | ||
``` | ``` | ||
- | 我们可以看到SPI_config[]数组中的元素有三个参数,分别是`.fxnTablePtr`,`.object`,`.hwAttrs`,下面我们分别来看一下这三个参数的意义。 | + | |
- | * **.fxnTablePtr** | + | 可以看到 SPI_config[] 数组中的元素有三个参数:分别是 `.fxnTablePtr` |
- | `.fxnTablePtr`里面放的就是我们驱动具体的实现函数,这些驱动函数就是来自业务逻辑层。我们看到这里它被赋值成`SPICC26XXDMA_fxnTable`的指针,这个结构体就是List1中所示的业务逻辑层的驱动函数列表,在这里进行赋值配置之后,中间层的接口函数就可以链接使用它们了。 | + | 下面分别来看一下这三个参数的意义。 |
- | * **.object** | + | |
- | `.object`是用来存放SPI的各种参数数据的,例如控制参数,传输参数等。 | + | * ** .fxnTablePtr ** |
- | * **.hwAttrs** | + | `.fxnTablePtr` 里面是驱动具体的实现函数,这些驱动函数就是来自业务逻辑层。它被赋值成 `SPICC26XXDMA_fxnTable` 的指针,这个结构体就是清单 1 中所示的业务逻辑层的驱动函数列表。在这里进行赋值配置之后,中间层的接口函数就可以链接使用它们了。 |
- | `.hwAttrs`是用来存放SPI硬件配置参数的数组,如List3所示,这些硬件参数是需要我们在使用SPI之前需要配置好的。 | + | * ** .object ** |
- | ## SPI驱动接口 ## | + | `.object` 是用来存放 SPI 的各种参数数据的。例如控制参数、传输参数等。 |
- | 在SPI的中间件文件SPI.c和SPI.h中,我们看到一共给出了7个接口函数,下面我们分别看一下每个接口函数的功能,形参,返回值以及注意事项。 | + | * ** .hwAttrs ** |
+ | `.hwAttrs` 是用来存放 SPI 硬件配置参数的数组。如清单 3 所示,这些硬件参数需要在使用 SPI 之前需要配置好。 | ||
+ | |||
+ | ## SPI 驱动接口 ## | ||
+ | |||
+ | 在 SPI 的中间件文件 SPI.c 和 SPI.h 中,一共给出了 7 个接口函数。下面分别看一下每个接口函数的功能、形参、返回值以及注意事项。 | ||
1. `void SPI_init(void)` | 1. `void SPI_init(void)` | ||
- | * 函数功能:利用配置文件中设置的配置参数初始化SPI模块 | + | |
+ | * 函数功能:利用配置文件中设置的配置参数初始化 SPI 模块 | ||
* 形参:无 | * 形参:无 | ||
* 返回值:无 | * 返回值:无 | ||
- | * 注意事项:在调用此函数之前,SPI_config结构体中的参数必须配置好;此函数必须先于其他任何SPI接口函数调用。 | + | * 注意事项:在调用此函数之前 SPI_config 结构体中的参数必须配置好,此函数必须先于其他任何 SPI 接口函数调用。 |
- | 2. `void SPI_Params_init(SPI_Params *params)` | + | |
- | * 函数功能:将SPI_Params结构体中的参数初始化为默认值 | + | 2. `void SPI_Params_init( SPI_Params *params )` |
- | * 形参:`params`需要进行初始化的SPI_Params结构体的指针 | + | |
+ | * 函数功能:将 SPI_Params 结构体中的参数初始化为默认值 | ||
+ | * 形参:`params` 需要进行初始化的 SPI_Params 结构体的指针 | ||
* 返回值:无 | * 返回值:无 | ||
- | * 注意事项:初始化SPI_Params结构体的默认值如List4所示 | + | * 注意事项:初始化 SPI_Params 结构体的默认值如清单 4 所示 |
- | List4:初始化SPI_Params结构体的默认值 | + | |
+ | 清单4. | ||
```C | ```C | ||
- | /*!< 默认的SPI parameters 结构体 */ | + | /*!< 默认的 SPI parameters 结构体 */ |
const SPI_Params SPI_defaultParams = { | const SPI_Params SPI_defaultParams = { | ||
SPI_MODE_BLOCKING, | SPI_MODE_BLOCKING, | ||
行 113: | 行 151: | ||
}; | }; | ||
``` | ``` | ||
+ | |||
3. `SPI_Handle SPI_open(uint_least8_t index, SPI_Params *params)` | 3. `SPI_Handle SPI_open(uint_least8_t index, SPI_Params *params)` | ||
- | * 函数功能:打开给定的SPI外设 | + | |
- | * 形参:`index`编入SPI_config中的外设索引,`params`指向SPI_Params参数块的指针,如果为NULL就使用默认值 | + | * 函数功能:打开给定的 SPI 外设 |
- | * 返回值:`SPI_Handle`如果成功打开了SPI接口就返回此接口的参数配置数组(SPI_config)的句柄,如果发生错误则返回NULL | + | * 形参:`index` 编入 SPI_config 中的外设索引。`params` 指向 SPI_Params 参数块的指针,如果为 NULL 就使用默认值 |
- | * 注意事项:在调用SPI_open()之前,应先调用SPI_init()进行了初始化 | + | * 返回值:`SPI_Handle` 如果成功打开了 SPI 接口就返回此接口的参数配置数组(SPI_config)的句柄,如果发生错误则返回 NULL |
+ | * 注意事项:在调用 SPI_open()之前,应先调用 SPI_init()进行了初始化。 | ||
4. `bool SPI_transfer(SPI_Handle handle, SPI_Transaction *transaction)` | 4. `bool SPI_transfer(SPI_Handle handle, SPI_Transaction *transaction)` | ||
- | * 函数功能:执行SPI事务,发送/ | + | |
- | * 形参:`handle`SPI_open()中返回的句柄,`transaction`指向传输数据结构 | + | * 函数功能:执行 SPI 事务,发送/ |
- | * 返回值:`bool`SPI_transfer是否启动成功,启动成功返回true,否则返回false | + | * 形参:`handle` SPI_open()中返回的句柄。`transaction` 指向传输数据结构 |
- | * 注意事项:执行SPI事务有两种模式:**SPI_MODE_BLOCKING**和**SPI_MODE_CALLBACK**,在**SPI_MODE_BLOCKING**模式下,SPI_transfer将阻止任务的执行,直达事务完成。在**SPI_MODE_CALLBACK**模式下,SPI_transfer会调用**SPI_CallbackFxn**,不会阻止任务执行 | + | * 返回值:`bool` SPI_transfer 是否启动成功。启动成功返回 true,否则返回 false |
+ | * 注意事项:执行 SPI 事务有两种模式:** SPI_MODE_BLOCKING **和** SPI_MODE_CALLBACK **。在** SPI_MODE_BLOCKING **模式下, SPI_transfer 将阻止任务的执行,直达事务完成。在** SPI_MODE_CALLBACK **模式下,SPI_transfer 会调用** SPI_CallbackFxn **,不会阻止任务执行。 | ||
5. `void SPI_transferCancel(SPI_Handle handle)` | 5. `void SPI_transferCancel(SPI_Handle handle)` | ||
- | * 函数功能:取消执行SPI事务 | + | |
- | * 形参:`handle`SPI_open()中返回的句柄 | + | * 函数功能:取消执行 SPI 事务 |
+ | * 形参:`handle` SPI_open() 中返回的句柄 | ||
* 返回值:无 | * 返回值:无 | ||
- | * 注意事项:在**SPI_MODE_BLOCKING**中,SPI_transferCancel()不起作用。在**SPI_MODE_CALLBACK**中,如果SPI_transfer()正在进行中,SPI_transferCancel()将停止SPI传输。 | + | * 注意事项:在** SPI_MODE_BLOCKING **中,SPI_transferCancel()不起作用。在** SPI_MODE_CALLBACK **中,如果 SPI_transfer()正在进行中,SPI_transferCancel()将停止 SPI 传输。 |
6. `int_fast16_t SPI_control(SPI_Handle handle, uint_fast16_t cmd, void *controlArg)` | 6. `int_fast16_t SPI_control(SPI_Handle handle, uint_fast16_t cmd, void *controlArg)` | ||
- | * 函数功能:在给定的SPI接口上执行特定的操作 | + | |
- | * 形参:`handle`SPI_open()中返回的句柄;`cmd`特定的处理命令(该命令可以来自SPI.h或者逻辑应用层的.h文件,例如SPICC26XX.h);`controlArg`伴随`cmd`的可选择的读写命令 | + | * 函数功能:在给定的 SPI 接口上执行特定的操作 |
+ | * 形参:`handle` SPI_open()中返回的句柄。`cmd` 特定的处理命令(该命令可以来自 SPI.h 或者逻辑应用层的 .h 文件,例如 SPICC26XX.h )。 `controlArg` 伴随 `cmd` 的可选择的读写命令 | ||
* 返回值:不同命令处理的特定返回值,如果为负值表示操作不成功 | * 返回值:不同命令处理的特定返回值,如果为负值表示操作不成功 | ||
- | * 注意事项:在调用该函数之前必须先调用SPI_open() | + | * 注意事项:在调用该函数之前必须先调用 SPI_open() |
7. `void SPI_close(SPI_Handle handle)` | 7. `void SPI_close(SPI_Handle handle)` | ||
- | * 函数功能:关闭指定的SPI外设接口 | + | |
- | * 形参:`handle`SPI_open()中返回的句柄 | + | * 函数功能:关闭指定的 SPI 外设接口 |
+ | * 形参:`handle` SPI_open() 中返回的句柄 | ||
* 返回值:无 | * 返回值:无 | ||
- | * 注意事项:在调用该函数之前,必须先调用SPI_close() | + | * 注意事项:在调用该函数之前,必须先调用 SPI_close() |
- | ## SPI数据传输的程序实现 ## | + | |
- | 下面我们调用SPI接口函数,将SPI设置为Master,实现SPI的数据传输。这里我们直接给出函数的实现。 | + | ## SPI 数据传输的程序实现 ## |
+ | |||
+ | 下面调用 SPI 接口函数,将 SPI 设置为 Master,实现 SPI 的数据传输。 | ||
```C | ```C | ||
SPI_Handle | SPI_Handle | ||
行 163: | 行 214: | ||
} | } | ||
``` | ``` | ||
- | 1. 我们可以看到,程序首先调用`SPI_init()`,利用SPI_config中的配置参数对SPI进行初始化,这一步函数的调用必须要在其他SPI接口函数调用之前进行,且SPI_config中的配置参数已经设置完成。 | + | |
- | 2. 调用`SPI_Params_init()`将SPI_Params中的参数全部初始化为默认值,这些默认值在该接口函数说明中已经给出。 | + | 1. 可以看到程序首先调用 `SPI_init()`,利用 SPI_config 中的配置参数对 SPI 进行初始化。这一步函数的调用必须要在其他 SPI 接口函数调用之前进行,且 SPI_config 中的配置参数已经设置完成。 |
- | 3. 对于各自的应用,SPI_Params结构体中的参数值可能和默认值不同,接下来可以对它们进行相应的修改。 | + | 2. 调用 `SPI_Params_init()` 将 SPI_Params 中的参数全部初始化为默认值,这些默认值在该接口函数说明中已经给出。 |
- | 4. 调用`SPI_open()`打开对应的SPI外设接口 | + | 3. 各自应用的 SPI_Params 结构体中的参数值可能和默认值不同,接下来可以对它们进行相应的修改。 |
- | 5. 为数据传输结构体`SPI_Transaction`进行赋值,设置数据接收和传输缓冲区,以及传输事务的帧数(MSGSIZE)。 | + | 4. 调用 `SPI_open()` 打开对应的 SPI 外设接口 |
- | 6. 调用`SPI_transfer()`开始进行数据传输。我们看到在结构体`SPI_Transaction`中有两个参数,分别是`*txBuf`(输入缓冲区),`*rxBuf`(输出缓冲区),当我们调用`SPI_transfer()`进行数据输出的时候,我们只需要将数据放入`*txBuf`中,调用函数之后数据就会传输出去;当我们调用`SPI_transfer()`进行数据接收的时候更加方便,调用函数之后,接收到的数据直接就会放入`*rxBuf`中。至此我们就完成了SPI接口的打开与数据传输。 | + | 5. 对数据传输结构体 `SPI_Transaction` 进行赋值,设置数据接收和传输缓冲区、以及传输事务的帧数( MSGSIZE )。 |
- | 7. 根据以上接口的调用我们在例程SPIFlash.c中又写了一下函数,根据上面的调用实现,应该不难理解。 | + | 6. 调用 `SPI_transfer()` 开始进行数据传输。结构体 `SPI_Transaction` 中有两个参数,分别是 `*txBuf` (输入缓冲区)和 `*rxBuf` (输出缓冲区)。当调用 `SPI_transfer()` 进行数据输出的时候,只需要将数据放入 `*txBuf` 中,调用函数之后数据就会传输出去。当调用 `SPI_transfer()` 进行数据接收的时候更加方便,调用函数之后,接收到的数据直接就会放入 `*rxBuf` 中。至此就完成了 SPI 接口的打开与数据传输。 |
+ | 7. 根据以上接口的调用在例程 SPIFlash.c 中又写了一下函数,有了上面的调用实现,下面的代码应该不难理解。 | ||
```C | ```C | ||
static bool Spi_Open(uint32_t bitRate) { | static bool Spi_Open(uint32_t bitRate) { | ||
行 211: | 行 264: | ||
} | } | ||
``` | ``` | ||
- | ## 使用SPI从Flash中读取数据示例 ## | + | |
- | 在上面中我们已经讲过,芯片与外围设备之间的SPI接口是通过4个管脚连接进行控制和数据交换的,我们要通过SPI进行数据传输就必须有效控制这些管脚,打通数据交换链路。其次就是要根据外围设备自己的特性进行相关处理命令控制以读取写入数据。图2展示了数据通过SPI在Master和Slave之间的传输示意图: | + | ## 使用 SPI 从 Flash 中读取数据示例 ## |
+ | |||
+ | 芯片与外围设备之间的 SPI 接口是通过 4 个管脚连接进行控制和数据交换的,如果想采用 | ||
+ | |||
+ | 图 2 展示了数据通过 SPI 在 Master 和 Slave 之间的传输示意图。 | ||
![](http:// | ![](http:// | ||
- | * **SS/ | + | 图2. Master 和 Slave 数据通过 SPI 传输过程 |
- | * **SCK**(Serial Clock)主要的作用是 Master 设备往 Slave 设备传输时钟信号, | + | |
- | * **SDO/ | + | * ** SS/CS **( Slave Select/Chip Select ), 用于 Master 设备片选Slave设备, |
- | * **SDI/ | + | * ** SCK **( Serial Clock ),用于 Master 设备往 Slave 设备传输时钟信号, |
+ | * ** SDO/MOSI **( Serial Data Output/ | ||
+ | * ** SDI/MISO **( Serial Data Input/ | ||
+ | |||
+ | 调用 SPI 接口已经能够驱动芯片进行数据传输了,下面来讲讲要使 Flash 能够被写入和读出数据需要做哪些设置。 | ||
+ | |||
+ | 以[成都乐控畅联科技有限公司](http:// | ||
- | 上面我们调用SPI接口已经能够驱动芯片进行数据传输了,下面我们来讲讲要使Flash能够被写入和读出数据需要做哪些设置。这里我们利用以[成都乐控畅联科技有限公司](http:// | + | W25Q80BV 的 datasheet 管脚封装图如图 3 所示。 |
- | 在W25Q80BV的datasheet上我们可以看见其管脚封装图如图3所示: | + | |
![](http:// | ![](http:// | ||
- | 它与CC2640R2F之间的硬件连接已按照图2的原理连接好。我们看到管脚1是CS端,用于使能选中该Flash,所以在每次传输数据的时候我们首先需要将该管脚置为**低电平**来使能选中该Flash,不进行数传输的时候置为**高电平**,使其失能。在示例程序中,List1和List2就是在做这件事。 | + | 图3. 管脚封装图 |
- | List1:使能Flash | + | |
+ | 它与 CC2640R2F 之间的硬件连接已按照图 2 的原理连接好。管脚 1 是 CS 端,用于使能选中该 Flash。在每次传输数据的时候首先需要将该管脚置为**低电平**来使能选中该 Flash,不进行数传输的时候置为**高电平**,使其失能。 | ||
+ | |||
+ | 在示例程序中,清单 1 和清单 2 就是在做这件事。 | ||
+ | |||
+ | 清单1.使能 Flash | ||
```C | ```C | ||
static void flashSelect(void){ | static void flashSelect(void){ | ||
行 233: | 行 301: | ||
} | } | ||
``` | ``` | ||
- | List2:失能Flash | + | |
+ | 清单2.使能 Flash | ||
```C | ```C | ||
static void flashDeSelect(void){ | static void flashDeSelect(void){ | ||
行 239: | 行 309: | ||
} | } | ||
``` | ``` | ||
- | 通过阅读W25Q80BV的datasheet我们可以知道要对其进行读写操作是要写入相应指令的,每条指令都有其时序图。表1列出了部分指令,完整的指令集请查看其datasheet。 | + | |
- | 表1: W25Q80BV部分指令 | + | 阅读 W25Q80BV 的 datasheet |
+ | |||
+ | 表1. W25Q80BV 部分指令 | ||
|指令名|指令码| | |指令名|指令码| | ||
行 252: | 行 324: | ||
|读Manufacturer/ | |读Manufacturer/ | ||
- | List3展示了程序中对这些指令的宏定义。 | + | 清单 3 展示了程序中对这些指令的宏定义。 |
- | List3:W25Q80BV部分指令的宏定义 | + | |
+ | 清单3. | ||
```C | ```C | ||
#define FLASH_WRITE_ENABLE | #define FLASH_WRITE_ENABLE | ||
行 263: | 行 337: | ||
#define FLASH_READ_MANUDEVID | #define FLASH_READ_MANUDEVID | ||
``` | ``` | ||
- | 看了指令,我们再来看看相应指令的时序图。 | ||
- | ![图4.写使能时序图](http:// | + | 看完指令再来看看相应指令的时序图。 |
+ | |||
+ | ![](http:// | ||
图4.写使能时序图 | 图4.写使能时序图 | ||
- | ![图5.写失能时序图](http:// | + | ![](http:// |
- | 图5.写失能时序图 | + | |
+ | 图5.写使能时序图 | ||
+ | |||
+ | ![](http:// | ||
- | ![图6.读数据时序图](http:// | ||
图6.读数据时序图 | 图6.读数据时序图 | ||
- | ![图7.页编程时序图](http:// | + | ![](http:// |
图7.页编程时序图 | 图7.页编程时序图 | ||
- | ![图8.读readManufacturerDeviceID时序图](http:// | + | ![](http:// |
+ | 图8.读 readManufacturerDeviceID 时序图 | ||
+ | |||
+ | 如果想读出该 Flash 的 Manufacturer/ | ||
+ | |||
+ | 1. 写入**写使能**指令,保证可以向 Flash 中写指令和数据。清单 4 是这一步的代码实现。 | ||
+ | |||
+ | 清单4.写使能函数 | ||
- | 所以我们要想读出该Flash的Manufacturer/ | ||
- | 1. 写入**写使能**指令,保证我们能向Flash中写指令和数据。List4是这一步的代码实现。 | ||
- | List4: | ||
```C | ```C | ||
static bool Flash_WriteEnable(void) { | static bool Flash_WriteEnable(void) { | ||
行 293: | 行 376: | ||
} | } | ||
``` | ``` | ||
- | 2. 写入**读Manufacturer/ | + | |
- | 3. 写入地址**000000h**。这在图8,读readManufacturerDeviceID时序图中我们可以看到在写入**读Manufacturer/ | + | 2. 写入**读 Manufacturer/ |
- | 4. 读取两个字节的Manufacturer/ | + | 3. 写入地址** 000000h **。这在图 8 读 readManufacturerDeviceID 时序图中可以看到,在写入**读 Manufacturer/ |
+ | 4. 读取两个字节的 Manufacturer/ | ||
+ | |||
+ | 清单 5 展示了将**读 Manufacturer/ | ||
+ | |||
+ | 清单5. 读 Manufacturer/ | ||
```C | ```C | ||
static bool Flash_GetInfo(void) { | static bool Flash_GetInfo(void) { | ||
bool flag; | bool flag; | ||
- | const uint8_t comAdd[] = {FLASH_READ_MANUDEVID, | + | const uint8_t comAdd[] = {FLASH_READ_MANUDEVID, |
- | FlashSelect();// | + | FlashSelect();// |
- | flag = Spi_Write(comAdd, | + | flag = Spi_Write(comAdd, |
if(!flag) { | if(!flag) { | ||
FlashDeSelect(); | FlashDeSelect(); | ||
return false; | return false; | ||
} | } | ||
- | flag = Spi_Read(devInfoBuf, | + | flag = Spi_Read(devInfoBuf, |
- | FlashDeSelect();// | + | FlashDeSelect();// |
return flag; | return flag; | ||
} | } | ||
``` | ``` | ||
- | 完整的代码实现文件你可以在[SPIFlash.c](files/ | + | |
- | 1. 将源文件[SPIFlash.c](files/ | + | 完整的代码实现文件可以在[ SPIFlash.c ](http:// |
- | 2. 在`C: | + | |
+ | 1. 将源文件[ SPIFlash.c ](http:// | ||
+ | 2. 在 `C: | ||
![](http:// | ![](http:// | ||
- | 3. 选中`uartcho.c`文件,点击右键,选择**remove**,这时你可以看到`uartcho.c`文件被移出工程。 | + | 图9. IAR 工程目录 |
- | 4. 在工程目录下选中`source files`文件夹,选择**Add**条目下的**Add Files...**,然后将我们存放的`SPIFlash.c`添加进工程项目,如图9所示。 | + | |
+ | 3. 选中 `uartcho.c` 文件,点击右键,选择** remove **,这时 `uartcho.c` 文件被移出工程。 | ||
+ | 4. 在工程目录下选中 `source files` 文件夹,选择** Add **条目下的** Add Files... **,然后将 `SPIFlash.c` 添加进工程项目,如图 | ||
![](http:// | ![](http:// | ||
- | 5. 选中工程文件`uartecho-Debug`点击右键,选择`Rebuild All`编译工程。 | + | 图10. 将文件添加进 IAR 工程目录 |
- | 6. 保证已经下载了蓝牙协议栈镜像文件的调试板接入电脑,选择**Project-> | + | |
- | 7. 打开串口调试助手,连接上对应的端口,在[CC13X0/ | + | 5. 选中工程文件 `uartecho-Debug` 点击右键,选择 `Rebuild All` 编译工程。 |
+ | 6. 保证已经下载了蓝牙协议栈镜像文件的调试板接入电脑,选择** Project -> Download -> Download Active Application ** 。如图 | ||
+ | 7. 打开串口调试助手,连接上对应的端口,在[ CC13X0/ | ||
![](http:// | ![](http:// | ||
- | 8. 如图11所示,将逻辑分析仪的各个通道分别连接[CC13X0/ | + | 图11. Manufacturer/ |
+ | |||
+ | 8. 如图 | ||
![](http:// | ![](http:// | ||
- | 9. 打开逻辑分析仪(Saleae Logic 1.1.15),首先配置好SPI接口,这在软件右侧**Analyzers**下的SPI选项下配置,如图12所示。 | + | 图12. 逻辑分析仪的通道连接 |
+ | |||
+ | 9. 打开逻辑分析仪( Saleae Logic 1.1.15 ),首先配置好 SPI 接口,软件右侧** Analyzers **下的 SPI 选项下配置,如图13所示。 | ||
![](http:// | ![](http:// | ||
- | 10. 选择之后你可以看到如图13所示的配置界面,将各个端口对应的通道配置好,点击**Save**保存。 | + | 图13. SPI 接口的配置 |
+ | |||
+ | 10. 选择之后你可以看到如图 | ||
![](http:// | ![](http:// | ||
- | 11. 点击界面上方的**Start**按钮如图14所示, | + | |
+ | 图14. 逻辑分析仪的配置界面 | ||
+ | |||
+ | 11. 如图 15 所示点击界面上方的** Start **按钮,会弹出如图 | ||
![](http:// | ![](http:// | ||
+ | |||
+ | 图15. Start 界面 | ||
![](http:// | ![](http:// | ||
- | 12. 在[CC13X0/ | + | 图16. 弹出的界面 |
- | 13. 这样我们就可以看到我们向Flash输入数据,读出数据的时序图,如图16所示。我们可以看到,我们输入的指令与我们程序是完全符合的,读出Manufacturer/ | + | |
+ | 12. 在[ CC13X0/ | ||
+ | 13. 这样就可以看到向 Flash 输入数据、读出数据的时序图。如图17所示,输入的指令与程序是完全符合的,读出 Manufacturer/ | ||
![](http:// | ![](http:// | ||
+ | |||
+ | 图17. Flash 输入数据、读出数据的时序图 | ||
## 加入我们 ## | ## 加入我们 ## | ||
+ | |||
文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | ||
<div> | <div> | ||
行 358: | 行 470: | ||
</ | </ | ||
</ | </ | ||
- |