【STM32H7教程】第48章 STM32H7的FMC總線應用之是32路高速IO擴展


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

第48章       STM32H7的FMC總線應用之是32路高速IO擴展

本章教程為大家講解利用STM32H7的FMC總線擴展出32路高速IO,且使用簡單,實際項目中也比較有實用價值。

48.1 初學者重要提示

48.2 FMC擴展IO硬件設計

48.3 FMC擴展IO驅動設計

48.4 FMC擴展IO板級支持包(bsp_fmc_io.c)

48.5 FMC擴展IO驅動移植和使用

48.6 實驗例程設計框架

48.7 實驗例程說明(MDK)

48.8 實驗例程說明(IAR)

48.9 總結

 

 

48.1 初學者重要提示

  1.   學習本章節前,務必優先學習第47章,需要對FMC的基礎知識和HAL庫的幾個常用API有個認識。
  2.   為什么要做IO擴展,不是已經用了240腳的H743XIH6嗎?因為開發板使用了32位SDRAM和RGB888硬件接口,消耗IO巨大,所以必須得擴展了。
  3.   擴展的32路高速IO非常實用,且使用簡單,只需初始下FMC,32路IO就可以隨意使用了。當前的擴展方式只支持高速輸出。
  4.   FMC總線擴展32路高速IO理解成GPIO的ODR寄存器就很簡單了,其實就是一個東西。

FMC擴展IO是對地址0x60001000的32bit數據空間的0和1的操作。GPIOA的ODR寄存器是對地址 0x40000000 + 0x18020000 + 0x14 空間的操作。但只能操作16個引腳。

使用總線的優勢就在這里了,相當於在GPIOA到GPIOK的基礎上,又擴展出GPIOL和GPIOM。

#define PERIPH_BASE            ((uint32_t)0x40000000)
#define D3_AHB1PERIPH_BASE     (PERIPH_BASE + 0x18020000)
#define GPIOA_BASE             (D3_AHB1PERIPH_BASE + 0x0000)
#define GPIOA                  ((GPIO_TypeDef *) GPIOA_BASE)

typedef struct
{
  __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
  __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */
  __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
} GPIO_TypeDef;

 

48.2 FMC擴展IO硬件設計

擴展IO涉及到的知識點稍多,下面逐一為大家做個說明。

48.2.1 第1步,先來看FMC的塊區分配

注,這個知識點在前面第47章的2.3小節有詳細說明。

FMC總線可操作的地址范圍0x60000000到0xDFFFFFFF,具體的框圖如下:

 

從上面的框圖可以看出,NOR/PSRAM/SRAM塊區有4個片選NE1,NE2,NE3和NE4,但由於引腳復用,部分片選對應的引腳要用於其他功能,而且要控制的總線外設較多,導致片選不夠用。因此需要增加譯碼器。

48.2.2 第2步,增加譯碼器及其地址計算

有了前面的認識之后再來看下面的譯碼器電路:

 

SN74LVC1G139APWR是雙2-4線地址譯碼器,也就是帶了兩個譯碼器。原理圖上僅用了一個。下面是139的真值表和引腳功能:

 

 

通過上面的原理圖和真值表就比較好理解了,真值表的輸出是由片選FMC_NE1和地址線FMC_A10、FMC_A11控制。

FMC_NE1 輸出低電平:

  •   FMC_A11(B),FMC_A10(A) = 00時,1Y0輸出的低電平,選擇的是OLED。
  •   FMC_A11(B),FMC_A10(A) = 01時,1Y1輸出的低電平,選擇的是74HC574。
  •   FMC_A11(B),FMC_A10(A) = 10時,1Y2輸出的低電平,選擇的是DM9000。
  •   FMC_A11(B),FMC_A10(A) = 11時,1Y3輸出的低電平,選擇的是AD7606。

然后我們再計算譯碼器的地址,注意,這里地址的計算都是按照FMC的32bit訪問模式計算的,因為我們的V7程序中是將NE1對應的FMC配置為32bit模式了。

