STM32F4 DISCOVERY BOARD 를 이용한 I/O 제어
지난시간에는 첫번째 줄 클럭 enable 에 대해서 작성을 했었다.
/* GPIOD Periph clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
오늘은 그럼 포트의 출력설정에 대해서 알아보도록 하자
(이게 얼마나 길어질지는 나도 모르겠네...ㅠㅠ
/* Configure PD12, PD13, PD14 and PD15 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure란 변수는 main 함수에 아래와 같이 선언되어 있다.
GPIO_InitTypeDef GPIO_InitStructure;
그럼 우리는 아래의 함수를 캐보도록 하자
GPIO_InitTypeDef
함수가 아니라 구조체로군... 이 구조체(struct)는 stm32f4xx.gpio.h에 있다.
/**
* @brief GPIO Init structure definition
*/
typedef struct
{
uint32_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOOType_TypeDef GPIO_OType; /*!< Specifies the operating output type for the selected pins.
This parameter can be a value of @ref GPIOOType_TypeDef */
GPIOPuPd_TypeDef GPIO_PuPd; /*!< Specifies the operating Pull-up/Pull down for the selected pins.
This parameter can be a value of @ref GPIOPuPd_TypeDef */
}GPIO_InitTypeDef;
그러면 각각 GPIO_Pin, GPIO_Mode, GPIO_Speed, GPIO_OType, GPIO_PuPd 에 대해 알아보도록 하자!!
/**
* @brief GPIO Configuration Mode enumeration
*/
typedef enum
{
GPIO_Mode_IN = 0x00, /*!< GPIO Input Mode */
GPIO_Mode_OUT = 0x01, /*!< GPIO Output Mode */
GPIO_Mode_AF = 0x02, /*!< GPIO Alternate function Mode */
GPIO_Mode_AN = 0x03 /*!< GPIO Analog Mode */
}GPIOMode_TypeDef;
/**
* @brief GPIO Output type enumeration
*/
typedef enum
{
GPIO_OType_PP = 0x00,
GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;
/**
* @brief GPIO Output Maximum frequency enumeration
*/
typedef enum
{
GPIO_Speed_2MHz = 0x00, /*!< Low speed */
GPIO_Speed_25MHz = 0x01, /*!< Medium speed */
GPIO_Speed_50MHz = 0x02, /*!< Fast speed */
GPIO_Speed_100MHz = 0x03 /*!< High speed on 30 pF (80 MHz Output max speed on 15 pF) */
}GPIOSpeed_TypeDef;
/**
* @brief GPIO Configuration PullUp PullDown enumeration
*/
typedef enum
{
GPIO_PuPd_NOPULL = 0x00,
GPIO_PuPd_UP = 0x01,
GPIO_PuPd_DOWN = 0x02
}GPIOPuPd_TypeDef;
http://www.st.com/internet/mcu/product/252140.jsp 의 REFERENCE MANUALS
GPIO Configuration Mode 에는
GPIO Input, Output, Alternate function,Analog의 mode 가 있다.
GPIO Output type은
PP(push-pull), OD(open-drain) 이 있다.
GPIO Output Maximum frequency
는 2M, 25M, 50M 100MHz가 있으며 이건 솔찍히 무슨 내용인지 잘 모르겠다.
데이타시트를 보니 최대 속도를 소프트웨어적으로 제한시켜준다는거 같은데 그러면 어떻게 동작되는지 그거에 대해서는 모르겠다.
GPIO Configuration PullUp PullDown
GP(general-purpose), PU(pull-up), PD(pull-down) 모드가 있다.
여기서 GP는 아마도 NOPULL로 선언된듯 싶다.
여기서 함수들의 기능은 위의 코드와 데이터 시트를 보면 적관적으로 이해되리라 생각된다.
아날로그 입력과 아날로그 출력, 입출력, Alternate function(타이머, I2C 등의 확장기능의 핀맵핑이라고 보면 된다), 이 있고
push pull 반전, open-drain, pull-up, pull-down 등은 그래프를 보고 직관적으로 이해하면 될 듯 싶다.
이제 각각의 구조체와 함수들을 살펴보도록 하자.
GPIO_Pin 같은 경우 stm32f4xx_gpio.h 파일에
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */
에서부터 15번핀까지 그리고
#define GPIO_Pin_All ((uint16_t)0xFFFF) /* All pins selected */
모든핀 선택 해서 핀정보에 대해 정의되어 있다.
그럼 GPIO_Pin 에서 설정할때는 각각의 핀을 비트연산을 통해 계산한다고 생각하면 된다.
따라서 위이 예제에서도 소스코드를 보면
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
와 같아 비트연산을 통해 값을 설정한 것을 확인할 수 있다.
나머지 부분들은 enum을 통해 선언되었으므로 구조체 초기화에 대해서는 이해했을것이다.
그러면 그 다음으로 나온 함수가 GPIO_Init()함수이다.
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
위의 함수는 stm32f4xx_gpio.h에 선언되어 있고 stm32f4xx_gpio.c 에 정의되어 있다.
/**
* @brief Initializes the GPIOx peripheral according to the specified parameters in the GPIO_InitStruct.
* @param GPIOx: where x can be (A..I) 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 pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
/* -------------------------Configure the port pins---------------- */
/*-- GPIO Mode Configuration --*/
for (pinpos = 0x00; pinpos < 0x10; pinpos++)
{
pos = ((uint32_t)0x01) << pinpos;
/* Get the port pins position */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
{
/* Check Speed mode parameters */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* Speed mode configuration */
GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
/* Check Output mode parameters */
assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
/* Output mode configuration*/
GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;
GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
}
/* Pull-up Pull down resistor configuration*/
GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
}
}
}
코드를 분석해보면 for 문으로 0~15번 핀까지 반복한다.
여기서 GPIO_InitStructure.GPIO)Pin 에서 설정된 핀들의 경우 레지스터 값을 설정하게 된다.
일단 mode를 설정하고 output mode 일 경우 output type, speed, pupd의 값들을 세팅하게 된다.
세팅 과정은 각각의 output type, speed, pupd의 레지스터를 지우고 다시쓰는 방식을 취하고 있다.
한번에 연산해서 레지스터에 집어넣는게 빠르지 않을까란 생각이 들긴 한다.
CMSIS 에 의해 직접 억세스 하지 않고 간접적으로 접근하는 방식을 선택한 것일까?
하여튼 직접 억세스하여 레지스터를 기록하는거보다는 많은 클럭을 잡아먹을것이라 예상된다.
하지만 M4의 성능이라면 그 몇클럭이 의미가 있을가 싶다. 그리고 이것의 경우 계속사용하는게 아니라 초기화이기 때문에 포트의 상태를 변화시키지 않는 한 초기화 시킬때 한번이라면
신경쓰지 않아도 될듯 하다.
아래의 내용도 진행을 시키려 했는데.......
SetBits, ResetBits 를 살펴보던중 이 명령어들은 bit banding 이랑 관련이 되어있다는걸 알게 되었다.
이건...대박아이디어인데?....
비트밴딩이란게 이런거구나.......
수정 : 공부해본 결과 bit banding이랑은 다르다. 마치 RS 플립플롭처럼 레지스터를 set reset 해주는 역할을 한다.
이것만으로도 편한걸?
그런 의미로 SetBits랑 ResetBits는 다음블로깅으로 넘기도록 하겠다..... ㅋㅋㅋㅋ!!!
Cortex-M4....대박이야!!!!
while (1)
{
/* PD12 to be toggled */
GPIO_SetBits(GPIOD, GPIO_Pin_12);
GPIO_ResetBits(GPIOD, GPIO_Pin_1|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
}