1.SPI概念

spi是摩托罗拉公司设计的用于板间的串行通信方式,应用场景类似于IIC,速度快于IIC。

spi属于高速,全双工,同步的通信总线;和设备连接占用4根线,比较占用引脚,速度最快达到40Mbps。

2.硬件连接

CS(chip select):片选引脚

主设备控制,用于选择当前通信的从设备 ,  一个 Slave 设备要想能够接收到 Master 发过来的控制信号, 必须在此之前能够被 Master 设备进行访问 (Access),所以, Master 设备必须首先通过 CS对 Slave 设备进行片选, 把想要访问的 Slave 设备选上.

SCLK:时钟线

产生时钟信号,由主设备控制

MISO(Master Input Slave output)

主设备输入,从设备输出

MOSI(Master Output Slave Input)

主设备输出,从设备输入(MISO和MOSI实现全双工)

3.spi协议

spi每一位数据的发送和接收是在时钟信号的边沿完成,根据选择的上升沿/下降沿和时钟信号的高低电平顺序,一共有4种情况。

CPOL表示高低电平顺序,叫做极性;若CPOL = 0,串行同步时钟的空闲状态为低电平;

若CPOL = 1,串行同步时钟的空闲状态为高电平;

CPHA表示第一个/第二个边沿,叫做相位。

SPI输出串行同步时钟极性和相位可以根据外设工作要求进行配置。 SPI 设备间的数据传输又被称为数据交换, 是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)". 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了.

分别是以下四种情况:

MODE0:CPOL= 0,CPHA=0。串行时钟SCLK空闲状态时为低电平,数据在SCLK时钟的上升沿采样,下降沿输出;

MODE1:CPOL= 0,CPHA=1。串行时钟SCLK空闲状态时为低电平,数据在SCLK时钟的下降沿采样,上升沿输出;

MODE2:CPOL= 1,CPHA=0。串行时钟SCLK空闲状态时为高电平,数据在SCLK时钟的下降沿采样,上升沿输出;

MODE3:CPOL= 1,CPHA=1。串行时钟SCLK空闲状态时为高电平,数据在SCLK时钟的上升沿采样,下降沿输出;

起始信号

NSS信号线由高变低,是SPI通讯的起始信号
 结束信号

NSS信号由低变高,是SPI通讯的停止信号
 数据传输

SPI使用MOSI以及MISO信号来传输数据,使用SCK信号线进行数据同步。

MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出时同时进行的。

SPI每次传输数据可以8位或16位为单位,每次传输的单位数不受限制

4.spi flash

原理图:

spi接口的CS连接到了PB14,SCLK,MISO,MOSI分别连接到了PB3 PB4 PB5,这三个IO口具有SPI的复用功能。

5.spi通信的实现(两种方法)

(1)使用GPIO接口模拟SPI的时序,只要是IO口即可以使用。

(2)使用SPI控制器,只需要配置好SPI的通信参数后,直接进行传输,接口必须有SPI复用功能。

6.spi控制器的库函数编程

添加spi的库函数源码:

(1)开启时钟

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);

(2)将GPIO配置为SPI复用功能(CS配置为输出)

GPIO_Init(...); GPIO_AFPinConfig(...);

(3)初始化SPI

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);参数: SPIx - 哪个SPISPI_InitStruct - SPI初始化结构typedef struct{uint16_t SPI_Direction; /*!< 传输方向 单向/双向 @ref SPI_data_direction */
uint16_t SPI_Mode; /*!< 主/从设备选择 @ref SPI_mode */
uint16_t SPI_DataSize; /*!< 数据位长度 @ref SPI_data_size */
uint16_t SPI_CPOL; /*!< 极性 @ref SPI_Clock_Polarity */
uint16_t SPI_CPHA; /*!< 相位 @ref SPI_Clock_Phase */
uint16_t SPI_NSS; /*!< 片选信号选择 软件/硬件 @ref SPI_Slave_Select_management */
uint16_t SPI_BaudRatePrescaler; /*!< 参考时钟的预分频系数 10M左右*/
uint16_t SPI_FirstBit; /*!< 传输位顺序 高位/低位 @ref SPI_MSB_LSB_transmission */
uint16_t SPI_CRCPolynomial; /*!< CRC校验,无需配置 */}SPI_InitTypeDef;

