4 분 소요

목표

  • 임베디드 시스템의 기본 원리 습득
  • 레지스터와 주소 제어를 통한 임베디드 펌웨어 개발 이해

세부 실험 내용

  • Datasheet 및 Reference Manual 참고하여 해당 레지스터 및 주소에 대한 이해
  • IAR EW 프로젝트 생성
  • 버튼을 이용한 LED 제어
    • KEY1 버튼: PD2, PD7 LED On
    • KEY2 버튼: PD2, PD7 LED Off
    • Key3 버튼: PD3, PD4 LED On
    • Key4 버튼: PD3, PD4 LED Off
  • 정상적인 동작 유무 확인
  • 오실로스코프를 이용한 디버깅

실험 방법

  1. RCC(reset and clock control)를 이용하여 각 포트에 clock을 인가한다.
  2. 각 포트에서 쓸 포트 번호를 설정한다(Port Configuration Low/High register 이용).
  3. Port Input Data Register를 검사하여 버튼이 눌렸는지 확인하고, 버튼이 눌렸으면 Port bit set/reset register를 사용하여 LED를 켜고 끈다.

코드 작성

stm32f10x.h 넣기

 #include "stm32f10x.h"

레지스터 주소 찾기

버튼과 LED의 포트와 포트번호 찾기

button

(그림1. STM32F107VCT6의 회로도 중 버튼 부분)
STM32F107VCT6의 Schematic을 보면

  • Key1: PC4(포트 C의 4번)
  • Key2: PB10(포트 B의 10번)
  • Key3: PC13(포트 C의 13번)
  • Key4: PA0(포트 A의 0번)

에 연결되어있다. 그리고 버튼은 풀업저항으로 연결되어있다. 즉 버튼이 눌리면 0, 눌리지 않았을 때 1이다.

LED 회로도

(그림2. STM32F107VCT6의 회로도 중 LED 부분)
STM32F107VCT6의 Schematic을 보면

  • LED1: PD2(포트D의 2번)
  • LED2: PD3(포트D의 3번)
  • LED3: PD4(포트D의 4번)
  • LED4: PD7(포트D의 7번) 에 연결되어있다.

또한 그림2의 회로도를 보았을 때, PD2,3,4,7에 0이 인가되어야 각 핀이 그라운드로 되면서 전류가 흘러 LED가 켜지게 된다.

RCC와 포트의 base 주소 찾기

Memory map

(그림3. STM32F107xx memory mapping)
STM32 datasheet에 있는 memory mapping을 보면, RCC와 포트의 base주소는 다음과 같다.

  • RCC: 0x4002 1000
  • Port A: 0x4001 0800
  • Port B: 0x4001 0C00
  • port C: 0x4001 1000
  • port D: 0x4001 1400

RCC_APB2ENR의 주소 찾기

GPIO is connected to APB2

(그림4. STM32F105xx and STM32F107xx connectivity line block diagram 일부)
위 그림과 같이 STM32F107xx connectivity line block diagram 을 보면 GPIO는 APB2에 연결되어있다. 따라서 RCC_APB2ENR에서 각 GPIO에 clock을 인가하여야한다.

RCC_APB2ENR 설명 Address가 0x18이므로, 우리가 쓸 RCC_APB2ENR의 주소는 RCC의 base주소에 0x18을 더한 값이다.

Port Configuration Low register의 주소 찾기

Port configuration low register

(그림5. port configuration low register)
port configuration low register는 각 포트의 0번부터 7번까지 input/output 모드를 설정하고, input일 경우 아날로그 또는 pull-up/down 등, output일 경우 push-pull/open-drain을 설정하게 된다. 나중에 port configuration low register를 설정할 때 더 자세히 기술한다.

위 그림5에서, port configuration low register의 address는 0x00으로, 각 포트의 base주소와 같다.

Port Configuration High register의 주소 찾기

Port Configuration high register

(그림6. port configuration high register)
port configuration low register는 각 포트의 8번부터 15번까지 input/output 모드를 설정하고, input일 경우 아날로그 또는 pull-up/down 등, output일 경우 push-pull/open-drain을 설정하게 된다. 나중에 port configuration low register를 설정할 때 더 자세히 기술한다.

위 그림6에서, port configuration high register의 address는 0x04로, 각 포트의 base 주소 + 0x04와 같다.

Port input data register의 주소 찾기

port input data register

