stm32串行设备接口SPI控制max31865

本人是刚入行的嵌入式,之前也没有多少项目经验,故在公司的这几个月里,可谓是如履薄冰,对于公司不同项目使用的不同的设备之多,数据手册之繁杂,让我不禁望洋兴叹,故而不愿意放弃周末这大好的自我提升时间,努力耕耘,特开此园,与诸君共论(咳咳,有点羞耻,算了就这样吧,不改了)。
现阶段我比较注重各种协议,所以今后几个月内会不间断的更新各种简单常用的协议,这也算给自己立一个flag吧,督促自己。
——————————————————————————————————————————分割线———————————————————————————————————
本篇讲述的是常用的工业级标准串行协议SPI,经常用于各种嵌入式系统,能够将微处理器连接到各种片外传感器、存储器和控制设备等。
SPI使用两根数据线、一根时钟线、一根控制线(片选线)实现串行通信:

MOSI 主设备数据输出从设备数据输入线
MISO 主设备数据输出从设备数据输入线
SCK 主设备输出从设备输入时钟线(用于同步数据位)
NSS(CS) 主设备输出从设备输入片选线(低电平有效)

SPI由于其时钟SCK的极性和相性的不同,其工作模式一共有4种:
【stm32串行设备接口SPI控制max31865】在这里我要感谢我的高频电子线路的老师,谢谢他教会我的许多模电知识(话说我模电课是在干嘛?)
对于不知道什么是极性和相性的同学,可以先简单的将这两个名词分别记为极性(波形的上下高低起伏)、相性(波形沿时间线的前后的变化)(实在不了解的就当作名词使用)
在SPI的SCK时钟线极性为0时,SPI在空闲时SCK为低电平,工作时由低电平起跳到高电平
SCK时钟线极性为1时,SPI在空闲时SCK为高电平,工作时由高电平起跳到低电平
在SPI的SCK时钟线相性为0时,SPI会在工作时从第一个时钟沿开始采集数据
SCK时钟线相性为1时,SPI会在工作时从第二个时钟沿开始采集数据
stm32串行设备接口SPI控制max31865
文章图片


但我一般不会去记这四种工作模式分别是什么极性什么相位,我只会记住极性为0是低电平跳到高电平,相性为0是第一个边沿采集,反之亦然,这样不管在遇到什么从设备时,你都能根据datasheet优雅的设置SPI工作模式。

对于整个SPI通信协议来说,由数据的收发和对收发的控制两部分组成,其收发数据的整个流程为:
主设备将数据copy到SPI发送缓存区——》主设备拉低要通信的从设备的NSS(CS)片选线电平——》SPI检测到片选线被拉低后进入工作模式——》位移寄存器将发送缓存区的数据并串转换发送出去(同时,在从设备中数据由位移寄存器串并转换到接收缓存区中)
这里要注意:不管是主设备还是从设备的位移转换器都在主设备的SCK作用下完成移位的,故从设备想要发送数据到主设备必须依赖主设备的时钟,也就是主设备要空发一个没用的数据,从设备利用此时主设备给的时钟趁机将数据发给主设备(从设备好惨啊~~~)
stm32串行设备接口SPI控制max31865
文章图片

这是SPI的方框图,对于收发控制方面感兴趣的可以看一下(晚上照的,灯光不好,请见谅)
图中的NSS(CS)就是片选,低电平有效,可以使得主设备在与多个设备连接时,也能单独与某一个从设备进行通信,而不受干扰(这就是渣男的梦想吗?doge)
图中右下部分CR1这个寄存器中的SSM位是用于控制上面说讲的NSS是否有效的,SSM为0时,此时NSS就有效;SSM为1时,此时NSS就无效,
但此时NSS无效了我该怎么实现片选这个功能呢?
此时就可以通过图中边上的那个CR1寄存器中的SSI位来实现,此位一般主设备设为1,当某个从设备设为0时,表示选中该设备了

