簡(jiǎn)介:
? ? ? ? 由于剛開始沒有學(xué)懂GPIO的配置原理,導(dǎo)致后面學(xué)習(xí)其它外設(shè)的時(shí)候總是產(chǎn)生阻礙,因?yàn)槠渌庠O(shè)要使用前,大部分都要配置GPIO的初始化,因此這幾天重新學(xué)習(xí)了一遍GPIO的配置,記錄如下。
? ? ? ? 首先我們要知道芯片上的引腳,并不是只有GPIO的功能,還能復(fù)用成其他功能,比如PA9和PA10引腳,既可以配置成普通的IO口輸出高低電平,也可以配置成UART1的TX和RX引腳。今天就以這這兩個(gè)例子記錄一下配置過程。
實(shí)驗(yàn)平臺(tái):芯片是stmf103,野火的指南者開發(fā)板。使用hal庫(kù)開發(fā)。
例1:PB5\PB0\PB1配置成普通IO口,控制LED燈的亮滅,
例2:將PA9配置成UART1的TX,將PA10配置成UART1的RX。
一、GPIO的功能框圖
此圖來自野火的《零死角玩轉(zhuǎn)stm32》,此圖在《stm32f10x參考手冊(cè)》的GPIO章節(jié)也有,野火的標(biāo)注得有需要,方便說明。
1.輸出模式:即由單片機(jī)控制引腳的高低電平。
輸出模式有:推挽輸出、開漏輸出、復(fù)用推挽輸出、復(fù)用開漏輸出。常用推挽輸出,復(fù)用推挽輸出。
①處就是芯片外部引出的引腳,左邊的是芯片內(nèi)部的,從外面看不見。一個(gè)引腳在同一時(shí)刻,要么是輸出,要么是輸入,不能同時(shí)存在。
②處兩個(gè)反向串聯(lián)的MOS管,就實(shí)現(xiàn)了推挽輸出,這種模式就是普通的IO口輸出高低電平,通過操作③和位置位/清除寄存器(BSRR)來控制引腳輸出高低電平,從而控制外部的燈的亮滅。
④處是接片上外設(shè),將引腳配置成其他功能,如我們將PA9配置成UART1的TX功能,這時(shí)候就不需要去配置③處的ODR和BSRR寄存器。?
2.輸入模式:即由外部控制引腳的高低電平
????????輸入模式有:浮空輸入、模擬輸入、上拉輸入、下拉輸入,如果是輸入模式的話,就是由外部控制引腳的高低電平變化,單片機(jī)去讀引腳的電平變化,從而知道外部干了什么。
????????比如外部接了按鍵,按鍵按下產(chǎn)生低電平,按鍵釋放產(chǎn)生高電平,這時(shí)候單片機(jī)通過引腳高低變化就知道了按鍵按下了,進(jìn)而產(chǎn)生相應(yīng)的處理,此功能往往和中斷一起。在此不做過多的講解,后面學(xué)了中斷之后再記錄。
3.GPIO寄存器
在《stm32f10x參考手冊(cè)》GPIO寄存器章節(jié)有各個(gè)寄存器的功能和作用講解,由于我們使用hal庫(kù)開發(fā),操作寄存器的工作由hal做了,我們只需要了解有哪些參數(shù)可以修改及其作用。如果有興趣可以看看源碼是怎么操作寄存器的。
1.GPIO寄存器結(jié)構(gòu)體定義
GPIO_TypeDef中就是控制GPIO的7個(gè)32位寄存器,具體每個(gè)bit位的作用記得看參考手冊(cè)。
這個(gè)結(jié)構(gòu)體不需要我們傳入?yún)?shù)。我們需要修改的是GPIO_InitTypeDef結(jié)構(gòu)體。
typedef struct { __IO uint32_t CRL; __IO uint32_t CRH; __IO uint32_t IDR; __IO uint32_t ODR; __IO uint32_t BSRR; __IO uint32_t BRR; __IO uint32_t LCKR; } GPIO_TypeDef;
GPIO_InitTypeDef結(jié)構(gòu)體定義如下,可以由我們自己配置gpio引腳,速度,模式,這三個(gè)參數(shù)都是去修改GPIO_TypeDef里面的成員的某個(gè)位的。
typedef struct { uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins_define */ GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. This parameter can be a value of @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIOMode_TypeDef */ }GPIO_InitTypeDef;
2.驅(qū)動(dòng)GPIO_TypeDef中寄存器的庫(kù)函數(shù)函數(shù)GPIO_Init
????????我們只需要傳入端口和初始化結(jié)構(gòu)體即可。請(qǐng)看下面二的例程。
/** * @brief Initializes the GPIOx peripheral according to the specified * parameters in the GPIO_InitStruct. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that * contains the configuration information for the specified GPIO peripheral. * @retval None */ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) { uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00; uint32_t tmpreg = 0x00, pinmask = 0x00; /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); /*---------------------------- GPIO Mode Configuration -----------------------*/ currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00) { /* Check the parameters */ assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed)); /* Output mode */ currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed; } /*---------------------------- GPIO CRL Configuration ------------------------*/ /* Configure the eight low port pins */ if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00) { tmpreg = GPIOx->CRL; for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = ((uint32_t)0x01) << pinpos; /* Get the port pins position */ currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; if (currentpin == pos) { pos = pinpos << 2; /* Clear the corresponding low control register bits */ pinmask = ((uint32_t)0x0F) << pos; tmpreg &= ~pinmask; /* Write the mode configuration in the corresponding bits */ tmpreg |= (currentmode << pos); /* Reset the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) { GPIOx->BRR = (((uint32_t)0x01) << pinpos); } else { /* Set the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) { GPIOx->BSRR = (((uint32_t)0x01) << pinpos); } } } } GPIOx->CRL = tmpreg; } /*---------------------------- GPIO CRH Configuration ------------------------*/ /* Configure the eight high port pins */ if (GPIO_InitStruct->GPIO_Pin > 0x00FF) { tmpreg = GPIOx->CRH; for (pinpos = 0x00; pinpos < 0x08; pinpos++) { pos = (((uint32_t)0x01) << (pinpos + 0x08)); /* Get the port pins position */ currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos); if (currentpin == pos) { pos = pinpos << 2; /* Clear the corresponding high control register bits */ pinmask = ((uint32_t)0x0F) << pos; tmpreg &= ~pinmask; /* Write the mode configuration in the corresponding bits */ tmpreg |= (currentmode << pos); /* Reset the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) { GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08)); } /* Set the corresponding ODR bit */ if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) { GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08)); } } } GPIOx->CRH = tmpreg; } }
二、PB5\PB0\PB1點(diǎn)亮LED燈例程
????????指南者的RGB燈接在stm32f103上的PB5、PB0、PB1三個(gè)引腳上,故我們控制對(duì)應(yīng)引腳輸出低電平,即可點(diǎn)亮LED燈。
PB5點(diǎn)亮LED1的代碼如下。編譯后,野火的指南者扳子上,RGB燈以0.5s閃爍紅燈。其余兩個(gè)LED燈的控制,把PB5換成PB0或者PB1即可。
#include "stm32f10x.h" void delay_nms(u16 time); void LED_GPIO_Config(void); //1.主函數(shù) int main(void) { /* LED 端口初始化 */ LED_GPIO_Config(); while(1) { //GPIO_Pin_5置低電平,則LED1亮 GPIO_ResetBits(GPIOB, GPIO_Pin_5); //延時(shí)讓led1亮保持 delay_nms(500); //GPIO_Pin_5置高電平,則LED1滅 GPIO_SetBits(GPIOB, GPIO_Pin_5); //延時(shí)讓led1亮保持 delay_nms(500); } } //2.毫秒延時(shí),72M,需要精確延時(shí)的話還是得定時(shí)器 void delay_nms(u16 time) { u16 i=0; while(time--) { i=12000; //自己定義 while(i--) ; } } //3.LED1初始化函數(shù) void LED_GPIO_Config(void) { /*定義一個(gè)GPIO_InitTypeDef類型的結(jié)構(gòu)體*/ GPIO_InitTypeDef GPIO_InitStructure; /*開啟GPIOB外設(shè)時(shí)鐘*/ RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); /*選擇要控制的GPIO引腳*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; /*設(shè)置引腳模式為通用推挽輸出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*設(shè)置引腳速率為50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*調(diào)用庫(kù)函數(shù),初始化GPIO*/ GPIO_Init(GPIOB, &GPIO_InitStructure); /* 給GPIO_Pin_5置高電平,即LED1關(guān)閉 */ GPIO_SetBits(GPIOB, GPIO_Pin_5); delay_nms(20); }
三、PA9\PA10做UART1的TX和RX例程
????????控制LED燈的亮滅只需要控制引腳輸出高低電平即可,但是串口是通信接口,所以需要先說一下串口的數(shù)據(jù)幀格式,至于串口發(fā)送的時(shí)序圖,咱們寫軟件的暫時(shí)可以不了解。
從上面的幀格式可以知道可變的是數(shù)據(jù)位、校驗(yàn)位、停止位,起始位固定由庫(kù)函數(shù)實(shí)現(xiàn)。
在stm32f103單片機(jī)上,UART1是由PA9和PA10復(fù)用過來的,所以我們需要對(duì)GPIOA的pin9是TX要配置成復(fù)用推挽輸出模式,pin10引腳是RX要配置成浮空輸入模式。
配置流程:
①GPIO復(fù)用功能配置
②UART串口配置
③main函數(shù)里面調(diào)用串口收發(fā)函數(shù),和PC上的串口助手互相發(fā)送消息
1.GPIO初始化配置
{ GPIO_InitTypeDef GPIO_InitStructure; // 打開串口GPIOA的時(shí)鐘 DEBUG_USART_GPIO_APBxClkCmd(RCC_APB2Periph_GPIOA, ENABLE); // 將PA9的GPIO配置為推挽復(fù)用模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); // 將USART Rx的GPIO配置為浮空輸入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); }
2.串口1初始化
????????由于串口接受是由外部控制引腳電平,所以需要開啟串口接受中斷。
{ USART_InitTypeDef USART_InitStructure; // 打開串口外設(shè)的時(shí)鐘 DEBUG_USART_APBxClkCmd(RCC_APB2Periph_USART1, ENABLE); // 配置串口的工作參數(shù) // 配置波特率 USART_InitStructure.USART_BaudRate = 115200; // 配置 針數(shù)據(jù)字長(zhǎng) USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 配置停止位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 配置校驗(yàn)位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 配置硬件流控制 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 配置工作模式,收發(fā)一起 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 完成串口的初始化配置 USART_Init(USART1, &USART_InitStructure); //串口中斷優(yōu)先級(jí)配置 NVIC_Configuration(); // 使能串口接收中斷 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能串口 USART_Cmd(USART1, ENABLE); }
3.串口發(fā)送和發(fā)送函數(shù)
串口是按照字節(jié)發(fā)送的,每次發(fā)送一個(gè)字節(jié),所以要發(fā)送多個(gè)字節(jié)的時(shí)候,需要用用循環(huán)發(fā)送。
/* 發(fā)送一個(gè)字節(jié) */ void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data) { USART_SendData(pUSARTx, data); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET ); }
4.完成代碼:
????????實(shí)現(xiàn)下載到開發(fā)板之后,自動(dòng)向串口1發(fā)送字符A,接受到PC發(fā)送的字符后,立馬發(fā)給PC.文章來源:http://www.zghlxwxcb.cn/news/detail-854770.html
代碼全部寫在main.c里面即可。文章來源地址http://www.zghlxwxcb.cn/news/detail-854770.html
#include "stm32f10x.h" // #include "bsp_led.h" // #include "bsp_usart.h" // #include "stdlib.h" void USART_Config(void); void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data); static void NVIC_Configuration(void); //1.主函數(shù) int main(void) { USART_Config(); Usart_SendByte(USART1,'A'); while(1) { } return 0; } //2.串口初始化函數(shù) void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 打開串口GPIO的時(shí)鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 打開串口外設(shè)的時(shí)鐘 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 將USART Tx的GPIO配置為推挽復(fù)用模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 將USART Rx的GPIO配置為浮空輸入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置串口的工作參數(shù) // 配置波特率 USART_InitStructure.USART_BaudRate = 115200; // 配置 針數(shù)據(jù)字長(zhǎng) USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 配置停止位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 配置校驗(yàn)位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 配置硬件流控制 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 配置工作模式,收發(fā)一起 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 完成串口的初始化配置 USART_Init(USART1, &USART_InitStructure); //串口中斷優(yōu)先級(jí)配置 NVIC_Configuration(); // 使能串口接收中斷 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能串口 USART_Cmd(USART1, ENABLE); } //3.串口接受中斷函數(shù) static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 嵌套向量中斷控制器組選擇 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 配置USART為中斷源 */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; /* 搶斷優(yōu)先級(jí)*/ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 子優(yōu)先級(jí) */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中斷 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 初始化配置NVIC */ NVIC_Init(&NVIC_InitStructure); } //4.串口發(fā)送函數(shù) void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data) { USART_SendData(pUSARTx, data); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET ); } //5.串口接受中斷服務(wù)函數(shù),USART1_IRQHandler在startup_stm32f10x_hs.s中定義,發(fā)生串口中斷,自動(dòng)執(zhí)行這個(gè)函數(shù)。 void USART1_IRQHandler(void) { uint8_t ucTemp; if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { ucTemp = USART_ReceiveData(USART1); Usart_SendByte(USART1,ucTemp); } }
到了這里,關(guān)于STM32的GPIO初始化配置-學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!