SW 개발자를 위한 하드웨어의 이해 2
: 마이크로 콘트롤러의 이해
http://goo.gl/ztX1n
셋째날 시간표
오전
- 버튼 디바운스
- IC간 통신: UART
- Timer
- ADC
오후
- IC간 통신: I2C
- 시한폭탄 만들기
- 데모
- 회고
UART
UART(Universal Asynchronous Receiver/Transmitter) 이름으로 알 수 있듯이 리시버와 트랜시버가 따로 존재하고 시간 클럭 정보가 없는 비동기식 통신 형태를 말한다.
그럼 클럭 정보 없이 데이터를 어떻게 구분할것인가 궁금할 것 이다.

시작(start bit)과 끝(stop bit) 신호가 있고 그 사이를 일정 시간으로 잘라서 데이터의 비트를 구분한다.
UART는 통신의 속도가 설정가능하고 데이터의 형식(갯수) 설정이 가능하다. UART의 종류로 RS–232, RS–422, RS–485 등이 있다.
참고
- IC간 통신 –from dh8.kr wiki
UART in
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 "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.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() 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 핀/리셋 스위치 왼쪽에 TXD, RXD 테스트 포인트)를 오실로 스코프로 읽어보자. 예제
UARTPrintf 추가하기
UARTPrintf를 이용하여 화면에 출력 해보자.
프로젝트에 Drag and Drop으로 utils/uartstdio.c 추가
소스에 헤더 추가 #include “utils/uartstdio.h”
UARTPrintf 함수의 위치
C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\examples\TexasInstruments\Stellaris\utils
uartstdio.c
Serial port setup: 11520 Baud rate
타이머
특정시간이 되면 인터럽트가 걸려서 인터럽트 핸들러를 실행!
- PWM 만들기
- 특정 주기마다 일을 수행할 때 사용
Control Sequence
- Timer Peripheral Enable
- Timer 세팅
- Timer 간격 로드
- 인터럽트 Enable
타이머
https://gist.github.com/donghee/5183470
#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_32_BIT_PER);
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_1|GPIO_PIN_2|GPIO_PIN_3, 0);
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 4);
}
}
startup.c에 이벤트 핸들러 등록!
수정할 내용
IntDefaultHandler, // Timer 0 subtimer A
를 Timer0IntHandler, 로 수정
extern void __iar_program_start(void);
아래에 extern void Timer0IntHandler(void); 추가
참고: Stellaris LaunchPad Workbook
System tick
간단한 타이머. 카운팅을 하는 용도.
#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 "utils/uartstdio.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을 사용하여 메인 시간을 관리 해보자
해보기
- watch 기능으로 sysTickUptime 변수 값을 관찰하자.
- 화면에 sysTickUptime을 UART로 출력 해보자.
- SysTickUptime을 이용하여 1초에 timerBoomCounter를 1씩 줄어도록 해보자.
미션!
- 시한 폭탄 디자인하기
- 시한 폭탄 1차 버전 만들기
- 디자인 공유
PWM 발생
Timer로 PWM 만들기
기본 타이머 세팅.
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
//TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM);
ulPeriod = (SysCtlClockGet() / 10) / 2;
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 포트를 찍어보자.
- PF1, PF2, PF3에 PWM을 보내 보자.
- PB6에 부저를 연결하자.
- TR을 연결하여 부저 모듈의 소리를 크게 만들어 보자.
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);
해보기
- 1초가 될때 마다 부저가 울리도록 한다. // ENABLE, DISABLE
Analog to Digital Converter
- 아날로그 신호를 디지털로 변환
- 스펙: 분해능(resolution), 샘플링 처리속도
- 처리속도: 입력신호의 최고 주파수 2배이상은 샘플링 해야 입력 신호를 제대로 읽을 수 있다. 그래서 샘플링 처리속도를 아는게 중요. (Nyquist 정리)
- Opamp와 같이 사용하여 신호 흐름을 좋게 하거나, 증폭, 잡음제거할 수 있다.
- 구현방법: 병렬비교 ADC 구현, 계수형 ADC 구현, …
참고:
Analog to Digital Converter
Stellaris LM4F MCU는 2개의 ADC 모듈이 있다. ADC0, ADC1
특징
- 12-bit resolution
- sample rate: 1초당 백만(1MSPS)
- ADC 끝나면 인터럽트나 트리거 발생
ADC code
칩 내부의 온도 센서 읽기.
https://gist.github.com/donghee/5190724
ADC Control Sequence
- ADC sequence를 세팅하기 전에, 사용할 Sample Sequencer레지스터를 비활성화 한다. 우리는 3번.
- 원하는 Sample Sequencer를 고른다.
- 읽을 센서(채널)을 선택하고 ADC가 완료 되었을때 인터럽트 방법 지정
- 1번에서 비활성화한 Sample Sequencer를 다시 활성화!
온도센서를 읽을려면?
- ADC_CTL_TS
다른센서를 읽을려면?
- ADC_CTL_CH0~ADC_CTL_CH11 을 선택하면 해당 포트의 센서를 읽을 수 있다.
해보기
- A11에 빛센서를 연결하여 ADC 값을 읽어 보자.
- ADC_CTL_TS -> ADC_CTL_CH11
- 어두워지면 LED가 켜지도록 만들어 보자.
배선(E:\Dropbox\Camera Uploads\2013–03–19 06.15.00.jpg)
임베디드 소프트웨어의 특징
- 이벤트 중심 프로그램.
- 반응이 일정 시간안에 실행 되도록 보장되어야 한다. (Real Time)
- Delay를 쓰는 폴링 구조는 이벤트에 반응 하기 어렴다.
- State Machine
- RTOS (시간으로 스케쥴링)
이벤트 구조 : State Machine을 Switch/Case로 구성하기
Timer Boom State Machine을 Switch/Case로 구성하기
상태(STATE)
이벤트(SIGNAL)
..
enum BombSignal {
UP_SIG,
DOWN_SIG,
START_SIG,
TICK_SIG,
};
enum BombState {
SETTING_STATE,
TIMING_STATE
};
uint8_t bomb_state;
#define TRANS(target_) (bomb_state = (uint8_t) target_)
void Bomb_init() {
TRANS(SETTING_STATE);
bomb_counter = 10; //default is 10
}
void Bomb_dispatch(BombSignal sig)
{
switch (bomb_state) {
case SETTING_STATE:
switch(sig) {
case UP_SIG:
if (bomb_counter < 60) // MAX
bomb_counter++;
break;
case DOWN_SIG:
if (bomb_counter > 1)
bomb_counter--;
break;
case START_SIG:
TRANS(TIMING_STATE)
break;
}
break;
case TIMING_STATE:
switch(sig) {
case TICK_SIG:
if (is_tick_updatable()) {
make_beep();
bomb_counter--;
UARTPrintf("BOMB COUNTER: %d\n", bomb_counter);
if (bomb_counter == 0)
UARTPrintf("BOMB!");
else
TRANS(TIMING_STATE);
}
break;
}
}
}
상태 머신 구현
Switch-case
typedef enum states { STATE_A, STATE_B, STATE_C, STATE_D } state_type;
state_type StateA(void);
state_type StateB(void);
state_type StateC(void);
state_type StateD(void);
(void) State_Machine(void) {
static state_type state=StateA, last_state=StateA;
switch (state) {
case STATE_A: last_state=state; state = StateA(); break;
case STATE_B: last_state=state; state = StateB(); break;
case STATE_C: last_state=state; state = StateC(); break;
case STATE_D: last_state=state; state = StateD(); break;
default: Panic(last_state, state); break;
}
}
참고: http://www.edn.com/electronics-blogs/embedded-basics/4406821/Function-pointers—Part–3–State-machines
상태 머신 구현
Function pointer
typedef void (*state_handler)(void);
state_handler state;
void Sm_StateA(void);
void Sm_StateB(void);
void Sm_StateC(void);
void Sm_StateD(void);
void Sm_StateA(void) {
// run state code
state = Sm_StateB; // transition
}
void Sm_StateB(void) {
// run state code
state = Sm_StateD; // transition
}
void Sm_StateD(void) {
// run state code
state = Sm_StateC;
}
void Sm_StateD(void) {
// run state code
state = Sm_StateA;
}
(void) State_Machine(void) {
state();
}
RTOS
RealTime (일정한 시간안에는 테스크가 실행 되는것을 보장!)
시간에 따라 등록된 테스크 스케쥴링 예
참고: http://www.edn.com/electronics-blogs/embedded-basics/4404895/Function-pointers—Part–2–Task-Scheduling
Log 만들기
header
typedef enum {
I2C_SERVICE = 0,
ADC_SERVICE = 1,
LOG_SERVICE = 2,
RTC_SERVICE = 3
} system_t;
typedef enum {
NONE_LEVEL = 0,
NOTICE_LEVEL = 1,
WARNING_LEVEL = 2,
ERROR_LEVEL = 3
} logLevel_t;
void Log_init();
void Log(system_t s, logLevel_t level, char* msg);
void Log_withNum(system_t s, logLevel_t level, char* msg, int number);
void Log_setOutputLevel(system_t sys, logLevel_t level);
<!-- void Log_setOutputLevel(ADC_SERVICE, WARNING_LEVEL); -->
// turn on/ turn off log
void Log_globalOn();
void Log_globalOff();
사용 예제
// setup
Log_init();
Log_setOutputLevel(ADC_SERVICE, WARNING_LEVEL);
Log_globalOn();
//usage
Log(ADC_SERVICE, WARNING_LEVEL, "Cannot Read ADC");
//00:00:05, ADC, WARNING, Cannot Read ADC
Log_withNum(ADC_SERVICE, NOTICE_LEVEL, "ADC Value:", 500);
//00:00:06, ADC, NOTICE, ADC Value: 500
해보기:
- UART를 통해서 로그 모듈을 구현해 보자.
I2C
I2C는 필립스사에서 고안한 방식, MCU와 주변장치의 시리얼통신 하는데 사용.
두선 사용. 여러개의 마스터 지원.
I2C 연결
특징, 통신선로 pull-up
I2C 데이터 타임차트
I2C 데이터 포멧
I2C: tmp102
I2C module Control Sequence
- Peripheral Enable
- GPIO Pin muxing
- I2C Clock Setting
- I2C Master/Slave Enable
DS3231 RTC
- Real Time Clock
- 시간, 날짜, 두개의 알람. 현재 온도
- 내부에 internal oscillator
- low power mode: 수명 6~7년
- Datasheet
실습: Time Boom
시간: 2시간
프로젝트 공유:
요구사항
시한폭탄에는 세팅모드와 동작 모드가 있다.
세팅 모드에서는 두개의 버튼을 이용하며, 왼쪽 버튼이 눌리면 폭탄 카운트 시간을 증가
오른쪽 버튼이 눌렸을때 폭탄 카운트 시간이 감소한다.
세번째 버튼을 눌렀을때 세팅모드에서 동작 모드로 전환한다.
동작모드가 되면 정한 시간 후 폭탄이 터진다.
동작모드에서 일정 이상의 빛이나 온도가 변하면 폭탄이 터진다.
참고
LaunchPad Worhop in TI: http://processors.wiki.ti.com/index.php/Getting_Started_with_the_Stellaris_EK-LM4F120XL_LaunchPad_Workshop
DDJ 2003: http://www.drdobbs.com/back-to-basics/184401737?pgno=2
http://www.state-machine.com/quickstart/
Maxfield, Brown. The Definitive Guide to How Computers Do Math
Bebop to the Boolean Boogie: An Unconventional Guide to Electronics