SW 개발자를 위한 하드웨어의 이해 2
: 마이크로 콘트롤러의 이해



http://goo.gl/ztX1n


셋째날 시간표

오전

오후


UART

UART(Universal Asynchronous Receiver/Transmitter) 이름으로 알 수 있듯이 리시버와 트랜시버가 따로 존재하고 시간 클럭 정보가 없는 비동기식 통신 형태를 말한다.

그럼 클럭 정보 없이 데이터를 어떻게 구분할것인가 궁금할 것 이다.

시작(start bit)과 끝(stop bit) 신호가 있고 그 사이를 일정 시간으로 잘라서 데이터의 비트를 구분한다.

UART는 통신의 속도가 설정가능하고 데이터의 형식(갯수) 설정이 가능하다. UART의 종류로 RS–232, RS–422, RS–485 등이 있다.

참고


UART in

UART module Control Sequence

API

.

#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 함수의 위치

C:\Program Files (x86)\IAR Systems\Embedded Workbench 6.5\arm\examples\TexasInstruments\Stellaris\utils

uartstdio.c

Serial port setup: 11520 Baud rate


타이머

특정시간이 되면 인터럽트가 걸려서 인터럽트 핸들러를 실행!

Control Sequence


타이머

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을 사용하여 메인 시간을 관리 해보자

해보기


미션!


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 실습

해보기


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);

해보기


Analog to Digital Converter

참고:


Analog to Digital Converter

Stellaris LM4F MCU는 2개의 ADC 모듈이 있다. ADC0, ADC1

특징


ADC code

칩 내부의 온도 센서 읽기.

https://gist.github.com/donghee/5190724

ADC Control Sequence

  1. ADC sequence를 세팅하기 전에, 사용할 Sample Sequencer레지스터를 비활성화 한다. 우리는 3번.
  2. 원하는 Sample Sequencer를 고른다.
  3. 읽을 센서(채널)을 선택하고 ADC가 완료 되었을때 인터럽트 방법 지정
  4. 1번에서 비활성화한 Sample Sequencer를 다시 활성화!

온도센서를 읽을려면?

다른센서를 읽을려면?

해보기

배선(E:\Dropbox\Camera Uploads\2013–03–19 06.15.00.jpg)

코드


임베디드 소프트웨어의 특징


이벤트 구조 : State Machine을 Switch/Case로 구성하기

..

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

해보기:


I2C

I2C는 필립스사에서 고안한 방식, MCU와 주변장치의 시리얼통신 하는데 사용.

두선 사용. 여러개의 마스터 지원.


I2C 연결

특징, 통신선로 pull-up


I2C 데이터 타임차트


I2C 데이터 포멧


I2C: tmp102

코드

I2C module Control Sequence


DS3231 RTC

코드


실습: Time Boom


참고