[Tiva] I2CMasterControl()

임베디드/TI 2014. 8. 25. 23:58

 void I2CMasterControl(uint32_t ui32Baseuint32_t ui32Cmd)

 설명 : I2C 통신시 Master의 동작을 설정하는 함수입니다.

 

 uint32_t ui32Base

 I2C 통신을 하게 될 Master의 Base Address를 설정합니다.


 uint32_t ui32Cmd

 I2C 통신을 하게 될 Master가 하게 될 동작을 설정합니다. 이 인자의 경우 다음과 같은 값들을 입력하실 수 있습니다.


I2C_MASTER_CMD_SINGLE_SEND

I2C_MASTER_CMD_SINGLE_RECEIVE

I2C_MASTER_CMD_BURST_SEND_START

I2C_MASTER_CMD_BURST_SEND_CONT

I2C_MASTER_CMD_BURST_SEND_FINISH

I2C_MASTER_CMD_BURST_SEND_ERROR_STOP

I2C_MASTER_CMD_BURST_RECEIVE_START

I2C_MASTER_CMD_BURST_RECEIVE_CONT

I2C_MASTER_CMD_BURST_RECEIVE_FINISH

I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP

I2C_MASTER_CMD_QUICK_COMMAND

I2C_MASTER_CMD_HS_MASTER_CODE_SEND


 이들 중 빈번하게 사옹되는 것으로 다음과 같은 값들이 있습니다.


 I2C_MASTER_CMD_SINGLE_SEND

 Master가 Slave에게 명령어를 전달하는 모드입니다.


 I2C_MASTER_CMD_SINGLE_RECEIVE

 Master가 Slave로부터 명령어를 전달받는 모드입니다.


 I2C_MASTER_CMD_BURST_SEND_START

 Master가 Slave에게 명령어를 전달하는 모드입니다. 위의 I2C_MASTER_CMD_SINGLE_SEND와 달리 바로 명령어를 전달하지 않고 일단 Slave에게 명령어를 전달하겠다는 설정만 합니다.


 I2C_MASTER_CMD_BURST_SEND_CONT

 위의 I2C_MASTER_CMD_BURST_SEND_START를 실행한 후 Slave에게 입력할 명령어를 전달합니다.


 I2C_MASTER_CMD_BURST_SEND_FINISH

 Slave에게 마지막 명령어를 전달한 후 I2C 통신을 종료합니다.


 I2C_MASTER_CMD_BURST_RECEIVE_START

 I2C_MASTER_CMD_BURST_RECEIVE_CONT

 I2C_MASTER_CMD_BURST_RECEIVE_FINISH


 Master가 Slave로부터 명령어를 전달받는 모드로 SINGLE모드와 BURST 모드의 차이는 위의 SEND와 유사합니다.


다음은 SINGLE 모드와 BURST 모드로 I2C통신시 파형을 나타냅니다.


SINGLE_SEND


ROM_I2CMasterSlaveAddrSet(I2C0_BASE, 0xE0 / 2, false);

ROM_I2CMasterDataPut(I2C0_BASE, 0x7E);

ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);


while (ROM_I2CMasterBusy(I2C0_BASE)) {


}




BURST_SEND


ROM_I2CMasterSlaveAddrSet(I2C0_BASE, 0xE0 / 2, true);

ROM_I2CMasterDataPut(I2C0_BASE, 0x02);

ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);


while (ROM_I2CMasterBusy(I2C0_BASE)) {


}


ROM_I2CMasterDataPut(I2C0_BASE, 0xFF);

ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);


while (ROM_I2CMasterBusy(I2C0_BASE)) {


}









300x250

[Tiva] tm4c 시리즈 MCU로 TextLCD(CLCD) 동작