具體FMC的32bit訪問模式,16bit訪問模式和8bit訪問模式的區別在第47章的2.4小節有詳細講解。

 

32bit模式下,我們計算A10和A11的時候,實際上需要按HADDR12和HADDR13計算的。

如果來算NE1 + HADDR12 + HADDR13的四種組合地址就是如下:

NE1 + HADDR13 + HADDR12 = 0x6000000 +  0<<13 + 0<<12 = 0x60000000

NE1 + HADDR13 + HADDR12 = 0x6000000 +  0<<13 + 1<<12 = 0x60001000

NE1 + HADDR13 + HADDR12 = 0x6000000 +  1<<13 + 0<<12 = 0x60002000

NE1 + HADDR13 + HADDR12 = 0x6000000 +  1<<13 + 1<<12 = 0x60003000

這樣一來,原理圖里面給的地址就對應上了。同理如果配置為16位模式和8位模式,大家應該也都會計算了。

48.2.3 第3步,FMC的IO擴展部分

先來看下IO擴展的原理圖實現,如果不太了解FMC的通信時序和數字邏輯芯片的使用,可能會比較懵,下面逐一為大家說明。

 

有了這個原理圖,首先要做的就是了解74HC574和SN74HC02的功能。

74HC574是一款8位三態D觸發器,起到鎖存的功能,上升沿觸發,對應的真值表如下(L表示低電平,H表示高電平,Z表示高阻):

 

SN74HC02是一款2輸入或非門,一個芯片帶了四組或非門,對應的真值表如下(L表示低電平,H表示高電平):

 

有了這個認識后,我們再來看FMC的配置,V7開發板的BSP驅動包里面專門做了一個IO擴展的FMC配置,即文件bsp_fmc_io.c,配置方式是FMC_AccessMode_A,這種模式對應的寫時序是:

 

那么問題來了,我們要實現的功能是通過FMC輸出的數據要鎖存在擴展IO的輸出端,否則FMC時序信號消失了,擴展IO的輸出數據也消失了,就起不到控制作用了。所以就用到74HC574的鎖存功能,而鎖存的實現需要一個上升沿觸發,這個上升沿就是通過74HC02輸出的。

再結合上面FMC寫時序圖,在NE片選為低電平,NWE寫使能信號為高電平期間,即地址建立時間段ADDSET內,74HC02是輸出的低電平。

進入到DATAST數據建立階段,在NE片選為低電平,NWE寫使能信號也為低電平時,74HC02輸出高電平,正好是實現1個上升沿的變化,將數據總線上的數據鎖存到74HC574的輸出端了。這里隱含了一個知識點,數據還沒有完全建立起來就鎖存是不是會有問題。在下面的3.3小節配置具體時序參數時再為大家說明。

48.2.4 第4步,舉例擴展IO驅動LED應用

進行到這里,再回過頭來看LED驅動就比較好理解了。操作LED的亮滅就是操作FMC的數據引腳D8,D9,D10和D11。

 

 

對地址0x64001000發送數據就可以了,但是如何對這個地址發送數據呢? 反映到C語言的實現上就是通過固定地址的指針變量(跟我們操作寄存器是一樣的),即

#define  HC574_PORT     *(uint32_t *)0x64001000

如果要點亮LED1(低電平點亮),就是 HC574_PORT = 0x0000 0000。

如果要熄滅LED1就是HC574_PORT = 0x0000 0100,即操作FMC_D8的高低電平即可。

48.3 FMC擴展IO驅動設計

下面將程序設計中的相關問題逐一為大家做個說明。

48.3.1 FMC擴展IO所涉及到的GPIO配置

這里僅需把用到的GPIO時鍾、FMC時鍾、GPIO引腳和復用配置好即可:

