[20호]아날로그필터와 OPAMP Op Amp. 사용설명서 PART 1
[20호]MSP430용 절연 JTAG 에뮬레이터(MSP-SDS100i) 출시
(주)싱크웍스에서 MSP430 전용 절연JTAG 에뮬레이터(MSP -SDS100i)를 출시했다. ■ 제품 주요 특징 ■ · 절연 JTAG 인터페이스 설계로 PC와 프로세서를 전기적으로 분리 (전원단 절연 1,000Vrms, 신호단 절연 ,500Vrms(Galvanic Isolation) TEL. 031-781-2812
|
[20호]JK전자와 함께하는 ARM 완전정복(6)-1
JK전자와 함/께/하/는 ARM 완전 정복
Ⅳ. Cortex-M3 Applications
글 | JK전자 |
ARM Architecture에서 많은 내용을 다루어서 Cortex-M3의 이론적인 부분은 그나마 짧게 끝난 것 같습니다. 이번 파트에서는 STM32F103VC Dragon 개발보드 + 3.2 터치 LCD 가 부착된 실제 개발보드를 가지고 이론으로만 공부했던 내용을 실제 예제를 통해서 하나씩 배워나가보도록 하겠습니다. 예제들은 주로 Cortex-M3 Core 부분에 해당하는 부분을 다룰 것입니다. 처음에는 LCD, MP3 등의 디바이스 제어 예제도 진행하려고 했으나 이 부분들은 Cortex-M3의 공통된 부분도 아니고 디바이스에 따라서 많은 내용이 달라지기 때문에 STM32F10x 시리즈의 자체 기능과 Dragon 개발보드에 연결된 기본 예제들만 다루도록 하겠습니다. 추후 모든 디바이스들에 대한 예제들은 시간이 허락한다면 STM32 응용에 대한 강좌로 진행해 보도록 하겠습니다.
강의 전체 로드맵
I. ARM Architecture | 임베디드 시스템 개론에 대한 설명과 ARM7, ARM9 의 구조에 대해서 설명합니다.
II. ARM Applications | 삼성의 S3C2440(ARM9) 개발보드(S3C2440 Mini 개발보드)를 이용해서 어셈블리어와 UART, GPIO 등을 실습합니다.
III. Cortex-M3 Architecture | Cortex-M3의 특징과 구조에 대해서 설명합니다.
IV. Cortex-M3 Applications | STM32F103VCT6 Dragon 개발보드를 이용해서 GPIO, LCD, SPI, UART, MP3, SDIO, I2C 등을 실습합니다.
이 강의 자료에 대한 모든 질의사항은 http://cafe.naver.com/avrstudio의 ARM Architecture Q&A게시판에 글을 남겨주시거나 jk@deviceshop.net로 메일을 보내주시기 바랍니다. 가급적이면 여러 사람이 질문에 대한 답변을 공유할 수 있도록 네이버 카페 게시판을 이용해주셨으면 합니다. 감사합니다.
Ⅳ. Cortex-M3 Applications 목차
1. STM32F10x Overview
1.1 STM32F10x Block Diagram
1.2 STM32F10x Memory Map
1.3 STM32F10x Boot Modes
1.4 STM32F10x GPIO
2. STM32F103VC Dragon개발보드 소개
2.1 Features
3. Examples
3.1 GPIO Output without SDK
3.2 GPIO Output with SDK
3.3 GPIO Output with BitBand
3.4 GPIO Input – Polling
3.5 GPIO Input – Interrupt
3.6 General Purpose Timer
3.7 Systick – Delay
3.8 Systick – Interrupt
3.9 USART – Polling
3.10 USART – Interrupt
3.11 USART – Name Card
3.12 Interrupt Priority1
3.13 Interrupt Priority2
3.14 Power Management – Sleep
3.15 Power Management – Stop
3.16 Power Management – StandBy
3.17 Mode Privilege
3.18 USART Monitor Program
1. STM32F10x Overview
1.1 STM32F10x Block Diagram
Cortex-M3 Core를 Based로 하여 ICode, DCode, System Bus와 연결이 되어 있는 것을 볼 수 있습니다. Cortex-M3 Core는 ARM사에서 라이센싱한 것이고 아래의 그림에서 Vendor Defined Specfic 부분은 ST Microeclectonics사에서 구현한 것입니다. Bus Matrix를 통해서 AHB system bus와 Cortex-M3 Core가 연결되어 있고 APB1, APB2 bus가 AHB bus에 연결되어 있습니다.
1.2 STM32F10x Memory Map
Cortex-M3의 메모리 맵은 Architecture Defined 되어 있고, 4GB의 메모리 공간을 Access 할 수 있습니다. Architecture 차원에서 Memory Map이 정의되어 있기 때문에 같은 Cortex-M3를 기반으로한 CPU들 사이에서는 S/W 개발과 포팅을 쉽게 할 수 있습니다. 그리고 Memory Map에서 유심히 봐야할 부분은 Bit-Band region이 존재하고 있는 영역입니다.
-STM32F103VC CPU의 Memory layout
0×0800.0000 ~ 0×0801.FFFF (FLASH)
0×2000.0000 ~ 0×2000.BFFF (SRAM)
0×4000.0000 ~ 0×4002.3FFF
(Peripheral Memory Map)
0xE000.0000 ~ 0xE00F.FFFF
(Cortex-M3 Internal Peri.)
1.3 STM32F10x Boot Modes
STM32F 시리즈의 CPU는 3가지 모드로 부팅할 수 있습니다.
- User Flash memory : 일반적인 Normal booting입니다. STM32F CPU의 Internal Flash(0×0 or 0×8000000)에서 부팅을 하는 것입니다.
- System memory : STM32F CPU의 Internal System memory를 이용해서 부팅을 합니다. 이 모드로 부팅을 하면 CPU에 있는 내부 부트로더에 의해서 USART0가 초기화되고 ST 사에서 제공하는 ST Flashloader 프로그램을 이용해서 bin(or hex) 실행 이미지를 User Flash memory 영역에 다운로드 할 수 있습니다. JTAG 장비가 없을 경우 USART0을 이용해서 프로그램을 퓨징할 때 유용하게 사용할 수 있습니다.
- Embedded SRAM : Internal RAM에서 부팅을 시작하게 합니다. 이렇게 하려면 Startup 코드에서 Vector Table offset 위치를 RARM에 위치하도록 Register 설정을 해주어야 합니다.
위의 그림은 우리가 실습에 사용할 Dragon 개발보드의 BOOT 모드 회로도입니다.
2. STM32F103VC Dragon 개발보드 소개
2.1 Features
CPU Module – STM32 Rabbit CPU 모듈 사용 | |
CPU | STM32F103VCT6 Cortex-M3 Core(LQFP-100) |
SRAM | 32KB internal SRAM |
Flash | 128KB internal Flash memory |
ISP | 1 x ISP 모드 전환 점퍼 |
UART | 1 x UART( UART0) |
LEDs | 1 x User LED, 1 x Power LED |
Reset Button | 1 x Reset Button |
JTAG | 20Pin JTAG interface |
Size(WxH) | 68.4mm x 69.1mm(W x H) |
확장포트 | STM32F103VCT6의 모든 포트가 2 x 2.54mm으로 나와 있습니다. |
Bottom Board – Hardware Features | |
전원 | 5V DC 전원(외경:5.5mm, 내경:2mm), USB, USB to Serial 포트를 통해서 전원공급 가능 |
RS232 | 1 x USB2Serial 포트 내장 1 x DB9 시리얼 포트 ( MAX232 ) |
USB | x USB 2.0 Full speed |
LCD | 1 x 2.4, 4.3, 7.0 inch TFT 터치 LCD 인터페이스 1 x 1602 Char LCD 인터페이스 1 x 12864 그래픽 LCD 인터페이스 |
MP3 | 1 x VS1003B SPI Interface |
RF | 1 x nRF24L01 SPI Interface |
AHRS | 1 x AHRS Sensor Interface |
LEDs | 3 x User LED, 1 x Power LED |
Buttons | 5 x User Button, 1 x Wakeup Button |
CAN | 1 x CAN ( VP230 ) |
RS485 | 1 x RS485 ( SP3485 ) |
SD Memory | 1 x 4bit SD memory card interface |
Adjustable Resiter | 1 x ADC test |
EEPROM | 1 x IIC EEPROM |
Backup Battery | 1 x Backup battery holder |
Buzzer | 1 x Buzzer |
ADC | 2 x ADC Input( ADC12 In8, In9 ) |
DAC | 1 x DAC |
Size(WxH) | 155 x 110 mm |
3. 프로그램 다운로드 방법
3.1 JTAG을 이용한 다운로드 방법
3.1.1 ST-Link/V2
(1) ST-Link USB Driver Download and install
- http://www.st.com/web/en/catalog/tools/PF258167
(2) ST-Link Utility Download and install
- http://www.st.com/web/en/catalog/tools/PF258168
(3) ST-Link/V2 Utility Program 설치
- STM32 ST-LINK Utility_v2.5.0.exe
▼ |
▼ |
▼ |
(4) ST-Link/V2 USB 드라이버 설치
- ST-Link_v2_usbdriver.exe
▼ |
▼ |
▼ |
▼ |
- Windows7 의 경우 장치 드라이버가 자동으로 설치되지 않을 경우에 드라이버 소프트웨어 업데이트를 수행합니다.
▼ |
▼ |
▼ |
▼ |
(4) ST-Link/V2 Firmware upgrade
(5) Program Fusing with ST-Link/V2
ST-Link/V2의 20핀 JTAG Cable을 Dragon 개발보드의 CPU 모듈에 있는 20핀 JTAG 박스 헤더에 연결하고 ST-Link Utility에서 “Mode Setting” 메뉴에서 “SWD” 모드로 설정합니다. 일반적으로는 STM32F 시리즈에서 “JTAG” 모드로 설정해도 되지만 Dragon 개발보드에서는 JTAG 핀의 일부를 LCD 제어시 사용하고 있기 때문에 반드시 “SWD” 모드로 설정해야 합니다. 그리고 Connect Target 을 실행합니다.
- 참고로 IAR 컴파일러 환경에서는 bin 파일을 생성하기 위해서는 Ouput Converter에서 아래 그림과 같이 Binary파일 생성 옵션을 설정해 주어야 bin 파일이 생성됩니다.
- IAR, KEIL등의 개발 환경과 직접 연동을 하지 않을 경우에는 ST-Link Utility를 이용해서 실행 binary를 다운로드 받을 수 있습니다.
(6) EWARM Debug Environment – ST-Link/V2
- Debugger에서 Driver를 ST-Link를 선택합니다.
- ST-Link 설정에서 “SWD” 모드로 설정을 합니다.
- ST-Link/V2 제품은 IAR에서 사용할 경우 6.20 이상부터 연동을 할 수 있습니다.
3.1.2 ARM-JTAG
(1) ARM-JTAG Utility 설치
▼ |
▼ |
▼ |
▼ |
▼ |
(2) JICE Server 프로그램 실행
- ARM-JTAG의 20핀 JTAG Cable을 Dragon 개발보드의 CPU 모듈에 있는 20핀 JTAG 박스 헤더에 연결 하고 JICE Server 프로그램을 실행합니다.
연결이 정상적이라면 Core ID를 읽어옵니다.
Core ID가 읽어지지 않는다면 아래와 같이 JICE Server를 설정해 보시기 바랍니다.
(3) JICE Configuration
- Debug Port Configuration
- Tap Configuration
(4) JICE Commander
(5) Easy Flashloader 설정
STM32F103VC(STM32F10xxC) or STM32F103ZE(STM32F10xxE) 중에서 선택합니다.
- Start Download 를 실행하면 먼저 Flash를 Erase하고 나서 선택한 바이너리 파일을 다운로드합니다.
(6) EWARM Debug Environment – ARM-JTAG
- Debugger에서 Driver를 RDI로 선택합니다.
- Easy Flashloader에서 “Flash Download Before Debugging” 을 체크합니다.
- IAR 개발환경에서 “Download and Debugging”을 시작하면 “Check Flash Download State!” 실행창이 나오게 되고 JICE Commander에서 다운로드가 정상적으로 이루어 졌다면 “PASS” 버튼을 클릭합니다.
ARM-JTAG의 사용에 경우 Windows7 환경에서 보다 자세한 환경 설정은 아래 URL을 참조하시기 바랍니다.
http://www.jkelec.co.kr/img/jtag/arm/armjtag/arm_jtag_manual.html
3.2 STM32F 시리즈의 internal ISP(UART0)를 이용한 방법
STM32F 시리즈의 CPU들은 별도의 JTAG 장비없이 내장 ISP 기능을 이용해서 bin 파일을 다운로드 할 수 있습니다.
(1) ISP 모드로 진입
다운로드 모드로 진입하기 위해서는 아래 그림과 같이 Boot0 점퍼를 ISP라고 되어 있는 위치에 세팅하고 PC와 연결합니다.
Dragon Bottom 보드가 있을 경우에는 Bottom 보드의 UART 포트에 연결을 하면 됩니다. Dragon Bottom 보드에는 PL2303 USB to Serial 포트가 내장되어 있습니다.
(2) PL2303 USB to Serial 드라이버 설치
- PL2303_Prolific_DriverInstaller_v1210.exe
▼ |
▼ |
▼ |
▼ |
▼ |
(3) “STMicroelectronics flash loader.exe” 프로그램을 설치하고 실행합니다.
- Parity : “Even” , Baud Rate : “115200″ 으로 설정
▼ |
▼ |
- Binary or Hex 파일을 선택하고 보통은 “Erase necessary pages” 를 선택하면 됩니다.
▼ |
▼ |
[20호]JK전자와 함께하는 ARM 완전정복(6)-2
JK전자와 함/께/하/는 ARM 완전 정복
Ⅳ. Cortex-M3 Applications
글 | JK전자 |
4. Examples
4.1 GPIO Output without SDK
STM32F 시리즈에는 ST사에서 제공하는 CPU Datasheet를 보지 않더라도 함수 이름만 보고서도 따라할 수 있을 정도의 아주 훌륭한 Library가 있습니다. 하지만 우리는 처음에는 STM32F CPU의 레지스터들을 더 잘 이해하기 위해서 SDK 라이브러리를 사용하지 않고 직접 SFR 레지스터드를 설정하는 방법으로 해보도록 하겠습니다.
첫번째 예제로 Dragon 개발보드의 Bottom 보드에 있는 LED들 중에서 LED2, LED4는 On, LED3는 Off 시키는 예제를 해보도록 하겠습니다.
(1) Dragon 개발보드의 LED 회로
회로를 보면 LED를 켜기 위해서는 GPIO포트를 High로 하면 LED가 켜지도록 되어 있습니다.
(2) Peripheral Bus
GPIOE는 APB2 버스에 연결되어 있으므로 APB2 버스의 GPIOE 포트의 Clock을 Enable시켜주어야 합니다.
(3) STM32 Peripheral Boundary Address : 0×4002 1000 (STM32F103 Reference Guide Page 50)
(4) RCC_APB2ENR Boundary Address : 0×4002 1000 + 0×18 (STM32F103 Reference Guide Page 119)
(5) RCC_APB2ENR Boundary Address : 0×4002 1018 (STM32F103 Reference Guide Page 142)
- GPIOE Port Enable : (*(volatile unsigned *)0×4002 1018) |= 0×01 << 6
(6) GPIOE Port Boundary address : 0×4001 1800 ( STM32F103 Reference Guide Page 51 )
(7) GPIO and AFIO register maps (STM32F103 Reference Guide Page 188)
(8) GPIOE_CRL Register Boundary address : 0×4001 1800
- GPIOE Port2, 3, 4를 Output push-pull로 설정
(9) GPIOE_BSRR(Set/Reset) Register Boundary address : 0×4001 1810
BSRR 레지스터는 GPIO Set or Reset 전용 레지스터입니다. 0 ~ 15비트는 Set 전용이고 16 ~ 31비트는 Reset 레지스터입니다.
LED2(GPIOE2), LED4(GPIOE4) 포트를 Set하기 위해서는 BSRR 레지스터의 2, 4번 비트를 1로 만들면 됩니다. “0″을 Write했을때 “No action” 이라고 되어 있는 부분은 일반적으로 레지스터의 특정 비트를 Set or Clear하기 위해서는 LDR, Bit 조작, STR의 3단계가 필요한데, 이러한 Feature가 지원이 되면 Bit 조작 다음에 바로 STR 명령어에 의해서 레지스터 비트 조작이 가능하게 됩니다.
(10) GPIOE_BRR(Reset) Register Boundary address : 0×4001 1814
GPIO Reset 전용 레지스터를 이용해서 GPIOE3을 Reset 합니다.
(*(volatile unsigned *) 0×40011814) = (0×1 << 3); // PE3 Off
예제 전체 코드
void led_test_wo_sdk(void)
{
// APB2 Clock enable
// Reference Page 47, 50, 119, 142
// APB2 peripheral GPIOE clock enable register (RCC_APB2ENR)
(*(volatile unsigned *)0×40021018) |= 0×01 << 6;
// General Purpose output push-pull
// Reference Page 51, 188, 166
// GPIOE_CRL
(*(volatile unsigned *)0×40011800) &= ~(0xf << 8);
(*(volatile unsigned *)0×40011800) &= ~(0xf << 12);
(*(volatile unsigned *)0×40011800) &= ~(0xf << 16);
// PE2, 3, 4 Ouput mode configuration
// Reference Page 51, 188, 166
// GPIOE_CRL
(*(volatile unsigned *)0×40011800) |= (0×3 << 8); // PE2 Ouput Mode 50MHz
(*(volatile unsigned *)0×40011800) |= (0×3 << 12); // PE3 Ouput Mode 50MHz
(*(volatile unsigned *)0×40011800) |= (0×3 << 16); // PE4 Ouput Mode 50MHz
// Reference Page 168
// GPIOE_BSRR
(*(volatile unsigned *)0×40011810) = (0×1 << 2); // PE2 On
// GPIOE_BSRR
(*(volatile unsigned *)0×40011810) = (0×1 << 4); // PE4 On
// GPIOE_BRR
(*(volatile unsigned *)0×40011814) = (0×1 << 3); // PE3 Off
}
4.2 GPIO Output with SDK
(1) 첫번째 예제와 동일한 동작을 하는데 이번에는 ST사에서 제공한 SDK 라이브러리를 이용해서 해보도록 하겠습니다.
(2) 프로젝트와 stm32f10x_conf.h 파일 수정
GPIO 라이브러리를 사용하기 위해서는 프로젝트에 stm32f10x_gpio.c 파일을 추가 하고 stm32f10x_conf.h 에서 stm32f10x_gpio.h 를 include 해야 합니다.
예제 전체 코드
void led_test_wt_sdk(void)
{
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
/* Configure the GPIOE ports for output*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE, GPIO_Pin_2); // LED2 On
GPIO_ResetBits(GPIOE, GPIO_Pin_3); // LED3 Off
GPIO_SetBits(GPIOE, GPIO_Pin_4); // LED4 On
}
별도의 설명을 하지 않아도 함수의 이름만 봐도 쉽게 코드를 이해할 수 있습니다.
4.3 GPIO Output with BitBand
(1) 예제 2번과 동일한 작업을 하는데, 이번에는 Bitband를 이용해서 해보도록 하겠습니다.
Peripheral 영역의 Bitband 영역을 이용해야하기 때문에 Bitband base 영역은 0×40000000이 되고 Bitword base는 0×42000000 이 됩니다.
Bitband 영역의 계산식을 다시 한번 상기하면서 소스코드를 분석해 보시기 바랍니다. GPIOE의 ODR 레지스터를 이용해서 구현하였습니다.
bit_word_offset = (byte_offset x 32) + (bit_number × 4)
bit_word_addr = bit_band_base + bit_word_offset
// GPIOE_ODR : 0x4001180C
// 2,3,4 Bit : PE2, PE3, PE4
예제 전체 코드
#define PERI_BASE 0×40000000
#define PERI_BB_BASE 0×42000000
void led_test_wt_bitband(void)
{
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
/* Configure the GPIOE ports */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
(*(volatile unsigned *)(PERI_BB_BASE + (0x4001180C-PERI_BASE)*32 + 2*4)) = 0×1; // PE2 On
(*(volatile unsigned *)(PERI_BB_BASE + (0x4001180C-PERI_BASE)*32 + 3*4)) = 0×0; // PE3 On
(*(volatile unsigned *)(PERI_BB_BASE + (0x4001180C-PERI_BASE)*32 + 4*4)) = 0×1; // PE4 On
}
4.4 GPIO Input – Polling
Dragon 개발보드에 있는 버튼 입력을 Polling 방식으로 처리해 보도록 하겠습니다.
(1) BTN2가 눌리면 LED2를 ON 시키고, BTN2가 눌리지 않은 상태면 LED2를 Off 합니다. 동일한 방법으로 BTN3, BTN4 에도 적용 합니다. 그리고 BTN1을 누르면 테스트를 종료 합니다.
BTN 회로를 보면 버튼이 눌리지 않았을때는 VCC에 연결이 되어 High 상태이고 버튼이 눌리면 GND에 연결이 되면서 Low 상태가 됩니다.
(2) GPIOC 그룹을 사용하기 위해서는 APB2 버스의 GPIOC 그룹의 Clock을 Enable 해야 합니다.
(3) GPIOC1, 2, 3을 Input floating으로 설정합니다.
예제 전체 코드
void key_input_test_polling_wt_sdk(void)
{
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable for LED
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
// APB2 Clock enable for KEY
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* Configure the GPIOE ports for output*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* Configure the GPIOC ports for input */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
while(1)
{
// BTN1
if( (!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0 )) )
{
break;
}
// BTN2
if( (!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1 )) )
{
GPIO_SetBits(GPIOE, GPIO_Pin_2); // LED2 On
}
else
{
GPIO_ResetBits(GPIOE, GPIO_Pin_2); // LED2 Off
}
// BTN3
if( (!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_2 )) )
{
GPIO_SetBits(GPIOE, GPIO_Pin_3); // LED3 On
}
else
{
GPIO_ResetBits(GPIOE, GPIO_Pin_3); // LED3 Off
}
// BTN4
if( (!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_3 )) )
{
GPIO_SetBits(GPIOE, GPIO_Pin_4); // LED4 On
}
else
{
GPIO_ResetBits(GPIOE, GPIO_Pin_4); // LED4 Off
}
}
}
4.5 GPIO Input – Interrupt
이번에는 BTN 입력을 폴링 방식이 아닌 인터럽트 방식으로 구현해 보도록 하겠습니다.
(1) BTN2를 한번 누르면 LED2를 On 시키고, 다시 BTN2를 누르면 LED2를 Off 합니다.
(2) BTN 입력을 Falling Edge에서 검출되도록 설정합니다.
Falling Edge에서 검출한다는 것은 KEY Down에서 인터럽트가 발생하도록 한다는 것이죠. KEY Up을 검출하기 위해서는 Rising Edge 에서 검출되도록 설정하면 됩니다.
(3) PC1 포트의 인터럽트 입력을 받으려면 외부 인터럽트 1번에 연결해야 합니다. 당연히 PC2는 외부 인터럽트 2번에 연결해야 하겠죠.
(4) 인터럽트 레지스터 설정
(5) SDK 라이브러리 추가
(6) 인터럽트 우선순위
STM32에서 인터럽트 우선순위 비트는 8비트중에서 상위 4비트만 사용합니다. 4비트 중에서 이번 예제에서는 Priority group 2를 사용해서 Group 우선순위 2비트 Sub 우선순위 2비트를 사용하도록 설정합니다.
(7) 인터럽트 서비스 흐름도
외부 인터럽트가 발생해서 인터럽트 핸들러 함수로 분기하기까지의 과정을 도식화 해보았습니다.
(8) 예제 코드 작성 순서
□ APB2 Clock enable for LED
□ APB2 Clock enable for KEY
□ Configure the GPIOE ports for output
□ Configure the GPIOC ports for input
□ Configure EXTI to generate an interrupt on falling edge
□ EXTIPR : pending 여부및 pending clear(1:pending clear)
□ ICPR : pending clear Cortex-M3
예제 전체 코드
void key_input_test_interrupt(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable for LED
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
// APB2 Clock enable for KEY
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// for Interrupt
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure the GPIOE ports for output*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* Configure the GPIOC ports for input */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Connect EXTI */
// External Interrupt configuration register1 (AFIO_EXTICR1)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource1);
/* Configure EXTI1 to generate an interrupt on falling edge */
EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 2 bit for pre-emption priority, 2 bits for subpriority
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Clear EXTI Line Pending Bit */
// STM32F10x Pending Register : 0×40010400 + 0×14
EXTI_ClearITPendingBit(EXTI_Line1);
/* Enable the Key EXTI line Interrupt */
// Cortex-M3 Interrupt Clear-Pending Register : 0xE000E280-0xE000E29C
NVIC_ClearPendingIRQ(EXTI1_IRQn);
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line1);
// Blink
GPIOE->ODR ^= (0×1 << 2);
}
}
4.6 General Purpose Timer
임베디드 시스템에서 Timer는 가장 중요하고 필수적인 요소로 OS에서 Task스케줄링을 위해서 사용되기도 하고, 전자액자, 차량용 블랙박스 등의 응용 어플리케이션에서도 특정 시간 이후에 인터럽트를 발생시켜 정해진 일을 수행하는 경우 등 응용분야는 무수히 많습니다.
■ 참고 ■
CPU의 클럭 속도가 1Hz 라는 것은 무엇을 의미하는 것일까요?
이것은 1초에 1번의 Tick이 발생한다는 것임. STM32가 72MHz 로 동작한다면 1초에 7천 2백만번의 Tick이 발생하는 것
이번 예제에서는 STM32 CPU에 내장된 Timer2를 이용해서 1초에 한번씩 Timer 인터럽트를 발생시켜 LDE2, LED3를 Toggle(On/Off 를 반복하는것)하는 실험을 해보도록 하겠습니다.
■ 참고 ■
1sec = 1,000ms = 1,000,000us = 1,000,000,000ns
1Hz = 1KHz = 1MHz = 1GHz
(1) STM32F CPU의 타이머 종류
(2) Timer Clock
Timer2는 APB1 버스에 연결되어 있습니다. APB1 버스의 최대 동작 속도는 36MHz이지만 위의 그림을 잘 보면 APB1의 Prescaler가 1이 아니면 PB1 Clock x2를 하고 있습니다. 그러므로 Timer2가 APB1에 연결되어 있지만 Timer2에 공급되는 Clock은 72MHz가 됩니다. 주의해서 보지 않으면 Timer2에 공급되는 Clock이 36MHz라고 착각할 수 있습니다.
(3) 프로젝트와 stm32f10x_conf.h 파일 수정
(4) 예제 코드 작성 순서
□ APB1 Clock enable for TIM2
□ GPIO Init for LED
□ Configure the TIM2 interrupt
□ Time base configuration
□ TIM2 Enable
□ TIM2 interrupt enable
예제 전체 코드
void gpio_init_led(void)
{
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
/* Configure the GPIOE ports for output*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
void gpio_init_key(void)
{
// Define GPIO Init structure
GPIO_InitTypeDef GPIO_InitStructure;
// APB2 Clock enable for KEY
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
// for Interrupt
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure the GPIOC ports for input */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET )
{
// Also cleared the wrong interrupt flag in the ISR
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // Clear the interrupt flag
// Blink
GPIOE->ODR ^= (0×1 << 2);
GPIOE->ODR ^= (0×1 << 3);
}
}
void timer2_test(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
gpio_init_led();
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// TIM2CLK = 72 MHz( APB1은 원래 36MHz 인데 분주를 해서 사용하므로 36*2 = 72MHz 가 된다. )
// 시간의 기본단위 :S(초)–>nS.uS.mS.S.
// 72000000/60000=1200 즉 1초에 1200번 클럭이 발생하므로
// ARR 레지스터를 1199+1 번에 한번 인터럽트가
// 발생하도록 설정하면 1초에 한번 인터럽트가 발생된다.
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 1200-1; // ARR(Auto reload register)
TIM_TimeBaseStructure.TIM_Prescaler = 60000-1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update , ENABLE);
}
4.7 Systick – Delay
SysTick Timer를 이용해서 1us Delay 함수를 구현하자. delay_ms 함수를 이용하여 LED2, LED3을 1초 간격으로 Toggle(On/Off) 해 봅시다.
(1) System Control Space
System timer인 SysTick은 System Control Space 영역에 위치하고 있습니다.
(2) SysTick Control and Status Register
이번에는 SysTick 인터럽트는 사용하지 않을 예정이며, CLKSOURCE는 core clock(72MHz)을 그대로 사용할 것입니다.
(3) Systick Reload Register
Systick Current Value Register를 보면 Systick Timer는 24비트 타이머라는것을 알 수 있습니다.
예제 전체 코드
// 1us delay 함수
void delay_us (const uint32_t usec)
{
RCC_ClocksTypeDef RCC_Clocks;
/* Configure HCLK clock as SysTick clock source */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
RCC_GetClocksFreq(&RCC_Clocks);
// Set SysTick Reload(1us) register and Enable
// usec * (RCC_Clocks.HCLK_Frequency / 1000000) < 0xFFFFFFUL — because of 24bit timer
// RCC_Clocks.HCLK_Frequency = 72000000
// Systick Reload Value Register = 72
// 72 / 72000000 = 1us
SysTick_Config(usec * (RCC_Clocks.HCLK_Frequency / 1000000));
// SysTick Interrupt Disable
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk ;
// Until Tick count is 0
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
// 1ms delay 함수
void delay_ms (const uint32_t msec)
{
delay_us(1000 * msec);
}
void systick_test_delay(void)
{
gpio_init_led();
gpio_init_key();
while(1)
{
// BTN1
if( (!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0 )) )
{
break;
}
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
delay_ms(100);
// Blink
GPIOE->ODR ^= (0×1 << 2);
GPIOE->ODR ^= (0×1 << 3);
}
}
4.8 Systick – Interrupt
SysTick Timer를 이용해서 100msec 간격으로 SysTick_Handler 인터럽트를 발생시키고 LED2, LED3을 Toggle(On/Off) 해 봅시다.
예제 전체 코드
void SysTick_Handler(void)
{
// Blink
GPIOE->ODR ^= (0×1 << 2);
GPIOE->ODR ^= (0×1 << 3);
}
void systick_test_interrupt(void)
{
RCC_ClocksTypeDef RCC_Clocks;
gpio_init_led();
/* Configure HCLK clock as SysTick clock source */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
RCC_GetClocksFreq(&RCC_Clocks);
/* Setup SysTick Timer for 100 msec interrupts */
if (SysTick_Config(RCC_Clocks.HCLK_Frequency / 10))
{
/* Capture error */
while (1);
}
}
이전 SysTick Delay 예제에서는 인터럽트가 발생하지 않도록 하기 위해서,
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk ;
위와 같은 코드가 삽입되어 있었습니다.
4.9 USART – Polling
UART1를 PC와 115200bps Baudrate로 통신(RX, TX)을 하는 Echo server로 만들어 봅시다.
RX, TX 통신을 폴링 방식으로 처리합니다. 터미널에서 ‘x’ 가 입력되면 폴링을 종료합니다.
(1) Dragon 개발보드의 UART 회로
Dragon 개발보드에는 COM포트가 없는 노트북, 데스크탑에서 편리하게 사용하게 하기 위해서 USB to Serial 포트가 내장되어 있습니다.
회로도를 보면 PA10이 RX, PA9가 TX 포트입니다. 이번 예제 테스트를 위해서 단순히 USB 미니케이블을 이용해서 Dragon Bottom 보드에 있는 UART 0번과 PCB의 USB 포트에 연결하면 됩니다. 이때 아직 USB to Serial USB 드라이버를 설치하지 않았다면 이전 강좌에서 설명한 “PL2303 USB to Serial 드라이버 설치” 부분을 참조하시기 바랍니다.
(2) Peripheral Bus
APB2 버스에 연결되어 있는 USART1과 GPA9, 10번 포트도 사용되고 있기 때문에 2개의 Peripheral Clock을 모두 Enable해 주어야 합니다.
(3) UART1 사용을 위한 GPIO 포트 초기화
(4) 프로젝트와 stm32f10x_conf.h 파일 수정
(5) USART Status register
예제 전체 코드
void usart1_test_polling(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
char receive_data;
// APB2 Clock enable for USART(GPIOA9, A10)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* Configure the GPIO ports( USART1 Transmit and Receive Lines) */
/* Configure the USART1_Tx as Alternate function Push-Pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable the USART1 */
USART_Cmd(USART1, ENABLE);
while(1)
{
// Rx not empty 가 될때까지 Polling
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET );
// Rx not empty가 되면 USART Data register 에서 data를 읽어옴
receive_data = USART_ReceiveData(USART1) & 0xFF;
// Tx data 전송
USART_SendData(USART1, receive_data);
// Tx empty가 상태가 될때까지 Polling
// Tx 전송시 이 코드를 생략하면 빠른 Data 전송시 중간에 Tx Data 가 유실될수 있음
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );
if( receive_data == ‘x’ )
break;
}
}
4.10 USART – Interrupt
UART1를 PC와 115200bps Baudrate로 통신(RX, TX)을 하는 Echo server로 만들어 봅시다. TX는 폴링 방식으로 처리하고 RX는 Interrupt 방식으로 처리합니다.
(1) USART1 Control Register
예제 전체 코드
void USART1_IRQHandler(void)
{
char receive_data;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
receive_data = USART_ReceiveData(USART1) & 0xFF;
USART_SendData(USART1, receive_data);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
void usart1_test_interrupt(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// APB2 Clock enable for USART(GPIOA9, A10)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure the GPIO ports( USART1 Transmit and Receive Lines) */
/* Configure the USART1_Tx as Alternate function Push-Pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// Rx Not empty interrupt enable
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* Enable the USART1 */
USART_Cmd(USART1, ENABLE);
}
4.11 USART – Name Card
PC의 터미널에 다음과 같이 명함을 출력해 보세요.
*************************************
* Name : Kyung Yeon Kim *
* Company : JK Electronics *
* No : 010-XXXX-XXXX *
*************************************
참고1. 터미널의 행 개행 문자는 “\r\n”
참고2. 터미널 문자열 출력을 하는데 문자열 출력 함수를 만들어 사용하세요.
void usart1_send_string(char *data);
참고3. usart1 초기(폴링방식)화 함수를 작성하세요.
void usart1_init(void);
예제 전체 코드
void usart1_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// APB2 Clock enable for USART(GPIOA9, A10)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* USART1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* Configure the GPIO ports( USART1 Transmit and Receive Lines) */
/* Configure the USART1_Tx as Alternate function Push-Pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1_Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable the USART1 */
USART_Cmd(USART1, ENABLE);
}
void usart1_send_string(char* data)
{
while(*data != ”)
{
USART_SendData(USART1, *(unsigned char *)data);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET );
data++;
}
}
void usart1_test_namecard(void)
{
usart1_init();
usart1_send_string(“\r\n”);
usart1_send_string(“****************************************\r\n”);
usart1_send_string(“* Name : Kyung Yeon Kim *\r\n”);
usart1_send_string(“* Company : JK Electronics *\r\n”);
usart1_send_string(“* No : 010-XXXX-XXXX *\r\n”);
usart1_send_string(“****************************************\r\n”);
usart1_send_string(“\r\n”);
}
4.12 Interrupt Priority1
BTN3를 누르면 LED3를 무한 반복을 하면서 On을 시키고 BTN4를 누르면 LED4를 무한 반복을 하면서 On을 시킨다.
(1) Group, Sub Priority bit를 각각 2Bit씩 설정
(2) BTN3의 Group Priority를 2, Sub Priority를 0으로 설정
(3) BTN4의 Group Priority를 1, Sub Priority를 0으로 설정 후 테스트
예제 전체 코드
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line2);
while(1)
{
GPIO_SetBits(GPIOE, GPIO_Pin_3); // LED3 On
}
}
}
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line3);
while(1)
{
GPIO_SetBits(GPIOE, GPIO_Pin_4); // LED4 On
}
}
}
void interrupt_priority1_test(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
gpio_init_led();
gpio_init_key();
/* Connect EXTI */
// External Interrupt configuration register1 (AFIO_EXTICR1)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource3);
/* Configure EXTI2 to generate an interrupt on falling edge */
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Configure EXTI3 to generate an interrupt on falling edge */
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 2 bit for pre-emption priority, 2 bits for subpriority
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Clear EXTI Line Pending Bit */
// STM32F10x Pending Register : 0×40010400 + 0×14
EXTI_ClearITPendingBit(EXTI_Line2);
EXTI_ClearITPendingBit(EXTI_Line3);
/* Enable the Key EXTI line Interrupt */
// Cortex-M3 Interrupt Clear-Pending Register : 0xE000E280-0xE000E29C
NVIC_ClearPendingIRQ(EXTI2_IRQn);
NVIC_ClearPendingIRQ(EXTI3_IRQn);
}
4.13 Interrupt Priority2
BTN3를 누르면 LED3를 무한 반복을 하면서 On을 시키고 BTN4를 누르면 LED4를 무한 반복을 하면서 On을 시킨다.
(1) Group, Sub Priority bit를 각각 2Bit씩 설정
(2) BTN3의 Group Priority를 2, Sub Priority를 0으로 설정
(3) BTN4의 Group Priority를 2, Sub Priority를 1로 설정 후 테스트
예제 전체 코드
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line2);
while(1)
{
GPIO_SetBits(GPIOE, GPIO_Pin_3); // LED3 On
}
}
}
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line3);
while(1)
{
GPIO_SetBits(GPIOE, GPIO_Pin_4); // LED4 On
}
}
}
void interrupt_priority1_test(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
gpio_init_led();
gpio_init_key();
/* Connect EXTI */
// External Interrupt configuration register1 (AFIO_EXTICR1)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource3);
/* Configure EXTI2 to generate an interrupt on falling edge */
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/* Configure EXTI3 to generate an interrupt on falling edge */
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 2 bit for pre-emption priority, 2 bits for subpriority
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Clear EXTI Line Pending Bit */
// STM32F10x Pending Register : 0×40010400 + 0×14
EXTI_ClearITPendingBit(EXTI_Line2);
EXTI_ClearITPendingBit(EXTI_Line3);
/* Enable the Key EXTI line Interrupt */
// Cortex-M3 Interrupt Clear-Pending Register : 0xE000E280-0xE000E29C
NVIC_ClearPendingIRQ(EXTI2_IRQn);
NVIC_ClearPendingIRQ(EXTI3_IRQn);
}
이번 예제에서는 BTN3과 4의 Group 우선순위가 같기 때문에 서로 인터럽트 수행 중에는 선점을 하지 못합니다.
4.14 Power Management – Sleep
Cortex-M3에서 Power Management 기능은 NVIC에 포함되어 있습니다.
(1) Sleep 모드 테스트
터미널 창에 아래와 같이 표시되도록 합니다.
Entered Sleep mode.
__WFI(); // Sleep mode 로 진입
Exit Sleep mode.
(2) BTN2를 인터럽트 모드로 입력 받아서 Sleep 모드를 빠져 나오도록 합니다. 추가로 BTN2를 누르면 LED2를 On 시키고 BTN2를 떼면 LED2를 Off 합니다.
(3) STM32F Low Power Mode
STM32에서 Sleep 모드로 진입하기 위해서는 WFI(Wait for Interrupt) or WFE(Wait for Event) 어셈블리어에 의해서 진입할 수 있습니다. 이번 예제에서는 WFI를 이용해서 Sleep 모드로 진입하도록 하겠습니다. WFI에 의해서 진입한 Sleep 모드에서 깨어나기 위해서는 어떠한 인터럽트 발생에 의해서라도 깨어날 수 있습니다.
예제 전체 코드
void power_management_sleep_test(void)
{
key_input_test_interrupt();
usart1_send_string(“\r\nEnter Sleep mode.\r\n”);
__WFI();
usart1_send_string(“\r\nExit Sleep mode.\r\n”);
}
Sleep 모드별 Wakeup 방식과 Clock Management 방법입니다.
4.15 Power Management – Stop
Stop, StandBy 모드는 모두 Cortex-M3 Deep sleep 모드에 해당 합니다.
(1) Stop 모드 실험
□ Timer2 인터럽트를 1초 간격으로 발생시켜 LED2, LED3를 Toggle
□ PWR_EnterSTOPMode() 함수를 호출하여 Stop 모드로 진입 합니다.
□ BTN2를 인터럽트 모드로 입력 받아서 Stop 모드를 빠져 나오도록 합니다. 추가로 BTN2를 누르면 LED2를 On 시키고 BTN2를 떼면 LED2를 Off 합니다.
□ STOP 모드를 빠져나온 이후에 LED2, LED3이 1초 간격으로 Toggle
□ PWR_EnterSTOPMode를 호출하면 진입
■ SRAM, Register 상태는 유지
■ 1.8V Domain에 있는 모든 클럭이 Stop
■ PLL, HIS RC, HSE crystal oscillator 모두 disable
■ Voltage regulator는 normal or low power mode
□ Wakeup
■ EXTI line 중의 하나를 받아야 함
■ 16개의 EXTI line or PVD output, RTC alarm, USB wakeup
(2) Peripheral Bus – PWR
STM32F의 Power 블럭은 APB1 버스에 연결되어 있습니다.
(3) 프로젝트와 stm32f10x_conf.h 파일 수정
(4) STM32 PWR 블럭도
(5) Cortex-M3 System Control Register
Stop 모드로 진입시키기 위해서는 2번 비트의 SLEEPDEEP을 Set해야 합니다.
(6) STM32 Power Control Register
PDDS를 o으로, LPDS를 0으로 할 수도 있고, 1로 할 수도 있으나 이번 예제에서는 1로 설정하여 Vdd Domain에 있는 Voltage Regulator를 low-power 모드로 설정합니다.
예제 전체 코드
// STOP 모드에 진입하면 HSE Clock 등이 Disale 되기 때문에 Wakeup시에 다시 설정을 해주어야 합니다.
void system_clock_config_stop(void)
{
RCC_HSEConfig(RCC_HSE_ON); // Enable HSE
if( RCC_WaitForHSEStartUp() == SUCCESS)
{
RCC_PLLCmd(ENABLE); // Enable PLL
// Wait until PLL is ready
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}
// Select PLL as system clock source
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// wait until PLL is used as system clock source
while(RCC_GetSYSCLKSource() != 0×08) {}
}
}
void power_management_stop_test(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
timer2_test();
key_input_test_interrupt();
usart1_send_string(“\r\nEnter Stop mode.\r\n”);
// STOP 모드로 진입하고 외부 인터럽트에 의해서 깨어남
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
system_clock_config_stop();
usart1_send_string(“\r\nExit Stop mode.\r\n”);
}
4.16 Power Management – StandBy
(1) PWR_WakeUpPinCmd(ENABLE)를 호출, PA0를 Wakeup 핀으로 설정
(2) BTN2를 인터럽트 모드로 입력 받아서 Stop 모드를 빠져나오도록 설정
(3) PWR_EnterSTANDBYMode()를 호출, StandBy 모드로 진입
StandBy모드로 진입하는 방법은 STOP모드로 진입하는 방법에서 STM32의 PWR_CR 레지스터의 PDDS를 1로 설정하는것을 제외하면 동일합니다. 단지 StandBy모드에서는 Wakeup 하는 방법에서 차이가 있으면 StandBy모드에서 Wakeup을 한다는 것은 CPU가 처음부터 다시 부팅하는 절차와 동일합니다. 하지만 StandBy모드에서 Wakeup이 되는 경우에는 PWR_CSR 레지스터의 Standby Flag가 H/W 적으로 Set이 되어 있습니다.
(4) Power Control Register
(5) System Control Register
(6) Enable Wakeup PIN
StandBy모드에서 깨어나기 위해서 Wakeup핀을 Enable 합니다. 반드시 PA0 핀이 이용됩니다.
(7) Power control/status register
※ StandbyMode에서 깨어났을 경우에 Hardware적으로 1로 설정됨
예제 전체 코드
void power_management_standby_test(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
// Enable Wakeup Pin(should be PA0)
PWR_WakeUpPinCmd(ENABLE);
usart1_send_string(“\r\nWakeup pin enabled.\r\n”);
usart1_send_string(“Enter Standby mode.\r\n”);
// Standby Mode
PWR_EnterSTANDBYMode();
// 이 코드는 실행될수 없음
usart1_send_string(“\r\nExit Standby mode.\r\n”);
}
4.17 Mode Privilege
(1) Reset 이후의 Mode(Privilege or Unprivilege)와 어떤 Stack(Main Stack, Process Stack)을 사용하는지를 PC의 터미널에 표시합니다.
(2) Mode를 Unprivilege 모드로 전환합니다.
(3) 모든 전환 이후에 Mode(Privilege or Unprivilege)와 어떤 Stack(Main Stack, Process Stack)을 사용하는지를 PC의 터미널에 표시합니다.
(4) SVC 명령어 “__ASM(“svc #1″)” 를 사용해서 SVC_Handler Exception을 발생시키고 SVC_Handler 핸들러 진입 여부를 터미널에 표시합니다.
(5) SVC 핸들러내에서 Mode를 Privilege 모드로 전환합니다.
(6) 모든 전환 이후에 Mode(Privilege or Unprivilege)와 어떤 Stack(Main Stack, Process Stack)을 사용하는지를 PC의 터미널에 표시합니다.
예제 전체 코드
void SVC_Handler(void)
{
usart1_send_string(“\r\nSVC_Handler\r\n”);
usart1_send_string(“Mode change to privilege\r\n”);
__set_CONTROL(0×0);
}
void mode_privilege_test(void)
{
usart1_send_string(“\r\n\r\n—- mode privilege test start —-\r\n\r\n”);
// Mode
if( (__get_CONTROL() & 0×1) == 0×1 )
usart1_send_string(“Mode = Unprivilege\r\n”);
else if( (__get_CONTROL() & 0×1) == 0×0 )
usart1_send_string(“Mode = Privilege Mode\r\n”);
// Stack
if( (__get_CONTROL() & 0×2) == 0×0 )
usart1_send_string(“Stack = MSP Stack\r\n”);
else if( (__get_CONTROL() & 0×2) == 0×1 )
usart1_send_string(“Stack = PSP Stack\r\n”);
// mode change to unprivilege
usart1_send_string(“Mode change to unprivilege\r\n”);
__set_CONTROL(0×1);
// Mode
if( (__get_CONTROL() & 0×1) == 0×1 )
usart1_send_string(“Mode = Unprivilege\r\n”);
else if( (__get_CONTROL() & 0×1) == 0×0 )
usart1_send_string(“Mode = Privilege Mode\r\n”);
// Stack
if( (__get_CONTROL() & 0×2) == 0×0 )
usart1_send_string(“Stack = MSP Stack\r\n”);
else if( (__get_CONTROL() & 0×2) == 0×1 )
usart1_send_string(“Stack = PSP Stack\r\n”);
__ASM(“svc #1″);
// Mode
if( (__get_CONTROL() & 0×1) == 0×1 )
usart1_send_string(“Mode = Unprivilege\r\n”);
else if( (__get_CONTROL() & 0×1) == 0×0 )
usart1_send_string(“Mode = Privilege Mode\r\n”);
// Stack
if( (__get_CONTROL() & 0×2) == 0×0 )
usart1_send_string(“Stack = MSP Stack\r\n”);
else if( (__get_CONTROL() & 0×2) == 0×1 )
usart1_send_string(“Stack = PSP Stack\r\n”);
usart1_send_string(“—- mode privilege test end —-\r\n\r\n”);
}
여기까지 전통적인 ARM 프로세서인 ARM9의 구조를 시작으로 ARM9 Application을 거쳐 Cortex-M3 Architecture, Cortex-M3 Application의 모든 과정이 마무리되었습니다. 머리 속에 있는 내용을 글로 옮긴다는 것이 쉽지는 않았던 것 같습니다. Cortex-M3 Application part에서 기본적인 예제만 다룬 것이 아쉬움이 남네요. 기회가 된다면 STM32 Dragon 개발보드에 있는 모든 디바이스들을 제어해보는 연재를 다시 시작해볼까 합니다. 그리고 개발 환경도 GCC와 이클립스 환경으로 바꾸어 좀 더 사이즈가 크고 전문적인 S/W개발 방법에 대해서 공부해보도록 하겠습니다. 특히 SD메모리와 LCD를 이용해서 예쁜 GUI를 구성하는 부분에 대해서 집중적으로 준비해보도록 하겠습니다.
수고하셨습니다.
JK전자와 함께하는 ARM 완전정복 시즌1이 모두 마무리 되었습니다.
약 6개월 뒤쯤 새롭게 시작될 시즌 2도 기대해주시기 바랍니다.
아울러 컨텐츠를 제공해주신 JK전자 관계자분들께 다시 한번 감사의 말씀을 드립니다.
[20호]DIY 프로젝트 공모전 입선작 – EVALARM 물체회피 기능을 가진 알람카
작품설명 제작 동기 일반시계와 알람시계의 가장 큰 차이점은 알람기능 수행 여부의 차이입니다. 그 중 알람기능은 정해진 시간에 맞추어 사용자가 일어날 수 있도록 도와주는 것이 가장 큰 핵심이라고 할 수 있습니다. 하지만 기존의 알람시계는 소리를 잘 듣기위해 곁에 두고 자게 되고, 이로 인해 사용자가 무의식중에 알람을 꺼버려서 지각을 하거나 그 기능을 상실해 버리는 경우가 대부분입니다. 그렇기 때문에 저희 Evalam은 소리가 잘 들리게 끔 곁에 두지만 일어나지 않으면 꺼지지 않게 끔 설계되어 있어 가장 중요하고 큰 기능이 상실되는 것을 막고 이를 재미와 접목시켜 상쾌한 아침을 맞을 수 있도록 만들어졌습니다. 유사 작품 | 클로키로보틱 알람시계
작품 기능 및 설명
주요 동작 및 특징 알람이 울릴 시 전체 시나리오 4개의 거리 측정 센서로 물체에 감지하는 상황은 총 7가지 경우로 생각해 보았습니다. 그리고 그 상황에 따라 동작은 5가지로 구성했습니다. 먼저 주요 동작 조건을 사진으로 확인해 보겠습니다.
설정 시 전체 시나리오 : 시간 수정, 알람 설정, 알람 시간
위와 같은 표처럼 알람 시간과 알람 설정 그리고 시간을 4개의 스위치로 설정할 수 있도록 설계되어 있습니다. 전체 시스템 구성 하드웨어 구성 TEXT LCD는 7비트로 제어가 가능하도록 설계했으며, 스위치 외부 인터럽트를 사용하여 제어하도록 했습니다. 리모컨 센서는 외부 인터럽트와 타이머 카운트로 인식 받도록 설계했습니다. 적외선 센서는 AD변환을 통해 거리를 측정하도록 설계했습니다. 적외선 센서(L298)
사용시스템 1) 리모컨 관련 시스템 및 설명
각 비트는 1 또는 0을 나타내는데 펄스 폭의 길이로 구별합니다. (아래 그림 참조) 즉, 펄스 폭이 긴 것(A)이 1을 나타내고, 짧은 것(B)은 0을 나타냅니다. 시간은 각각 0.5625 msec와 1.6875 msec입니다.
아래에 삼성 리모컨(TV, VCR)의 코드 값을 예로 들었습니다. 우리는 이 코드표를 사용하는 것이 아니라, 프로그램을 통해서 이 코드 값을 알아내고(표와 일치하는지에 대한 확인), 알아낸 코드 값을 이용해서 LCD를 제어하도록 하겠습니다. 삼성 리모컨뿐만 아니라 TC9012나 D6121 형식의 리모컨은 어떤 것이나 코드 값을 알아내서 사용하는 것이 목적입니다. 2) 삼성리모컨에 대한 전반적인 데이터 표 3) rc카 시스템
단계별 제작 과정 기타(회로도, 소스코드, 참고문헌 등) 소스코드보기#include <avr/io.h> #define ir_ready 0 void clock_data_m_change(); char moter_data; void delay_us(unsigned int us) for(i=0;i<us;i++){ void delay_ms(unsigned int ms) #define CUR11 0×02 /* 커서 홈에 위치시키는 명령어 값 */ typedef unsigned char byte; void lcd_cmd(unsigned char ch){ /LCD에 명령어값 1바이트 쓰는 서브루틴, ch는 명령어 값 / void lcd_ln11(void){ /* LCD의 1행1열에 커서를 위치시키는 서브루틴 */ void lcd_dat(unsigned char ch){ /* LCD에 1글자 쓰는 서브루틴, ch는 글자의 아스키 코드 값 */ void lcd_init(void){ /* LCD 초기화 서브루틴 */ void lcd_str(char *str){ /* LCD에 flash에 저장된 문자열을 출력하는 함수 */ //시간 관련 소스 //키관련 소스 //1s //시간 변경 함수 void key_data(){ //시간 시분초 변경 //시간 시분초 변경 //시간 시분초 변경 //시간 시분초 변경 void lcd_display(){ lcd_cmd(0xc0); char buzzer=0; delay_ms(1); if(ir_go==1){ char sr,sl,sf,sb; void senser(){ ADMUX=0X62; //어느핀 변화시킬건지 ADMUX=0X63; //어느핀 변화시킬건지 void ir_car_run(){ }else{ void putch(unsigned char ch){ /* 한개의 문자를 pc로 송신한다. */ char ir_oper; void ir_remote(){ int main(void){ //외무 인터럽트 SIGNAL(INT4_vect){ SIGNAL(INT0_vect){ case ir_data ://진짜 데이터 3번 데이터 활용 입선을 수상하신 경성대학교 김가희님 외 3분께 진심으로 축하의 말씀을 드립니다. |