임베디드/TI 2014. 8. 21. 02:30

 MCU로 프로그램을 실행할 때 자신이 확인하고 싶은 값을 보기 위해서 주로 UART통신 또는 TextLCD에 값을 띄워보는 방법이 있습니다.

 MCU가 컴퓨터와 항상 연결되어 있을 때엔 당연히 UART를 통해 원하는 값을 모니터에 출력하는 것이 가능합니다. 그러나 일반적으로 MCU를 응용하시는 분들께서는 컴퓨터와 연결하여 사용하는 경우는 많지 않으실 겁니다. 이럴 때 가장 좋은 방법이 TextLCD를 활용하는 것이지요.


 자신이 원하는 값이 숫자와 같은 수치라면 FND를 사용하는 방법또한 있습니다만 FND의 경우 원하는 값을 얻기애 연결해야 하는 핀의 수가 TextLCD보다 상대적으로 많이 사용되는 편이기도 합니다. 심지어 TextLCD는 텍스트 값 또한 출력이 가능하기 때문에 간단한 수치를 확인하는 목적 이상을 원한다면 TextLCD를 사용하는 쪽이 바람직할 듯 합니다.


 가장 많이 사용되는 TextLCD는 2 X 16일 겁니다. 인터넷 상에서도 Atmega128을 기반으로 한 소스코드도 쉽게 구할 수 있어 생각보다 번거롭지 않게 TextLCD에 값을 출력하실 수 있을 것입니다.


 아래의 코드는 Atmega128에서 많이 사용되는 코드를 TivaWare를 활용하는 MCU에서 바로 사용할 수 있게 작성되었습니다.



참고로 회로는 다음과 같이 연결하시면 되겠습니다.

RS    = A6,    RW    = GND,    EN    = A7

데이터0~7    = B0 ~ B7


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
 
void EnablePulse(void) {
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_PIN_7);
    SysCtlDelay(SysCtlClockGet() / 1200);
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, 0);
}
 
void sendLCDcommand(unsigned char command) {
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_6, 0);
    GPIOPinWrite(GPIO_PORTB_BASE, 0xFF, command);
    EnablePulse();
    SysCtlDelay(SysCtlClockGet() / 1200);
}
 
void InitLcd(void) {
    SysCtlDelay(SysCtlClockGet() / 10);
    sendLCDcommand(0x38);
    sendLCDcommand(0x0C);
    sendLCDcommand(0x01);
    SysCtlDelay(SysCtlClockGet() / 1200);
}
 
void sendLCDdata(unsigned char data) {
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_6, GPIO_PIN_6);
    GPIOPinWrite(GPIO_PORTB_BASE, 0xFF, data);
    EnablePulse();
    SysCtlDelay(SysCtlClockGet() / 1200);
 
}
 
void displayStr(char *str){
    while(*str){
        sendLCDdata(*str);
        str++;
    }
}
 
void setLocate(int x, int y){
    unsigned char addr;
    if(x==0)
        addr = 0x80+y;
    else
        addr = 0xC0+y;
    sendLCDcommand(addr);
}
 
void setLcdShift(char a){
    unsigned char shift;
    //0 == Left    1 == Right
    if(a==0)
        shift = 0x18;
    else if(a==1)
        shift = 0x1C;
    sendLCDcommand(shift);
    SysCtlDelay(SysCtlClockGet() / 10);
}
 
int main(void) {
 
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
 
    GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7);
    GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,
            GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4
                    | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
 
    GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7, 0x00);
    GPIOPinWrite(GPIO_PORTB_BASE,
            GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4
                    | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7, 0x00);
 
 
    InitLcd();
    setLocate(0,1);
    displayStr("Hello, World!");
    setLocate(1,0);
    displayStr("http://elecs.tistory.com/");
 
    while (1) {
        setLcdShift(0);
    }
}
 


해당 코드를 런치패드에 실행시키면 CLCD에 다음과 같은 값이 출력됨을 확인하실 수 있습니다.



300x250

[Tiva]startup_ccs.c 파일에 외부 인터럽트 추가하기

임베디드/TI 2014. 8. 9. 22:33

 Tiva C 기반의 MCU로 외부 인터럽트를 설정할 때 일반적으로 메인함수에서 IntRegister()와 같이 해당 인터럽트가 발생하였을 때 실행할 함수를 직접 설정하는 방법이 있습니다. 이 방법 이외에 인터럽트 함수를 등록할 수 있는 방법이 하나 존재합니다!

 프로젝트 내부에 있는 startup_ccs.c 파일에 인터럽트 함수명을 설정해두면 main() 함수에서 정의를 하지 않아도 해당 함수를 인터럽트로 바로 사용하실 수 있습니다.



프로젝트에서 startup_ccs.c 파일을 열어보면 다음과 같은 함수들을 확인하실 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdint.h>
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
 
extern void Timer0IntHandler(void);
extern void Timer1IntHandler(void);
 