(그림7. Port input data register)
port input data register는 각 포트의 0번부터 15번까지 input 값이 젹혀있는 register이다. 버튼이 풀업저항이므로, 버튼이 눌렸을 때 레지스터에 0이 저장되고, 버튼이 눌리지 않았을 때 해당하는 레지스터에 1이 저장된다.

위 그림7에서, port input data register의 address는 0x08로, 각 포트의 base 주소 + 0x08과 같다.

Port bit set/reset register의 주소 찾기

Port bit set/reset register

(그림8. Port bit set/reset register)
포트D의 set/reset register를 이용하여 LED에 해당하는 포트의 set register를 1로 하면 LED가 켜지고, reset register를 1로 하면 LED가 꺼지게 된다. set register와 reset register가 동시에 1인 경우, set register를 우선으로 한다.

위 그림8에서, port bit set/reset register의 address는 0x10으로, 각 포트의 base 주소 + 0x10과 같다.

여러 주소 define하여 매크로로 사용하기

버튼과 LED의 포트와 포트번호 찾기 파트를 보면 포트A는 configuration low register만 필요하고, 포트B는 configuration high register, 포트C는 둘다, 포트D는 configuration low register만 필요하다.

또한 port input data register의 경우 포트 A,B,C에 필요하고, port bit set/reset register는 포트D에 필요하다. 따라서 다음과 같이 define할 수 있다.

#include "stm32f10x.h"

#define RCC (*(volatile unsigned int*)0x40021018)
/*
 - RCC:     0x4002 1000
 - Port A:  0x4001 0800
 - Port B:  0x4001 0C00
 - port C:  0x4001 1000
 - port D:  0x4001 1400
*/
//RCC_APB2ENR == 0x40021000 + 0x18

#define PA_CRL (*(volatile unsigned int*)0x40010800)//PA_CRL == PA_base_address
#define PC_CRL (*(volatile unsigned int*)0x40011000)//PC_CRL == PC_base_address
#define PD_CRL (*(volatile unsigned int*)0x40011400)//PD_CRL == PD_base_address

#define PB_CRH (*(volatile unsigned int*)0x40010C04)//PB_CRH == PB_base_address + 0x04
#define PC_CRH (*(volatile unsigned int*)0x40011004)//PC_CRH == PC_base_address + 0x04

#define PA_IDR (*(volatile unsigned int*)0x40010808)//PA_IDR == PA_base_address + 0x08
#define PB_IDR (*(volatile unsigned int*)0x40010C08)//PB_IDR == PB_base_address + 0x08
#define PC_IDR (*(volatile unsigned int*)0x40011008)//PC_IDR == PC_base_address + 0x08

#define PD_BSRR (*(volatile unsigned int*)0x40011410)//PD_BSRR == PD_base_address + 0x10

포트 번호 define 하기

//IDR과 BSRR 레지스터에서 해당 핀을 확인하는 용도 및 해당핀을 set/reset하는데 쓰인다.
#define PIN_0 0x00000001 
#define PIN_2 0x00000004
#define PIN_3 0x00000008
#define PIN_4 0x00000010
#define PIN_7 0x00000080
#define PIN_10 0x00000400
#define PIN_13 0x00002000
//그림7에서 알 수 있듯, 예를 들어 포트A의 0번 핀이 0인지 확인하려면
//!(PA_IDR & PIN_0)로 확인할 수 있다.

//그림8에서 알 수 있듯, 예를 들어 BSRR 레지스터에 2번핀을 set으로 하려면
//PD_BSRR |= PIN_2로 하면된다. 그리고 그림2에서 알 수 있듯, PD2,3,4,7이 reset이 되어야 LED가 켜지게 된다.

RCC를 이용하여 clock enable하기

10

(그림9. RCC_APB2ENR에 대한 자세한 설명)

그림9에서, 우리는 IOPA,IOPB,IOPC,IOPD를 활성화해야하므로(계산 편의를 위해 IOPE도 활성화하였다.) RCC를 0b0000 0000 0000 0000 0000 0000 0011 1110으로 초기화해야한다. 이를 아스키코드로 바꾸면 0x3c이므로 다음과 같이 초기화한다.

//RCC의 초기값은 0x00이라서, or 연산자를 이용해 초기화해도됨
RCC |= 0x0000003c;

CRL, CRH 초기화

GPIOx_CRL 설명 GPIOx_CRH 설명