(4)使能SPI

SPI_Cmd(...);

(5)使用SPI传输数据

//发送和接收同时进行 void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data); uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

(6)查询SPI传输状态

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
参数:
* @arg SPI_I2S_FLAG_TXE: Transmit buffer empty flag.
* @arg SPI_I2S_FLAG_RXNE: Receive buffer not empty flag.
* @arg SPI_I2S_FLAG_BSY: Busy flag.
* @arg SPI_I2S_FLAG_OVR: Overrun flag.
* @arg SPI_FLAG_MODF: Mode Fault flag.
* @arg SPI_FLAG_CRCERR: CRC Error flag.
* @arg SPI_I2S_FLAG_TIFRFE: Format Error.
* @arg I2S_FLAG_UDR: Underrun Error flag.
* @arg I2S_FLAG_CHSIDE: Channel Side flag.

7.W25Q128芯片

(1)接口和状态寄存器

CS WP HOLD低电平有效,WP和HOLD接VCC,功能关闭,片选是低电平选中。

SPI接口支持CPOL和CPHA为0,0和1,1的模式

(2)操作时序

在进行任何操作前开启片选,操作完成后关闭片选。

1)读设备ID

发送90H ===> 发送24位0地址 ===> 收到厂家ID(0xef)和设备ID(0x17)

*************************************************************************************************************

//代码实现W25Q128芯片的SPI通信

//功能函数