#pragma DATA_SECTION(g_pfnVectors, ".intvecs")
void (* const g_pfnVectors[])(void) =
{
    (void (*)(void))((uint32_t)&__STACK_TOP),
                                            // The initial stack pointer
    ResetISR,                               // The reset handler
    NmiSR,                                  // The NMI handler
    FaultISR,                               // The hard fault handler
    //--------코드 생략--------//
    Timer0IntHandler,                       // Timer 0 subtimer A
    IntDefaultHandler,                      // Timer 0 subtimer B
    Timer1IntHandler,                       // Timer 1 subtimer A
    IntDefaultHandler,                      // Timer 1 subtimer B
    IntDefaultHandler,                      // Timer 2 subtimer A
    IntDefaultHandler,                      // Timer 2 subtimer B
    //--------코드 생략--------//
    IntDefaultHandler,                      // PWM 1 Generator 0
    IntDefaultHandler,                      // PWM 1 Generator 1
    IntDefaultHandler,                      // PWM 1 Generator 2
    IntDefaultHandler,                      // PWM 1 Generator 3
    IntDefaultHandler                       // PWM 1 Fault
};


위의 함수에서 extern void는 사용하게 될 인터럽트의 명칭들입니다.

그리고 아래는 등록하고자 하는 코드들의 모임입니다.


extern void로 Timer0IntHandler()와 Time1IntHandler() 함수를 다음과 같이 입력한 후

아래 주석 부분에 자신이 해당 코드를 실행하고자 하는 인터럽트 부분에 함수명을 적어주면 됩니다.


이 과정을 거치신 후 프로그램을 실행하시면 인터럽트가 원활하게 동작되는 것을 확인하실 수 있습니다.

300x250

진동모터 연결선이 빠졌을 때 대처법

임베디드 2014. 8. 8. 23:33

 열심히 작동하던 진동모터가 지속되던 진동으로 인해 연결선 끝부분이 쏙 하고 빠져버리는 대참사가 벌어졌습니다.

 처음에는 어떻게 해야할지 잠시 당황했으나 다시 대처 방법을 공부한 후 이를 고쳐보기로 하였습니다.


보시는 대로 진동모터의 끝부분이 참혹하게 빠져버렸습니다.

그나미 끝 부분에 빠져나간 선의 일부가 약간이나마 남아서 다시 연결 할 수 있을 듯 합니다.



우선 잘려나간 부분의 끝을 칼로 살살 도려냅니다.

계속 까다보면 잘려나간 틈으로 전선이 살짝 남는 것을 보실 수 있습니다.



까진 부분에 잽싸게 선을 연결한 후 납으로 때워버립니다.



드디어 성공!



끝부분이 다시 떨어지지 않도록 글루건으로 완전히 고정시켜 버립니다.



조금 멋이 떨어지지만 아무튼 동작이 잘 되는 것으로 보아 성공한 듯 합니다.

다음은 이러한 참사가 다시 벌어지지 않도록 진동모터 끝부분을 확실히 마무리 하는 작업에 들어갑니다.



진동모터를 고정시킬 PCB판을 잘라냅니다.

휘어지는 PCB판을 사용하시면 좀 더 유연하게 사용하실 수 있습니다.



PCB판에 진동모터 끝부분을 납땜합니다.



이로서 진동모터의 짫은 선 문제는 해결되었습니다.


글루건으로 납땜한 부분을 마무리하면 어떤 상황에서도 진동모터를 무사히(?) 사용하실 수 있습니다!

300x250

파워서플라이로 모듈 사용시 주의사항

임베디드 2014. 8. 2. 12:41

 매번 파워서플라이를 만지면서도 아차 하는 실수로 모듈 하나가 타버리는 실수를 해버렸습니다. 파워서플라이를 옮기던 중 눈금이 돌아가 있던 듯 한데 수치를 보지 않고 그냥 모듈에 연결해 버린것이 화근이었던 듯 합니다.




 이렇게 모듈이 타버려 두 번 다시 사용할 수 없는 상황이 되어버린다면 정말 마음아프지 않을 수 없습니다. 특히 고가격의 모듈이라면 스트레스가 상당할 겁니다.


 임베디드 분야를 공부하시는 분들이라면 매우 기본적인 상식이기도 하지만 순간의 실수로 비싼 모듈을 망가뜨릴 위험이 있습니다. 원숭이도 나무에서 떨어질 때가 있듯이 말이지요. 파워서플라이를 사용할 때 마다 다음과 같은 수칙을 확인하고 사용하도록 합시다.


 1. 파워서플라이를 켜기 전 가급적이면 모듈과 분리시킨 후 키도록 합니다.

 2. 모듈에 파워서플라이를 연결하기 전, 파워서플라이가 해당 모듈의 정격전압에 맞는지 확인합니다.

 3. 모듈에서 이상이 발생시 즉시 파워서플라이로부터 모듈을 떼어내 버립니다.

 4. 파워서플라이를 사용 후 반드시 전원을 끄도록 합니다.

