第9章 SPI介绍及基础使用

第9章 SPI介绍及基础使用

第九章 SPI介绍及基础使用

1. F28P550的硬件I2C

TMS320F28P550中的串行外设接口 (SPI) 外设支持以下主要特性:

SPIPOCI:SPI 外设输出/控制器输入引脚

SPIPICO:SPI 外设输入/控制器输出引脚

SPIPTE:SPI 外设发送使能引脚

SPICLK:SPI 串行时钟引脚

两种工作模式:控制器和外设

波特率:125 个不同的可编程速率。可采用的最大波特率受限于 SPI 引脚上使用的 I/O 缓冲器的最大速度。

数据字长度:1 至 16 数据位

四种时钟方案(由时钟极性和时钟相位的位控制)包含:

无相位延迟的下降沿:SPICLK 高电平有效。SPI 在 SPICLK 信号的下降沿上发送数据,在 SPICLK 信号的 上升沿上接收数据。

有相位延迟的下降沿:SPICLK 高电平有效。SPI 在 SPICLK 信号下降沿提前半个周期发送数据,在 SPICLK 信号的下降沿上接收数据。

无相位延迟的上升沿:SPICLK 低电平无效。SPI 在 SPICLK 信号的上升沿上发送数据,在 SPICLK 信号的 下降沿上接收数据。

有相位延迟的上升沿:SPICLK 低电平无效。SPI 在 SPICLK 信号上升沿的半个周期之前发送数据,而在 SPICLK 信号的上升沿上接收数据。

同时接收和发送操作(可在软件中禁用发送功能)

发送器和接收器操作通过中断驱动或轮询算法完成

16 级发送/接收 FIFO

DMA 支持

高速模式

延迟的发送控制

3 线 SPI 模式

在带有两个 SPI 模块的器件上实现数字音频接口接收模式的 SPIPTE 反转

使用硬件SPI的优势:

支持中断和DMA: 硬件SPI可以与中断控制器和DMA控制器配合使用,实现数据的高效处理和传输。

硬件缓冲区: 硬件SPI具有内部缓冲区,可以在主机和外设之间进行数据中转,提高数据的传输效率。

高速传输: 硬件SPI使用硬件模块进行数据传输,速度通常比软件实现的SPI更快。

2. W25Q32介绍

W25Q32是一种常见的串行闪存器件,它采用SPI(Serial Peripheral Interface)接口协议,具有高速读写和擦除功能,可用于存储和读取数据。W25Q32芯片容量为32 Mbit(4 MB),其中名称后的数字代表不同的容量选项。不同的型号和容量选项可以满足不同应用的需求,比如W25Q16、W25Q64、W25Q128等。通常被用于嵌入式设备、存储设备、路由器等高性能电子设备中。 W25Q32闪存芯片的内存分配是按照扇区(Sector)和块(Block)进行的,每个扇区的大小为4KB,每个块包含16个扇区,即一个块的大小为64KB。

W25Q32存储芯片,其引脚的说明,见下表。

CLK

从外部获取时间,为输入输出功能提供时钟

DI

标准SPI使用单向的DI,来串行的写入指令,地址,或者数据到FLASH中,在时钟的上升沿。

DO

标准SPI使用单向的DO,来从处于下降边沿时钟的设备,读取数据或者状态。

WP

防止状态寄存器被写入

HOLD

当它有效时允许设备暂停,低电平:DO引脚高阻态,DI CLK引脚的信号被忽略。高电平:设备重新开始,当多个设备共享相同的SPI信 号的时候该功能可能会被用到

CS

CS高电平的时候其他引脚成高阻态,处于低电平的时候,可以读写数据

它与开发板的连接如下:

开发板(主机)

W25Q32(从机)

说明

GPIO0

CS(NSS)

片选线

GPIO3

CLK

时钟线

GPIO1

DO(IO1)(MISO)

主机输入从机输出线

GPIO2

DI(IO0)(MOSI)

主机输出从机输入线

GND

GND

电源线

VCC

3V3

电源线

