完整教程下載地址: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 初學者重要提示
- 學習本章節前,務必優先學習第47章,需要對FMC的基礎知識和HAL庫的幾個常用API有個認識。
- 為什么要做IO擴展,不是已經用了240腳的H743XIH6嗎?因為開發板使用了32位SDRAM和RGB888硬件接口,消耗IO巨大,所以必須得擴展了。
- 擴展的32路高速IO非常實用,且使用簡單,只需初始下FMC,32路IO就可以隨意使用了。當前的擴展方式只支持高速輸出。
- 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
實驗目的:
- 學習FMC總線擴展32路高速IO。
實驗內容:
- 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
- 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。
實驗操作:
- K1按鍵按下,開啟TIM6的周期性中斷。
- 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
實驗目的:
- 學習FMC總線擴展32路高速IO。
實驗內容:
- 系統上電后驅動了1個軟件定時器,每100ms翻轉一次LED2。
- 啟動1個TIM6周期性中斷,頻率10KHz,在中斷服務程序里面翻轉FMC擴展引腳20和23。
實驗操作:
- K1按鍵按下,開啟TIM6的周期性中斷。
- 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且使用簡單,所以實際項目中也比較有實用價值,望初學者熟練掌握。