300x250

[Tiva] I2CMasterSlaveAddrSet()

임베디드/TI 2014. 8. 1. 21:02

I2CMasterSlaveAddrSet(uint32_t ui32Base, uint8_t ui8SlaveAddr, bool bReceive)


 이 함수는 MCU가 I2C 통신을 개시하기 전에 통신할 Slave의 주소값을 설정한 후 통신을 개시할 때 쓰는 함수이다.


uint32_t ui32Base

첫 번째 인자는 I2C 통신을 하게 될 Master 의 Base Address이다.


uint8_t ui8SlaveAddr

두 번째 인자는 I2C 통신시 전달할 Slave의 Address이다. 이 때 Slave의 Address값은 7bit값이어야 한다.

시중에 있는 I2C 통신의 DataSheet에서 설정되어 있는 주소는 일반적으로 8비트로 구성되어있는데

이는 해당 8비트 주소의 오른쪽 끝부분이 R/S를 설정하는 부분이기 때문이다.

DataSheet에 설정된 Address를 사용할 경우 실제 Slave의 Address를 오른쪽으로 비트이동을 하면 된다. 간단하게 ui8SlaveAddr / 2를 하면 쉽게 적용할 수 있다.


bool bReceive

세 번째 인자는 I2C 통신시 Slave에게 하고자 하는 R/S를 설정합니다.

true - R(Slave로부터 데이터를 읽어옵니다.)

false - S(Slave에 값을 저장합니다.)



다음은 해당 함수를 적용한 소스코드와 결과이다.


ROM_I2CMasterSlaveAddrSet(I2C0_BASE, 0xE0 / 2, false);

ROM_I2CMasterDataPut(I2C0_BASE, 0x7E);

ROM_I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);


while (ROM_I2CMasterBusy(I2C0_BASE)) {


}




① : SLAVE의 ADDRESS 주소

② : R/S

③ : SLAVE의 ACK

④ : R의 경우 = Master가 Slave로부터 전달되는 값을 읽음

S의 경우 = Master가 Slave에게 값을 전달함

⑤ : Master의 ACK


300x250

[Tiva] TM4C의 I2C 통신 파형 보는 방법

임베디드/TI 2014. 7. 28. 21:50

 I2C 통신을 처음 입문하는 분들은 종종 파형의 원리를 깨닫지 못해 많이 해메는 경우가 많습니다. 코드 상으로는 감이 오지 않는 I2C 통신을 오실로스코프를 통해 파형을 확인해보면 그 원리를 쉽게 알 수 있습니다.




 위 사진은 오실로스코프에 찍은 I2C 통신중인 파형이고 노란색은 SDA, 파란색은 SCL입니다. 아래는 I2C 회로를 설계하는데 참고할 수 있는 데이터시트의 파형입니다.


 위 사진의 파형을 쉽게 구분할 수 있게 7단계로 나누었고 그에 대한 각각의 부분은 다음과 같이 설명할 수 있습니다.


① : START

SCL이 HIGH 상태일 때 SDA가 HIGH에서  LOW로 변하는 것으로 I2C 통신을 개시합니다.


② : Slave address

Master가 통신하기 원하는 Salve address 주소를 전송합니다. 1번째부터 7번째 SCL의 edge 클록에서의 7비트가 Slave Address 주소입니다.


③ : R/S

Master가 Salve 에서 할 동작을 설정합니다. HIGH일 때는 Slave로부터 데이터를 전달받고 LOW일 때는 Slave에 데이터를 전송합니다.


④ : ACK

