ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【stm32】SPI复习笔记

【stm32】SPI复习笔记 1.SPI总线的电路结构SPI总线里有一个主机多个从机引脚有MOSIMISOSCKNSS。MOSI主发从收。主机的MOSI是一个发送引脚从机的MOSI为接收引脚使用的时候要连起来。MISO主收从发。主机的MISO是接收引脚SCL串行时钟主机产生时钟信号发送给从机通过时钟信号控制通信快慢NSS低电压有效的从机选择引脚。主机向NSS发送低电压代表选中这个从机与其通信。通信时在SCK高电压期间主机通过MOSI向从机发送一个数据同时从机通过MISO向主机返回一个比特位。主机发送多少位就会收到多少位的回应。2.参数①时钟信号的极性和相位低极性空闲状态为低电平。高极性空闲状态为高电平。第一边沿空闲状态过后第一个出现的边沿。第二边沿空闲状态过后第二个出现的边沿。相位第一边沿采集0。第二边沿采集1。②SPI的四种时钟模式模式0极性0 相位0模式1极性0 相位1模式2极性1 相位0模式3极性1 模式1③比特位的传输顺序LSB First 先传最低有效位01234567MSB First 先传最高有效位76543210④数据宽度8Bit 每八个字节为一组传输16Bit每十六个字节为一组传输3.SPI IO引脚初始化W25Q64模块引脚连接如上图所示。SPI引脚分布及模式设定代码对SPI模块外漏引脚初始化void App_SPI_Init(void){//1.对SPI1模块进行重映射RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);//2.初始化SPI模块的IO引脚//PB3 SCK AF_PP 2MHZRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_3;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOB,GPIO_InitStruct);//PB4 MISO IPURCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_IPU;GPIO_InitStruct.GPIO_PinGPIO_Pin_4;GPIO_Init(GPIOB,GPIO_InitStruct);//PB5 MOSI AF_PP 2MHZRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_5;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOB,GPIO_InitStruct);//普通IO引脚cpu控制高低电平 PA15RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_15;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOA,GPIO_InitStruct);}4.SPI模块本身初始化①硬件结构原理观察上图SPI硬件结构可以看到时钟那一条线路上有总开关时钟是单片机内部地时钟主机通过SCK引脚向外发出时钟信号。要打开总开关SPI模块才能进行通信。后面时钟控制电路参数有极性相位波特率。左上侧电路有TDR和移位寄存器负责数据的发送发数据时候要把数据写入TDR这个数据会移动到移位寄存器中移位寄存器一位一位地通过MOSI引脚发送出去。而接受数据时波形通过MISO引脚传进来数据控制电路一位一位地解析数据并放入移位寄存器中。当所有的数据都填进了移位数据寄存器中他就会进入RDR编程者再从RDR中读出数据。下部通信方向选择有五种选择方式。而右侧NSS只有在做从机的时候NSS引脚才有用但是作为主机也需要对NSS引脚进行一定的配置。右上方有三个标志位通过查询不同的标志位可以知道SPI的工作状态。TxE是TDR空RxNE为RDR非空。②SPI模块初始化参数初始化SPI模块时候用到SPI_InitStruct要看从机要求什么参数就设置成什么参数。加入SPI与从机W25Q64通信就要看W25Q64要求什么参数。参数SPI_Direction:通信方向2线全双工标准的SPI总线格式 2线只读一般用于从机广播单线接收 单线方向是发送单线发送 单线方向是接收SPI_DataSize-数据宽度 可选8 bit 半字 16bitSPI_CPOL-时钟极性SPI_CPHA -时钟相位SPI_NSS -软件NSS或硬件NSSSPI_BaudRatePrescaler -用来选择波特率分频器的分频系数SPI_FirstBit -比特位的传输顺序 SPI_FirstBit_MSB SPI_FirstBit_LSB③如何设置参数设置参数时候要看从机的要求因为主机是与从机进行通信的所以要满足从机要求。上图为单片机向W25Q64发送“0x04”指令时的实际波形图。第一个波形NSS代表片选给NSS低电压时候选中W25Q64第二个波形为时钟信号时钟信号上实线代表Mode0Mode0是极性0相位0由图中观察可得空闲时候为低电平故极性为0而MOSI引脚上的波形代表从机接受可见得从机在第一边沿采集信号故相位0这就代表Mode0。而虚线上的Mode3代表空闲时刻为高电平则极性1那么极性1时下降沿为第一边沿所以W25Q64在第二边沿采集信号则相位1这就是Mode3。0x04对应二进制数为00000100则对比可得出数据传输顺序时MSB。④设置W25Q64的波特率设置波特率时候要看SPI内部结构框图时钟那一条线路。从时钟信号出发经过分频器分频器只能分2的幂次的频率分完频得到得到波特率。最终要产生1MHZ左右的波特率则应选择64分频假设输入时钟为72MHZ72/641.125MHZ最为接近。⑤NSS的配置方式作为从机时通过NSS引脚输入电压的高低判断NSS是否被选中。在主机里也需要对NSS引脚配置给它接3.3v高电压。因为SPI支持多主机模式如上图右侧有两个主机MOSIMISOSCK对应相连主机一与主机二普通IO引脚和NSS交叉连接如果主机一想和主机二通信主机一通过IO引脚给主机二的NSS发送低电平此时主机二被选中主机二丢失主机身份变为从机。因此为了防止主机的NSS被其他外部因素输入为低电压就需要一直向NSS接通高电压防止主机变为从机。看SPI结构图硬NSS表示的是直接通过外部电路连接给NSS高电平。软NSS表示直接通过内部NSS比特位写1在内部给它设为高电压即可。我们操作时候先把它设为软NSS然后给软NSS位写1。代码void App_SPI_Init(void){//1.对SPI1模块进行重映射RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SPI1,ENABLE);//2.初始化SPI模块的IO引脚//PB3 SCK AF_PP 2MHZRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_3;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOB,GPIO_InitStruct);//PB4 MISO IPURCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_IPU;GPIO_InitStruct.GPIO_PinGPIO_Pin_4;GPIO_Init(GPIOB,GPIO_InitStruct);//PB5 MOSI AF_PP 2MHZRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_5;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOB,GPIO_InitStruct);//普通IO引脚cpu控制高低电平 NSS PA15RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStruct.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_PinGPIO_Pin_15;GPIO_InitStruct.GPIO_SpeedGPIO_Speed_2MHz;GPIO_Init(GPIOA,GPIO_InitStruct);//2.对SPI模块本身进行初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);SPI_InitTypeDef SPI_InitStruct;SPI_InitStruct.SPI_ModeSPI_Mode_Master;//主模式SPI_InitStruct.SPI_DataSizeSPI_DataSize_8b;//8bit数据宽度SPI_InitStruct.SPI_CPHASPI_CPHA_2Edge;//第二边沿采集SPI_InitStruct.SPI_CPOLSPI_CPOL_High;//高极性SPI_InitStruct.SPI_DirectionSPI_Direction_2Lines_FullDuplex;//两线全双工SPI_InitStruct.SPI_FirstBitSPI_FirstBit_MSB;//先传最高有效位SPI_InitStruct.SPI_BaudRatePrescalerSPI_BaudRatePrescaler_64;//设置64分频得到波特率传输数据速度SPI_InitStruct.SPI_NSSSPI_NSS_Soft;//设为软件NSSSPI_Init(SPI1,SPI_InitStruct);SPI_NSSInternalSoftwareConfig(SPI1,ENABLE);//给SPI1的软件NSS引脚写1防止主机变为从机。}5.SPI数据收发最大特点SPI数据收发是双向的每发送一个比特位必然会收到一个比特位。代码void App_SPI_MasterTransmitReceive(SPITypeDef *SPIx,const uint8_t *pDataTx,uint8_t *pDataRX,uint16_t Size){//1.闭合总开关SPI_Cmd(SPI1,ENABLE);//2.发送第一个字节SPI_I2S_SendData(SPI1,pDataTx[0]);//3.for(uint16_t i0;iSize-1;i){//向TDR写数据while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)RESET);SPI_I2S_SendData(SPI1,pDataTx[1]);//从RDR读数据while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)RESET);pDataRX[i]SPI_I2S_ReceiveData(SPI1);}//读取最后一个字节的数据while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)RESET);pDataRX[Size-1]SPI_I2S_ReceiveData(SPI1);//断开总开关SPI_Cmd(SPI1,DISABLE);}
返回列表