发板上默认已经为大家贴好了该存储芯片,大家只需要了解连接的是哪一个引脚即可。

需要注意的是,我们使用的是硬件SPI方式驱动W25Q32,因此我们需要确定我们设置的引脚是否有硬件SPI外设接口。在数据手册中,GPIO0 ~ GPIO3 可以复用为SPIA的4根通信线。

这里需要注意的是PICO,表示的是外设输入控制器输出,即对应SPI中的MOSI。POCI表示外设输出控制器输入,即对应MISO。

3. 软件设计

3.1 SPI配置

打开工程下的 .syscfg 文件。找到 SPI 选项开始配置:

在SPI外设的配置中,重新命名叫 FLASH_SPI;

设置传输协议为 mode 0;

设置SPI模式为主机模式,即控制器模式;

设置通信速率为 1MHz;

设置参数数据位长度为 8位;

设置引脚配置自定义,关闭 PTE 的配置,然后设置各引脚。

这里需要注意,由于大多数的SPI协议中,整个时序里在发送接收时片选是要一直拉低的,而SPI外设的片选在每次发送和接收完一帧后会拉高,所以CS片选线需要用MCU的IO口独立控制,没有办法使用SPI外设的CS管脚。这里使用GPIO的方式(软件方式)去控制CS引脚的输出。

新建一个GPIO。命名为FLASH_CS,引脚选择我们现在接入模块CS的引脚GPIO0。其配置如下:

3.2 W25Q32驱动

#ifndef _BSP_W25QXX_H__

#define _BSP_W25QXX_H__

#include "board.h"

//CS引脚的输出控制

//x=0时输出低电平

//x=1时输出高电平

#define SPI_CS(x) ( (x) ? GPIO_writePin(FLASH_CS,1) : GPIO_writePin(FLASH_CS,0) )

uint8_t spi_read_write_byte(uint8_t dat);//SPI读写一个字节

uint16_t w25qxx_read_id(void);//读取W25QXX的ID

void w25qxx_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte); //W25QXX写数据

void w25qxx_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length);//W25QXX读数据

#endif

#include "bsp_w25qxx.h"

#include "spi.h"

uint8_t spi_read_write_byte(uint8_t dat)

{

uint8_t data = 0;

// Transmit data

SPI_writeDataNonBlocking(FLASH_SPI_BASE, dat<<8);

// Block until data is received and then return it

data = SPI_readDataBlockingNonFIFO(FLASH_SPI_BASE);

return data;

}

//读取芯片ID

//返回值如下:

//0XEF13,表示芯片型号为W25Q80

//0XEF14,表示芯片型号为W25Q16

//0XEF15,表示芯片型号为W25Q32

//0XEF16,表示芯片型号为W25Q64

//0XEF17,表示芯片型号为W25Q128

//读取设备ID

uint16_t w25qxx_read_id(void)

{

uint16_t temp = 0;

//将CS端拉低为低电平

SPI_CS(0);

//发送指令90h

spi_read_write_byte(0x90);//发送读取ID命令

//发送地址 000000H

spi_read_write_byte(0x00);

spi_read_write_byte(0x00);

spi_read_write_byte(0x00);

//接收数据

//接收制造商ID

temp |= spi_read_write_byte(0xFF)<<8;

//接收设备ID

temp |= spi_read_write_byte(0xFF);

//恢复CS端为高电平

SPI_CS(1);

//返回ID

return temp;

}

//发送写使能

void w25qxx_write_enable(void)

{

//拉低CS端为低电平

SPI_CS(0);

//发送指令06h

spi_read_write_byte(0x06);

//拉高CS端为高电平

SPI_CS(1);

}

//器件忙判断

void w25qxx_wait_busy(void)

{

unsigned char byte = 0;

do

{

//拉低CS端为低电平

SPI_CS(0);

//发送指令05h

spi_read_write_byte(0x05);

//接收状态寄存器值

byte = spi_read_write_byte(0Xff);

//恢复CS端为高电平

SPI_CS(1);

//判断BUSY位是否为1 如果为1说明在忙,重新读写BUSY位直到为0

}while( ( byte & 0x01 ) == 1 );

}