Slave가 Master에게 데이터를 제대로 전송하였음을 확인하는 신호입니다. Slave가 데이터를 완벽히 수신하였을 때 SDA의 값을 LOW로 끌어내려 SCL에 edge 클록이 들어올 때 까지 고정시키다가 값이 제대로 전달되면 SDA를 놓아줍니다다.


⑤ : DATA

R/S에서 설정 대로 데이터를 보내거나 받는다. 데이터 값은 8비트입니다.


⑥ : ACK

Master가 Slave에게 보내는 ACK 신호입니다.


⑦ : STOP

SCL이 HIGH 상태일 때 SDA가 LOW에서 HIGH로 변하는 것으로 I2C 통신을 종료합니다.


300x250

MCU 입력으로 설정한 핀의 값이 일정하지 않을 때(Floating)

임베디드/MCU 2014. 7. 27. 16:40

 MCU를 설계할 때 많은 빈도로 사용되는 기능 중 하나는 아마 GPIO(General Purpose Input Output)일 겁니다. 거의 모든 MCU 설계의 기본이기도 하지요.


 MCU를 처음으로 설계하는 분들 중 MCU의 핀 하나를 입력으로 설정하여 입력을 받는게 받아들이는 값이 지멋대로 튀어 원치않는 값을 받는 경우를 경험하신 분들이 계실거라 생각합니다. 분명 VCC에 걸리면 HIGH 상태가 되고 GND에 연결하면 LOW 상태가 확실히 걸리는데 유독 아무런 값도 걸지 않은 상태에서는 값이 자기 맘대로 튀어 당황하신 분들도 계실거라 생각합니다.


 이와 같은 현상을 플로팅(Floating)이라 하며, 해당 핀의 상태가 HIGH도 아니고 LOW도 아닌 값을 가지고 있는 상황이 되는 것입니다. 즉, 확실한 값을 가지고 있지 않기 때문에 이 상태에서 입력값을 받으면 MCU는 계속해서 이상한 값을 가지게 되는 것입니다.


 이러한 상태를 해결하는 방법으로 풀업(pull-up)과 풀다운(pull-down) 방식이 있습니다.

 pull-up방식은 해당 입력핀의 연결라인 중간에 저항을 연결하고 해당 저항이 HIGH상태(주로 VCC)에 연결되어 있는 상황을 말합니다. 이렇게 회로를 설계하면 해당 입력핀의 값은 HIGH를 유지하다가 전압이 0으로 내려가면 LOW상태가 되며 값이 플로팅 상태 때보다 매우 안정적인 값을 얻으실 수 있습니다. pull-up 방식은 주로 I2C 통신에서 많이 사용되는 회로 입니다.



 위 그림은 I2C회로의 간략한 회로입니다. I2C회로는 각 기기들과 2개의 선으로 연결되어 있는데 해당 2개의 선인 SDA와 SCL은 다른 기기들과 그냥 연결하게 되면 Open Drain 상태가 되여 마스터의 입력이 HIGH가 되어도 Slave는 LOW값을 받아들이는 상태만 지속됩니다. 이러한 두 선에 pull-up 회로를 연결하게 되면 보통때에 HIGH 상태를 유지하던 회로가 MASTER에 의해 LOW 상태가 되면 똑같이 LOW 상태를 보이며 회로가 원하는 디래 동작을 할 수 있게 됩니다.


 pull-down 방식은 위해서 설명한 pull-up 방식과는 반대로 저항의 끝에 GND를 연결하여 LOW 상태를 유지하게 해줍니다. 이 상태에서 HIGH 값이 들어오게 되면 입력핀에서도 HIGH 값을 받을 수 있게 되는 것입니다. 




 위 그림은 pull-down 방식이 적용된 MCU의 상황입니다. 저항과 GND를 연결한 상태로 값이 들어오고 있지 않는 상황에서는 LOW 상태를 유지하다가 VCC나 다른 장치로부터 값이 들어오게 되면 MCU가 HIGH 상태를 인식할 수 있게 되는 것이지요.


 위와 같은 회로를 설계에 참고하신다면 입력값의 Floating 상태를 적절하게 방지하실 수 있을 것으로 보입니다.



좀 더 자세한 내용을 알고 싶으신 분들께 관련 내용이 잘 정리되어 있는 블로그를 알려드리겠습니다.

http://minyoub.tistory.com/5

300x250

[Tiva] TM4C 시리즈에서 외부인터럽트 구현

