您好,歡迎進入深圳市穎特新科技有限公司官方網(wǎng)站!
/**************************************************************************//** * @file main.c * @version V3.00 * $Revision: 3 $ * $Date: 15/09/02 10:03a $ * @brief Demonstrate how to set GPIO pin mode and use pin data input/output control. 演示如何設置GPIO引腳模式并使用引腳數(shù)據(jù)輸入/輸出控制。 * @note * Copyright (C) 2013~2015 Nuvoton Technology Corp. All rights reserved. * ******************************************************************************/ #include "stdio.h" #include "M451Series.h" #include "NuEdu-Basic01.h" #define PLL_CLOCK 72000000 void SYS_Init(void) { /*---------------------------------------------------------------------------------------------------------*/ /* Init System Clock */ /*---------------------------------------------------------------------------------------------------------*/ /* Enable HIRC clock (Internal RC 22.1184MHz) */ CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk); /* Wait for HIRC clock ready */ CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk); /* Select HCLK clock source as HIRC and and HCLK clock divider as 1 */ CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1)); /* Enable HXT clock (external XTAL 12MHz) */ CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk); /* Wait for HXT clock ready */ CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk); /* Set core clock as PLL_CLOCK from PLL */ CLK_SetCoreClock(PLL_CLOCK); /* Enable UART module clock */ CLK_EnableModuleClock(UART0_MODULE); /* Select UART module clock source as HXT and UART module clock divider as 1 */ CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HXT, CLK_CLKDIV0_UART(1)); /*---------------------------------------------------------------------------------------------------------*/ /* Init I/O Multi-function */ /*---------------------------------------------------------------------------------------------------------*/ /* Set PD multi-function pins for UART0 RXD(PD.6) and TXD(PD.1) */ SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD6MFP_Msk | SYS_GPD_MFPL_PD1MFP_Msk); SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD6MFP_UART0_RXD | SYS_GPD_MFPL_PD1MFP_UART0_TXD); } void UART0_Init() { /*---------------------------------------------------------------------------------------------------------*/ /* Init UART */ /*---------------------------------------------------------------------------------------------------------*/ /* Reset UART module */ SYS_ResetModule(UART0_RST); /* Configure UART0 and set UART0 baud rate */ UART_Open(UART0, 115200); } /*---------------------------------------------------------------------------------------------------------*/ /* Main Function */ /*---------------------------------------------------------------------------------------------------------*/ int32_t main(void) { int i, j; /* Unlock protected registers */ SYS_UnlockReg(); /* Init System, peripheral clock and multi-function I/O */ SYS_Init(); /* Lock protected registers */ SYS_LockReg(); /* Init UART0 for printf */ UART0_Init(); printf("\n\nCPU @ %dHz\n", SystemCoreClock); printf("LED test\n\r"); Initial_LED(); while(1) { for(i = 0; i < 9; i++) { Write_LED_Bar(i); for(j = 0; j < 600; j++) CLK_SysTickDelay(1000); } 有必要說說LED,正所謂每一個程序剛開始都要寫“HELLO WORLD”,而單片機或嵌入式都要點亮小燈,萬物從點燈開始,哈哈 它的結(jié)構(gòu)很簡單
LED,是一種能夠?qū)㈦娔苻D(zhuǎn)化為可見光的固態(tài)的半導體器件,即發(fā)光二極管,它可以直接把電轉(zhuǎn)化為光。LED的心臟是一個半導體的晶片,晶片的一端附在一個支架上,一端是負極, 另一端連接電源的正極,使整個晶片被環(huán)氧樹脂封裝起來。半導體晶片由兩部分組成,一部分是P型半導體,在它里面空穴占主導地位,另一端是N型半導體,在這邊主要是電子。 這兩種半導體連接起來的時候,它們之間就形成一個P-N結(jié)。當電流通過導線作用于這個晶片的時候,電子就會被推向P區(qū),在P區(qū)里電子跟空穴復合,然后就會以光子的形式發(fā)出能量, 這就是LED燈發(fā)光的原理。而光的波長也就是光的顏色,是由形成P-N結(jié)的材料決定的。 單個的LED燈珠只能在低電壓(約3V)、低電流(約幾毫安)下工作,發(fā)出的光線很微弱。需要將許多LED燈珠串聯(lián)或并聯(lián)起來; 同時單個LED燈珠是單向?qū)щ姷,為了充分利用交流電的正負半周電流,這就需要一塊集成電路芯片,將交流220V電源轉(zhuǎn)變?yōu)殡妷、電流能與LED集合相匹配的直流電, 以滿足LED燈珠集合體的要求,使其能正常發(fā)光。 下圖是
MCU 外圍連接
電路圖您明白了么?再看軟件的相應的庫文件
/* Peripheral and SRAM base address */ #define SRAM_BASE (0x20000000UL) /*!< (SRAM ) Base Address */ #define PERIPH_BASE (0x40000000UL) /*!< (Peripheral) Base Address */
#define AHBPERIPH_BASE PERIPH_BASE #define APBPERIPH_BASE (PERIPH_BASE + 0x00040000)
#define GCR_BASE (AHBPERIPH_BASE + 0x00000) #define CLK_BASE (AHBPERIPH_BASE + 0x00200) #define INT_BASE (AHBPERIPH_BASE + 0x00300) #define GPIO_BASE (AHBPERIPH_BASE + 0x04000) #define GPIOA_BASE (AHBPERIPH_BASE + 0x04000) #define GPIOB_BASE (AHBPERIPH_BASE + 0x04040) #define GPIOC_BASE (AHBPERIPH_BASE + 0x04080) #define GPIOD_BASE (AHBPERIPH_BASE + 0x040C0) #define GPIOE_BASE (AHBPERIPH_BASE + 0x04100) #define GPIOF_BASE (AHBPERIPH_BASE + 0x04140) #define GPIO_DBCTL_BASE (AHBPERIPH_BASE + 0x04440) #define GPIO_PIN_DATA_BASE (AHBPERIPH_BASE + 0x04800) #define PDMA_BASE (AHBPERIPH_BASE + 0x08000) #define USBH_BASE (AHBPERIPH_BASE + 0x09000) #define FMC_BASE (AHBPERIPH_BASE + 0x0C000) #define EBI_BASE (AHBPERIPH_BASE + 0x10000) #define CRC_BASE (AHBPERIPH_BASE + 0x31000)
#define SYS ((SYS_T *) GCR_BASE) #define SYSINT ((SYS_INT_T *) INT_BASE) #define CLK ((CLK_T *) CLK_BASE) #define PA ((GPIO_T *) GPIOA_BASE) #define PB ((GPIO_T *) GPIOB_BASE) #define PC ((GPIO_T *) GPIOC_BASE) #define PD ((GPIO_T *) GPIOD_BASE) #define PE ((GPIO_T *) GPIOE_BASE) #define PF ((GPIO_T *) GPIOF_BASE) #define GPIO ((GPIO_DBCTL_T *) GPIO_DBCTL_BASE) #define PDMA ((PDMA_T *) PDMA_BASE) #define USBH ((USBH_T *) USBH_BASE) #define FMC ((FMC_T *) FMC_BASE) #define EBI ((EBI_T *) EBI_BASE) #define CRC ((CRC_T *) CRC_BASE) #define GPIO_MODE_INPUT 0x0UL /*!< Input Mode */ #define GPIO_MODE_OUTPUT 0x1UL /*!< Output Mode */ #define GPIO_MODE_OPEN_DRAIN 0x2UL /*!< Open-Drain Mode */ #define GPIO_MODE_QUASI 0x3UL /*!< Quasi-bidirectional Mode */ /* One Bit Mask Definitions */ #define BIT0 0x00000001 #define BIT1 0x00000002 #define BIT2 0x00000004 #define BIT3 0x00000008 #define BIT4 0x00000010 #define BIT5 0x00000020 #define BIT6 0x00000040 #define BIT7 0x00000080 #define BIT8 0x00000100 #define BIT9 0x00000200 #define BIT10 0x00000400 #define BIT11 0x00000800 #define BIT12 0x00001000 #define BIT13 0x00002000 #define BIT14 0x00004000 #define BIT15 0x00008000 #define BIT16 0x00010000 #define BIT17 0x00020000 #define BIT18 0x00040000 #define BIT19 0x00080000 #define BIT20 0x00100000 #define BIT21 0x00200000 #define BIT22 0x00400000 #define BIT23 0x00800000 #define BIT24 0x01000000 #define BIT25 0x02000000 #define BIT26 0x04000000 #define BIT27 0x08000000 #define BIT28 0x10000000 #define BIT29 0x20000000 #define BIT30 0x40000000 #define BIT31 0x80000000 void GPIO_SetMode(GPIO_T *port, uint32_t u32PinMask, uint32_t u32Mode) { uint32_t i; for(i = 0; i < GPIO_PIN_MAX; i++) { if(u32PinMask & (1 << i)) { port->MODE = (port->MODE & ~(0x3 << (i << 1))) | (u32Mode << (i << 1)); } } } void Initial_LED(void) { GPIO_SetMode(PB, BIT2, GPIO_MODE_OUTPUT); //LED1 GPIO_SetMode(PB, BIT3, GPIO_MODE_OUTPUT); //LED2 GPIO_SetMode(PC, BIT3, GPIO_MODE_OUTPUT); //LED3 GPIO_SetMode(PC, BIT2, GPIO_MODE_OUTPUT); //LED4 GPIO_SetMode(PA, BIT9, GPIO_MODE_OUTPUT); //LED5 GPIO_SetMode(PB, BIT1, GPIO_MODE_OUTPUT); //LED6 GPIO_SetMode(PC, BIT7, GPIO_MODE_OUTPUT); //LED7 }
關(guān)于AHB與APB在我的內(nèi)核架構(gòu)一文中有描述,再此不加說明。
你把這幾部分放到一個程序中完全可以運行
I/O管腳的I狀態(tài)可由軟件獨立地配置為輸入,推挽式的輸出,開漏或準雙向模式。復位之后,所有管腳的模式取決于CIOIN (CONFIG0[10])的設置。
每個I/O管腳有一個阻值為110K~300K的弱上拉電阻接到VDD 上,VDD范圍從5.0 V 到2.5 V。
首先我們來看上拉電阻與下拉電阻:
兩者共同的作用是:避免電壓的“懸浮”,造成電路的不穩(wěn)定;
一、上拉電阻如圖所示
1、概念:將一個不確定的信號,通過一個電阻與電源VCC相連,固定在高電平;2、上拉是對器件注入電流;灌電流;3、當一個接有上拉電阻的IO端口設置為輸入狀態(tài)時,它的常態(tài)為高電平;
二、下拉電阻如圖所示:
1、 概念:將一個不確定的信號,通過一個電阻與地GND相連,固定在低電平;
2、下拉是從器件輸出電流;拉電流;3、當一個接有下拉電阻的IO端口設置為輸入狀態(tài)時,它的常態(tài)為低電平;
要理解推挽輸出,首先要理解好三極管(晶體管)的原理。下面這種三極管有三個端口,分別是基極(Base)、集電極(Collector)和發(fā)射極(Emitter)。
下圖是NPN型晶體管。
這種三極管是電流控制型元器件,注意關(guān)鍵詞電流控制。意思就是說,只要基極B有輸入(或輸出)電流就可以對這個晶體管進行控制了。
下面請允許我換一下概念,把基極B視為控制端,集電極C視為輸入端,發(fā)射極E視為輸出端。這里輸入輸出是指電流流動的方向。
而PNP管正好相反,當有電流從控制端流出時,就會有電流從輸入端流到輸出端。
上面的三極管是N型三極管,下面的三極管是P型三極管,請留意控制端、輸入端和輸出端。
當Vin電壓為V+時,上面的N型三極管控制端有電流輸入,Q3導通,于是電流從上往下通過,提供電流給負載。
經(jīng)過上面的N型三極管提供電流給負載(Rload),這就叫「推」
當Vin電壓為V-時,下面的三極管有電流流出,Q4導通,有電流從上往下流過。
經(jīng)過下面的P型三極管提供電流給負載(Rload),這就叫「挽」。
以上,這就是推挽(push-pull)電路。
那么什么是開漏呢?這個在我答案一開頭給出的「網(wǎng)上資料」里講得很詳細了,我這里也簡單寫一下。
要理解開漏,可以先理解開集。
如圖,開集的意思,就是集電極C一端什么都不接,直接作為輸出端口。
如果要用這種電路帶一個負載,比如一個LED,必須接一個上拉電阻,就像這樣。
當Vin沒有電流,Q5斷開時,LED亮。 當Vin流入電流,Q5導通時,LED滅。
開漏電路,就是把上圖中的三極管換成場效應管(MOSFET)。
N型場效應管各個端口的名稱:
場效應管是電壓控制型元器件,只要對柵極施加電壓,DS就會導通。
結(jié)型場效應管有一個特性就是它的輸入阻抗非常大,這意味著:沒有電流從控制電路流出,也沒有電流進入控制電路。沒有電流流入或流出,就不會燒壞控制電路。而雙極型晶體管不同,是電流控制性元器件,如果使用開集電路,可能會燒壞控制電路。這大概就是我們總是聽到開漏電路而很少聽到開集電路的原因吧?因為開集電路被淘汰了。
1、開漏全部名字是,內(nèi)部mos管漏極開路(也可理解為晶體管集電極開路)。輸出引腳只有對地低阻抗以及高阻態(tài)兩種模式。
2、推挽內(nèi)部有上下兩個mos(或晶體管),輸出引腳有對VCC低阻抗以及對地低阻抗兩種模式。
下面付一個無腦版的,用開關(guān)代替晶體管或MOS管。等效電路。
1. 推挽輸出能夠輸出高或者低,而開漏輸出只能輸出低,或者關(guān)閉輸出,因此開漏輸出總是要配一個上拉電阻使用。2. 開漏輸出的上拉電阻不能太小,太小的話,當開漏輸出的下管導通時,電源到地的電壓在電阻上會造成很大的功耗,因此這個電阻阻值通常在10k以上,這樣開漏輸出在從輸出低電平切換到高電平時,速度是很慢的。3. 推挽輸出任意時刻的輸出要么是高,要么是低,所以不能將多個輸出短接,而開漏輸出可以將多個輸出短接,共用一個上拉,此時這些開漏輸出的驅(qū)動其實是與非的關(guān)系。4. 推挽輸出輸出高時,其電壓等于推挽電路的電源,通常為一個定值,而開漏輸出的高取決于上拉電阻接的電壓,不取決于前級電壓,所以經(jīng)常用來做電平轉(zhuǎn)換,用低電壓邏輯驅(qū)動高電壓邏輯,比如3.3v帶5v。
在單片機學習、開發(fā)和應用中,IO口的配置對功能的實現(xiàn)起著重要的作用,下面介紹常見的四種配置,而現(xiàn)在很多單片機都兼有這四種配置,可供選擇。
一.準雙向口配置
如下圖,當IO輸出為高電平時,其驅(qū)動能力很弱,外部負載很容易將其拉至低電平。當IO輸出為低電平時,其驅(qū)動能力很強,可吸收相當大的電流。
準雙向口有三個上拉晶體管,一個“極弱上拉”,當端鎖存器為邏輯“1”時打開,當端口懸空時,“極弱上拉”將端口上拉至高電平。
第二個上拉晶體管為“弱上拉”,當端口鎖存器為邏輯“1”且端口本身也為“1”時打開,此上拉提供的電流,使準雙向口輸出為“1”。如果此時端口被外部裝置拉到邏輯“0”時,通過施密特觸發(fā)器,控制“弱上拉”關(guān)閉,而“極弱上拉”維持開狀態(tài),為了把這個端口拉低,外部裝置必須有足夠的灌電流能力,使管腳上的電壓,降到門檻電以下。
第三個上拉晶體管為“強上拉”,當端口鎖存器由“0”跳變到“1”時,這個上拉用來加快端口由邏輯“0”到邏輯“1”的轉(zhuǎn)換速度。
準雙向口做為輸入時,通個一個施密特觸如器和一個非門,用以干擾和濾波。
準雙向口用作輸入時,可對地接按鍵,如下圖1,當然也可以去掉R1直接接按鍵,當按鍵閉合時,端口被拉至低電平,當按鍵松開時,端口被內(nèi)部“極弱上拉”晶體管拉至高電平。當端口作為輸出時,不應對地外接LED如圖形控制,這樣端口的驅(qū)動能力很弱,LED只能發(fā)很微弱的光,如果要驅(qū)動LED,要采用圖3的方法,這樣準雙向口在輸出為低時,可吸收20mA的電流,故能驅(qū)動LED。圖4的方法也可以,不過LED不發(fā)光時,端口要吸收收很大電流。
二.開漏輸出配置
這種配置,關(guān)閉所有上拉晶體管,只驅(qū)動下拉晶體管,下拉與準雙向口下拉配置相同,因此只能輸出低電平(吸收電流),和高阻狀態(tài)。不能輸出高電平(輸也電流)。如果要作為邏輯輸出,必須接上拉電阻到VCC。這種配置也可以通過上圖3和圖4來驅(qū)動LED。
三.推挽輸出配置
這種配置的下拉與準雙向口和開漏配置相同,具有較強的拉電流能力,不同的是,具有持續(xù)的強上拉。因此可以用上圖2的方法來驅(qū)動LED。
四.僅為輸入配置(高阻配置)
這種配置不能輸出電流,也不能有收電流,只能作為輸入數(shù)據(jù)使用。
以上四種配置各有其特點,在使用中應根據(jù)其特點靈活運用。
準雙向口的最大特點是既可以作為輸入,也可以作為輸出,不需要通過控制切換。
推挽輸出的特點是,無論輸也高電平還是低電平都有較大的驅(qū)動能力,在輸也高電平時,也能直接點亮LED,這在準雙向口中是不能辦到的。這種配置不宜作為輸入,因為這需要外部設備有很強的拉電流的能胃。
僅為輸入配置的特點是端口只能作為輸入使用,可以獲得很高的輸入阻抗,在有模擬比較器或ADC的端口中用得較多。
開漏輸出配置與準又向口相似,但內(nèi)部沒有上拉電阻。有很好的電氣兼容性,外部接上拉電阻到3V電源,就能和3V邏輯器件連接。外部接上拉電阻到5V電源,就要以和5V器件連接。
需要說明的是以上四種配置均可以作為輸入,也就是都可以檢測端的邏輯狀態(tài),但其特性不同,不是每種配置都可以直接接按鍵
就講到這里,其實這就是I/O口的輸入輸出模式,具體看程序就可以清楚明白了
GPIO_SetMode(PC, BIT7, GPIO_MODE_OUTPUT);//#define GPIO_MODE_OUTPUT 0x0UL /*!< Input Mode */
設置成為了輸出模式,之后我們在來看,這個函數(shù)的具體功能
while(1) { for(i = 0; i < 9; i++) { Write_LED_Bar(i); for(j = 0; j < 600; j++) CLK_SysTickDelay(1000); } }
#define _LED1 PB2#define _LED2 PB3#define _LED3 PC3#define _LED4 PC2#define _LED5 PA9#define _LED6 PB1#define _LED7 PC7
#define _LED_Bar_Count 7 void Write_LED_Bar(uint32_t Number) { uint32_t i; volatile uint32_t *ptrLED[_LED_Bar_Count] = {&_LED1, &_LED2, &_LED3, &_LED4, &_LED5, &_LED6, &_LED7}; for(i = 0; i < _LED_Bar_Count; i++) { if((Number > i) & 0x01) *ptrLED[i] = 0; //LED ON else *ptrLED[i] = 1; //LED OFF } }
如果我們假設Number為一的話,i為零時,1大于零,為真,為一,遇上0X01之后為真,則進行負值為零(開燈)的操作,它是共陽級的操作。
試試我寫的這個
#define _LED1 PB2#define _LED2 PB3#define _LED3 PC3#define _LED4 PC2#define _LED5 PA9#define _LED6 PB1#define _LED7 PC7
volatile uint32_t *ptrLED[_LED_Bar_Count] = {&_LED1, &_LED2, &_LED3, &_LED4, &_LED5, &_LED6, &_LED7};
while(1) { for(i = 0; i < 7; i++) { *ptrLED[i]=0; for(j = 0; j < 600; j++) { CLK_SysTickDelay(1000); } *ptrLED[i]=1; for(j = 0; j < 600; j++) { CLK_SysTickDelay(1000); } if(i==8) { i=0; } } }
這里需要的庫文件有很多,另外LED很簡單,不需要用到很多的庫函數(shù),下回我說說GPIO.H吧!
上一篇:文檔的壓縮與打包
下一篇:說說SPI協(xié)議
掃碼關(guān)注我們
傳真:0755-82591176
郵箱:vicky@yingtexin.net
地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08