/*
*********************************************************************************************************
*    函 數 名: HC574_ConfigGPIO
*    功能說明: 配置GPIO,FMC管腳設置為復用功能
*    形    參:  無
*    返 回 值: 無
*********************************************************************************************************
*/
static void HC574_ConfigGPIO(void)
{
/*
    安富萊STM32-H7開發板接線方法:4片74HC574掛在FMC 32位總線上。1個地址端口可以擴展出32個IO
    PD0/FMC_D2
    PD1/FMC_D3
    PD4/FMC_NOE        ---- 讀控制信號,OE = Output Enable , N 表示低有效
    PD5/FMC_NWE        -XX- 寫控制信號,AD7606 只有讀,無寫信號
    PD8/FMC_D13
    PD9/FMC_D14
    PD10/FMC_D15
    PD14/FMC_D0
    PD15/FMC_D1

    PE7/FMC_D4
    PE8/FMC_D5
    PE9/FMC_D6
    PE10/FMC_D7
    PE11/FMC_D8
    PE12/FMC_D9
    PE13/FMC_D10
    PE14/FMC_D11
    PE15/FMC_D12
    
    PG0/FMC_A10        --- 和主片選FMC_NE2一起譯碼
    PG1/FMC_A11        --- 和主片選FMC_NE2一起譯碼
    XX --- PG9/FMC_NE2        --- 主片選(OLED, 74HC574, DM9000, AD7606)    
     --- PD7/FMC_NE1        --- 主片選(OLED, 74HC574, DM9000, AD7606)    
    
     +-------------------+------------------+
     +   32-bits Mode: D31-D16              +
     +-------------------+------------------+
     | PH8 <-> FMC_D16   | PI0 <-> FMC_D24  |
     | PH9 <-> FMC_D17   | PI1 <-> FMC_D25  |
     | PH10 <-> FMC_D18  | PI2 <-> FMC_D26  |
     | PH11 <-> FMC_D19  | PI3 <-> FMC_D27  |
     | PH12 <-> FMC_D20  | PI6 <-> FMC_D28  |
     | PH13 <-> FMC_D21  | PI7 <-> FMC_D29  |
     | PH14 <-> FMC_D22  | PI9 <-> FMC_D30  |
     | PH15 <-> FMC_D23  | PI10 <-> FMC_D31 |
     +------------------+-------------------+    
*/    

    GPIO_InitTypeDef gpio_init_structure;

    /* 使能 GPIO時鍾 */
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    __HAL_RCC_GPIOH_CLK_ENABLE();
    __HAL_RCC_GPIOI_CLK_ENABLE();


    /* 使能FMC時鍾 */
    __HAL_RCC_FMC_CLK_ENABLE();

    /* 設置 GPIOD 相關的IO為復用推挽輸出 */
    gpio_init_structure.Mode = GPIO_MODE_AF_PP;
    gpio_init_structure.Pull = GPIO_PULLUP;
    gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_structure.Alternate = GPIO_AF12_FMC;
    
    /* 配置GPIOD */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 |
                                GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOD, &gpio_init_structure);

    /* 配置GPIOE */
    gpio_init_structure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |
                                GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |
                                GPIO_PIN_15;
    HAL_GPIO_Init(GPIOE, &gpio_init_structure);

    /* 配置GPIOG */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1;
    HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    
    /* 配置GPIOH */
    gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12
                        | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOH, &gpio_init_structure);

    /* 配置GPIOI */
    gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6
                        | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    HAL_GPIO_Init(GPIOI, &gpio_init_structure);
}

 

48.3.2 FMC擴展IO時鍾源選擇

使用FMC可以選擇如下幾種時鍾源HCLK3,PLL1Q,PLL2R和PER_CK:

 

我們這里直接使用HCLK3,配置STM32H7的主頻為400MHz的時候,HCLK3輸出的200MHz,這個速度是FMC支持的最高時鍾,正好用於這里:

 

48.3.3 時序配置(重要)

這里要補充兩個重要的知識點,74HC574的CP端接收到上升沿觸發到Qn輸出的時間參數:

 

 

通過時序圖和對應的參數要了解到以下幾點:

  •   tpd傳輸延遲在這里等效於tPHL和tPLH
  •   V7開發板的74HC574有三片是3.3V供電,另外一片是5V供電。參數表格里面沒有給3.3V供電時的參數,也沒有最小值。

 