임베디드/TI 2014. 7. 26. 22:09

 MCU를 활용하는 사람이면 거의 대다수가 맨 처음 프로그램을 구동할 때GPIO(General Purpose Input Output)를 구현하여 동작을 확인하는 경험을 해보셨을 것이라 생각합니다. TI에서 제공하는 Tiva C 시리즈인 TM4C MCU 시리즈 또한 이를 잘 구현해 놓았지요.


 ATmega시리즈로 순식간에 인터럽트를 설정하시던 분들도 Tiva 시리즈를 다룰 때 다소 해메시는 분들도 계실것이라 생각합니다. 하지만 그렇다고 처음부터 겁을 먹을 필요는 없습니다. 코드로 표현하는 방식이 다를 뿐 다른 MCU들 처럼 구현할 수 있는 기능은 똑같으니 말이지요.


 이번에는 MCU 프로그램의 필수 요소라 할 수 있는 Interrupt를 구현해보도록 하겠습니다. 인터럽트와 관련이 있는 부분은 직접 주석으로 적어두었으므로 하나씩 읽으면서 익혀두셨으면 합니다.

 

 단, 처음에 Input으로 설정한 핀을 그대로 입력하고자 하는 곳에 꽃게 되면 플로팅 상태가 되기 때문에 입력값이 들쭉날쭉 하는 현상이 벌어집니다. 플로팅 상태의 입력을 안정하게 하는 방법은 다음 포스트를 참고해주시길 바랍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
 
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif
 
    //자신이 설정하고자 하는 인터럽트 핸들러를 설정한다.
void INT_IntHandler(void) {
    ROM_IntDisable(INT_GPIOA);
    GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    UARTprintf("Hello, Interrput!\n");
    ROM_IntEnable(INT_GPIOA);
}
 
 
void ConfigureUART(void) {
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
 
    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
 
    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
 
    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
 
    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}
 
