zoukankan      html  css  js  c++  java
  • STM32F4 SPI 学习笔记

    SPI 全称Serial Peripheral Interface

    SPI的最高时钟高达45MHZ

    下图表达了SPI的工程原理,通过两根线(MISO和MOSI)进行数据传输,数据的读写同步进行,通过移位寄存器完成数据的交换。

    SPI的4条通讯线:

    MISO: Master Input Slave Output

    MOSI: Master Output Slave Input

    CS: Chip Select

    SCLK: System Clock

    前两根线是数据传输线,最后一跟 是系统时钟线 ,主要看第三根线CS,信号片选:

    这是别人写的一个不错的解释  https://blog.csdn.net/STL1634614466/article/details/69375138

    总结如下:

    CS在也叫NSS(Number Slave Select),分为软件和硬件控制,如果用硬件NSS,只能通过PF6这个口控制,因此只能于一台Slave连接。因此一般用软件NSS,通过普通IO输出数字电平到Slave的片选端口,如果是低电平,则工作,如果是高电平则不连接。

    SPI的相关配置如下:

    1. 时钟极性:CPOL = 0 -> 时钟空闲为低电平; CPOL = 1 -> 时钟空闲为高电平;

    2. 时钟相位:CPHA = 0 -> 时钟第一个跳变沿采集数据; CPHA = 1 -> 时钟第二个跳变沿采集数据;(两种不同的传输协议)

    提醒:主从机的时钟极性和时钟相位应保持一致。

    3. MODE: 可以配置为Master 或者 Slave,主从模式的区别在于时钟来源于主机

    4. 传输方向:只读,双向单线,双向双线

    5. SPI波特率分频系数

    6. 开始位:MSB 或者 LSB

    7. NSS软件控制还是硬件控制

    8. 帧格式:SPI Motorola 或者 TI

    9. 是否开启CRC校验

    10. 如果开启CRC,测需要设置CRC多项式

    关于SPI的配置,差不多就这些了,需要配置相关的寄存器。当然,HAL库早就将其封装好了,直接调用就行了。

    下面就是STM32中SPI的使用过程了:

    STEP1:打开SPI时钟

    __HAL_RCC_SPI5_CLK_ENABLE();
    

    STEP2:对SPI进行相关的配置

    HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
    

      上面的代码中相关的配置很多,具体已经在之前罗列了,大概就10个相关的配置。

    STEP3:使能SPI

    __HAL_SPI_ENABLE(&SPI5_Handler);
    

    STEP4:数据传输

    HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size,uint32_t Timeout);
    HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData,uint16_t Size,uint32_t Timeout);
    HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData,uint8_t *pRxData, uint16_t Size, uint32_t Timeout);

    以上部分大多参考正点原子的资料整理的。下面我们来通过CUBEMX配置自己的SPI,通过SPI连接到一个FLASH,并实现批量数据的传输。

     首先打开在CUBEMX上的配置:

    选择全双工,并且禁止硬件NSS。接下来就是在Configuration界面配置SPI的一些参数,下面我随便配置了一下,注意主机和从机一些参数要保持一致,DMA和中断我们就先不用了。

    配置好了,接下来我们生成工程文件吧!

     打开生成好的文件,找到Application/User目录下spi.c,里面有个函数就是用于配置我们在图形界面配置的参数。

    void MX_SPI5_Init(void)
    {
    
      hspi5.Instance = SPI5;
      hspi5.Init.Mode = SPI_MODE_MASTER;
      hspi5.Init.Direction = SPI_DIRECTION_2LINES;
      hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
      hspi5.Init.CLKPolarity = SPI_POLARITY_HIGH;
      hspi5.Init.CLKPhase = SPI_PHASE_2EDGE;
      hspi5.Init.NSS = SPI_NSS_SOFT;
      hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
      hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
      hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
      hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
      hspi5.Init.CRCPolynomial = 10;
      if (HAL_SPI_Init(&hspi5) != HAL_OK)
      {
        _Error_Handler(__FILE__, __LINE__);
      }
    }
    void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
    {
    
      GPIO_InitTypeDef GPIO_InitStruct;
      if(spiHandle->Instance==SPI5)
      {
      /* USER CODE BEGIN SPI5_MspInit 0 */
    
      /* USER CODE END SPI5_MspInit 0 */
        /* SPI5 clock enable */
        __HAL_RCC_SPI5_CLK_ENABLE();
      
        /**SPI5 GPIO Configuration    
        PF7     ------> SPI5_SCK
        PF8     ------> SPI5_MISO
        PF9     ------> SPI5_MOSI 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
        HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
    
      /* USER CODE BEGIN SPI5_MspInit 1 */
    
      /* USER CODE END SPI5_MspInit 1 */
      }
    }

    这个函数在main.c里已经帮你调用了,CUBEMX真棒!

    接下来我们就用STM32这个主机与一块FLASH从机进行通讯吧!

    我们在这里调用一下正点原子整理的两个文件spi.c和w25qxx.c以及他们的头文件。通过一个定时器不断扫描按键状态,并且在while(1)里面写入:

      while (1)
      {
            switch (SPI_KEY) {
                case KEY0_PRES:
                    W25QXX_Write((u8*)"showtimewalker", 0x00, 14);
                    LED0 = !LED0;
                    break;
                case KEY1_PRES:
                    W25QXX_Write((u8*)"ShowTimeWalker", 0x00, 14);
                    LED0 = !LED0;
                    break;
                case KEY2_PRES:
                    W25QXX_Read(DisPlayString, 0x00, 14);
                    POINT_COLOR = BLUE;
                    LCD_ShowString(10 + DisPlayCutoffX,60 + DisPlayCutoffY,300,16,16,(char*)DisPlayString);
                    DisPlayCutoffY += 18;
                    if (DisPlayCutoffY == 198){
                        DisPlayCutoffX += 120;
                        DisPlayCutoffY = 0;
                    }
                    LED0 = !LED0;
                    delay_ms(50);
                    break;
            }

    编译通过,然后我们按下KEY0,写入“showtimewalker”,按下KEY1,写入“ShowTimeWalker”,按下KEY2,读出数据,并且显示到显示屏上。看一下实际测试结果吧:

    OK,调试成功,值得一提的是,FLASH中的数据是掉电不消失的哦,因此可以用于存储一下特殊数据。

    有疑问欢迎大家讨论。谢谢阅读。

  • 相关阅读:
    Java数据类型转换
    Java数据类型
    Revisiting Network Support for RDMA
    FBOSS: Building Switch Software at Scale
    Edge-assisted Traffic Engineering and applications in the IoT
    Edge Intelligence: On-Demand Deep Learning Model Co-Inference with Device-Edge Synergy
    ARVE: Augmented Reality Applications in Vehicle to Edge Networks
    Deployment Characteristics of "The Edge" in Mobile Edge Computing
    CABaRet: Leveraging Recommendation Systems for Mobile Edge Caching
    Anveshak: Placing Edge Servers In The Wild
  • 原文地址:https://www.cnblogs.com/showtime20190824/p/11414598.html
Copyright © 2011-2022 走看看