了解了74HC574,再來看SN74HC02:

 

 

通過時序圖和對應的參數要了解到以下幾點:

  •   tpd傳輸延遲在這里等效於tPHL和tPLH
  •   tt過渡時間等效於tr上升沿時間和tf下降沿時間。
  •   V7開發板的74HC574有兩片是3.3V供電,另外兩片是5V供電。參數表格里面沒有給3.3V和5V供電時的參數,也沒有最小值。

                                                   

對應74HC574和74HC02的時序參數有個了解后,再來看本章2.3小節末尾的問題:

 

當寫使能信號NWE出現下降沿后,74H02或非門就會輸出一個上升沿,然后觸發74HC574做鎖存。此時我們要考慮到一個重要的知識點,就是使用的數字邏輯芯片有個傳輸延遲問題,也就是要我們要保證74HC02的tpd傳輸延遲時間 + 74HC02的tr傳輸延遲時間 + 74HC574的tpd傳輸延遲時間的這段時間內,數據總線上要有數據,所以保證DATAST數據建立時間夠大就行。實際測試FMC頻率在200MHz的情況下,2-3個FMC時鍾周期就已經可以正常使用。

有了這些認識后,再來看FMC的時序配置就比較好理解了:

1.    /*
2.    ******************************************************************************************************
3.    *    函 數 名: HC574_ConfigFMC
4.    *    功能說明: 配置FMC並口訪問時序
5.    *    形    參:  無
6.    *    返 回 值: 無
7.    ******************************************************************************************************
8.    */
9.    static void HC574_ConfigFMC(void)
10.    {
11.        SRAM_HandleTypeDef hsram = {0};
12.        FMC_NORSRAM_TimingTypeDef SRAM_Timing = {0};
13.            
14.        hsram.Instance  = FMC_NORSRAM_DEVICE;
15.        hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;
16.    
17.        /* FMC使用的HCLK3,主頻200MHz,1個FMC時鍾周期就是5ns */
18.        /* SRAM 總線時序配置 4-1-2-1-2-2 不穩定,5-2-2-1-2-2 穩定 */  
19.        SRAM_Timing.AddressSetupTime   = 5;  /* 5*5ns=25ns,地址建立時間,范圍0 -15個FMC時鍾周期個數 */
20.        SRAM_Timing.AddressHoldTime    = 2;  /* 地址保持時間,配置為模式A時,用不到此參數 范圍1 -15個時
21.                                                  鍾周期個數 */
22.        SRAM_Timing.DataSetupTime          = 2;  /* 2*5ns=10ns,數據保持時間,范圍1 -255個時鍾周期個數 */
23.        SRAM_Timing.BusTurnAroundDuration  = 1;  /* 此配置用不到這個參數 */
24.        SRAM_Timing.CLKDivision            = 2;  /* 此配置用不到這個參數 */
25.        SRAM_Timing.DataLatency            = 2;  /* 此配置用不到這個參數 */
26.        SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; /* 配置為模式A */
27.    
28.        hsram.Init.NSBank             = FMC_NORSRAM_BANK1;   /* 使用的BANK1,即使用的片選FMC_NE1 */
29.        hsram.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止地址數據復用 */
30.        hsram.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存儲器類型SRAM */
31.        hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;    /* 32位總線寬度 */
32.        hsram.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 關閉突發模式 */
33.        hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用於設置等待信號的極性,關閉突
34.                                                                            發模式,此參數無效 */
35.        hsram.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 關閉突發模式,此參數無效 */
36.        hsram.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用於使能或者禁止寫保護 */
37.        hsram.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 關閉突發模式,此參數無效 */
38.        hsram.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止擴展模式 */
39.        hsram.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用於異步傳輸期間,使能或者禁止
40.                                                                            等待信號,這里選擇關閉 */
41.        hsram.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止寫突發 */
42.        hsram.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 僅同步模式才做時鍾輸出 */
43.        hsram.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;           /* 使能寫FIFO */
44.    
45.        /* 初始化SRAM控制器 */
46.        if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
47.        {
48.            /* 初始化錯誤 */
49.            Error_Handler(__FILE__, __LINE__);
50.        }
51.    }

 