int main(void) {
 
    //
    // Enable lazy stacking for interrupt handlers. This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();
 
    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(    SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
 
    //INTERRUPT를 사용할 GPIO핀을 활성화 시킨다.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
 
    // Initialize the UART.
    ConfigureUART();
 
    //인터럽트를 받아들일 핀을 Input으로 설정한다.
    ROM_GPIOPinTypeGPIOInput(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    //인터럽트되는 핀의 입력 형태를 선택한다. LOW에서 HIGH 상태로 넘어갈 때 인터럽트를 발생시키고자 하면
    //GPIO_RISING_EDGE를 선택한다.
    ROM_GPIOIntTypeSet(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6, GPIO_RISING_EDGE);
 
    //PORTA의 GPIO 인터럽트를 허용한다.
    GPIOIntEnable(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    //PORTA에서 인터럽트가 발생하였을 때 실행할 함수를 설정한다.
    GPIOIntRegister(GPIO_PORTA_BASE, INT_IntHandler);
 
    //인터럽트가 시작되기 전 Clear 시켜준다.(인터럽트 관련 레지스터를 0으로 만든다.)
    GPIOIntClear(GPIO_PORTA_BASE, GPIO_PIN_4 | GPIO_PIN_6);
 
    ROM_IntEnable(INT_GPIOA);
 
    while (1) {
    }
}


4번 핀과 6번 핀에 HIGH_EDGE 입력이 들어오면 mcu에서 인터럽트가 발생하여 설정된 함수를 실행한다.



300x250

[Tiva] TM4C 시리즈에서 타이머 구현

임베디드/TI 2014. 7. 26. 13:53

 MCU를 다룰 때 타이머의 가장 인상적인 점이라면 개발자가 원하는 주기 마다 인터럽트를 발생시켜 구현하고자 하는 동작을 만들 수 있다는 점이지요.

 TM4C에서 타이머의 가장 큰 특징은 1개의 TIMER를 A와 B로 나누어서 사용할 수 있다는 점입니다. 본 예제에서는 각 타이머의 A만을 다루나 A를 B로 바꾸어서 쓰거나 B를 추가 하는 것 만으로도 동작이 되는 것을 보실 수 있습니다.

 2개의 타이머 인터럽트를 사용하여 1초씩 카운트 하는 프로그램을 구성해 보았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
 
uint32_t count=0, count1=0;
 
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif
 
    //TIMER0을 설정하는 인터럽트 핸들러
void Timer0IntHandler(void) {
 
    //
    // Clear the timer interrupt.
    //
    ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
 
    count++;
    if(count>=100)
        count=0;
 
    ROM_IntMasterDisable();
    UARTprintf("\r%2d : %2d",count1, count);
    ROM_IntMasterEnable();
}
 
    //TIMER1을 설정하는 인터럽트 핸들러
void Timer1IntHandler(void) {
 
    //
    // Clear the timer interrupt.
    //
    ROM_TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
 
    count1++;
    if(count1>=60)
        count1=0;
 
    ROM_IntMasterDisable();
    UARTprintf("\r%2d : %2d",count1, count);
    ROM_IntMasterEnable();
}
 
    //UART 출력을 설정한다.
void ConfigureUART(void) {
    //
    // Enable the GPIO Peripheral used by the UART.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
 
    //
    // Enable UART0
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
 
    //
    // Configure GPIO Pins for UART mode.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
 
    //
    // Use the internal 16MHz oscillator as the UART clock source.
    //
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
 
    //
    // Initialize the UART for console I/O.
    //
    UARTStdioConfig(0, 115200, 16000000);
}
 
int main(void) {
    //
    // Enable lazy stacking for interrupt handlers.  This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of
    // extra stack usage.
    //
    ROM_FPULazyStackingEnable();
 
    //
    // Set the clocking to run directly from the crystal.
    //
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
    SYSCTL_XTAL_16MHZ);
 
    //
    // Initialize the UART and write status.
    //
    ConfigureUART();
 
    //
    // TIMER0과 TIMER1이 동작할 수 있도록 설정해준다.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
 
    //
    // MCU가 인터럽트를 사용할 수 있도록 설정해준다.
    //
    ROM_IntMasterEnable();
 
    //
    // 타이머의 동작 방식을 설정해준다. 매 주기마다 동작할 수 있도록 TIMER_CFG_PERIODIC을 설정한다.
    //
    ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
    ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);
 
    //
    // TIMERA의 주기를 설정한다. ROM_SysCtlClockGet()의 값은 MCU의 주파수를 뜻하며 1초당 동작수를 말한다.
    //
    ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet() / 100);
    ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, ROM_SysCtlClockGet() / 1);
 
    //
    // 각 타이머의 인터럽트를 사용할 수 있도록 설정한다.
    //
    ROM_IntEnable(INT_TIMER0A);
    ROM_IntEnable(INT_TIMER1A);
 
    //
    // 설정한 타이머의 인터럽트가 동작할 수 있도록 허용한다.
    //
    ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    ROM_TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
 
    //
    // 타이머를 동작시킨다.
    //
    ROM_TimerEnable(TIMER0_BASE, TIMER_A);
    ROM_TimerEnable(TIMER1_BASE, TIMER_A);
 
    //
    // Loop forever while the timers run.
    //
    while (1) {
    }
}
 




300x250

mcu를 활용한 진동모터 회로 설계

임베디드/MCU 2014. 7. 25. 23:52


 mcu의 출력을 직접 진동모터에 연결해보니 진동모터에 걸리는 전압이 급격히 낮아지면서 진동모터가 전혀 동작을 하지 않는 것을 깨닫고 방법을 생각하던 중 문득 트랜지스터를 사용하는 방법을 깨닫게 되었습니다.

 전자회로에 대한 기본 베이스가 약하신 분들의 경우 직접 설계를 하는 것이 약간은 어려울 것이라 생각합니다. 대략 아래와 같이 설계해본 결과 진동모터가 수월하게 작동하는 것을 확인할 수 있었습니다.





 대략 회로를 설명드리자면 MCU에서 OUTPUT으로 설정한 핀을 트랜지스터의 Base에 연결하고 진동모터를 Collector에 연결한 후 Emitter는 GND로 연결한 상황입니다.

 Output 단자의 출력이 0일 경우 Collector에는 전류가 흐르지 않습니다.

 Output 단자의 출력이 1일 경우 Collector에는 전류가 흐르게 되어 진동모터가 동작을 하게 됩니다.

 약간은 단순한 회로이지만 다른 MCU의 전압으로는 동작이 곤란한 회로에 활용할 수 있을 것입니다.

300x250

I2C(TWI) 통신

