내가 좋아하는 켈리 클락슨
서두르지 말고 천천히 천천히 시작하자고 넣었다.
이번 챕터는 바로 안 보고 제껴도 된다.
당장 현실성 없는 이야기만 지껄이고 있고,
'그래서 이걸 어따 씀?'이란 생각이 먼저 들 수도 있기 때문.
나중에 필요할 때 돌아와서 확인하면 된다.
그때그때 링크 걸어줄게 Marius.
하염없이 LED만 껌뻑거리는 아두이노를
연산장치로 써보자.
통신은 시리얼 통신으로 할 것이다.
4편 시리얼 콤을 참고하면 디테일을 볼 수 있다.
https://thewanderer.tistory.com/36
기본적으로
아두이노는 C++ 언어를 쓴다.
C가 아냐.
둘이 무슨 차이냐고?
나도 몰라. ㅋ.
C++은 "객체지향" 언어, Object Oriented Programming Language인데
Java처럼 찐객체는 아니고
어셈블리어보다 좀 나은 C언어, 코딩하는 사람보다 하드웨어에 더 친화적인 언어에
기능을 쫌 얹기위해 노력했다. 정도.
그럼 객체는 무엇이냐.
그건 나중에.ㅋㅋㅋ
아두이노에서 객체 개념을 써본 적은 없으니까. 나중에 필요하면 여기에 추가하것음.
- 기본 단위 데이터 (Unit Data)
이건 내가 이름 붙인거라 어디 검색해도 안 나올듯.
정수와 실수.
1과 1.00000000...
정수를 표현하는법:
- int
- int의 변종들
실수를 표현하는법: (2.2 편 참조)
- Double
- Float
- Sorry my mistake
정수
int:
인티져, integer.
https://cdn.arduino.cc/reference/en/language/variables/data-types/int/
글로 구구절절 쓰니 안 읽힌다.
사진 두 장 때려박을테니 참고하세요.
아두이노에서 int는 16비트로 구성되어있다.
0000 0000 0000 0000 = 0
이런 식으로.
값의 범위:
[ ( (2^16) / 2 ) ] ~ [ ( (2^16) / 2 ) - 1 ]
여기서 마이너스가 보이지?
음수를 표현할 수 있다.
맨 앞자리비트 (MSB? 이건 확인하고 적어드림)가
- 0이면 양수
- 1이면 음수
로 표현한다.
그래서 -값의 끝자리 절대값은 +값의 끝자리 절대값보다 1이 더 커.
좌우 반띵했는데, 양의 자리 첫 번째 숫자를 0으로 양보해야되거든.
Unsigned Int
만약에 나는 음수를 쓸 일이 없어.
그래서 16비트를 0 ~ 66,535까지 쓰고싶다.
(예를 들어, 죽빵을 날리고 횟수를 카운트를 할 때 -1번째, -2번째... 같은 숫자는 없으니까)
그러면 unsigned 를 붙이면 됩니다.
unsigned int 값의 범위:
0 ~ 66,535
(2^16 - 1)
Byte < Short = int < Long < Long Long
아두이노는 8비트 프로세서를 탑재했어.
이 말은, 한 번에 8비트씩 묶어서 계산할 수 있다는 말임.
16비트 int 를 8비트 프로세서로 연산하려면?
반띵해서 두 번 연산.
나는 죽빵을 200번 이상 날릴 일이 없다고 치자.
그럼 굳이 16비트의 큰 데이터를 쓸 일이 있을까?
Byte
그럴때 쓰는게 Byte.
8비트, Byte는 unsigned 무조건
0 ~ 255 의 값만 표현할 수 있다.
스타크래프트 1에서 유닛 킬 수가 255를 못 넘기는거 아려나?
(마린이나 드랍십 파일럿은 스타 하도 많이해서 알 듯)
그게 Byte로 저장해서 그려.
Short.
16비트의 정수를 나타낼 때 쓴다.
short: -32,768 ~ 32,767
unsigned short: 0 ~ 65,535
...?
그럼 short랑 int랑 똑같은데 왜 이름이 달러?
대부분의 오~래된 PC는 32비트 프로세서를 탑재하고 있어서
int의 길이가 32비트야.
이런 PC에서 C코딩을 하면 short가 int의 반쪽짜리 데이터가 되는데
아두이노는 후져서 원래 16비트 데이터를 int로 쓸 뿐,
int를 쓰면 "너가 쓰는 기본 길이의 정수를 쓰셈" 이고
short를 쓰면 "너 16비트로 고정" 이라고 지칭하는 정도가 된다.
그냥 그런가보다~ 하면 된다.
Long
죽빵을 unsigned int로 육만 오천번정도 때릴 수 있는데,
나는 십만번을 때리고 싶어.
16비트를 2배 뻥튀기해서 32비트로 저장하자
그게 long
long int:
-2,147,483,648 ~ 2,147,483,647
unsigned long int:
0 ~ 429,496,735
언사인드 인트를 쓰면
무려 4.29억번을 때릴 수 있다.
물론 큰 숫자를 저장할 수 있어서 좋다만,
- 연산을 4번 해야한다는 점 (8비트 프로세서이므로)
- 메모리를 일반 int에 비해 2배를 잡아먹는 점
등으로 진짜 큰 수 아니면 잘 안써.
Long Long
long으로도 부족하다.
32비트 데이터도 부족하다.
64비트로 저장해버리면 된다.
이름은 long long.
unsigned long long int_64bit;
나였으면 unsigned extra long ~~~;
이렇게 했을 것 같은데.
(아님 뻑킹 롱 ㅋㅋ)
unsigned 기준
0 ~ 2^64
까지 표현 할 수 있다.
long long은 센서에서 요구하는정도가 아니면
잘 쓸 일이 없긴 하다.
알아두면 좋다.
예제를 함 보까.
byte flag=0;
byte int_byte = 0; // flag = 0
short int_short = 32766; // flag = 1
int int_int = 32766; // flag = 2
long int_long = 2147483600; // flag = 3
long long int_longlong = 9223372036854775000; // flag = 4
unsigned short usign_int_short = 0; // flag = 5
unsigned int usign_int_int = 0; // flag = 6
unsigned long usign_int_long = 4294967290; // flag = 7
unsigned long long usign_int_longlong = 18446744073709550000;// flag = 8
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Begining...");
}
void loop()
{
switch(flag)
{
case 0: //unsigned byte, 8 bit
if(hasOverflowedYet(int_byte++)) {
Serial.print("byte range: ");
Serial.print(int_byte);
Serial.print(" ~ ");
Serial.println( (byte)(int_byte-1) );
flag++;
}
break;
case 1: //signed short int , 16bit
//Serial.println(int_short);
if(hasOverflowedYet(int_short++)) {
Serial.print("signed short int range: ");
Serial.print(int_short-1); //already overflowed. from positive to negative
Serial.print(" ~ +");
Serial.println( (short)(int_short-2) );//re overflow
flag++;
}
break;
case 2: //signed int , 16bit
if(hasOverflowedYet(int_int++)) {
Serial.print("signed int range: ");
Serial.print(int_int - 1);
Serial.print(" ~ +");
Serial.println( (short)(int_int - 2) );
flag++;
}
break;
case 3: //signed long int , 32bit
if(hasOverflowedYet(int_long++)) {
Serial.print("signed long int range: ");
Serial.print(int_long - 1);
Serial.print(" ~ +");
Serial.println( (long)(int_long - 2) );
flag++;
}
break;
case 4: //signed long long int , 64bit
if(hasOverflowedYet(int_longlong++)) {
Serial.print("signed long long int range: ");
print_ll(int_longlong - 1);
Serial.print(" ~ +");
print_ll( (long long)(int_longlong - 2) );
Serial.println();
flag++;
}
break;
case 5: //unsigned short int , 16nit
if(hasOverflowedYet(usign_int_short++)) {
Serial.print("unsigned short int range: ");
Serial.print(usign_int_short);
Serial.print(" ~ ");
Serial.println( (unsigned short int)(usign_int_short - 1) );
flag++;
}
break;
case 6: //unsigned int , 16bit
if(hasOverflowedYet(usign_int_int++)) {
Serial.print("unsigned int range: ");
Serial.print(usign_int_int);
Serial.print(" ~ ");
Serial.println( (unsigned int)(usign_int_int - 1) );
flag++;
}
break;
case 7: //unsigned long int , 32bit
if(hasOverflowedYet(usign_int_long++)) {
Serial.print("unsigned long int range: ");
Serial.print(usign_int_long);
Serial.print(" ~ ");
Serial.println( (unsigned long int)(usign_int_long - 1) );
flag++;
}
break;
case 8: //unsigned long long int , 64bit
if(hasOverflowedYet(usign_int_longlong++)) {
Serial.print("unsigned long long int range: ");
//Serial.print("%llu", usign_int_longlong);
print_ll(usign_int_longlong);
Serial.print(" ~ ");
//Serial.println("%llu", (unsigned long long int)(usign_int_longlong - 1) );
print_ll( (unsigned long long int)(usign_int_longlong - 1) );
flag++;
}
break;
case 9:
Serial.println("\nDone. Peace out.");
Serial.end();
break;
default://sleep
delay(1000);
}
}
bool hasOverflowedYet(byte datum) { return ( (byte)(datum + 1) < datum); }
bool hasOverflowedYet(short datum) { return ( datum < 0 ); }
bool hasOverflowedYet(int datum) { return ( datum < 0 ); }
bool hasOverflowedYet(long datum) { return ( datum < 0 ); }
bool hasOverflowedYet(long long datum) { return ( datum < 0 ); }
bool hasOverflowedYet(unsigned short datum) { return ( (unsigned short)(datum + 1) < datum); }
bool hasOverflowedYet(unsigned int datum) { return ( (unsigned int)(datum + 1) < datum); }
bool hasOverflowedYet(unsigned long datum) { return ( (unsigned long)(datum + 1) < datum); }
bool hasOverflowedYet(unsigned long long datum) { return ( (unsigned long long)(datum + 1) < datum); }
void print_ll(unsigned long long val) {
if (val < 10) Serial.print((unsigned int) val);
else {
print_ll(val / 10);
Serial.print((int)(val % 10));
}
}
void print_ll(long long val) {
int sign = 1;
if (val < 0) { Serial.print("-"); sign = -1; }
print_ll( (unsigned long long)(val * sign) );
}
(일일이 캐스팅해주고, signed는 저 오버플로 감지 (datum+1 < datum)가 안 먹혀서 결국 0이랑 비교했다)
(signed int의 경우 MSB를 1과 and 연산시키려다가, < 0 이 보기 더 좋을 것 같아서 다시 고쳤다 ㅋㅋ)
(사용자 정의 함수의 경우, 인풋 타입과 내용은 다르지만 함수명은 같게 할 수 있다는 것을 보여주기 위해 저렇게 했다)
(16비트 이상의 숫자는 연산이 너~무 오래걸려서 초기값, init을 Range에 근접하게 주었다)
각 타입별로 1씩 더했다.
막무가내로 막 더했다.
그러면, 아까 위에서 언급한 숫자의 범위 (e.g. 바이트 = 0 ~ 255) 를 넘으면 어떻게되나요?
Unsigned의 경우: 0으로 초기화된다
Signed의 경우: -쪽 제일 낮은 값으로 바뀐다.
엄밀히 말하자면
"오버플로우"가 생긴 것이다.
넘쳐서 흐른다, 오버 플로우.
이런 메모리 오버플로로 서버를 공격해서
정보를 탈취한다든지 시스템을 고장낸다든지 할 수 있다.
아두이노 레벨에서는 단순히 값이 초기화되고 만다.
이를 역이용해서 자동 초기화를 시켜줄 수도 있다.
Marius, 이해 되나???
너무 상위레벨 코드를 막 던져버린건가.
트레일 블레이저 뽑았던 형님이면 알아들을텐데.
아, 애초에 그정도면 데이터타입 설명할 필요가 없겠구나.
궁금한거 있으면 톡 때리셈.
아휴. 실수랑 구조체도 해야되는데
너무 길어진다.
포스팅을 쪼개야겠다.
끝. End of Document
'1.B. Low Level Engineering > Micro Controller Unit' 카테고리의 다른 글
[MCU] P3. 적외선 리모컨 사용하기 (수신, 발신) (0) | 2023.03.21 |
---|---|
[MCU] P2. PWM 신호 만들기 (LED 밝기조절, 피에조, 모터 신호...) (0) | 2023.03.06 |
[MCU] P1. I2C 디스플레이 연결하기 (1602A LCD + Adafruit OLED) (0) | 2023.02.15 |
[MCU] P0. IDE & 드라이버 설치, 예제 (0) | 2023.01.31 |
[MCU] T0. 아두이노 개론. (0) | 2023.01.31 |
Comment(s)