飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • AngularJS教程
  • UEditor使用文档
  • ThinkPHP5.0教程

stm32串行设备接口SPI控制max31865

时间:2021-12-09  作者:zblank  

  本人是刚入行的嵌入式,之前也没有多少项目经验,故在公司的这几个月里,可谓是如履薄冰,对于公司不同项目使用的不同的设备之多,数据手册之繁杂,让我不禁望洋兴叹,故而不愿意放弃周末这大好的自我提升时间,努力耕耘,特开此园,与诸君共论(咳咳,有点羞耻,算了就这样吧,不改了)。

  现阶段我比较注重各种协议,所以今后几个月内会不间断的更新各种简单常用的协议,这也算给自己立一个flag吧,督促自己。

——————————————————————————————————————————分割线———————————————————————————————————

  本篇讲述的是常用的工业级标准串行协议SPI,经常用于各种嵌入式系统,能够将微处理器连接到各种片外传感器、存储器和控制设备等。

       SPI使用两根数据线、一根时钟线、一根控制线(片选线)实现串行通信:

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

 

  SPI由于其时钟SCK的极性和相性的不同,其工作模式一共有4种:

  在这里我要感谢我的高频电子线路的老师,谢谢他教会我的许多模电知识(话说我模电课是在干嘛?)

  对于不知道什么是极性和相性的同学,可以先简单的将这两个名词分别记为极性(波形的上下高低起伏)、相性(波形沿时间线的前后的变化)(实在不了解的就当作名词使用)

  在SPI的SCK时钟线极性为0时,SPI在空闲时SCK为低电平,工作时由低电平起跳到高电平

       SCK时钟线极性为1时,SPI在空闲时SCK为高电平,工作时由高电平起跳到低电平

  在SPI的SCK时钟线相性为0时,SPI会在工作时从第一个时钟沿开始采集数据

               SCK时钟线相性为1时,SPI会在工作时从第二个时钟沿开始采集数据 

                                                                                                                                                                           

                                                                                                                                             

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

       

       对于整个SPI通信协议来说,由数据的收发和对收发的控制两部分组成,其收发数据的整个流程为:

主设备将数据copy到SPI发送缓存区——》主设备拉低要通信的从设备的NSS(CS)片选线电平——》SPI检测到片选线被拉低后进入工作模式——》位移寄存器将发送缓存区的数据并串转换发送出去(同时,在从设备中数据由位移寄存器串并转换到接收缓存区中)

这里要注意:不管是主设备还是从设备的位移转换器都在主设备的SCK作用下完成移位的,故从设备想要发送数据到主设备必须依赖主设备的时钟,也就是主设备要空发一个没用的数据,从设备利用此时主设备给的时钟趁机将数据发给主设备(从设备好惨啊~~~)

      这是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 = 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 = 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)+域名*(temps*temps)+域名*temps-域名; 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 );

    域名_Pin = GPIO_Pin_12;  
     域名_Mode = GPIO_Mode_Out_PP;  
    域名_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_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA, ENABLE );//PORTBʱÖÓʹÄÜ 
    
    RCC_APB1PeriphClockCmd(    RCC_APB1Periph_SPI2,  ENABLE );//SPI2ʱÖÓʹÄÜ     
 
    域名_Pin = GPIO_Pin_13  | GPIO_Pin_15;
    域名_Mode = GPIO_Mode_AF_PP;  //PB13/14/15¸´ÓÃÍÆÍìÊä³ö cs miso mosi
    域名_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);//³õʼ»¯GPIOB
    
    域名_Pin = GPIO_Pin_14;
    域名_Mode = GPIO_Mode_IPU;
    域名_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
     GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15ÉÏÀ­
    
     
    
 
    

    域名Direction = SPI_Direction_2Lines_FullDuplex; 
    域名Mode = SPI_Mode_Master;       
    域名DataSize = SPI_DataSize_8b;       
    域名CPOL = SPI_CPOL_Low;        
    域名CPHA = SPI_CPHA_2Edge;    
    域名NSS = SPI_NSS_Soft;        
    域名BaudRatePrescaler = SPI_BaudRatePrescaler_256;        
    域名FirstBit = SPI_FirstBit_MSB;    
    域名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); 
                      
}

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。