임베디드/MCU 2014. 7. 24. 16:12

 오랜만에 MCU를 다루게 되면서 처음으로 I2C 통신을 사용하는 부품을 쓰는 일이 생겨 무려 3일동안 I2C에 대해 연구를 해보았습니다. 맨 처음엔 TivaWare에 들어있던 I2C 예제를 복붙해서 그냥 빌드시켜 프로그램을 실행해 보았는데 프로그램 구동 자체가 되지 않는 것을 확인하고는 상당히 시간을 많이 들여 공부해야 되겠다는 생각이 들더군요.

 직접 오실로스코프를 사용하여 화면에 나오는 값들을 일일해 측정하며 I2C 통신의 동작원리를 이해하였고 소스코드도 일일히 하나씩 써 가며 파형이 어떻게 달라지는지를 확인한 결과 드디어 센서가 정상적으로 동작함을 확인할 수 있었습니다.

 이후에 I2C를 공부하게 될 사람들에게 조금이나마 도움을 드리고자 I2C 통신에 처음으로 입문하시는 분들께 큰 힘이 되기를 바랍니다.


 I2C는 Inter Integrated Cricuit의 줄임말로 TWI(Two Wire Interface)라고도 불립니다. I2C 통신의 가장 큰 장점은 바로 단 2가닥의 선으로 여러 대의 기기와 연결하여 통신이 가능하다는 점입니다.



 위 그림에서 보았을 때 I2C 통신은 2종류의 주체로 나눌 수 있습니다. 통신의 중심이 되는 Master와 통신을 하고자 하는 주변 기기를 Slave로 설정할 수 있습니다. 일반적으로 mcu 1대와 여러대의 I2C 통신을 지원하는 기기와 연결할 경우 Master 1대와 Slave 여러대로 통신이 가능합니다. 물론 Master는 2대 이상도 가능하다.


 I2C에서 사용되는 두 선은 SDA와 SCL로 구성되어 있습니다. 통신이 개시되었을 때 SDA는 데이터를 주고 받는 역할을 하고 SCL은 동기화를 위한 CLOCK 역할을 합니다. 여기서 Master와 Slave 각각에 SDA와 SCL을 바로 연결해주기만 하면 되는 것이죠. 그러나 이러한 회로만으로는 오픈 드레인(회로에 + 전압이 걸리지 않는 상황) 상태가 되기 때문에 SDA와 SCL 회로에 풀업 저항(Pull-up resistor)을 달아 전압을 공급해 줘야 합니다. 풀업 저항에 대해 자세히 알고 싶으신 분은 아래 블로그를 참조해 주시길 바랍니다.


풀업(pull-up) 풀다운(pull-down) 저항 1

http://air802.tistory.com/2



 I2C 통신이 이루어지지 않고 있는 상황일 때에는 SDA와 SCL은 High 상태를 유지합니다. Master가 I2C 통신을 개시하게 될 때 SDA가 Low 상태로 내려가게 됩니다. 이 때 SCL은 I2C 통신이 시작되었음을 인지하고 클럭 역할을 하기 시작합니다.

 I2C 통신이 완료되었을 때 Master는 I2C 통신을 종료하게 됩니다. SCL이 HIGH 상태로 돌아왔을 때 SDA가 Low에서 High로 변하게 되면 I2C 통신이 완료됩니다.






 I2C Slave는 각각 자신의 고유 주소를 가지고 있습니다. 주소는 총 7비트로 구성되어 있으며 Master에서 맨 처음 통신하기를 원하는 Slave의 주소를 SDA를 통해 전달하면 해당 주소의 Slave는 Master에게 주소를 수신하였다는 ACK를 Low 상태로 끌어내려 마스터에게 발신합니다. 이후 Master는 Slave에게 데이터를 전송할 수 있게 됩니다. 이 때 데이터는 한 번에 8비트씩 여러번 전송이 가능합니다.




 위에서 Slave address가 모두 전송된 뒤 R/S 부분을 보실 수 있습니다. 이는 해당 Slave의 레지스터를 읽거나 쓸건지의 여부를 물어보는 레지스터로 R의 경우 SDA가 High 상태로 Slave에게 Data를 전달받을 수 있으며 S의 경우 SDA가 Low 상태로 Slave에게 Data를 전송할 수 있습니다.

 


참고자료 : http://mintnlatte.tistory.com/201

300x250