void w25qxx_erase_sector(uint32_t addr)

{

//计算扇区号,一个扇区4KB=4096

addr *= 4096;

w25qxx_write_enable(); //写使能

w25qxx_wait_busy(); //判断忙,如果忙则一直等待

//拉低CS端为低电平

SPI_CS(0);

//发送指令20h

spi_read_write_byte(0x20);

//发送24位扇区地址的高8位

spi_read_write_byte((uint8_t)((addr)>>16));

//发送24位扇区地址的中8位

spi_read_write_byte((uint8_t)((addr)>>8));

//发送24位扇区地址的低8位

spi_read_write_byte((uint8_t)addr);

//恢复CS端为高电平

SPI_CS(1);

//等待擦除完成

w25qxx_wait_busy();

}

void w25qxx_write(uint8_t* buffer, uint32_t addr, uint16_t numbyte)

{

unsigned int i = 0;

//擦除扇区数据

w25qxx_erase_sector(addr/4096);

//写使能

w25qxx_write_enable();

//忙检测

w25qxx_wait_busy();

//写入数据

//拉低CS端为低电平

SPI_CS(0);

//发送指令02h

spi_read_write_byte(0x02);

//发送写入的24位地址中的高8位

spi_read_write_byte((uint8_t)((addr)>>16));

//发送写入的24位地址中的中8位

spi_read_write_byte((uint8_t)((addr)>>8));

//发送写入的24位地址中的低8位

spi_read_write_byte((uint8_t)addr);

//根据写入的字节长度连续写入数据buffer

for(i=0;i

{

spi_read_write_byte(buffer[i]);

}

//恢复CS端为高电平

SPI_CS(1);

//忙检测

w25qxx_wait_busy();

}

void w25qxx_read(uint8_t* buffer,uint32_t read_addr,uint16_t read_length)

{

uint16_t i;

//拉低CS端为低电平

SPI_CS(0);

//发送指令03h

spi_read_write_byte(0x03);

//发送24位读取数据地址的高8位

spi_read_write_byte((uint8_t)((read_addr)>>16));

//发送24位读取数据地址的中8位

spi_read_write_byte((uint8_t)((read_addr)>>8));

//发送24位读取数据地址的低8位

spi_read_write_byte((uint8_t)read_addr);

//根据读取长度读取出地址保存到buffer中

for(i=0;i

{

buffer[i]= spi_read_write_byte(0XFF);

}

//恢复CS端为高电平

SPI_CS(1);

}

3.3 主函数测试

#include "driverlib.h"

#include "device.h"

#include "board.h"

#include "c2000ware_libraries.h"

#include "bsp_w25qxx.h"

#include

#include

void main(void)

{

Device_init();

Device_initGPIO();

Interrupt_initModule();

Interrupt_initVectorTable();

Board_init();

C2000Ware_libraries_init();

EINT;

ERTM;

int flash_id=0;

char read_write_buff[10] = {0};

char read_write_buff2[10] = {0};

//读取器件ID

flash_id = w25qxx_read_id();

DEVICE_DELAY_US(500000);

printf("flash_id=%X\r\n",flash_id);

//往0地址写入5个字节数据,分别是"12345"

w25qxx_write("12345", 0x00, 5);

DEVICE_DELAY_US(500000);

//读取0地址的5个字节数据到buff

w25qxx_read(read_write_buff, 0x00, 5);

//通过CIO输出

printf("%s\r\n",read_write_buff);

DEVICE_DELAY_US(500000);

/* 字符串测试 */

w25qxx_write("hello", 0x01, sizeof(read_write_buff2));

DEVICE_DELAY_US(500000);

w25qxx_read(read_write_buff2, 0x01, sizeof(read_write_buff2));

printf("%s\r\n",read_write_buff2);

DEVICE_DELAY_US(500000);

while(1);

}


【科普帖】轮椅的选择及使用知识
身体开始糖化,皮肤会有这3个表现!若出现了,说明你该抗糖化了