#include <stm32f4xx.h>
#include <spi_flash.h>
#include <delay.h>
#include <stdio.h>void spi1_init(void)
{GPIO_InitTypeDef GPIO_InitStruct;SPI_InitTypeDef SPI_InitStruct;//1.开始时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//2.配置GPIO为SPI功能GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//输出模式GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//高速GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;//PB14GPIO_Init(GPIOB,&GPIO_InitStruct);//片选关闭W25Q128_CS = 1;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//复用模式GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3 PB4 PB5GPIO_Init(GPIOB,&GPIO_InitStruct);GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);//3.SPI初始化SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双向全双工SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主设备SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//数据长度8位SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//极性 mode 0SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//相位 mode 0SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//软件片选SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//APB2总线时钟 84M/16分频 = 5.25MSPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//高位先出SPI_Init(SPI1,&SPI_InitStruct);//4.使能SPI1SPI_Cmd(SPI1,ENABLE);
}//发送接收数据 ----- 同时进行
//data是要发送的数据,返回接收的数据
u8 spi1_send_recv_byte(u8 data)
{//等待发送缓冲区为空while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);//发送数据SPI_I2S_SendData(SPI1,data);//等待接收缓冲区非空while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);//接收数据return SPI_I2S_ReceiveData(SPI1);
}//读取W25Q128的ID
u16 w25q128_read_id(void)
{u16 id = 0;//片选选中W25Q128_CS = 0;//发送90Hspi1_send_recv_byte(0x90);//发送24位0地址spi1_send_recv_byte(0x00);spi1_send_recv_byte(0x00);spi1_send_recv_byte(0x00);//接收厂家ID放在高8位id |= spi1_send_recv_byte(0xff)<<8;//接收设备ID放在低8位id |= spi1_send_recv_byte(0xff);//片选关闭W25Q128_CS = 1;return id;
}//读取W25Q128的数据
void w25q128_read_data(u32 addr,u8 *data,u8 len)
{//片选选中W25Q128_CS = 0;//发送03Hspi1_send_recv_byte(0x03);//发送24位读的地址,高位先出spi1_send_recv_byte((addr>>16)&0xff);//16~23位spi1_send_recv_byte((addr>>8)&0xff);//8~15位spi1_send_recv_byte((addr>>0)&0xff);//0~7位//读取数据while(len--){*data++ = spi1_send_recv_byte(0xff);}//片选关闭W25Q128_CS = 1;
}//开启/关闭写使能  enable = 0 开启写使能  enable = 1 关闭写使能
void w25q128_write_enable_disable(u8 enable)
{//片选选中W25Q128_CS = 0;if(enable){spi1_send_recv_byte(0x04);}else{spi1_send_recv_byte(0x06);}//片选关闭W25Q128_CS = 1;
}//读状态寄存器1
u8 w25q128_read_status(void)
{u8 status = 0;//片选选中W25Q128_CS = 0;//发送05Hspi1_send_recv_byte(0x05);status = spi1_send_recv_byte(0xff);//片选关闭W25Q128_CS = 1;return status;
}//扇区擦除
void w25q128_sector_erase(u32 addr)
{//开启写使能w25q128_write_enable_disable(0);//延时,让片选信号保持一段时间,使W25Q128感受到delay_us(50);//片选选中W25Q128_CS = 0;//发送20Hspi1_send_recv_byte(0x20);//发送24位擦除地址,高位先出spi1_send_recv_byte((addr>>16)&0xff);//16~23位spi1_send_recv_byte((addr>>8)&0xff);//8~15位spi1_send_recv_byte((addr>>0)&0xff);//0~7位//片选关闭W25Q128_CS = 1;delay_us(50);//等待擦除完成while(w25q128_read_status()&0x1){//1ms查询一次delay_ms(1);}delay_us(50);//关闭写使能w25q128_write_enable_disable(1);
}//写数据(按页)
void w25q128_write_page(u32 addr,u8 *data,u8 len)
{//开启写使能w25q128_write_enable_disable(0);//延时,让片选信号保持一段时间,使W25Q128感受到delay_us(50);//片选选中W25Q128_CS = 0;//发送02Hspi1_send_recv_byte(0x02);//发送24位写地址,高位先出spi1_send_recv_byte((addr>>16)&0xff);//16~23位spi1_send_recv_byte((addr>>8)&0xff);//8~15位spi1_send_recv_byte((addr>>0)&0xff);//0~7位//发送写的数据while(len--){spi1_send_recv_byte(*data++);}//片选关闭W25Q128_CS = 1;delay_us(50);//等待写完成while(w25q128_read_status()&0x1){//1ms查询一次delay_ms(1);}delay_us(50);//关闭写使能w25q128_write_enable_disable(1);
}void flash_write_temp(u8 temp)
{u8 buf[16] = {0},i,data = 0;u32 addr = 0;//找到未被写过的块while(1){w25q128_read_data(addr,buf,16);if(buf[0]!=0x55)break;addr += 16;}data = 0x55;//写入0x55w25q128_write_page(addr,&data,1);//写入tempw25q128_write_page(addr+1,&temp,1);data = 0;//求校验和w25q128_read_data(addr,buf,16);for(i=0;i<15;i++){data = (data + buf[i])&0xff;}//写入校验和w25q128_write_page(addr+15,&data,1);
}void flash_read_temp(void)
{u8 buf[16] = {0},i,data;u32 addr = 0;//找出所有写入温度的块while(1){data = 0;w25q128_read_data(addr,buf,16);if(buf[0]!=0x55)break;//求校验和for(i=0;i<15;i++){data = (data + buf[i])&0xff;}if(data!=buf[15]){printf("checksum error!data = %#x,buf[15] = %#x\r\n",data,buf[15]);break;}printf("0x%x temp = %d\r\n",addr,buf[1]);addr += 16;}
}

//头文件声明

#ifndef _SPI_FLASH_H_
#define _SPI_FLASH_H_#include <sys.h>#define W25Q128_CS PBout(14) void spi1_init(void);
u16 w25q128_read_id(void);
void w25q128_read_data(u32 addr,u8 *data,u8 len);
void w25q128_sector_erase(u32 addr);
void w25q128_write_page(u32 addr,u8 *data,u8 len);void flash_write_temp(u8 temp);
void flash_read_temp(void);#endif

第十五篇,STM32的SPI串行通信协议相关推荐

  1. 坚持的力量 第十五篇

    第十五篇        漩涡鸣人 从他身上,我看到了进步和向上的力量,经别人推荐,我发现我渐渐的喜欢上了<火影忍者>. 首先,<火影>中的歌曲很有震撼力和穿透力,产生心灵的共鸣 ...

  2. 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题

    秒杀多线程第十五篇 关键段,事件,互斥量,信号量的"遗弃"问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回 ...

  3. CCIE理论-第十五篇-IPV6-重分布+ACL+前缀列表

    CCIE理论-第十五篇-IPV6-重分布+ACL+前缀列表 重分布前面讲过,这里再讲一次+实操+效果看看 在ipv6中重分布直连路由是需要加上include-connected的 环境 就这么简单哈, ...

  4. CCIE-LAB-第十五篇-IPV6-BGP+VPN6+RT

    CCIE-LAB-第十五篇-IPV6-BGP+VPN6+RT 实际中,思科只会给你5个小时去做下面的全部配置 这个是CCIE-LAB的拓扑图 问题 翻译: 根据这些要求,将IPV6连接从总部通过SP扩 ...

  5. CCNP-第十五篇-VXLAN(一)

    CCNP-第十五篇-VXLAN(一) 到了这个阶段呢,怎么说呢,简单的NP级别的交换我都跳过去了,但是后期会补, 所以这个衔接可能新手或者不会的看的有的迷茫 但是出自个人原因只好这么干了,VXLAN3 ...

  6. CCNA-第十五篇-DHCP配置+SDN介绍(最后一章)

    CCNA-第十五篇-DHCP配置+SDN介绍 各位好,如果有一直看下来的谢谢支持 这里是CCNA的最后一篇了,如果真的能吸收很多内容,那么普通的东西基本上都没什么大问题了.除非就是工作经验 下一篇就到 ...

  7. python连载第十五篇~史上最全列表知识源码+答案

    #2017-12-19 19:34:41 December Tuesday the 51 week, the 353 day #python连载第十五篇~list列表#列表定义,访问,索引,操作,切片 ...

  8. Python之路【第十五篇】:Web框架

    Python之路[第十五篇]:Web框架 Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. 1 2 3 4 5 6 7 ...

  9. 第七十五篇:学术论文写作技巧

    第七十五篇:学术论文写作技巧 论文写作技巧 下载 论文写作技巧 最近听了一下网课,把大牛们的论文讲课记录和网上一些关于论文写作技巧的文章总结了一下.总结了一些关于论文的润色.语法.投稿.回复审稿人等需 ...

  10. python学习[第十五篇] 文件系统

    python学习[第十五篇] 文件系统 对文件系统访问大多数都通过os模块实现. os 模块文件/目录访问函数 文件处理 mkfifo() 创建命名通道只用于linux remove(path)/un ...

最新文章

  1. 组合数学实验——二分图匹配算法
  2. jmeter脚本结合ant执行测试用例并生成测试报告
  3. RabbitMQ订阅者模式
  4. kubernetes1.8.4 安装指南 -- 11. 安装kubernetes dashboard
  5. python如何执行代码漏洞_命令执行与代码执行漏洞原理
  6. Django获取当前页面的URL——小记
  7. 问题:Excel在“xxx.xlsx”中发现不可读取的内容。是否恢复此工作薄的内容?【原创】...
  8. [NOI2018]你的名字(68pts)
  9. openwrt: Makefile 框架分析[转载]
  10. 文本转语音(TTS)工具Balabolka
  11. 飞机订票系统(c语言)
  12. Android 自定义锁屏_分享一款MIUI10精致主题,多种时间样式可选,可自定义字体颜色...
  13. Office word编辑公式居中,编号右对齐的简单方法,非表格法和制表符法
  14. vim修改sessions存放目录_不拆机直接修改黑群晖的SN和MAC
  15. ML:机器学习算法中—因子模型(多变量)、时序模型/时间序列模型(主要以单变量)算法对比的简介、带有时序性的因子模型概述、案例应用之详细攻略
  16. 问题记录:从EXCEL2013导入数据CSV文件,提示报错“此文本文件包含的数据无法放置在一个工作表中。如要继续导入可容纳的数据,请单击确定。。。”
  17. oracle数据库的并发控制,Oracle数据库—— 事务处理与并发控制
  18. opencv2 读取16bit图像数据小结
  19. 图床/图片服务器项目详细设计
  20. yacs--Python代码运行时的配置系统

热门文章

  1. 如何打开.pos文件
  2. Ubuntu16.04常用工具
  3. allegro 过孔删除不掉
  4. oracle adpatch 回退,Oracle EBS施用adpatch工具打patch过程
  5. 图片转换成文字并导出的方法
  6. 泪目!这篇博士论文致谢走红:感谢博一与我结婚的妻子...
  7. 聚宽数据(JQData)本地化解决方案:基于MongoDB
  8. 奇虎360面经:测试
  9. Ambiguous method call.both
  10. linux系统编译时make出错,centos 编译安装cmake和常见过程错误解决办法(linux系统均适用,以爬坑。。)...