SW 개발자를 위한 하드웨어의 이해. 두번째
마이크로 콘트롤러의 이해
2019년
http://goo.gl/ztX1n
셋째날 시간표
- 체크인 (모닝페이지) 그리고 퀴즈
- 디지털 출력: GPIO 이용하여 LED 켜기
- 메모리맵
- 라이브러리 사용하여 주변장치 제어
- 디지털 입력(버튼입력)
- 인터럽트: 핀인터럽트.
- 오실로 스코프, UART 사용 실습
- 버튼 디바운스
- IC간 통신: UART
- Timer
- systick timer
- PWM
Quiz
http://dh8.kr/wiki/doku.php/workshop:quiz
직접 메모리 제어하기: 포인터
C에서 메모리 주소를 변수에 저장하여 사용. -> 포인터
- type * : 포인터 타입
- * : dereference 연산자
- & : reference 연산자
Lesson 4: Blinky program
이제 프로그램을 만들어 보자!
포인터를 이용!
*((unsigned int *)0x400FE608U) = 0x20; // clock gateing
*((unsigned int *)0x40025400U) = 0x0E; // pin direction output
*((unsigned int *)0x4002551CU) = 0x0E; // PF_1, PF_2, PF_3 enable!
*((unsigned int *)0x400253FCU) = 0x02; // RED ON!
해보기:
- 포인터 구문을 이용하여 파란색 LED를 깜박여 보자!
- delay … while(counter < 16) {++counter;}
코드 pointer.c
Lesson 5: 전처리자
전처리자의 종류
//#define, #include #if #ifdef …
해보기:
#define RCGCGPIO (*(unsigned int*) 0x400FE608U)
#define GPIOF_BASE
#define GPIOF_DIR
#define GPIOF_DEN
#define GPIOF_DATA
- 위 코드를 보고, 전처리자를 사용하여 코드를 정리해 보자.
- optimize 레벨을 HIGH로 올려리고 타겟 보드에 프로그램을 올려보자.
코드 preprocessor.c
Lesson 5: 전처리자 2
lm4f120h5qr.h 사용.
해보기
- lm4f120h5qr.h 를 복사해서 워크스페이스 안에 넣어 둔다. lm4f120h5qr.h 에서 RCGCPIO 찾기
- 또는 Workspace Option-> C/C++ Compiler -> Preprocessor -> Additional include directory에 lm4f120h5qr.h 위치를 복사.
RCGGPIO -> SYSCTL_RCGCGPIO_R
GPIOF_DIR -> GPIO_PORTF_DIR_R
GPIOF_DEN -> GPIO_PORTF_DEN_R
GPIOF_DATA -> GPIO_PORTF_DATA_R
#include "lm4f120h5qr.h"
int main() {
SYSCTL_RCGCGPIO_R = 0x20; // clocking gpio
GPIO_PORTF_DIR_R = 0x0E; // Portf pin 1,2,3
GPIO_PORTF_DEN_R = 0x0E;
while (1) {
GPIO_PORTF_DATA_R = 0x02;
int volatile counter = 0;
while (counter < 1000000) {
++counter;
}
GPIO_PORTF_DATA_R = 0x00;
counter = 0;
while (counter < 1000000) {
++counter;
}
}
return 0;
}
코드 lm4fheader.c
Lesson 6: 비트연산
비트연산 왜 필요하나? pin setting, clear, toggle, shift
6개 비트 연산
- | : OR
- & : AND
- ^ : XOR
- ~ : 1의 보수. not
- >> : shift right
- << : shift left
- |=
- &=
- ^=
Lesson 6: 비트연산 2
unsigned int a = 0x5A5A5A5A;
unsigned int b = 0x66666666;
unsigned int c;
c = a | b;
c = a & b;
c = a ^ b;
c = ~b;
c = b >> 1;
c = b << 3;
해보기
- 디버그로 C 값 변화 보기! (디버그 세팅 : Optimize none, simulator)
- watch1에서 a, b, c를 넣고 a,b,c를 관찰 해보자.
Lesson 6: 비트연산 3
부호(signed) 타입 C에서, C에 음수가 일때 있을때, c >> 1 을 실행하면, 최상위 비트에 1이 따라감. 왜?
ASR(산술 shift right)을 사용.
정말 그런지 증명해 봅시다.
해보기 - c를 signed int 타입로 선언하고, c에 음수를 넣고 shift right 해보자.
Lesson 6: 비트연산 사용하여 코드 정리!
2 == (1U << 1)
4 == (1U << 2)
8 == (1U << 3)
0x20 = (1U << 5)
다음을 이용하여, LED_RED, LED_BLUE, LED_GREEN 전처리자를 만들어 보자. 코드
#include "lm4f120h5qr.h"
#define LED_RED (1U << 1)
#define LED_BLUE (1U << 2)
#define LED_GREEN (1U << 3)
int main() {
SYSCTL_RCGCGPIO_R |= (1U << 5);
GPIO_PORTF_DIR_R |= (LED_RED | LED_BLUE | LED_GREEN);
GPIO_PORTF_DEN_R |= (LED_RED | LED_BLUE | LED_GREEN);
GPIO_PORTF_DATA_R |= LED_BLUE;
while (1) {
GPIO_PORTF_DATA_R |= LED_RED;
int volatile counter = 0;
while (counter < 1000000) {
++counter;
}
GPIO_PORTF_DATA_R &= ~LED_RED;
counter = 0;
while (counter < 1000000) {
++counter;
}
}
return 0;
}
Byte Order
해보기
- IAR의 Symbolic Memory View를 이용하여 메모리
0x20000000U
위치에0x0A0B0C0D
값을 넣고, Memory View로0x20000000
의 값을 확인하자. - ARM Cortex M–4는 Byte Order는 Big-endian 일까? Little-endian 일까?
Memory endianness: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/I1835.html
Structure Memory Mapping
#include <stdint.h>
// uint16_t
// uint32_t
// uint8_t
#include <stdint.h>
#include "lm4f120h5qr.h"
#define LED_RED (1U << 1)
#define LED_BLUE (1U << 2)
#define LED_GREEN (1U << 3)
typedef struct {
uint16_t x;
uint8_t y;
} Point;
Point p1, p2;
int main() {
p1.x = sizeof(Point);
p1.y = p1.x - 3U;
SYSCTL_RCGCGPIO_R |= (1U << 5);
GPIO_PORTF_DIR_R |= (LED_RED | LED_BLUE | LED_GREEN);
GPIO_PORTF_DEN_R |= (LED_RED | LED_BLUE | LED_GREEN);
GPIO_PORTF_DATA_R |= LED_BLUE;
while (1) {
// 생략
}
return 0;
}
해보기
p1.x의 값은 몇일까?
watch기능을 이용하여, p1의 요소 x, y 메모리 주소를 알아보자.
p1의 요소의 x, y 정의 순서를 바꾸어 보자.
__packed
키워드를 사용해보자.typedef __packed struct { uint8_t y; uint16_t x; } Point;
더 해보기
- struct를 이용하여 GPIO Register Memory를 매핑해보자.
- TM4C123GH6PM 데이터쉬트 Table 10–6
CMSIS (software interface standard)
tm4c_cmsis.h를 추가하고, core_cm4.h의 디렉토리 위치를 Preprocessor옵션 위치에 추가하자.
#define GPIOF ((GPIO_Type*) GPIO_PORTF_BASE)
.
#include <stdint.h>
#include "tm4c_cmsis.h"
#define LED_RED (1U << 1)
#define LED_BLUE (1U << 2)
#define LED_GREEN (1U << 3)
int main() {
SYSCTL->RCGC2 |= (1U << 5);
GPIOF->DIR |= (LED_RED | LED_BLUE | LED_GREEN);
GPIOF->DEN |= (LED_RED | LED_BLUE | LED_GREEN);
// GPIOF->DATA_Bits[LED_BLUE] |= LED_BLUE;
GPIOF->DATA |= LED_BLUE;
while (1) {
GPIOF->DATA |= LED_RED;
int volatile counter = 0;
while (counter < 1000000) {
++counter;
}
GPIOF->DATA &= ~LED_RED;
counter = 0;
while (counter < 1000000) {
++counter;
}
}
return 0;
}
해보기
- tm4c_cmsis.h http://okin.cc/~donghee/hw2 에 위치.
- tm4c_cmsis.h의 GPIO_Type;과 GPIO Register Map을 비교해보자.
TivaWare
TI에서 제공하는 Cortex-M 라이브러리
- 주변장치 드라이버
- 그래픽 라이브러리
- USB라이브러리
- Ethernet
- Bootloader
Tivaware Peripheral Driver Library USER’S GUIDE
- API: http://www.ti.com/lit/ug/spmu298d/spmu298d.pdf
- Download: http://okin.cc/~donghee/hw2/SW-TM4C-2.1.1.71.exe
해보기
- Tivaware Peripheral Driver Library 읽기 (GPIO/SysTicks/UART/Interrupt/ADC)
새 프로젝트 만들기: project0
TivaWare 를 사용해보자. 다운로드 받은 SW-TM4C–2.1.1.71.exe를 실행하자.
- C:\ti\TivaWare_C_Series–2.1.1.71\examples\boards\ek-tm4c123gxl\
- ek-tm4c123gxl.eww IAR IDE Workspace 열기
- project0 활성화. Set As Active
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#define RED_LED GPIO_PIN_1
#define BLUE_LED GPIO_PIN_2
#define GREEN_LED GPIO_PIN_3
int
main(void)
{
//
// Setup the system clock to run at 50 Mhz from PLL with crystal reference
//
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|
SYSCTL_OSC_MAIN);
//
// Enable and configure the GPIO port for the LED operation.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED);
while(1)
{
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED, RED_LED);
SysCtlDelay(2000000);
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED, BLUE_LED);
SysCtlDelay(2000000);
}
}
해보기
- PORTB 6번 핀에 LED를 연결하여 제어해보자.
버튼 읽기
- PF_4 : PUSH 1 // START BUTTON
- PF_0 : PUSH 2 // STOP BUTTON
https://gist.github.com/donghee/e9182ab4a3af65b613de
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "inc/hw_gpio.h"
#define RED_LED GPIO_PIN_1
#define BLUE_LED GPIO_PIN_2
#define GREEN_LED GPIO_PIN_3
#define BUTTON_2 GPIO_PIN_0
#define BUTTON_1 GPIO_PIN_4
int main(void)
{
int LED = 2;
// configure system clock to run at 50 MHz
// use external crystal (16 MHz) and PLL
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
// Enable PORT F GPIO
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED);
// Button Setup.
// UNLOCK BUTTON_2
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
GPIOPadConfigSet(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
while(1)
{
LED = 0;
if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_1))
LED |= RED_LED;
if(!GPIOPinRead(GPIO_PORTF_BASE, BUTTON_2))
LED |= BLUE_LED;
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|BLUE_LED|GREEN_LED, LED);
// Delay for a bit
//SysCtlDelay(5000000);
}
}
- PB7에 pull up 버튼을 연결후, 버튼을 누르면 PB6에 연결된 LED가 켜지도록 해보자.
GPIO의 핀세팅
입력핀
- Pull-Up
- Pull Down
출력핀
- Open-Drain
- Push-Pull
GPIO 입출력 구조
from datasheet (stm32f)
인터럽트 콘트롤러 (Nested Vectored Interrupt Controller (NVIC))
- 인터럽트나 예외 처리를 한다.
- 인터럽트는 8개의 우선순위 레벨이 있다.
- 7개의 예외처리, 65개의 인터럽트
- 자동으로 상태 저장 (인터럽트가 발생하면 R0-R3, R12, LR, PSR, PC를 스택에 자동 저장, 인터럽트가 끝나면 레지스터값을 스택에서 복원)
- Tail Chaining: 인터럽트 간의 처리 시간을 최적화.
버튼 읽기: 핀 인터럽트
핀의 입력을 감시하다가 핀의 입력이 변할때 인터럽트 핸들러를 실행
- GPIOPinTypeGPIOInput
- GPIOIntTypeSet
- GPIOIntEnable
- GPIOIntClear
- GPIOIntStatus
https://gist.github.com/donghee/8d0ad90e264ddcb59f9e
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
//#include "driverlib/pin_map.h"
//#include "driverlib/uart.h"
//#include "utils/uartstdio.h"
#define RED_LED GPIO_PIN_1
#define BLUE_LED GPIO_PIN_2
#define GREEN_LED GPIO_PIN_3
#define BUTTON_2 GPIO_PIN_0
#define BUTTON_1 GPIO_PIN_4
// handler if button 1, 2 is pressed
// when pressed it changes which LED colors
void GPIOFIntHandler(void)
{
unsigned int isr = GPIOIntStatus(GPIO_PORTF_BASE, true);
GPIOIntClear(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|GREEN_LED|BLUE_LED, 0); // turn off LEDs
if (isr & BUTTON_1) {
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|BLUE_LED, RED_LED);
//UARTprintf("Button 1\n");
}
if (isr & BUTTON_2) {
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|BLUE_LED, BLUE_LED);
//UARTprintf("Button 2\n");
}
if (!GPIOPinRead(GPIO_PORTF_BASE, (BUTTON_1)) &&
!GPIOPinRead(GPIO_PORTF_BASE, (BUTTON_2))) {
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|GREEN_LED|BLUE_LED, 0); // turn off LEDs
GPIOPinWrite(GPIO_PORTF_BASE, GREEN_LED, GREEN_LED);
}
}
int main(void) {
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
// launchpad 스위치 쓸려면 lock을 풀어줘야함!
// Unlock PF0 so we can change it to a GPIO input
// Once we have enabled (unlocked) the commit register then re-lock it
// to prevent further changes. PF0 is muxed with NMI thus a special case.
//
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
HWREG(GPIO_PORTF_BASE+ GPIO_O_LOCK) = 0;
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, RED_LED|GREEN_LED|BLUE_LED);
GPIOPinWrite(GPIO_PORTF_BASE, RED_LED|GREEN_LED|BLUE_LED, 0); // turn off LEDs
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
GPIOPadConfigSet(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
GPIOIntTypeSet(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2, GPIO_FALLING_EDGE); // falling edge
IntEnable(INT_GPIOF);
GPIOIntEnable(GPIO_PORTF_BASE, BUTTON_1|BUTTON_2);
IntMasterEnable();
//
// Initialize the UART.
//
//SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//GPIOPinConfigure(GPIO_PA0_U0RX);
//GPIOPinConfigure(GPIO_PA1_U0TX);
//GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
//UARTStdioConfig(0, 115200, 16000000);
while(1)
{
}
}
// startup_ewarm.c
// extern void GPIOFIntHandler(void);
// GPIOFIntHandler, // GPIO Port F
해보기:
- PORTE 포트의 2번 핀에 pull-up 버튼 추가! 버튼이 눌릴때 녹색 LED가 켜지도록 만들어 보자. 코드
Button Debounce 와 오실로 스코프
해보기
- 오실로 스코프를 이용하여 Button이 Debounce 되는것을 관찰해보자.
- 버튼의 디바운싱 처리를 해보자. 30ms
- 바운스 여부는 UART로 확인.
- base code https://gist.github.com/donghee/8d0ad90e264ddcb59f9e
Button Debounce 하드웨어
1uF 추가. 추가후 오실로 스코프로 관찰하기.
참고
- A Guide to Debouncing by Jack G. Ganssle
- http://dh8.kr/wiki/doku.php/debouncing
UART
UART(Universal Asynchronous Receiver/Transmitter) 이름으로 알 수 있듯이 리시버와 트랜시버가 따로 존재하고 시간 클럭 정보가 없는 비동기식 통신 형태를 말한다.
그럼 클럭 정보 없이 데이터를 어떻게 구분할것인가 궁금할 것 이다.