一般在实际项目的使用中都会用到多个从设备,所有的从设备都共用MOSI、MISO、SCK这三根线,但每个从设备都必须拥有独属于自己的NSS片选线,但我不可能有几个从设备就直接从主设备连几根NSS线,这太消耗资源了,此时一般都会使用多路复用器来控制。
SPI寄存器一共有7个,分别为CR1(控制寄存器1)、CR2(控制寄存器2)、SR(状态寄存器)、DR(数字寄存器)、CRCPR(CRC多项式寄存器)、RXCRCR(接收CRC寄存器)、TXCRCR(发送CRC寄存器);
这些寄存器在STM32中都是被定义过的,可以不用管该寄存器的地址,我在这里就不一一列举各个寄存器的使用了,感兴趣的可以私下了解一番。
——————————————————————————分割线————————————————————————————————————-——————————————
以上就是对于SPI的理论知识,对于具体的代码实现的话,由于大家所应用的环境不同,故我这里只能给出一份基于STM32F407GT6通过SPI控制MAX31865温度采集的代码示范:



//本来想写点注释的,但……懒癌犯了,直接复制应该没有问题的
int main(void) { int temvalue = https://www.it610.com/article/0,RTDs = 0,i = 0; uint16_t data = 0; float temps = 0; uint16_t dtemp[2] = {0}; char tem_buff[30] ="0"; char *temptr= tem_buff; RCC_Configuration(); delay_init(); uart_init(115200); SPI2_Init(); MAX31865_Init(); LED_Init(); delay_ms(10); GPIO_ResetBits (GPIOB, GPIO_Pin_12); SPI2_ReadWriteByte(0x80); SPI2_ReadWriteByte(0xC1); GPIO_SetBits (GPIOB, GPIO_Pin_12); while(1) { dtemp[0] ='0'; dtemp[1] ='0'; data = https://www.it610.com/article/0; temps = 0; RTDs = 0; SPI_ReadWrite(dtemp); data=((dtemp[0]<<7) | dtemp[1]); temps=data; temps = (temps*402)/32768; RTDs = (int)temps; temvalue = 9e-10*(temps*temps*temps*temps)+4e-7*(temps*temps*temps)+0.0008*(temps*temps)+2.3828*temps-246.81; sprintf(temptr,"RTD:%d temp:%d,data:%d\r\n",RTDs,temvalue,data); for(i = 0; i<30; i++) { USART_SendData(UART4, temptr[i]); while(USART_GetFlagStatus(UART4,USART_FLAG_TC) ==RESET); }GPIO_ResetBits(GPIOC,GPIO_Pin_14); delay_ms(500); GPIO_SetBits(GPIOC,GPIO_Pin_14); delay_ms(500); }}

void MAX31865_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_12); GPIO_SetBits (GPIOB, GPIO_Pin_12); //cs_H SPI2_Init(); SPI2_SetSpeed(SPI_BaudRatePrescaler_256); }

void RCC_Configuration(void) { ErrorStatus HSEStartUpStatus; RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div4); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08) { } } RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); }

void SPI2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDefSPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA, ENABLE ); //PORTBê±?óê1?ü RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE ); //SPI2ê±?óê1?ü GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13| GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15?′ó?í?íìê?3? cs miso mosi GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //3?ê??ˉGPIOBGPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15é?à- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); SPI2_ReadWriteByte(0xff); }

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler)); SPI2->CR1&=0XFFC7; SPI2->CR1|=SPI_BaudRatePrescaler; SPI_Cmd(SPI2,ENABLE); }

u8 SPI2_ReadWriteByte(u8 TxData) { u8 retry=0; while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) { retry++; if(retry>200) {return 0; } } SPI_I2S_SendData(SPI2, TxData); retry=0; while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) { retry++; if(retry>200) {return 0; } } return SPI_I2S_ReceiveData(SPI2); }


    推荐阅读