【STM32H7教程】第94章 STM32H7的SPI總線應用之雙機通信(DMA方式)

完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第94章       STM32H7的SPI總線應用之雙機通信(DMA方式)

本章節爲大家講解SPI DMA方式雙機通信。

94.1 初學者重要提示

94.2 SPI DMA主從機硬件接線

94.3 SPI DMA主機程序設計

94.4 SPI DMA從機程序設計

94.5 SPI DMA主從機使用注意事項

94.6 SPI DMA主從機驅動移植和使用

94.7 實驗例程設計框架

94.8 實驗例程說明(MDK)

94.9 實驗例程說明(IAR)

94.10 總結

 

 

94.1 初學者重要提示

  1.   學習本章節前,務必優先學習第72章SPI基礎和第73章SPI Flash的DMA玩法方式。本章實現的SPI DMA通信方式的主機和從機,跟SPI DMA方式驅動SPI Flash是類似的。
  2.   本章是採用的SPI DMA全雙工通信方式。
  3.   大家根據自己接線的穩定性,可以適當調節SPI主機和從機的時鐘速度,其中從機的時鐘速度是可以高於主機速度的,這樣通信的容錯性更好些。

94.2 SPI DMA主從機硬件接線

接線方式如下,使用的兩塊V7板子,一塊板子做主機,一塊板子做從機。

 

對應的引腳信息如下:

實際項目中使用,推薦大家務必比將硬件片選引腳NSS接上,實現全程硬件控制收發。如果大家不使用硬件片選,而使用下面的方式:

這種方式有個比較明顯的缺點,主從機上電次序不同,很容易造成從機CLK識別錯誤,即高低電平變化導致數據傳輸錯位。改成加入硬件SPI片選NSS引腳後,完美解決了這個問題

94.3 SPI DMA主機程序設計

SPI DMA主機程序實現和本教程72的SPI DMA配置是一樣的,只是多了SPI硬件片選引腳NSS配置。

94.3.1 第1步:SPI總線配置

SPI總線配置通過如下兩個函數實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIBus
*    功能說明: 配置SPI總線。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitSPIBus(void)
{    
    g_spi_busy = 0;
    
    bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_16, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
}

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIParam
*    功能說明: 配置SPI總線參數,時鐘分頻,時鐘相位和時鐘極性。
*    形    參: _BaudRatePrescaler  SPI總線時鐘分頻設置,支持的參數如下:
*                                 SPI_BAUDRATEPRESCALER_2    2分頻
*                                 SPI_BAUDRATEPRESCALER_4    4分頻
*                                 SPI_BAUDRATEPRESCALER_8    8分頻
*                                 SPI_BAUDRATEPRESCALER_16   16分頻
*                                 SPI_BAUDRATEPRESCALER_32   32分頻
*                                 SPI_BAUDRATEPRESCALER_64   64分頻
*                                 SPI_BAUDRATEPRESCALER_128  128分頻
*                                 SPI_BAUDRATEPRESCALER_256  256分頻
*                                                        
*             _CLKPhase           時鐘相位,支持的參數如下:
*                                 SPI_PHASE_1EDGE     SCK引腳的第1個邊沿捕獲傳輸的第1個數據
*                                 SPI_PHASE_2EDGE     SCK引腳的第2個邊沿捕獲傳輸的第1個數據
*                                 
*             _CLKPolarity        時鐘極性,支持的參數如下:
*                                 SPI_POLARITY_LOW    SCK引腳在空閒狀態處於低電平
*                                 SPI_POLARITY_HIGH   SCK引腳在空閒狀態處於高電平
*
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
{
    /* 提高執行效率,只有在SPI硬件參數發生變化時,才執行HAL_Init */
    if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)
    {        
        return;
    }

    s_BaudRatePrescaler = _BaudRatePrescaler;    
    s_CLKPhase = _CLKPhase;
    s_CLKPolarity = _CLKPolarity;
    
    
    /* 設置SPI參數 */
    hspi.Instance               = SPIx;                   /* 例化SPI */
    hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 設置波特率 */
    hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全雙工 */
    hspi.Init.CLKPhase          = _CLKPhase;              /* 配置時鐘相位 */
    hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置時鐘極性 */
    hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 設置數據寬度 */
    hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 數據傳輸先傳高位 */
    hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
    hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC後,此位無效 */
    hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC後,此位無效 */
    hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用軟件方式管理片選引腳 */
    hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 設置FIFO大小是一個數據項 */
    hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脈衝輸出 */
    hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI後,SPI相關引腳保持當前狀態 */  
    hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */

    /* 設置SPI參數 */
    hspi.Instance               = SPIx;                           /* 例化SPI */
    hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 設置波特率 */
    hspi.Init.Direction         = SPI_DIRECTION_2LINES;         /* 全雙工 */
    hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置時鐘相位 */
    hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置時鐘極性 */
    hspi.Init.DataSize          = SPI_DATASIZE_8BIT;               /* 設置數據寬度 */
    hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 數據傳輸先傳高位 */
    hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */
    hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */
    hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC後,此位無效 */
    hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC後,此位無效 */
    hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_16DATA;    /* 設置FIFO大小是一個數據項 */
    
    hspi.Init.NSS         = SPI_NSS_HARD_OUTPUT;                 /* 片選引腳 */
    hspi.Init.NSSPMode    = SPI_NSS_PULSE_DISABLE;                /* 禁止脈衝輸出 */
    hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低電平有效 */
    hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE; /* MSS, 插入到NSS有效邊沿和第一個
數據開始之間的額外延遲,單位SPI時鐘週期個數 */

    hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE; /* MIDI, 兩個連續數據幀之間
插入的最小時間延遲,單位SPI時鐘週期個數 */
    
    hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI後,SPI相關引腳保持當前狀態 */  
    hspi.Init.Mode                  = SPI_MODE_MASTER;                

    /* 復位配置 */
    if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }    

    /* 初始化配置 */
    if (HAL_SPI_Init(&hspi) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }    
}