시작(start bit)과 끝(stop bit) 신호가 있고 그 사이를 일정 시간으로 잘라서 데이터의 비트를 구분한다.
UART는 통신의 속도가 설정가능하고 데이터의 형식(갯수) 설정이 가능하다. UART의 종류로 TTL UART(0–5v), RS–232(+–12), RS–422, RS–485 등이 있다.
참고
- IC간 통신 –from dh8.kr wiki
UART rx/tx
UART module Control Sequence
- Peripheral Enable
- GPIO Pin muxing
- GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
- UART 속도, 설정. UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
API
- UARTCharPut(UART0_BASE, ‘0’);
- UARTCharGet(UART0_BASE, ‘0’);
.
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/uart.h"
#include "driverlib/gpio.h"
//! - UART0 peripheral
//! - GPIO Port A peripheral (for UART0 pins)
//! - UART0RX - PA0
//! - UART0TX - PA1
int main(void)
{
char cThisChar;
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
// Configure the GPIO pin muxing for the UART function.
// This is only necessary if your part supports GPIO pin function muxing.
// Study the data sheet to see which functions are allocated per pin.
// TODO: change this to select the port/pin you are using
//
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
//
// Since GPIO A0 and A1 are used for the UART function, they must be
// configured for use as a peripheral function (instead of GPIO).
// TODO: change this to match the port/pin you are using
//
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//
// Configure the UART for 115,200, 8-N-1 operation.
// This function uses SysCtlClockGet() = 16000000 to get the system clock
// frequency. This could be also be a variable or hard coded value
// instead of a function call.
//
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
//
// Put a character to show start of example. This will display on the
// terminal.
//
UARTCharPut(UART0_BASE, '!');
//
// Enter a loop to read characters from the UART, and write them back
// (echo). When a line end is received, the loop terminates.
//
do
{
//
// Read a character using the blocking read function. This function
// will not return until a character is available.
//
cThisChar = UARTCharGet(UART0_BASE);
//
// Write the same character using the blocking write function. This
// function will not return until there was space in the FIFO and
// the character is written.
//
UARTCharPut(UART0_BASE, cThisChar);
//
// Stay in the loop until either a CR or LF is received.
//
} while((cThisChar != '\n') && (cThisChar != '\r'));
UARTCharPut(UART0_BASE, '@');
return(0);
}
해보기
- UART 신호(PORTA 0 또는 1 핀)를 오실로 스코프로 읽어보자. 예제
- 로직분석기를 이용하여 UART 신호를 분석해보자.
UARTprintf 추가하기
UARTprintf를 이용하여 화면에 출력 해보자.
프로젝트에 Drag and Drop으로 utils/uartstdio.c 추가
소스에 헤더 추가 #include “utils/uartstdio.h”
UARTprintf 함수의 위치
C:\ti\Tivaware_C_Series_2.1.1.71\utils\uartstdio.c
Serial port setup: 11520 Baud rate
#include "driverlib/pin_map.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
UARTStdioConfig(0, 115200, 16000000);
UARTprintf("Hello World\n");
타이머
특정시간이 되면 인터럽트가 걸려서 인터럽트 핸들러를 실행!
- PWM 만들기
- 특정 주기마다 일을 수행할 때 사용
타이머
Https://gist.github.com/donghee/e5ee186f833d55a5d247
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
int main(void)
{
unsigned long ulPeriod;
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
// Init TIMER0
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
ulPeriod = (SysCtlClockGet() / 10) / 2;
TimerLoadSet(TIMER0_BASE, TIMER_A, ulPeriod -1);
// Interrupt Enable
IntEnable(INT_TIMER0A);
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
IntMasterEnable();
TimerEnable(TIMER0_BASE, TIMER_A);
while(1)
{
}
}
void Timer0IntHandler(void)
{
// Clear the timer interrupt
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Read the current state of the GPIO pin and
// write back the opposite state
if(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2))
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, ~GPIO_PIN_2);
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
}
}
startup.c에 이벤트 핸들러 등록!
수정할 내용
IntDefaultHandler, // Timer 0 subtimer A
를 Timer0IntHandler, 로 수정
extern void __iar_program_start(void);
아래에 extern void Timer0IntHandler(void); 추가
참고: Tiva LaunchPad Workbook
PWM 발생
아날로그 신호를 디지털로 인코딩(?) 하는 방식. 아날로그 신호를 신호의 duty cycle에 모듈레이션 한다.
스위치 파워서플라이, 모터제어, 서보제어, LED제어등에 사용한다.
Timer로 PWM 만들기
기본 타이머 세팅.
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
//TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM);
ulPeriod = 1000;
TimerLoadSet(TIMER0_BASE, TIMER_A, ulPeriod - 1);
//TimerMatchSet(TIMER0_BASE, TIMER_A, dutyCycle); // PWM: dutyCycle is low level width (off dutyCycle)
TimerEnable(TIMER0_BASE, TIMER_A);
PB6 포트에 PWM 세팅하기
필요한 헤더:
#include "driverlib/timer.h"
#include "driverlib/pin_map.h"
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB) ;
GPIOPinConfigure(GPIO_PB6_T0CCP0); // mux T0CCP0 to PB6
GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6); //to set PB6 pin
타이머 세팅
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM);
TimerLoadSet(TIMER0_BASE, TIMER_A, ulPeriod -1);
TimerMatchSet(TIMER0_BASE, TIMER_A, dutyCycle); // PWM: dutyCycle is low level width (off dutyCycle)
TimerEnable(TIMER0_BASE, TIMER_A);
PWM
PWM 실습
해보기
- 로직분석기나 오실로 스코프로 PB6 포트를 찍어보자. (주파수와 duty cycle 관찰)
- PB6에 스피커를 연결하자.
- TR을 연결하여 스피커 소리를 크게 만들어 보자.
- 스피커(4khz, 50%)가 울리도록 한다. // ENABLE, DISABLE
힌트
ulPeriod = 10000;
dutyCycle = 5000; // 50% duty cycle in buzzer
- PF1, PF2, PF3에 PWM을 보내 LED 밝기를 바꾸어 보자.
PWM API
PWMGenConfigure(PWM_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM_BASE, PWM_GEN_0, 400);
PWMPulseWidthSet(PWM_BASE, PWM_OUT_0, 100);
PWMGenEnable(PWM_BASE, PWM_GEN_0);
PWMOutputState(PWM_BASE, (PWM_OUT_0_BIT | PWM_OUT_1_BIT), true);
System tick
간단한 타이머. 카운팅을 하는 용도.
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/systick.h"
volatile unsigned long sysTickUptime=0;
void SysTick_Handler(void)
{
sysTickUptime++;
}
unsigned long millis()
{
return sysTickUptime;
}
int main(void) {
SysTickPeriodSet(SysCtlClockGet()/1000); //set to 1kHZ
SysTickIntRegister(SysTick_Handler);
SysTickIntEnable();
SysTickEnable();
while(1) {}
}
System tick을 사용하여 메인 시간을 관리 해보자
해보기
- watch 기능으로 sysTickUptime 변수 값을 관찰하자.
- 화면에 sysTickUptime을 UART로 출력 해보자.
- SysTickUptime을 이용하여 1초에 timerBoomCounter를 1씩 줄도록 해보자. (시한 폭탄 101)
- 1초가 될때 마다 스피커(4khz, 50%)가 울리도록 한다.
미션!
- 시한 폭탄 디자인하기
- 시한 폭탄 1차 버전 만들기
- 디자인 공유