위의 코드에서 알 수 있듯, CRL에서 0번핀을 초기화하려면 hex값에서 뒤에서 0번째 hex값을 input일 경우 8(=0b1000, 10은 CNF에서 input with pull-up/pull-down을 의미하고, 뒤의 00은 input mode를 의미한다.)로 설정하고

CRH에서 8번핀을 초기화하려면 input mode에서 hex값에서 뒤에서 0번쨰 hex값을 8로 설정하면 되고

CRL에서 0번핀을 output으로 초기화하려면 뒤에서 0번째 hex값을 1(=0b0001, 앞의 00은 General purose output push-pull을 의미하고, 뒤의 01은 output mode max speed 10mhz를 의미한다)로 설정한다.

  PA_CRL &= 0xfffffff0;//PA0를 설정하기 위해 PA0의 CRL값을 0으로 먼저 초기화한다(or 연산으로 값을 넣기 위함).
  PA_CRL |= 0x00000008;//PA0을 input mode로 설정한다.
  
  PC_CRL &= 0xfff0ffff;//PC4를 설정하기 위해 PC4의 CRL값을 0으로 먼저 초기화한다.
  PC_CRL |= 0x00080000;//PC4을 input mode로 설정한다.
  

  PB_CRH &= 0xfffff0ff;//PB10을 설정하기 위해 PB10의 CRH의 값을 0으로 먼저 초기화한다.
  PB_CRH |= 0x00000800;//PB10을 input mode로 설정한다.

  PC_CRH &= 0xff0fffff;//PC13을 설정하기 위해 PC13의 CRH의 값을 0으로 먼저 초기화한다.
  PC_CRH |= 0x00800000;//PC13을 input mode로 설정한다.

  PD_CRL &= 0x0ff000ff;//PD2, PD3, PD4, PD7을 설정하기 위해 각 포트의 CRL값을 0으로 먼저 초기화한다.
  PD_CRL |= 0x10011100;//PD2,PD3,PD4,PD7을 output mode로 설정한다.

LED 끄기

  PD_BSRR |= PIN_2; // BSRR을 이용하여 PD2번 핀을 set한다. 그림2에서 PD2번핀이 set되면 LED가 꺼진다.
  PD_BSRR |= PIN_3; // BSRR을 이용하여 PD3번 핀을 set한다.
  PD_BSRR |= PIN_4; // BSRR을 이용하여 PD4번 핀을 set한다.
  PD_BSRR |= PIN_7; // BSRR을 이용하여 PD7번 핀을 set한다.

무한반복문 작성하기

 int key = 0;
  while(1) {
    if (!(PC_IDR & PORT_4)){ //Key1 버튼이 눌리면 PC_IDR에서 뒤에서 4번째 비트가 0이된다. 따라서 이것을 port_4와 & 하면 0이되고, key=1이 들어가게 된다.
      key = 1;
    }
    else if (!(PB_IDR & PORT_10)){ // key2 버튼이 눌리지 않는다면 PC_IDR에서 뒤에서 10번째 비트가 1이된다. 따라서 이것을 port_10과 & 하면 1이 되고, key=2가 실행되지 않는다.
      key = 2;
    }
    else if (!(PC_IDR & PORT_13)){
      key = 3;
    }
    else if (!(PA_IDR & PORT_0)){
      key = 4;
    }
    
    //printf("key = %d\n", key);
    
    switch (key) {
    case 0:
      break;
    case 1:
    //PD_BSRR을 이용하여 포트2번과 포트7번을 reset한다.
      PD_BSRR |= PORT_2<<16;
      PD_BSRR |= PORT_7<<16;
      break;
    case 2:
    //PD_BSRR을 이용하여 포트2번과 포트7번을 set한다.
      PD_BSRR |= PORT_2;
      PD_BSRR |= PORT_7;
      break;
    case 3:
    //PD_BSRR을 이용하여 포트3번과 포트4번을 reset한다.
      PD_BSRR |= PORT_3<<16;
      PD_BSRR |= PORT_4<<16;
      break;
    case 4:
     //PD_BSRR을 이용하여 포트3번과 포트4번을 set한다.
      PD_BSRR |= PORT_3;
      PD_BSRR |= PORT_4;
      break;
    }
    
    key = 0;
  }

작동 영상

오실로스코프을 사용한 디버깅 영상

key1을 오실로스코프에 연결하여 디버깅한 모습. key1 버튼은 풀업저항이므로, 버튼이 눌릴때마다 0을 출력하는 것을 알 수 있다.

Reference

댓글남기기