這里把幾個關鍵的地方闡釋下:

  •   第11 - 12行,對作為局部變量的HAL庫結構體做初始化,防止不確定值配置時出問題。
  •   第19行,地址建立時間,對於FMC的IO擴展來說,這個地方取值0都可以,因為主要還是ADDST數據建立時間起作用。但是考慮到擴展IO外接了多個控制設備,這里取值5個FMC時鍾周期,大家可以根據實際情況做減小出來。
  •   第20行,地址保持時間,對於FMC模式A來說,此參數用不到。
  •   第22行,數據建立時間,實際測試2個FMC時鍾周期就可以正常使用,大家可以根據情況加大此數值。
  •   第23 – 25行,當前配置用不到這三個參數。
  •   第28行,使用的BANK1,即使用的片選FMC_NE1。
  •   第31行,由於是擴展的32路IO,所以這里要配置為32位帶寬。

48.3.4 MPU配置

實際測試發現,使能FMC_NE1所管理的存儲區的Cache功能后,會出現擴展IO的NE片選和NWE信號輸出2次的問題。經過各種Cache方式配置、FMC帶寬配置、操作FMC時的數據位寬設置,發現禁止了Cache功能就正常了,也就是說,設置FMC_NE1所管理的存儲區MPU屬性為Device或者Strongly Ordered即可。

    /* 配置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);

 

MPU配置中直接從FMC_NE1的首地址開始配置,設置了64KB空間的屬性。將FMC_NE1通過譯碼器所管理的所有設備地址全部設置為此配置:

 

48.3.5 操作數據位寬注意事項

在bsp_fmc_io.c文件開頭有個宏定義#define  HC574_PORT  *(uint32_t *)0x60001000。特別注意,這里是要操作地址0x60001000上的32位數據空間,即做了一個強制轉換uint32_t *,要跟FMC配置時設置的位寬一致。這樣做的原因,在第47章的2.6小節有說明。

48.4 FMC擴展IO板級支持包(bsp_fmc_io.c)

驅動文件bsp_fmc_io.c提供了如下幾個函數供用戶調用:

  •   bsp_InitExtIO
  •   HC574_SetPin
  •   HC574_TogglePin
  •   HC574_GetPin

48.4.1 函數bsp_InitExtIO

函數原型:

/*
*********************************************************************************************************
*    函 數 名: bsp_InitExtIO
*    功能說明: 配置擴展IO相關的GPIO. 上電只能執行一次。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitExtIO(void)
{
    HC574_ConfigGPIO();
    HC574_ConfigFMC();
    
    /* 將開發板一些片選,LED口設置為高 */
    g_HC574 = (NRF24L01_CE | VS1053_XDCS | LED1 | LED2 | LED3 | LED4);
    HC574_PORT = g_HC574;    /* 寫硬件端口,更改IO狀態 */
}

 

函數描述:

此函數用於初始化FMC擴展IO所用到的GPIO和FMC的參數配置。

使用舉例:

作為初始化函數,直接在在bsp.c文件的bsp_Init函數里面調用即可。

48.4.2 函數HC574_SetPin

函數原型:

/*
*********************************************************************************************************
*    函 數 名: HC574_SetPin
*    功能說明: 設置74HC574端口值
*    形    參: _pin : 管腳號, 0-31; 只能選1個,不能多選
*              _value : 設定的值,0或1
*    返 回 值: 無
*********************************************************************************************************
*/
void HC574_SetPin(uint32_t _pin, uint8_t _value)
{
    if (_value == 0)
    {
        g_HC574 &= (~_pin);
    }
    else
    {
        g_HC574 |= _pin;
    }
    HC574_PORT = g_HC574;
}

 

函數描述:

此函數用於設置擴展IO的輸出狀態。調用此函數前,要保證調用了函數bsp_InitExtIO進行了初始化。

