8位数据帧就是一次发送一个字节,16位数据帧就是一次发送两个字节,SPI以及IIC都是高位先行(就是越左边越高),而串口通信一般是低位先行。
SPI1挂载在总线2,Fpclk是72MHz,SPI2挂载在总线1,fpclk是36MHz,具体频率只能是分频之后的数值,不有可能是其他。
支持多种模式,但是在这里我们只学SPI作为主机的模式。
可以全双工,也可以其他,半双工就是只用一根线,进行分时发送与接收;单工就是只要发送线或者接收线,进行只发或者只收。
DMA,快熟传输大量数据
I2S是一种数字音频信号的传输协议,也就是把存储器中的数字信号传送到音频解码器中的那根线,表示数据怎么发,代表的含义又是什么。在SPI使用手册中有具体的。
可以高位先行也可以低位先行,由LSBFIRST控制为控制。
SPI作为主机的时候,左边的那个交叉就不用看,作为从机就要看,但是第二根线有错误,如图所示。作为从机其实就是看SO以及SI,主机就是看MO以及MI嘛,左移在切换为从机的时候,发送要从SO发送,接收要从SI接收,原本发送是在MO,接收是在MI,所有有交叉。
缓存器其实就是DR寄存器,发送缓存器就是TDR,接收缓存就是RDR,两者共用一个地址,在移位寄存器空的时候,发送缓存器的数据就可以移动到移位寄存器中,一位一位的移动数据,这时候TXE置1,发送的同时接收数据,发送完毕接收也完毕,最后数据整体移动到接收缓存器,TXE置0,RXNE置1。再从RDR读取数据。
SPI全双工,发送接收同步进行,所以移位寄存器可以公用,而缓存器则要分开两个;IIC是半双工,不会同时进行发送以及接收,所以移位寄存器以及缓存器都是可以公用的,串口全双工,发送接收可以异步进行,所以他的缓存器以及数字移位寄存器都是要分开的。
波特率发生器其实是一个分频器,输入的是PCLK,36MHz或者是72MHz,输出SCK。时钟频率与数字移位寄存器相对应,每经过一个周期,就移动一位bit。右边BR012用来控制分频系数,数据手册中BR[2:0]就是BR0,1,2。SPE就是SPI ENABLE,就是使能SPI,函数SPIcmd配置的。BR就是Buad rate波特率。MSTR就是配置主从模式,1主从0.CPOL以及CPHA用来选择SPI四种模式。SR寄存器中的TXE以及TXNE比较重要,上面说过了。最上面的就是中断使能以及DMA使能啦。
NSS主要用于多主机模式,把所有设备的NSS连接在一起,然后谁要当主机就输出低电平,控制所有从机,但是这样的弊端就是没办法控制单一的从机,因为NSS手连接在一起,发的信号大家都收得到,可能要加上一个寻址功能才行。
简化图如上 ,这里缺少了SS选择从机的部分,我们这一部分不用硬件的来实现,直接选择使用软件模拟,硬件实现就是直接调用库函数就行,软件模拟就要全部自己写。
分为连续传输以及非连续传输部分,连续传输传输比较快,但是比较复杂,非连续虽然不够快,但是简单好理解。
对效率要求高就连续传输,否则非连续传输。
非连续传输:
非连续传输就是把一个数据传送过去,被读取之后,再开始往TDR传输下一个数据,而连续传输在TXE置1的时候,就要立刻把数据传送给TDR候着了。
费连续传输大致分为四步:封装于一个函数中,缺点就是数据传输的效率不高。
1.等待TXE为1
2.写入数据
3.等待RXNE等于0
4.读出RDR缓冲器的值 (也就是BSY)
【SPI协议是一种同步的串行通信协议,它使用共享的时钟信号(SCK)来控制数据的传输。在SPI非连续传输的时候,通常会在每个字节之间插入一个间隙,以便允许接收方处理接收到的数据,并为下一个字节的传输做好准备。这个间隙通常称为“闲置周期”。
当SPI传输一个字节时,它会在SCK信号上产生一个序列化的位流,每个位都在SCK的上升沿或下降沿进行传输。在传输完一个字节后,SPI会产生一个额外的SCK周期,以便为下一个字节的传输做好准备。在这个额外的SCK周期中,SCK信号处于高电平或低电平,这个周期通常称为“闲置周期”。
因此,当SPI非连续传输的时候,SCK时钟会产生间隙,以便允许接收方处理接收到的数据,并为下一个字节的传输做好准备。这个间隙通常是一个或多个SCK周期的长度,其大小取决于SPI设备的具体实现。需要注意的是,间隙的长度必须足够长,以确保接收方有足够的时间处理接收到的数据,并为下一个字节的传输做好准备。】
硬件紧贴下降沿,软件有一定延迟。
SPI通信协议是一种同步的串行通信协议,它使用共享的时钟信号(SCK)来控制数据的传输。在SPI通信中,主设备通过SCK信号向从设备发送时钟信号,从设备通过MISO信号向主设备发送数据,主设备通过MOSI信号向从设备发送数据。因此,在SPI通信中,SCK信号是控制数据传输的关键信号之一。
在硬件SPI通信中,SCK信号的变化紧贴下降沿,这是因为SPI硬件通信使用专用的SPI接口芯片,它可以在时钟信号边沿快速响应,从而实现高速的数据传输。硬件SPI通信的优点是速度快、稳定性高、可靠性强,因为它不需要CPU的干预,可以直接通过硬件接口进行数据传输。
在软件SPI通信中,SCK信号的变化有一定的延迟,这是因为SPI软件通信是通过CPU的软件实现的,CPU需要花费一定的时间来执行SPI通信的相关指令,从而导致时钟信号的变化有一定的延迟。软件SPI通信的优点是灵活性高、可移植性强,因为它不依赖于硬件接口,可以在不同的平台上实现。
需要注意的是,硬件SPI通信和软件SPI通信的实现方式不同,因此它们的特点和优缺点也不同。在选择SPI通信方式时,应该根据具体的应用场景、性能要求和可用资源等因素进行综合考虑,选择最适合的通信方式。
当通信速度要求较高、设备资源充足、并且不需要在不同的硬件平台上移植时,应该选择硬件SPI通信;当设备资源有限、需要在不同的硬件平台上移植、或者代码复杂度较低时,应该选择软件SPI通信。
NSS看不懂没关系,用软件模拟的方式实现,N代表低电平有效。
23.3.4错了,应该是半双工
硬件读写W25Q64,不能随便选择引脚,要根据引脚定义表选择。
但是因为NSS是用软件模拟,所以不一定使用指定的硬件引脚,可以任意使用。
如果被占用还可以进行重定义。‘’
由于原本软件实现进行了封装以及隔离,所以在应用层也就是W25Q64SPI.c文件中的只是调用底层函数,我们要修改的只是底层的函数,所以应用层以及调用层是不需要更改的。
只更改MYSPI.c文件即可。
硬件SPI步骤为:
1.开启SPI以及GPIO的时钟
2.初始化GPIO,其中 SCK以及MOSI是由硬件外设控制的输出信号,所以配置为复用推挽输出。MISO是上拉输入。SS是软件控制的信号,设置为通用推挽输出。
3.配置SPI,利用结构体SPI_Init,配置结构体
4.开关控制,cmd使能
由于SPI以及IIS共用一套电路,所以在函数中有些地方是两者都有的
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);实际的效果就是写·1DR寄存器
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);接收数据RDR
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//开启SPI时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;//配置推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;//配置复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//选择是主还是从机
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择模式,双工半双工
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位数据帧
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//高位先行
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//预分频器
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//时钟极性空闲默认低电平0或者1
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//第一个边沿开始采样的意思
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//硬件软件两种,这里是选用软件NSS
SPI_InitStructure.SPI_CRCPolynomial = 7;//CRC校验
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);//使能SPI
MySPI_W_SS(1);//默认SS高电平,不选择从机
}
生成时序四步
1.等待TXE为1,发送寄存器为空,不为空就不写
2.软件写入数据到TDR,因为发送以及接收是同步的,所以发送完成的时候,接收也完成了,RXNE会等于1,以此作为条件
3.等待RXNE为0
4.读取RDR数据
其中TXE以及TXNE会自动清除,在23.3.7中,大部分都要手动清除,比如中断。
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
SPI_I2S_SendData(SPI1, ByteSend);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
return SPI_I2S_ReceiveData(SPI1);
}
————————————————
版权声明:本文为CSDN博主「笔下觅封侯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_63148816/article/details/131268805