關於這兩個函數有以下四點要做個說明:

  •   函數bsp_InitSPIBus裏面的配置是個初始設置。實際驅動芯片時,會通過函數bsp_InitSPIParam做再配置。
  •   函數bsp_InitSPIParam提供了時鐘分頻,時鐘相位和時鐘極性配置。一般主機和從機此處設置爲一樣即可,但推薦
  •   SPI硬件片選NSS設置爲SPI_NSS_HARD_OUTPUT。
  •   這裏特別注意主機是hspi.Init.Mode = SPI_MODE_MASTER。

94.3.2 第2步:SPI DMA配置

配置代碼實現如下,註釋比較詳細:

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIParam
*    功能說明: 配置SPI總線時鐘,GPIO,中斷,DMA等
*    形    參: SPI_HandleTypeDef 類型指針變量
*    返 回 值: 無
*********************************************************************************************************
*/
void HAL_SPI_MspInit(SPI_HandleTypeDef *_hspi)
{
    /* 配置 SPI總線GPIO : SCK MOSI MISO */
    {
        GPIO_InitTypeDef  GPIO_InitStruct;
            
        /* SPI和GPIP時鐘 */
        SPIx_SCK_CLK_ENABLE();
        SPIx_MISO_CLK_ENABLE();
        SPIx_MOSI_CLK_ENABLE();
        SPIx_CLK_ENABLE();

        /* SPI SCK */
        GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull      = GPIO_PULLUP;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = SPIx_SCK_AF;
        HAL_GPIO_Init(SPIx_SCK_GPIO, &GPIO_InitStruct);

        /* SPI MISO */
        GPIO_InitStruct.Pin = SPIx_MISO_PIN;
        GPIO_InitStruct.Alternate = SPIx_MISO_AF;
        HAL_GPIO_Init(SPIx_MISO_GPIO, &GPIO_InitStruct);

        /* SPI MOSI */
        GPIO_InitStruct.Pin = SPIx_MOSI_PIN;
        GPIO_InitStruct.Alternate = SPIx_MOSI_AF;
        HAL_GPIO_Init(SPIx_MOSI_GPIO, &GPIO_InitStruct);
        
        /* SPI NSS */
        GPIO_InitStruct.Pin = SPIx_NSS_PIN;
        GPIO_InitStruct.Alternate = SPIx_NSS_AF;
        HAL_GPIO_Init(SPIx_NSS_GPIO, &GPIO_InitStruct);
    }

    /* 配置DMA和NVIC */
    #ifdef USE_SPI_DMA
    {
        /* 使能DMA時鐘 */
        DMAx_CLK_ENABLE();      

        /* SPI DMA發送配置 */        
        hdma_tx.Instance                = SPIx_TX_DMA_STREAM;    /* 例化使用的DMA數據流 */
        hdma_tx.Init.FIFOMode           = DMA_FIFOMODE_ENABLE;   /* FIFO*/
        hdma_tx.Init.FIFOThreshold      = DMA_FIFO_THRESHOLD_FULL;/* 禁止FIFO此位不起作用,用於設置閥值 */
        hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於存儲器突發 */
        hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;     /* 禁止FIFO此位不起作用,用於外設突發 */
        hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 請求類型 */  
        hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 傳輸方向是從存儲器到外設 */  
        hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外設地址自增禁止 */ 
        hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存儲器地址自增使能 */  
        hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;     /* 外設數據傳輸位寬選擇字節,即8bit */ 
        hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;     /* 存儲器數據傳輸位寬選擇字節,即8bit */    
        hdma_tx.Init.Mode                = DMA_NORMAL;              /* 正常模式 */
        hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 優先級低 */

         /* 復位DMA */
        if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }
        
         /* 初始化DMA */
        if(HAL_DMA_Init(&hdma_tx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }

        /* 關聯DMA句柄到SPI */
        __HAL_LINKDMA(_hspi, hdmatx, hdma_tx);    

        /* SPI DMA接收配置 */    
        hdma_rx.Instance                = SPIx_RX_DMA_STREAM;     /* 例化使用的DMA數據流 */
        hdma_rx.Init.FIFOMode           = DMA_FIFOMODE_ENABLE;    /* FIFO*/
        hdma_rx.Init.FIFOThreshold      = DMA_FIFO_THRESHOLD_FULL;/* 禁止FIFO此位不起作用,用於設置閥值 */
        hdma_rx.Init.MemBurst            = DMA_MBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於存儲器突發 */
        hdma_rx.Init.PeriphBurst         = DMA_PBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於外設突發 */
        hdma_rx.Init.Request             = SPIx_RX_DMA_REQUEST;    /* 請求類型 */  
        hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;   /* 傳輸方向從外設到存儲器 */  
        hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;       /* 外設地址自增禁止 */   
        hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;        /* 存儲器地址自增使能 */   
        hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    /* 外設數據傳輸位寬選擇字節,即8bit */ 
        hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;    /* 存儲器數據傳輸位寬選擇字節,即8bit */   
        hdma_rx.Init.Mode                = DMA_NORMAL;             /* 正常模式 */
        hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;      /* 優先級高 */

         /* 復位DMA */
        if(HAL_DMA_DeInit(&hdma_rx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }
        
         /* 初始化DMA */
        if(HAL_DMA_Init(&hdma_rx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }

        /* 關聯DMA句柄到SPI */
       __HAL_LINKDMA(_hspi, hdmarx, hdma_rx);    

        /* 配置DMA發送中斷 */
        HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_DMA_TX_IRQn);
        
        /* 配置DMA接收中斷 */
        HAL_NVIC_SetPriority(SPIx_DMA_RX_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_DMA_RX_IRQn);
        
        /* 配置SPI中斷 */
        HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_IRQn);
    }
    #endif
    
    #ifdef USE_SPI_INT
        /* 配置SPI中斷優先級並使能中斷 */
        HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_IRQn);
    #endif
}

94.3.3 第3步:SPI DMA傳輸設置和MPU配置

SPI DMA方式主要通過函數bsp_spiTransfer實現數據傳輸(代碼裏面的查詢和中斷方式請忽略):

/*
*********************************************************************************************************
*                                 選擇DMA,中斷或者查詢方式
*********************************************************************************************************
*/
#define USE_SPI_DMA    /* DMA方式  */
//#define USE_SPI_INT    /* 中斷方式 */
//#define USE_SPI_POLL   /* 查詢方式 */

/* 查詢模式 */
#if defined (USE_SPI_POLL)

uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  
uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];

/* 中斷模式 */
#elif defined (USE_SPI_INT)

uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];

/* DMA模式使用的SRAM4 */
#elif defined (USE_SPI_DMA)
    #if defined ( __CC_ARM )    /* IAR *******/
        __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #elif defined (__ICCARM__)   /* MDK ********/
        #pragma location = ".RAM_D3"
        uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        #pragma location = ".RAM_D3"
        uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #endif
#endif

/*
*********************************************************************************************************
*    函 數 名: bsp_spiTransfer
*    功能說明: 啓動數據傳輸
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_spiTransfer(void)
{
    if (g_spiLen > SPI_BUFFER_SIZE)
    {
        return;
    }
    
    /* DMA方式傳輸 */
#ifdef USE_SPI_DMA
    wTransferState = TRANSFER_WAIT;
    
    if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }
    
    while (wTransferState == TRANSFER_WAIT)
    {
        ;
    }
#endif

    /* 中斷方式傳輸 */    
#ifdef USE_SPI_INT
    wTransferState = TRANSFER_WAIT;

    if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }
    
    while (wTransferState == TRANSFER_WAIT)
    {
        ;
    }
#endif

    /* 查詢方式傳輸 */    
#ifdef USE_SPI_POLL
    if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }    
#endif
}

DMA方式要特別注意幾點:

  •   通過本手冊第26章的內存塊超方便使用方式,將DMA緩衝定義到SRAM4上。因爲本工程是用的DTCM做的主RAM空間,這個空間無法使用通用DMA1和DMA2。
  •   程序這裏SPI DMA方式主控用的是等待傳輸完成,大家根據自己實際應用可以做修改,詳情大家可以看此貼作爲拓展:【深入探討】DMA到底能不能起到加速程序執行的作用,DMA死等操作是否合理,多個DMA數據流同時刷是否處理過來https://www.armbbs.cn/forum.php?mod=viewthread&tid=109765
  •   由於程序裏面開啓了數據Cache,會造成DMA和CPU訪問SRAM4數據不一致的問題,特此將SRAM4空間關閉Cache。
    /* 配置SRAM4的MPU屬性爲Non-cacheable */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

分散加載設置:

94.3.4 第4步:應用代碼設計

應用部分的代碼設計如下:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiMaster
*    功能說明: SPI 主機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiMaster(void)
{
    uint8_t count = 0;
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    
        gpio_init.Pull = GPIO_NOPULL;        
        gpio_init.Speed = GPIO_SPEED_HIGH;      
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,發送數據給從機*/
                    g_spiTxBuf[0] = count++;
                    g_spiTxBuf[1] = count++;
                    g_spiTxBuf[2] = count++;
                    g_spiTxBuf[3] = count++;
                    g_spiLen = 4;
                    printf("SPI主機發送數據:%d,%d,%d,%d\r\n",
 g_spiTxBuf[0],g_spiTxBuf[1],g_spiTxBuf[2],g_spiTxBuf[3]);

                    bsp_spiTransfer();
                    printf("SPI主機接收數據:%d,%d,%d,%d\r\n", 
g_spiRxBuf[0],g_spiRxBuf[1],g_spiRxBuf[2],g_spiRxBuf[3]);
                    break;
                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        
        }
    }
}

這部分代碼比較好理解,大家按下K1按鍵後,會打印發送的數據並打印SPI從機設備返回的數據。

 

94.4 SPI DMA從機程序設計

SPI DMA從機設計程序如下,與主機不同的是部分配置選項要設置爲從機方式。

94.4.1 第1步:SPI總線配置

SPI總線配置通過如

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIBus
*    功能說明: 配置SPI總線。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitSPIBus(void)
{    
    g_spi_busy = 0;
    
    bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_16, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
}

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIParam
*    功能說明: 配置SPI總線參數,時鐘分頻,時鐘相位和時鐘極性。
*    形    參: _BaudRatePrescaler  SPI總線時鐘分頻設置,支持的參數如下:
*                                 SPI_BAUDRATEPRESCALER_2    2分頻
*                                 SPI_BAUDRATEPRESCALER_4    4分頻
*                                 SPI_BAUDRATEPRESCALER_8    8分頻
*                                 SPI_BAUDRATEPRESCALER_16   16分頻
*                                 SPI_BAUDRATEPRESCALER_32   32分頻
*                                 SPI_BAUDRATEPRESCALER_64   64分頻
*                                 SPI_BAUDRATEPRESCALER_128  128分頻
*                                 SPI_BAUDRATEPRESCALER_256  256分頻
*                                                        
*             _CLKPhase           時鐘相位,支持的參數如下:
*                                 SPI_PHASE_1EDGE     SCK引腳的第1個邊沿捕獲傳輸的第1個數據
*                                 SPI_PHASE_2EDGE     SCK引腳的第2個邊沿捕獲傳輸的第1個數據
*                                 
*             _CLKPolarity        時鐘極性,支持的參數如下:
*                                 SPI_POLARITY_LOW    SCK引腳在空閒狀態處於低電平
*                                 SPI_POLARITY_HIGH   SCK引腳在空閒狀態處於高電平
*
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
{
    /* 提高執行效率,只有在SPI硬件參數發生變化時,才執行HAL_Init */
    if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)
    {        
        return;
    }

    s_BaudRatePrescaler = _BaudRatePrescaler;    
    s_CLKPhase = _CLKPhase;
    s_CLKPolarity = _CLKPolarity;
    
    
    /* 設置SPI參數 */
    hspi.Instance               = SPIx;                   /* 例化SPI */
    hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 設置波特率 */
    hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全雙工 */
    hspi.Init.CLKPhase          = _CLKPhase;              /* 配置時鐘相位 */
    hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置時鐘極性 */
    hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 設置數據寬度 */
    hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 數據傳輸先傳高位 */
    hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
    hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC後,此位無效 */
    hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC後,此位無效 */
    hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用軟件方式管理片選引腳 */
    hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 設置FIFO大小是一個數據項 */
    hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脈衝輸出 */
    hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI後,SPI相關引腳保持當前狀態 */  
    hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */

    /* 設置SPI參數 */
    hspi.Instance               = SPIx;                           /* 例化SPI */
    hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 設置波特率 */
    hspi.Init.Direction         = SPI_DIRECTION_2LINES;         /* 全雙工 */
    hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置時鐘相位 */
    hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置時鐘極性 */
    hspi.Init.DataSize          = SPI_DATASIZE_8BIT;               /* 設置數據寬度 */
    hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 數據傳輸先傳高位 */
    hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */
    hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */
    hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC後,此位無效 */
    hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC後,此位無效 */
    hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_16DATA;    /* 設置FIFO大小是一個數據項 */
    
    hspi.Init.NSS         = SPI_NSS_HARD_INPUT;                 /* 片選引腳 */
    hspi.Init.NSSPMode    = SPI_NSS_PULSE_DISABLE;                /* 禁止脈衝輸出 */
    hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低電平有效 */
    hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE; /* MSS, 插入到NSS有效邊沿和第一個
數據開始之間的額外延遲,單位SPI時鐘週期個數 */

    hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE; /* MIDI, 兩個連續數據幀之間
插入的最小時間延遲,單位SPI時鐘週期個數 */
    
    hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI後,SPI相關引腳保持當前狀態 */  
    hspi.Init.Mode                  = SPI_MODE_SLAVE;                

    /* 復位配置 */
    if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }    

    /* 初始化配置 */
    if (HAL_SPI_Init(&hspi) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }    
}

關於這兩個函數有以下三點要做個說明:

  •   函數bsp_InitSPIBus裏面的配置是個初始設置。實際驅動芯片時,會通過函數bsp_InitSPIParam做再配置。
  •   函數bsp_InitSPIParam提供了時鐘分頻,時鐘相位和時鐘極性配置。一般主機和從機此處設置爲一樣即可。
  •   SPI硬件片選NSS設置爲SPI_NSS_HARD_INPUT。
  •   這裏特別注意主機是hspi.Init.Mode = SPI_MODE_SLAVE。

94.4.2 第2步:SPI DMA配置

配置代碼實現如下,註釋比較詳細:

/*
*********************************************************************************************************
*    函 數 名: bsp_InitSPIParam
*    功能說明: 配置SPI總線時鐘,GPIO,中斷,DMA等
*    形    參: SPI_HandleTypeDef 類型指針變量
*    返 回 值: 無
*********************************************************************************************************
*/
void HAL_SPI_MspInit(SPI_HandleTypeDef *_hspi)
{
    /* 配置 SPI總線GPIO : SCK MOSI MISO */
    {
        GPIO_InitTypeDef  GPIO_InitStruct;
            
        /* SPI和GPIP時鐘 */
        SPIx_SCK_CLK_ENABLE();
        SPIx_MISO_CLK_ENABLE();
        SPIx_MOSI_CLK_ENABLE();
        SPIx_CLK_ENABLE();

        /* SPI SCK */
        GPIO_InitStruct.Pin       = SPIx_SCK_PIN;
        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull      = GPIO_PULLUP;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = SPIx_SCK_AF;
        HAL_GPIO_Init(SPIx_SCK_GPIO, &GPIO_InitStruct);

        /* SPI MISO */
        GPIO_InitStruct.Pin = SPIx_MISO_PIN;
        GPIO_InitStruct.Alternate = SPIx_MISO_AF;
        HAL_GPIO_Init(SPIx_MISO_GPIO, &GPIO_InitStruct);

        /* SPI MOSI */
        GPIO_InitStruct.Pin = SPIx_MOSI_PIN;
        GPIO_InitStruct.Alternate = SPIx_MOSI_AF;
        HAL_GPIO_Init(SPIx_MOSI_GPIO, &GPIO_InitStruct);
        
        /* SPI NSS */
        GPIO_InitStruct.Pin = SPIx_NSS_PIN;
        GPIO_InitStruct.Alternate = SPIx_NSS_AF;
        HAL_GPIO_Init(SPIx_NSS_GPIO, &GPIO_InitStruct);
    }

    /* 配置DMA和NVIC */
    #ifdef USE_SPI_DMA
    {
        /* 使能DMA時鐘 */
        DMAx_CLK_ENABLE();      

        /* SPI DMA發送配置 */        
        hdma_tx.Instance                = SPIx_TX_DMA_STREAM;    /* 例化使用的DMA數據流 */
        hdma_tx.Init.FIFOMode           = DMA_FIFOMODE_ENABLE;   /* FIFO*/
        hdma_tx.Init.FIFOThreshold      = DMA_FIFO_THRESHOLD_FULL;/* 禁止FIFO此位不起作用,用於設置閥值 */
        hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於存儲器突發 */
        hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;     /* 禁止FIFO此位不起作用,用於外設突發 */
        hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 請求類型 */  
        hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 傳輸方向是從存儲器到外設 */  
        hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外設地址自增禁止 */ 
        hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存儲器地址自增使能 */  
        hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;     /* 外設數據傳輸位寬選擇字節,即8bit */ 
        hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;     /* 存儲器數據傳輸位寬選擇字節,即8bit */    
        hdma_tx.Init.Mode                = DMA_NORMAL;              /* 正常模式 */
        hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 優先級低 */

         /* 復位DMA */
        if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }
        
         /* 初始化DMA */
        if(HAL_DMA_Init(&hdma_tx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }

        /* 關聯DMA句柄到SPI */
        __HAL_LINKDMA(_hspi, hdmatx, hdma_tx);    

        /* SPI DMA接收配置 */    
        hdma_rx.Instance                = SPIx_RX_DMA_STREAM;     /* 例化使用的DMA數據流 */
        hdma_rx.Init.FIFOMode           = DMA_FIFOMODE_ENABLE;    /* FIFO*/
        hdma_rx.Init.FIFOThreshold      = DMA_FIFO_THRESHOLD_FULL;/* 禁止FIFO此位不起作用,用於設置閥值 */
        hdma_rx.Init.MemBurst            = DMA_MBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於存儲器突發 */
        hdma_rx.Init.PeriphBurst         = DMA_PBURST_SINGLE;    /* 禁止FIFO此位不起作用,用於外設突發 */
        hdma_rx.Init.Request             = SPIx_RX_DMA_REQUEST;    /* 請求類型 */  
        hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;   /* 傳輸方向從外設到存儲器 */  
        hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;       /* 外設地址自增禁止 */   
        hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;        /* 存儲器地址自增使能 */   
        hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    /* 外設數據傳輸位寬選擇字節,即8bit */ 
        hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;    /* 存儲器數據傳輸位寬選擇字節,即8bit */   
        hdma_rx.Init.Mode                = DMA_NORMAL;             /* 正常模式 */
        hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;      /* 優先級高 */

         /* 復位DMA */
        if(HAL_DMA_DeInit(&hdma_rx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }
        
         /* 初始化DMA */
        if(HAL_DMA_Init(&hdma_rx) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);     
        }

        /* 關聯DMA句柄到SPI */
       __HAL_LINKDMA(_hspi, hdmarx, hdma_rx);    

        /* 配置DMA發送中斷 */
        HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_DMA_TX_IRQn);
        
        /* 配置DMA接收中斷 */
        HAL_NVIC_SetPriority(SPIx_DMA_RX_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_DMA_RX_IRQn);
        
        /* 配置SPI中斷 */
        HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_IRQn);
    }
    #endif
    
    #ifdef USE_SPI_INT
        /* 配置SPI中斷優先級並使能中斷 */
        HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPIx_IRQn);
    #endif
}

94.4.3 第3步:SPI DMA傳輸設置和MPU配置

SPI DMA方式主要通過函數bsp_spiTransfer實現數據傳輸(代碼裏面的查詢和中斷方式請忽略):

/*
*********************************************************************************************************
*                                 選擇DMA,中斷或者查詢方式
*********************************************************************************************************
*/
#define USE_SPI_DMA    /* DMA方式  */
//#define USE_SPI_INT    /* 中斷方式 */
//#define USE_SPI_POLL   /* 查詢方式 */

/* 查詢模式 */
#if defined (USE_SPI_POLL)

uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  
uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];

/* 中斷模式 */
#elif defined (USE_SPI_INT)

uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];

/* DMA模式使用的SRAM4 */
#elif defined (USE_SPI_DMA)
    #if defined ( __CC_ARM )    /* IAR *******/
        __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #elif defined (__ICCARM__)   /* MDK ********/
        #pragma location = ".RAM_D3"
        uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
        #pragma location = ".RAM_D3"
        uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    #endif
#endif

/*
*********************************************************************************************************
*    函 數 名: bsp_spiTransfer
*    功能說明: 啓動數據傳輸
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_spiTransfer(void)
{
    if (g_spiLen > SPI_BUFFER_SIZE)
    {
        return;
    }
    
    /* DMA方式傳輸 */
#ifdef USE_SPI_DMA
    wTransferState = TRANSFER_WAIT;
    
    if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }
    
    //while (wTransferState == TRANSFER_WAIT)
    //{
        ;
    //}
#endif

    /* 中斷方式傳輸 */    
#ifdef USE_SPI_INT
    wTransferState = TRANSFER_WAIT;

    if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }
    
    //while (wTransferState == TRANSFER_WAIT)
    //{
        ;
    //}
#endif

    /* 查詢方式傳輸 */    
#ifdef USE_SPI_POLL
    if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)    
    {
        Error_Handler(__FILE__, __LINE__);
    }    
#endif
}

DMA方式要特別注意兩點:

  • 通過本手冊第26章的內存塊超方便使用方式,將DMA緩衝定義到SRAM4上。因爲本工程是用的DTCM做的主RAM空間,這個空間無法使用通用DMA1和DMA2。
  • 由於程序裏面開啓了數據Cache,會造成DMA和CPU訪問SRAM4數據不一致的問題,特此將SRAM4空間關閉Cache。
    /* 配置SRAM4的MPU屬性爲Non-cacheable */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

分散加載設置:

94.4.4 第4步:應用代碼設計

應用部分的代碼設計如下:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiSlave
*    功能說明: SPI 從機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiSlave(void)
{
    uint8_t count = 0;
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    /* 設置推輓輸出 */
        gpio_init.Pull = GPIO_NOPULL;            /* 上下拉電阻不使能 */
        gpio_init.Speed = GPIO_SPEED_HIGH;      /* GPIO速度等級 */    
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    /* 上電後,準備接收主機命令 */
    g_spiTxBuf[0] = count++;
    g_spiTxBuf[1] = count++;
    g_spiTxBuf[2] = count++;
    g_spiTxBuf[3] = count++;
    g_spiLen = 4;
    bsp_spiTransfer();
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        if (wTransferState != TRANSFER_WAIT)
        {
            printf("SPI從機發送數據 = %d,%d,%d,%d\r\n", g_spiTxBuf[0], g_spiTxBuf[1], g_spiTxBuf[2],
 g_spiTxBuf[3]);
            printf("SPI從機接收數據 = %d,%d,%d,%d\r\n", g_spiRxBuf[0], g_spiRxBuf[1], g_spiRxBuf[2], 
g_spiRxBuf[3]);
            g_spiTxBuf[0] = count++;
            g_spiTxBuf[1] = count++;
            g_spiTxBuf[2] = count++;
            g_spiTxBuf[3] = count++;
            g_spiLen = 4;
            bsp_spiTransfer();
        }
    }
}

從機設計這裏主要注意兩點:

  • 上電後優先準備一次從機數據接收。
  • 從機接收到主機發送的數據後,將接收到的數據打印出來並打印發送的數據。

 

94.5 SPI DMA主從機使用注意事項

大家根據自己接線的穩定性,可以適當調節SPI主機和從機的時鐘速度,其中從機的時鐘速度是可以高於主機速度的,這樣通信的容錯性更好些。

94.6 SPI DMA主從機驅動移植和使用

移植步驟如下:

  • 第1步:複製bsp_spi_bus.c,bsp_spi_bus.h到自己的工程目錄,並添加到工程裏面。
  • 第2步:根據使用的第幾個SPI,SPI時鐘,SPI引腳和DMA通道等,修改bsp_spi_bus.c文件開頭的宏定義
/*
*********************************************************************************************************
*                                時鐘,引腳,DMA,中斷等宏定義
*********************************************************************************************************
*/
#define SPIx                            SPI1
#define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
#define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()

#define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
#define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()

#define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_SCK_GPIO                    GPIOB
#define SPIx_SCK_PIN                    GPIO_PIN_3
#define SPIx_SCK_AF                        GPIO_AF5_SPI1

#define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_MISO_GPIO                    GPIOB
#define SPIx_MISO_PIN                     GPIO_PIN_4
#define SPIx_MISO_AF                    GPIO_AF5_SPI1

#define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
#define SPIx_MOSI_GPIO                    GPIOB
#define SPIx_MOSI_PIN                     GPIO_PIN_5
#define SPIx_MOSI_AF                    GPIO_AF5_SPI1

#define SPIx_TX_DMA_STREAM               DMA2_Stream3
#define SPIx_RX_DMA_STREAM               DMA2_Stream2

#define SPIx_TX_DMA_REQUEST              DMA_REQUEST_SPI1_TX
#define SPIx_RX_DMA_REQUEST              DMA_REQUEST_SPI1_RX

#define SPIx_DMA_TX_IRQn                 DMA2_Stream3_IRQn
#define SPIx_DMA_RX_IRQn                 DMA2_Stream2_IRQn

#define SPIx_DMA_TX_IRQHandler           DMA2_Stream3_IRQHandler
#define SPIx_DMA_RX_IRQHandler           DMA2_Stream2_IRQHandler

#define SPIx_IRQn                        SPI1_IRQn
#define SPIx_IRQHandler                  SPI1_IRQHandler
  • 第3步:如果使用DMA方式的話,請不要使用TCM RAM,因爲通用DMA1和DMA2不支持。併爲了防止DMA和CPU同時訪問DMA緩衝造成的數據一致性問題,將這塊空間關閉Cache處理,比如使用的SRAM4:
/* 配置SRAM4的MPU屬性爲Non-cacheable */
MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress      = 0x38000000;
MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);
  • 第4步:初始化SPI。
/* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */
bsp_InitSPIBus();    /* 配置SPI總線 */    
  • 第5步:SPI Flash驅動主要用到HAL庫的SPI驅動文件,簡單省事些可以添加所有HAL庫C源文件進來。
  • 第6步:應用方法看本章節配套例子即可。特別注意分散加載設置:

94.7 實驗例程設計框架

通過程序設計框架,讓大家先對配套例程有一個全面的認識,然後再理解細節,本次實驗例程的設計框架如下:

 

第1階段,上電啓動階段:

  • 這部分在第14章進行了詳細說明。

第2階段,進入main函數:

  • 第1部分,硬件初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器和LED。
  • 第2部分,應用程序設計部分,實現SPI雙擊通信。

94.8 實驗例程說明(MDK)

配套例子:

V7-070_SPI DMA雙機通信(主機)

V7-071_SPI DMA雙機通信(從機)

實驗目的:

  1. 學習SPI Flash主從機通信實現。

實驗操作:

  1. K1按鍵按下,主機打印。
  2. SPI從機等待主機消息。

上電後串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1。

主機:

 

從機:

 

程序設計:

系統棧大小分配:

RAM空間用的DTCM:

硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設置NVIV優先級分組爲4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啓,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啓 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鐘週期計數器 */       
    bsp_InitKey();         /* 按鍵初始化,要放在滴答定時器之前,因爲按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();       /* 初始化滴答定時器 */
    bsp_InitLPUart();     /* 初始化串口 */
    bsp_InitExtIO();     /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();         /* 初始化LED */    
bsp_InitExtSDRAM(); /* 初始化SDRAM */

    /* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */
    bsp_InitSPIBus();    /* 配置SPI總線 */        

}

MPU配置和Cache配置:

數據Cache和指令Cache都開啓。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區以及SRAM4

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性爲Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性爲Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* 配置SRAM4的MPU屬性爲Non-cacheable */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

每10ms調用一次按鍵處理:

按鍵處理是在滴答定時器中斷裏面實現,每10ms執行一次檢測。

/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程序。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

主功能:

主機程序實現如下操作:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiMaster
*    功能說明: SPI 主機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiMaster(void)
{
    uint8_t count = 0;
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    
        gpio_init.Pull = GPIO_NOPULL;        
        gpio_init.Speed = GPIO_SPEED_HIGH;      
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,發送數據給從機*/
                    g_spiTxBuf[0] = count++;
                    g_spiTxBuf[1] = count++;
                    g_spiTxBuf[2] = count++;
                    g_spiTxBuf[3] = count++;
                    g_spiLen = 4;
                    printf("SPI主機發送數據:%d,%d,%d,%d\r\n",
 g_spiTxBuf[0],g_spiTxBuf[1],g_spiTxBuf[2],g_spiTxBuf[3]);

                    bsp_spiTransfer();
                    printf("SPI主機接收數據:%d,%d,%d,%d\r\n", 
g_spiRxBuf[0],g_spiRxBuf[1],g_spiRxBuf[2],g_spiRxBuf[3]);
                    break;
                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        
        }
    }
}

從機實現程序如下:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiSlave
*    功能說明: SPI 從機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiSlave(void)
{
    uint8_t count = 0;
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    /* 設置推輓輸出 */
        gpio_init.Pull = GPIO_NOPULL;            /* 上下拉電阻不使能 */
        gpio_init.Speed = GPIO_SPEED_HIGH;      /* GPIO速度等級 */    
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    /* 上電後,準備接收主機命令 */
    g_spiTxBuf[0] = count++;
    g_spiTxBuf[1] = count++;
    g_spiTxBuf[2] = count++;
    g_spiTxBuf[3] = count++;
    g_spiLen = 4;
    bsp_spiTransfer();
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        if (wTransferState != TRANSFER_WAIT)
        {
            printf("SPI從機發送數據 = %d,%d,%d,%d\r\n", g_spiTxBuf[0], g_spiTxBuf[1], g_spiTxBuf[2],
 g_spiTxBuf[3]);
            printf("SPI從機接收數據 = %d,%d,%d,%d\r\n", g_spiRxBuf[0], g_spiRxBuf[1], g_spiRxBuf[2], 
g_spiRxBuf[3]);
            g_spiTxBuf[0] = count++;
            g_spiTxBuf[1] = count++;
            g_spiTxBuf[2] = count++;
            g_spiTxBuf[3] = count++;
            g_spiLen = 4;
            bsp_spiTransfer();
        }
    }
}

94.9 實驗例程說明(IAR)

配套例子:

V7-070_SPI DMA雙機通信(主機)

V7-071_SPI DMA雙機通信(從機)

實驗目的:

  1. 學習SPI Flash主從機通信實現。

實驗操作:

  1. K1按鍵按下,主機打印。
  2. SPI從機等待主機消息。

上電後串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1。

主機:

從機:

程序設計:

系統棧大小分配:

RAM空間用的DTCM:

硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:
       - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。
       - 設置NVIV優先級分組爲4。
     */
    HAL_Init();

    /* 
       配置系統時鐘到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啓,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啓 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鐘週期計數器 */       
    bsp_InitKey();         /* 按鍵初始化,要放在滴答定時器之前,因爲按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();       /* 初始化滴答定時器 */
    bsp_InitLPUart();     /* 初始化串口 */
    bsp_InitExtIO();     /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();         /* 初始化LED */    
bsp_InitExtSDRAM(); /* 初始化SDRAM */

    /* 針對不同的應用程序,添加需要的底層驅動模塊初始化函數 */
    bsp_InitSPIBus();    /* 配置SPI總線 */        

}

MPU配置和Cache配置:

數據Cache和指令Cache都開啓。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區以及SRAM4

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性爲Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性爲Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* 配置SRAM4的MPU屬性爲Non-cacheable */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

每10ms調用一次按鍵處理:

按鍵處理是在滴答定時器中斷裏面實現,每10ms執行一次檢測。

/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程序。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

主功能:

主機程序實現如下操作:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiMaster
*    功能說明: SPI 主機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiMaster(void)
{
    uint8_t count = 0;
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    
        gpio_init.Pull = GPIO_NOPULL;        
        gpio_init.Speed = GPIO_SPEED_HIGH;      
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }
        
        /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1鍵按下,發送數據給從機*/
                    g_spiTxBuf[0] = count++;
                    g_spiTxBuf[1] = count++;
                    g_spiTxBuf[2] = count++;
                    g_spiTxBuf[3] = count++;
                    g_spiLen = 4;
                    printf("SPI主機發送數據:%d,%d,%d,%d\r\n",
 g_spiTxBuf[0],g_spiTxBuf[1],g_spiTxBuf[2],g_spiTxBuf[3]);

                    bsp_spiTransfer();
                    printf("SPI主機接收數據:%d,%d,%d,%d\r\n", 
g_spiRxBuf[0],g_spiRxBuf[1],g_spiRxBuf[2],g_spiRxBuf[3]);
                    break;
                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        
        }
    }
}

從機實現程序如下:

/*
*********************************************************************************************************
*    函 數 名: DemoSpiSlave
*    功能說明: SPI 從機通信
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void DemoSpiSlave(void)
{
    uint8_t count = 0;
    
    /***************設置SPI Flash片選上拉,防止影響 ***************/
    {
        GPIO_InitTypeDef gpio_init;

        /* 打開GPIO時鐘 */
        __HAL_RCC_GPIOD_CLK_ENABLE();
        
        gpio_init.Mode = GPIO_MODE_OUTPUT_PP;    /* 設置推輓輸出 */
        gpio_init.Pull = GPIO_NOPULL;            /* 上下拉電阻不使能 */
        gpio_init.Speed = GPIO_SPEED_HIGH;      /* GPIO速度等級 */    
        gpio_init.Pin = GPIO_PIN_13;    
        HAL_GPIO_Init(GPIOD, &gpio_init);

        GPIOD->BSRR = GPIO_PIN_13;
    }
    
    sfDispMenu();        /* 打印命令提示 */
    
    bsp_StartAutoTimer(0, 100);    /* 啓動1個100ms的自動重裝的定時器 */
    
    /* 上電後,準備接收主機命令 */
    g_spiTxBuf[0] = count++;
    g_spiTxBuf[1] = count++;
    g_spiTxBuf[2] = count++;
    g_spiTxBuf[3] = count++;
    g_spiLen = 4;
    bsp_spiTransfer();
    
    while(1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和餵狗 */
        
        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        if (wTransferState != TRANSFER_WAIT)
        {
            printf("SPI從機發送數據 = %d,%d,%d,%d\r\n", g_spiTxBuf[0], g_spiTxBuf[1], g_spiTxBuf[2],
 g_spiTxBuf[3]);
            printf("SPI從機接收數據 = %d,%d,%d,%d\r\n", g_spiRxBuf[0], g_spiRxBuf[1], g_spiRxBuf[2], 
g_spiRxBuf[3]);
            g_spiTxBuf[0] = count++;
            g_spiTxBuf[1] = count++;
            g_spiTxBuf[2] = count++;
            g_spiTxBuf[3] = count++;
            g_spiLen = 4;
            bsp_spiTransfer();
        }
    }
}

94.10   總結

本章節就爲大家講解這麼多,推薦大家做實際應用測試,實際項目中需要雙機通信,可以考慮這種方式,比較實用。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章