函數參數:

  •   第1個參數是擴展IO的引腳,支持的形參如下,每次僅支持調用下面1個,不支持多個IO一起操作。
#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
#define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */
#define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */
#define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
#define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */
#define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */
#define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */
#define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
#define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */
#define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */
#define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */
#define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */
#define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */
#define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */
#define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */
#define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */
#define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */

 

  •   第2個參數用於設置指定擴展IO的高低電平,0表示輸出低電平,1表示輸出高電平。

使用舉例:

比如設置擴展IO引腳GPIO_PIN_23為高電平:HC574_SetPin(GPIO_PIN_23, 1)。

48.4.3 函數HC574_TogglePin

函數原型:

/*
*********************************************************************************************************
*    函 數 名: HC574_TogglePin
*    功能說明: 飯庄74HC574端口值
*    形    參: _pin : 管腳號, 0-31; 只能選1個,不能多選
*    返 回 值: 無
*********************************************************************************************************
*/
void HC574_TogglePin(uint32_t _pin)
{
    if (g_HC574 & _pin)
    {
        g_HC574 &= (~_pin);
    }
    else
    {
        g_HC574 |= _pin;
    }
    HC574_PORT = g_HC574;
}

 

函數描述:

此函數用於FMC擴展IO的翻轉。調用此函數前,要保證調用了函數bsp_InitExtIO進行了初始化。

函數參數:

  •   第1個參數是擴展IO的引腳,支持的形參如下,每次僅支持調用下面1個,不支持多個IO一起操作。
#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
#define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */
#define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */
#define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
#define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */
#define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */
#define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */
#define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
#define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */
#define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */
#define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */
#define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */
#define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */
#define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */
#define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */
#define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */
#define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */

 

使用舉例:

比如翻轉擴展IO引腳GPIO_PIN_23為高電平:HC574_TogglePin(GPIO_PIN_23)。

48.4.4 函數HC574_GetPin

函數原型:

/*
*********************************************************************************************************
*    函 數 名: HC574_GetPin
*    功能說明: 判斷指定的管腳輸出是1還是0
*    形    參: _pin : 管腳號, 0-31; 只能選1個,不能多選
*    返 回 值: 0或1
*********************************************************************************************************
*/
uint8_t HC574_GetPin(uint32_t _pin)
{
    if (g_HC574 & _pin)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

 

函數描述:

此函數用於讀取FMC擴展IO的狀態。調用此函數前,要保證調用了函數bsp_InitExtIO進行了初始化。

函數參數:

  •   第1個參數是擴展IO的引腳,支持的形參如下,每次僅支持調用下面1個,不支持多個IO一起操作。
#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
#define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
#define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
#define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
#define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */
#define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */
#define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */
#define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */
#define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */
#define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
#define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */
#define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */
#define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
#define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */
#define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */
#define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */
#define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
#define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */
#define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */
#define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */
#define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */
#define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */
#define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */
#define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */
#define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */
#define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */

 

  •   返回值,返回0表示低電平,返回1表示高電平。

使用舉例:

比如獲取擴展IO的GPIO_PIN_23高低電平狀態,調用函數HC574_GetPin(GPIO_PIN_23)獲取即可。

48.5 FMC擴展IO驅動移植和使用

擴展IO的移植比較方便:

  •   第1步:復制bsp_fmc_io.c和bsp_fmc_io.h到自己的工程目錄,並添加到工程里面。
  •   第2步:這幾個驅動文件主要用到HAL庫的GPIO和FMC驅動文件,簡單省事些可以添加所有HAL庫.C源文件進來。
  •   第3步,應用方法看本章節配套例子即可,另外就是根據自己的需要做配置修改。

48.6 實驗例程設計框架

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

 

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

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

  第2階段,進入main函數:

  •  第1步,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器,LED和串口。
  •  第2步,按鍵應用程序設計部分。定時器中斷服務程序里面實現翻轉FMC擴展引腳20和23。

48.7 實驗例程說明(MDK)

配套例子:

V7-027-FMC總線擴展32路高速IO

實驗目的:

  1. 學習FMC總線擴展32路高速IO。

實驗內容:

  1. 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
  2. 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。

實驗操作:

  1. K1按鍵按下,開啟TIM6的周期性中斷。
  2. K2按鍵按下,關閉TIM6的周期性中斷。

FMC擴展引腳20和23的位置:

 

上電后串口打印的信息:

波特率 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_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

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

/*
*********************************************************************************************************
*    函 數 名: 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);

    /*使能 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();
}

 

  主功能:

主程序實現如下操作:

  •   K1按鍵按下,開啟TIM6的周期性中斷。
  •  K2按鍵按下,關閉TIM6的周期性中斷。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    

    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    bsp_SetTIMforInt(TIM6, 10000, 2, 0);    /* 設置為10KHz頻率定時器中斷*/    
    
    /* 進入主程序循環體 */
    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鍵按下,開啟TIM6的周期性中斷*/
                    TIM6->DIER |= TIM_IT_UPDATE;
                    break;

                case KEY_DOWN_K2:            /* K2鍵按下,關閉TIM6的周期性中斷*/
                    TIM6->DIER &= ~TIM_IT_UPDATE;
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

定時器6中斷服務程序:

/*
*********************************************************************************************************
*    函 數 名: TIM6_DAC_IRQHandler
*    功能說明: TIM6定時中斷服務程序
*    返 回 值: 無
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
    if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    {
        /* 清除更新標志 */
        TIM6->SR = ~ TIM_FLAG_UPDATE;
        
        /* 翻轉FMC擴展引腳20和23腳 */
        HC574_TogglePin(GPIO_PIN_23);
        HC574_TogglePin(GPIO_PIN_20);
    }
}

 

48.8 實驗例程說明(IAR)

配套例子:

V7-027-FMC總線擴展32路高速IO

實驗目的:

  1. 學習FMC總線擴展32路高速IO。

實驗內容:

  1. 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
  2. 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。

實驗操作:

  1. K1按鍵按下,開啟TIM6的周期性中斷。
  2. K2按鍵按下,關閉TIM6的周期性中斷。

FMC擴展引腳20和23的位置:

 

上電后串口打印的信息:

波特率 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_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

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

/*
*********************************************************************************************************
*    函 數 名: 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);

    /*使能 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();
}

 

 主功能:

主程序實現如下操作:

  •  K1按鍵按下,開啟TIM6的周期性中斷。
  •  K2按鍵按下,關閉TIM6的周期性中斷。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按鍵代碼 */
    

    bsp_Init();        /* 硬件初始化 */
    
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    bsp_SetTIMforInt(TIM6, 10000, 2, 0);    /* 設置為10KHz頻率定時器中斷*/    
    
    /* 進入主程序循環體 */
    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鍵按下,開啟TIM6的周期性中斷*/
                    TIM6->DIER |= TIM_IT_UPDATE;
                    break;

                case KEY_DOWN_K2:            /* K2鍵按下,關閉TIM6的周期性中斷*/
                    TIM6->DIER &= ~TIM_IT_UPDATE;
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

定時器6中斷服務程序:

/*
*********************************************************************************************************
*    函 數 名: TIM6_DAC_IRQHandler
*    功能說明: TIM6定時中斷服務程序
*    返 回 值: 無
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
    if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    {
        /* 清除更新標志 */
        TIM6->SR = ~ TIM_FLAG_UPDATE;
        
        /* 翻轉FMC擴展引腳20和23腳 */
        HC574_TogglePin(GPIO_PIN_23);
        HC574_TogglePin(GPIO_PIN_20);
    }
}

 

48.9 總結

本章節就為大家講解這么多,由於FMC總線可以擴展出32路高速IO且使用簡單,所以實際項目中也比較有實用價值,望初學者熟練掌握。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM