지난 글에서 교차개발환경 준비하고

부트로더, 커널 소스를 옮겨놨으니 이걸로 빌드를 하자

 

 

mywork 디렉토리에서 bootloader.tar.gz를 풀고 들어가면 이런식으로 나온다.

 

 

부트로더로 U-boot를 쓰는데 

이전 글에 환경변수로 ARCH, CROSS_COMPILE 등록했고

 

U-Boot 빌드 과정은 이렇다고한다.

1. 부트로더 초기화

 - make clean

 - make distclean : 기존의 오브젝트와 설정들을 삭제하고, 초기상태로 만듬

2. 설정

 - make menuconfig : Kconfig들을 참조해 설정 정보들이 GUI 화면으로 나오고 설정한 결과가 .config로 나온다.

 - make Kconfig

 - make defconfig : 타겟 플랫폼에 맞게 빌드하려면 설정할게 많다보니 아키텍처에 맞게 미리 설정해둔거

3. 컴파일

 - make

 

다 이전에 임베디드 자료 정리하면서 한두번 본내용이지만 다잊었다.

 

 

  부트로더 설정 정리

각 모듈별 설정이 kconfig

전체 부트로더 설정은 menuconfig

-> 각 Kconfig들을 참조하여 make menuconfig 한 결과가 .config

기본값으로 설정된 설정 파일이 defconfig(default configuration)

make하면 .config를 참고해 autoconf.h가 생성

 

 

 

defconfig로 .config 만들기

bootloader/configs에 hybus_embedq6_defconfig가 있는데

이걸 make 해주면 .config가 나온다.

make로 컴파일 하면 쭉 진행된다.

 

 

빌드가 끝나면

현 디렉토리에 u-boot 관련 바이너리들이 만들어졌다.

여기서 필요한건 u-boot-dtb.imx로 부트로더와 커널 연결역활로 유부트가 커널에 전달할 플랫폼정보를 담았다고한다.

 

make 전 디렉토리 내 리스트

 

 

 

 

커널 빌드 

지금 사용할 임베디드 보드에 맞는 부트로더를 준비했으니

이제 커널을 빌드할 차례 압축을 풀고 설정하자

 

 

 

 

빌드하기전에 이번에도 커널 설정

 

1. 커널 초기화

 - make clean

 - make distclean

2. 커널 설정

 - make menuconfig

 - make Kconfig

 - make defconfig

3. 컴파일

 

설정 내용이야 부트로더 때랑 똑같으니

아까처럼

distclean 한후

defconfig를 메이크 해주자.

 

빠른 빌드를 위해 -j4 옵션을 주고 메이크

 

조금 시간이 걸리긴 하지만

다끝나면 arch/arm/boot에 zImage가 생겼다.

 

 

 

커널 디바이스 트리

이게 뭔진 잘 모르겠지만 외부 확장 장치가 다양하니 설정도 복잡해서, 

각 디바이스들의 모든 정보를  바이너리로 저장하고 부팅 시

이를 읽어와 하드웨어 초기화에 필요한 정보들을 파싱하는데 쓰는 거라 한다.

 

이 디바이스 트리 dtb(Device Tree Blob/Binary) 파일은

DTS(Device Tree Script/Source) 파일이 DTSI(Device Tree Source Include)를 참조, 디바이스 트리 컴파일러 DTC로 만든다고한다.

 

디바이스 트리 관련 파일로 바이너리는 .dtb, 소스는 .dts, 소스 인클루드는 .dtsi

 

이 임베디드 보드의 디바이스 트리는

현경로의 dts 폴더에 있다.

 

 

 

 

 

 

지금까지 부트로더와 커널을 빌드했는데

원래 BSP에서 친절하게 리눅스 시스템 이미지 설치에 필요한 부트로더, 커널, 파일시스템을 모아서 제공하고 있었다.

 

부트로더 u-boot-dtb.imx

커널 zImage, imx6q-sabresd-ldo.dtb

파일시스템 filesystem.tar.bz2

 

이것들을 OTG 포트로 플래쉬할수 있도록

AP 개발업체인 NXP에서 MFGTools를 만들었는데 이걸 쓰면된다고한다.

근데 bsp 에 포함된 mfgtool이 실행이 안된다.

 

ModuleID[2] LevelID[1]: Error: Can't find the specified list(SDCard-Android) in the xml file
ModuleID[2] LevelID[1]: Parse ucl script failed, error code: 12

 

 

읽어보면 xml파일에서 SDCard-Android 리스트가 없다고 하는데

구글링해보니 이 list 머씨기가 ucl2.xml(프로파일즈/리눅스/OS펌웨어)에 있다고한다.

 

https://community.nxp.com/t5/i-MX-Processors/i-MX6ULL-mfgTool-error-12/m-p/806124

 

i.MX6ULL mfgTool error 12

I would like to get an image on a SD card, that a i.MX6ULL target will boot from.  I have verified this working with a pre-built binary.  The issue I am running concerns building a new bootable image.  I tried putting the image resulting from "mkimage.

community.nxp.com

 

 

 

이 파일이 뭐하는건진 잘 모르겠지만 리눅스, 안드로이드로 쓰는지에 따라 설정하는 내용같은데

에러 로그에서 나온 SDCard-Android는 없고 H-EMbed-TKU 뭐시기가 안드로이드, 리눅스, 리눅스-WOFS 3개 뿐이더라.

 

 

뭐가 뭔지 모르겠지만 리눅스 커널을 쓸거니

H-EMbed-TKU-SD-Linux로 리스트 설정(cfg.ini)해서 실행해보면

 

 

 

이번엔 디바이스트리 imx를 여는데 에러가떳다

 

DLL version: 2.6.2
Friday, September 16, 2022 18:50:19   Start new logging
ModuleID[2] LevelID[1]: Push command--file C:\Users\jdo\Desktop\04.MFGTools\Profiles\Linux\OS Firmware\files\Linux\u-boot-dtb.imx failed to open.errcode is 2
ModuleID[2] LevelID[1]: Parse ucl script failed, error code: 4

해당경로를 보니 텅비어있어서그런거같고,

아까 준비된 파일을 옮겨서 다시실행하면

 

 

이번엔 z이미지가 없다고 안된다.

 

DLL version: 2.6.2
Friday, September 16, 2022 18:52:08   Start new logging
ModuleID[2] LevelID[1]: Push command--file C:\Users\jdo\Desktop\04.MFGTools\Profiles\Linux\OS Firmware\files\Linux\zImage failed to open.errcode is 2
ModuleID[2] LevelID[1]: Parse ucl script failed, error code: 4

 

옮기고 실행하면

 

 

또안되니 그냥 아까 파일 4개 다옮기고다시실행하면

 

DLL version: 2.6.2
Friday, September 16, 2022 18:52:45   Start new logging
ModuleID[2] LevelID[1]: Push command--file C:\Users\jdo\Desktop\04.MFGTools\Profiles\Linux\OS Firmware\files\Linux\imx6q-sabresd-ldo.dtb failed to open.errcode is 2
ModuleID[2] LevelID[1]: Parse ucl script failed, error code: 4

 

이제서야 실행된다.

 

 

 

헤매고 나서야 알았는데

이 위치에 옮기라고 책에 나와있었다.

 

 

 

USB-OTG와 전원 케이블 연결하고..

잘안보이지만 SD카드가 이미꽂혀있다.

 

 

 

 

 

 

 

개발 보드 커널 설치

- 부트 모드로 부트 ROM에 접근해 이미지 플래쉬.

- 부드모드 진입 : BOOT MODE 버튼 누른 채 파워 온

 하면 인식한다.

 

 

중간에 갑자기 no device connected 떠서 놀랐는데 기다리니 쭉진행한다.

 

 

 

 

 

부팅 설정하기

이 보드는 SD 장치와 eMMC 를 제공하는데 SD에 리눅스를, emmc서 안드로이드를 부팅하는거같고

중간에 있는 스위치로 boot config를 설정하면 sd로 부팅할지, emmc로 부팅할지 설정하는거라고한다.

 

리눅스, 안드로이드 부팅시 이렇게 설정한다.

 

 

 

좀 걸리긴 했는데 Done

 

 

 

 

 

하이버스에서 EMbed-TKU BSP를 받아서

임베디드 기사 준비 겸 시작하게 됬는데

 

내가 가지고 있는 책과는 좀 다르지만 

안에 이런식으로 있다.

 

 

책을 따라서 디렉터리 구조부터 만들고

 

 

책 설명이 좀 했갈리는데

커널, 부트로더 gcc linaro를 이런 식으로 옮기고

 

개발환경 구축에 필요한 패키지들을 설치하자

 

u-boot-tools : uboot 부트로더 빌드 도구 패키지

ncurses-dev menuconfig 사용시 필요한 라이브러리

device-tree-compiler 디바이스 트리 빌드에 사용

lzop 리눅스 커널 빌드시 zImage 만듬

build-essential 소스코드 빌드에 필요한 기본패키지 gcc, make, patch 같은것들

 

 

 

교차 컴파일러 압축 푼뒤

 

usr로 옮기고

 

환경변수도 등록후 소스

 

 

방금 설치하고, 환경변수 등록한 gcc 버전확인

잘 나온다.

 

 

ARM 크로스 컴파일러가 잘되는지 헬로월드 해보면 

ARM 플랫폼꺼라고 실행이 안된다.

 

 

여기다가 메이크 파일도 만들고 써보면

 

아까처럼 잘된다.

I2C

- 선 2개

- 반이중

- 1:N, N:N, 128개 슬레이브

- 오픈컬랙터방식. 풀업 사용.

- 소프트웨어적으로 주소지정





 

 

 

 

 

UART

1. Idle Time : 송신기는 5V 유지

2. 통신시작 : 0V(start bit)

3. LSB부터 1비트씩 송신, 데이타(5~10비트)

4. 전송이 끝난뒤 5V 유지(stopbit 1 ~ 2bit)

 

 

 

SPI

- 1: N

- 선 4개  MOSI, MISO, CLK(통신),  SS(칩선택)

- 전이중

- HW 주소지정

 

 

 

 

 

 

 

마이크로컨트롤러 통신 방식

  I2C UART SPI
이름 Inter-Integrated Circuit Universal Asyncronous Recevier/Trasmitter Serial Peripheral Interface
전/반이중 반이중 전이중 전이중
동기/비동기 동기(별도클럭라인) 비동기(보드레이트설정) 동기(별도클럭라인)
관계 1:N, N:N 1:1 1:N
슬레이브 연결에 필요한선수 2 2N 3+N
슬레이브 선택방법 소프트웨어로 주소 지정 무관 SS라인으로 지정(하드웨어)
SPI(1:N, N:N)
UART 인터페이스 다이어그램, 프레임  
SPI  

 

 

 

 

 

적외선 센서

- 파장범위가 700nm ~ 1mm. 파장이 적색빛보다 길고 극초단파보단 짧은 전자기파

- 적외선 발광 -> 수광부에 들어오는 양으로 거리 측정

- ex : TCRT5000

https://diyver.tistory.com/104

 

아두이노에서 적외선 근접센서 TCRT5000 사용하는 방법

<목표> 아두이노를 사용함에 있어서 적외선 근접센서는 정말 손쉽게 접할 수 있는 센서이다. 쉽게 접할 수 있지만, 사용방법을 모르면 문제가 많은 센서이기도 하다. LED 이므로 +극과 -극을 정확

diyver.tistory.com

- 포토트랜지스터 : 수광 센서의 종류

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=fribot&logNo=60194290020 

 

포토트랜지스터의 동작원리

포토트랜지스란? 아두이노 로봇에 많이 사용하는 비접촉 센서들 중 하나이고, 발광소자가 아니라 수광소자...

blog.naver.com

 

 

 

포토커플러를 이용한 적외선 센서 동작 실험

- 버튼 off -> led 발광 x -> 수광 x -> 안흐른다. 

- 버튼 on -> led 발광 o -> 수광 o -> 흐른다.

 

비반전 비교기 출력 실험

- 기준전압 : 2.5v

- SW를 안 누르면 -> 비반전비교기 입력전압에 5V 인가 -> 입력 전압 > 기준 전압 -> HIGH 

<-> SW 누르면 -> 입력전압 0V 인가 -> 입력 v < 기준 V -> LOW

 

 

 

 

 

 

비반전 비교기 출력 실험 2

- LED 인디케이터 추가 : 보통 led에 5v vcc에 10ma 흐르도록 330옴을 붙이나 안밝아도되서 1k res 담.

 

 

 

검정라인 따라가는 라인트레이서 만들기

흰색(반사)=SW ON=SENSOR LOW=비반전비교기OUT LOW
검정 라인
-> LEFT, CENTER, Right 모두 HIGH => 전진
-> LEFT, CENTER HIGH, RIGHT LOW = 오른쪽으로 나갔다 = 좌회전하자
-> LEFT LOW, CENTER, RIGHT HOW = 왼쪽으로 나갔따 = 우회전 하자
-> LEFT, CENTER, RIGHT 모두 LOW = 검정라인 밖에 나갔다 = 역회전 하자.
-> 이도저도 아니면 멈추자.

 

/*
	
	project 4

	LEFT_SEN	- ard 11
	CENTER_SEN	- ard 12
	RIGHT_SEN	- ard 13

	L298 IN1 - ard 7
	L298 IN2 - ard 8
	L298 ENA - ard 5
	
	L297 IN3 ard 9
	L298 IN4 ard 10
	L298 ENB ard 6

*/

const uint8_t PIN_LEFT_SEN  = 11;
const uint8_t PIN_CENTER_SEN  = 12;
const uint8_t PIN_RIGHT_SEN  = 13;

const uint8_t PIN_IN3 = 9;
const uint8_t PIN_IN4 = 10;
const uint8_t PIN_ENB = 6;

const uint8_t PIN_IN1 = 7;
const uint8_t PIN_IN2 = 8;
const uint8_t PIN_ENA = 5;


#define STOP 0
#define FWD 1
#define LEFT_TURN 2
#define RIGHT_TURN 3
#define BWD 4

uint8_t Direction;
uint8_t pwm_value = 255;

void setup () {
	Serial.begin(9600);
	pinMode(PIN_LEFT_SEN, INPUT_PULLUP);
	pinMode(PIN_CENTER_SEN, INPUT_PULLUP);
	pinMode(PIN_RIGHT_SEN, INPUT_PULLUP);

	pinMode(PIN_IN1, OUTPUT);
	pinMode(PIN_IN2, OUTPUT);
	pinMode(PIN_ENA, OUTPUT);

	pinMode(PIN_IN3, OUTPUT);
	pinMode(PIN_IN4, OUTPUT);
	pinMode(PIN_ENB, OUTPUT);
}

void loop() {
	boolean LeftSenHL = digitalRead(PIN_LEFT_SEN);
	boolean CenterSenHL = digitalRead(PIN_CENTER_SEN);
	boolean RightSenHL = digitalRead(PIN_RIGHT_SEN);

	//읽은 센서값 시리얼 모니터에 출력
	Serial.print(LeftSenHL);
	Serial.print(" ");
	Serial.print(CenterSenHL);
	Serial.print(" ");
	Serial.println(RightSenHL);
	delay(10);

	/*
		흰색(반사)=SW ON=SENSOR LOW=비반전비교기OUT LOW
		검정 라인
		-> LEFT, CENTER, Right 모두 HIGH => 전진
		-> LEFT, CENTER HIGH, RIGHT LOW = 오른쪽으로 나갔다 = 좌회전하자
		-> LEFT LOW, CENTER, RIGHT HOW = 왼쪽으로 나갔따 = 우회전 하자
		-> LEFT, CENTER, RIGHT 모두 LOW = 검정라인 밖에 나갔다 = 역회전 하자.
		-> 이도저도 아니면 멈추자.
	*/
	if(LeftSenHL == 0 && CenterSenHL == 0 && RightSenHL == 0) Direction = 4;		// 역회전
	else if(LeftSenHL == 1 && CenterSenHL == 1 && RightSenHL == 1) Direction = 1;	// 정회전
	else if(LeftSenHL == 1 && CenterSenHL == 1 && RightSenHL == 0) Direction = 2;	// 우회전
	else if(LeftSenHL == 0 && CenterSenHL == 1 && RightSenHL == 1) Direction = 3;	// 좌회전
	else  Direction = 0;															// 정지

	switch(Direction)
	{
		case STOP:
			digitalWrite(PIN_IN1, LOW);
			digitalWrite(PIN_IN2, LOW);
			analogWrite(PIN_ENA, pwm_value);			
			digitalWrite(PIN_IN3, LOW);
			digitalWrite(PIN_IN4, LOW);
			analogWrite(PIN_ENB, pwm_value);
			break;
		case FWD:
			digitalWrite(PIN_IN1, HIGH);
			digitalWrite(PIN_IN2, LOW);
			analogWrite(PIN_ENA, pwm_value);			
			digitalWrite(PIN_IN3, HIGH);
			digitalWrite(PIN_IN4, LOW);
			analogWrite(PIN_ENB, pwm_value);		
			break;
		case LEFT_TURN:
			digitalWrite(PIN_IN1, HIGH);
			digitalWrite(PIN_IN2, LOW);
			analogWrite(PIN_ENA, pwm_value/4);			
			digitalWrite(PIN_IN3, HIGH);
			digitalWrite(PIN_IN4, LOW);
			analogWrite(PIN_ENB, pwm_value);			
			break;
		case RIGHT_TURN:
			digitalWrite(PIN_IN1, HIGH);
			digitalWrite(PIN_IN2, LOW);
			analogWrite(PIN_ENA, pwm_value);			
			digitalWrite(PIN_IN3, HIGH);
			digitalWrite(PIN_IN4, LOW);
			analogWrite(PIN_ENB, pwm_value/4);				
			break;
		case BWD:
			digitalWrite(PIN_IN1, LOW);
			digitalWrite(PIN_IN2, HIGH);
			analogWrite(PIN_ENA, pwm_value/2);			
			digitalWrite(PIN_IN3, LOW);
			digitalWrite(PIN_IN4, HIGH);
			analogWrite(PIN_ENB, pwm_value/2);	
			break;
	}
}

 

전진 L 1, M 1, R 1 

좌회전 L 1, M 1, R 0

우회전 L 0, M 1, R 1 

역회전 L 0, M 0, R 0

정지 이도저도아니면

 

 

임베디드 공부하면서 하드웨어, 부트로더, 커널 이미지, 디바이스 드라이버 구현 등 정리

 

 

임베디드 정리.pdf
0.73MB

 

모터 정역 제어

- 정역 제어 = 정방향(CW clock wise) + 역방향 제어(CCW counter CW)

- 전원 방향에 따라 정방향 역방향으로 회전한다 

  but) MCU IO 포트는 출력 전류가 약해 전류를 충분히 공급하지 못한다.

 => 전류 증폭이 필요. 트랜지스터?

 

DC 모터 구동

- TR 구동 : 이미터 부하, 컬랙터 부하 -> 컬랙터 부하를 주로 사용

 

간단한 모터 제어 회로

- NPN TR과 이미터 방향에 모터보호저항 단다.(과전류 보호)

갑자기 끄면 큰 역기전력이 발생-> TR 파괴

 

 

 

역기전력으로부터 모터 보호

- 환류 다이오드 사용

- 역기전력이 생겼을때 빼주기 : 저항이 작아진 소자 반대방향

환류 다이오드(free wheeling diode) 필요

but 여전히 한쪽방향만 돈다

 

 

 

모터 양방향 제어를 위한 H 브리지 제어 회로

- TR 하나로는 전류가 부족하다

-> TR을 4개 해서 전류도 증폭하고 양방향 제어가능한 회로

 

 

트랜지스터를 이용한 AND 게이트

 

 

 

L298

DUAL FULL-BRIDGE DRIVER : L298 하나로 모터 2개 제어가능

 

 

 

 

enable 핀을 통해 출력

-> enable off 시 마찰력에 의해 천천히 모터 멈춤

급정거 원할시 H 브리지에 1 1 인가

 

L298N 듀얼 풀 브리지 드라이버 모듈

 

 

 

L297N를 이용한 양방향 DC 모터 제어

 

 

L298N 드라이버 블록 다이어그램 이해하기

1. 방향 제어

 

2. Enable 신호 H일때 동작, L 되면 모터는 관성으로 정지

 

3. 속도 제어

 

 

시뮬레이션 예제

/*
	project1 : 가변 저항을 이용한 DC 모터 속도 제어
	가변저항 - A0
	L298 IN1 - ard 8
	L298 IN2 - ard 7
	L298 ENA - ard 6 980hz
*/


const uint8_t PIN_IN1 = 8;
const uint8_t PIN_IN2 = 7;
const uint8_t PIN_ENA = 6;
const uint8_t analog_pin = A0;


int analog_value;
int pwm_value;

void setup () {
	Serial.begin(9600);
	pinMode(PIN_IN1, OUTPUT);
	pinMode(PIN_IN2, OUTPUT);
	pinMode(PIN_ENA, OUTPUT);

}

void loop() {
	analog_value = analogRead(analog_pin);
	pwm_value = map(analog_value, 0, 1024, 0, 255);

	digitalWrite(PIN_IN1, HIGH);
	digitalWrite(PIN_IN2, LOW);
	analogWrite(PIN_ENA, pwm_value);
	Serial.print("pwm value : ");
	Serial.println(pwm_value);
}

 

 

 

 

 

 

 

 

 

https://ggamji.tistory.com/110

 

아두이노회로만들기 - 3. 베터리 쓰기, 내부 클럭 쓰기, LED 점멸

이 글을 쓰려고 몇 일을 허비했는지 모르겠다. 아무튼 이전에는 아두이노 쓸땐 USB전원으로 넣는것 말고는 잘 몰랐지만 아두이노 드론 같은데서 리튬 이온 베터리를 쓰고 충전하는걸 보면서 어

ggamji.tistory.com

 

지난번 내부 클럭 사용하도록 부트로더 굽고, 블링크 코드를 업로드한 아두이노를 18650 베터리 전원으로 동작시킨것에

태양광 패널을 충전하도록 추가했다.

 

 

 

 

우선 태양광 패널 충전 전압을 확인했을때

태양광을 얼마나 받는지에 따라서 2 ~ 6V 사이로 크게 바뀌고 있었고

 

Atmega328 데이터 시트 상에서 기존 16MHz 동작시에는 4.5 ~ 5.5v로 동작 전압에 비해 변동이 너무 컸다.

18650 베터리 3.7v 쯤 되는걸 가지고 쓰니 베터리 단독으로 쓰거나 태양광 패널 단독으로 쓰는 경우를 생각해서

8MHz 클럭 속도를 쓰도록 부트로더를 다시 구웠다.

 

 

 

다시 복습하면

부트로더는 이렇게 연결해서 구웠고

위에께 아두이노 ISP를 올린 호스트 보드

아래께 8MHz 부트로더 굽는 타겟 보드


https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=mapes_khkim&logNo=221890832646&view=img_1

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=mapes_khkim&logNo=221890832646&view=img_1

 

블링크 코드는 이 그림과 같이 연결해서 업로드 하였다.

디지털 핀 기준으로는 12번인데, Atmega328p 기준으로는 18번 핀이다.

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(12, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(12, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(100);                       // wait for a second
  digitalWrite(12, LOW);    // turn the LED off by making the voltage LOW
  delay(100);                       // wait for a second
}

https://create.arduino.cc/projecthub/techmirtz/arduino-without-external-clock-crystal-on-atmega328-d4fcc4

https://create.arduino.cc/projecthub/techmirtz/arduino-without-external-clock-crystal-on-atmega328-d4fcc4

태양광 충전과 배터리 전원으로 동작이 동시에 이뤄지는걸 방지하기위해서

스위치 두개를 추가했다.

태양광 충전만 ON 했을때 베터리 충전 모듈에 빨간 빛이 들어오며 충전되고 있는걸 확인할수있다.

 

 

 

 

 

 

스위치로 태양광 충전을 멈추고

배터리 전원을 쓰도록 했을때

블링크 동작이 잘 이루어진다.

 

 

나빌로스를 한답시고 보면서

정작 어셈블리 공부를 제대로 안하고 이상한걸 보고 있었다가 다시 돌아왔다.

 

 

너무 깊이 배울 필요는 없어서 자료 찾다보니

kldp의 자료가 적당할거같고 

mov는 어떻게 쓰고, jmp는 어떻게 쓰고 하는 자질구래한 내용들 많은 강좌보다는

섹션이 뭐고 다 포함하고 

이 내용을 따라했다.

https://wiki.kldp.org/HOWTO/html/Assembly-HOWTO/x848.html

 

소개

마침내 때가 왔다. 여러분이 아직도 이 문서를 읽고 있으며, 여전히 어셈블리로 무언가를 만들겠다는 광기어린(!) 생각을 가지고 있다면 (여기까지 읽은 사람이라면, 정말 어셈블리 팬임에 틀림

wiki.kldp.org

 

 

 

 

 

프로그램 레이아웃

- .text 섹션 : 작성한 코드

- .data 섹션 : 초기화된 데이터 

- .bss 섹션 : 초기화 되지 않은 데이터

 

 

NASM 코드 helloworld

 

section		.data						; 섹션 .data 선언

msg		db		"hello, world!", 0xa		; 출력할 문자열
len		equ		$ - msg				; 문자열 길이

section		.text						;여기부터 .text 섹션

	global _start						;ELF링커나 로더에게 프로그램 엔트리포인터알려줌.보통 _start

_start:
    mov		edx, len					; 시스템콜의 세번째 인수, 출력할 메시지 길이 가져옴
    mov		ecx, msg					; 시스템콜의 두번째 인수, 출력할 메시지 포인터 가져옴
    mov		ebx, 1						; 시스템콜의 첫번째 인수, 파일 디스크립터 전달.
    mov		eax, 4						; eax 레지스터에 시스템 콜번호 전달. 4번은 sys_write
    int		0x80						;커널 호출

; 출력 완료 후 exit 호출
    mov		ebx, 0						; exit 코드로 0을 준다.
    mov 	eax, 1						;시스템 콜 1번(sys_exit)
    int		0x80						;커널 호출

 

 

실행 결과

 

 

 

 

kldp 내용만 봐도 뭔지 대강알긴 하겠지만 설명이 좀 부족해보인다

 

여기보다는 이쪽 튜토리얼이 예제나 설명이 자세하다.

https://asmtutor.com/

 

 

NASM Assembly Language Tutorials - asmtutor.com

~$ nasm -f elf helloworld-input.asm ~$ ld -m elf_i386 helloworld-input.o -o helloworld-input ~$ ./helloworld-input Please enter your name: Daniel Givney Hello, Daniel Givney

asmtutor.com

 

 

lesson 1 예제를 보면

 

직접 어셈블리어로 하드웨어를 제어하고 싶을때 EAX 레지스터에다가 함수번호(오퍼레이션 코드)를 넣고,

나머지 레지스터에다가 필요한 값을 넣은 다음에, int 명령으로 소프트웨어 인터럽트를 주면 지정한 함수가 호출된다.

 

sys_exit()는 EAX=1을

sys_write()는 EAX=4 그리고 EBX, ECX, EDX에 필요한 값을 넣어주면 된다.

 

; Hello World Program - asmtutor.com
; Compile with: nasm -f elf helloworld.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld.o -o helloworld
; Run with: ./helloworld
 
SECTION .data
msg     db      'Hello World!', 0Ah     ; assign msg variable with your message string
 
SECTION .text
global  _start
 
_start:
 
    mov     edx, 13     ; number of bytes to write - one for each letter plus 0Ah (line feed character)
    mov     ecx, msg    ; move the memory address of our message string into ecx
    mov     ebx, 1      ; write to the STDOUT file
    mov     eax, 4      ; invoke SYS_WRITE (kernel opcode 4)
    int     80h

 

위에서 설명한대로

SECTION .data

데이터 섹션 그러니까 초기화 되는 변수 부분을 나타내는데

msg라는 변수에다가  'hello world'를 담는건 알겠지만 왼쪽에 db와 우측에는 ,0ah가 존재한다.

 

 

이게 뭐소리인가 찾아보니

db는 바이트 데이터 형을 의미한다하고,

다른데도 찾아보니 msg에 바이트 단위 배열로 넣기 위한 자료형 선언한다는 의미라한다.

ref : https://velog.io/@kyoung99u/%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC-%EC%96%B8%EC%96%B4

 

[Assembly] 어셈블리 기초

변수 어셈블리 프로그램에서 변수를 어떻게 사용할까? 어셈블리 프로그램에서는 타 프로그램과 같이 변수를 정의하여 사용하는데, 변수는 변수명과 데이터형, 초기값을 가진다. 0806 어셈블리언

velog.io

 

 

그러면 ,0ah는

쿼라에 질문올라온게 있긴한데

답변한 사람 말로는

0xA는 라인피드

0xD는 캐리지 리턴이라고 설명하고 있다.

그러면 0xAh는 뒤에 h가 붙어있는데 이건 뭔소린가 싶긴한데 설명이없다.

 

https://www.quora.com/What-is-the-meaning-of-0ah-0dh-in-assembly-language-for-displaying-any-message

 

What is the meaning of 0ah, 0dh in assembly language for displaying any message?

Answer (1 of 4): As everyone else answered, 0xA linefeed (\n), 0xD carriage return(\r), but have you wondered why is this two different notion in assembly? why did the carriage return and line feed was different back then ?? in high level languages when we

www.quora.com

 

 

여길 보니 0ah가 아스키 코드로 라인피드가 맞다고 하는걸 보니 답변자가 빠트린가 보다

https://www.sluiceartfair.com/2018/contributing/what-is-0ah-in-assembly/

 

What is 0AH in assembly? – Sluiceartfair.com

What is 0AH in assembly? 0Ah is the hexadecimal constant for the Line Feed character in ASCII, often abbreviated LF. 24h is the hexadecimal constant for the ‘$’ character in ASCII. What is carriage return in assembly language? Carriage Return Character

www.sluiceartfair.com

 

int 80h는 소프트웨어 인터럽트를 발생시키는 어셈블리 코드라고한다.

https://second.wiki/wiki/int_80h

 

Int 80h

Int 80h

second.wiki

 

 

일단 어셈블리와 링커를 돌린후 실행하보면 생각한데로 출력은 잘되는데 세그먼테이션 폴트가 뜬다.

이 문제는 다음 레슨에서 설명하는데

~$ nasm -f elf helloworld.asm
~$ ld -m elf_i386 helloworld.o -o helloworld
~$ ./helloworld
Hello World!
Segmentation fault

 

 

 

 

레슨 2 : 프로그램을 제대로 종료시키자

레슨 1에서는 어셈블리어로 sys_write 시스템 콜을 써서 출력을 시켰는데 이번 레슨에서는 sys_exit에 대해서 보자

설명이 쭉 있는데, 결국에는 모든 프로그램은 메모리를 공유하고 있다보니 global _start에서부터 순차적으로 실행해주는데 어디서 끝낼지 커널한태 알려주지 않으면 구현하지 않은 다음 주소에 있는것도 실행하게 되고 이게 세그먼테이션 폴트의 원인이 된다고 한다.

 

sys_exit를 쓰기 위해선

EBX에 0을 전달하도록 0을

EAX에 sys_exit를 호출하는 1을 넣으면 되서 코드는 이렇게 된다고 말한다.

; Hello World Program - asmtutor.com
; Compile with: nasm -f elf helloworld.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 helloworld.o -o helloworld
; Run with: ./helloworld
 
SECTION .data
msg     db      'Hello World!', 0Ah
 
SECTION .text
global  _start
 
_start:
 
    mov     edx, 13
    mov     ecx, msg
    mov     ebx, 1
    mov     eax, 4
    int     80h
 
    mov     ebx, 0      ; return 0 status on exit - 'No Errors'
    mov     eax, 1      ; invoke SYS_EXIT (kernel opcode 1)
    int     80h

 

정상적으로 프로그램을 종료시켜서인지 세그먼테이션 폴트가 뜨지 않고 잘 끝낫다!

 

 

나빌로스 하면서 링커스크립트를 좀 봐야할거같아서 찾아보니까 이 자료가 적당해보인다.

 

http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html

 

Using LD, the GNU linker - Linker Scripts

Every link is controlled by a linker script. This script is written in the linker command language. 모든 링크 과정은 링커 스크립트가 조정한다. 이 스크립트는 링커 명령 언어로 쓰여진다. The main purpose of the linker script

korea.gnu.org

 

 

링커

- 여러 오브젝트 파일을 합쳐서 실행 파일로 만들며, 각 오브젝트 파일은 섹션들을 갖고 있음.

 

 

SECTION 명령어 개요

- 링커 스크립트의 유일한 명령어로 코드, 초기화된 데이터, 초기화되지 않은 데이터가 있으면 .text, .data, .bss 섹션이 됨

 

 

 

링커 스크립트 예시

- 코드가 0x10000에 로드되고, 데이터는 0x800 0000에서 시작하는 경우'

- 첫째 줄에선 위치 카운터 '.'의 값을 0x1 0000로 설정.(처음에는 0이며, 설정안할시 현재 값)

- 둘째 줄에선 출력섹션으로 .text 정의하면서, ':' 뒤에 있는 *(.text)는 모든 입력 파일의 .text 섹션을 나타냄.

  + 출력 섹션 '.text'를 정의 할 때, 위치 카운터가 '0x1 0000'이므로 출력(실행) 파일의 .text 섹션 주소를 '0x1 0000'이 됨.

- 셋째 줄에선 위치 카운터 값을 '0x800 0000'로 설정

- 나머지 줄에선 .data, .bss 섹션 정의.

  + 출력 섹션 .data의 주소는 0x800 0000으로 설정

  + .bss의 주소는 .data 섹션의 바로 뒤에 위치

SECTIONS
{
  . = 0x10000;
  .text : { *(.text) }
  . = 0x8000000;
  .data : { *(.data) }
  .bss : { *(.data) }
}

 

 

 

 

SECTIONS 명령어

- 입력 섹션의 내용을 어떻게 출력 섹션에 매핑할지. 출력 섹션의 메모리 상 위치를 어떻게 할지 정의

- sections-command whdfb : entry 명령어, 심볼 입력, 출력 섹션 설명

SECTIONS
{
  sections-command
  sections-command
  ...
}

 

 

출력 섹션 형태

section [address] [(type)] : [AT(lma)]
{
  output-section-command
  output-section-command
  ...
}

 

 

 

 

ARM 아키텍처

- 아키텍처 = 명령어 체계 자체(명령어들을 instruction set 혹은 어셈블리어라 함)

- 아키텍처 기반으로 CPU 코어 설계

- 하나의 아키텍처로 여러 가지 코어 설계 가능

- ARM Cortex 시리즈 : A, R, M

* ARMv1 -> ARMv2 : MUL 추가

* ARMv2 -> ARMv3 : 프로세스 캐시 추가

 

ARM CPU

- STM32F103 : ARMv7-M 아키텍처 구조, ARM Cortex-M3 코어 사용

- Exynos4412 : ARMv7-A 아키텍처 구조, ARM Cortex-A9 코어 사용

 

 

 

ARM 코어 레지스터

- 동작 모드 : User, FIQ, SVC, Abort, IRQ, undefined

- 일정 조건에 따라 자동으로 바뀜

- User -> IRQ -> User : 펌웨어로 제어

- System : CPU가 특수 상황시 전환

- User : R0 ~ R15의 16개 + CPSR 레지스터 사용

- SVC(supervisor), Abort, IRQ, Undefined : R13, R14, SPSR 레지스터 사용

- 총 37개 레지스터 사용 = 17 + 8 + 3 + 3 + 3 + 3

- 동작 모드 별 레지스터

 

 

ARM 명령어 체계

- ARM 모드 : 32비트 체계

- Thumb 모드 : 16비트 명령어 체계, 일부 레지스터만 사용, 컴파일 시 Thumb 전용 컴파일러나 옵션으로 선택

 

ARM 코어 레지스터

- R15 : 프로그램 카운터

- R14 : 링크 레지스터, 서브루틴 함수서 돌아갈 주소

- R13 : 스택 포인터

- CPSR current program status register 

- SPSR saved program status register : cpsr 레지스터 값을 모드마다 백업용으로 저장하기 위해 사용.

 

 

 

명령어 집합 종류

- 분기 명령 : 함수, if then else 구조

- 데이터 처리 명령 : 프로세서 내부 데이터 이동, 산술, 논리, 비교 명령어

- 상태 레지스터 접근 명령 : CPSR, SPSR 제어 명령어

- Single register load and store instruction : 프로세서 메모리 간 단일 데이터 전송명령

- 다중 레지스터 로드 앤 스토어 레지스터 : 프로세서 메모리 간 다중 데이터 전송 명령

- 코프로세서 명령 : coprocossor(ARM core에서 처리못하거나 확장기능 구현시 사용하는 별도 프로세서) 제어 명령

 

ARM 명령어 집합 포멧

- 명령어 종류 별로 다른 형식

 

 

Thumb 명령어 집합

- 16비트 명령어 체계

- 보통 ARM 코드에 비해 30% 메모리 절약

- BX, BLX 명령으로 ARM 과 Thumb 상태 변환

+ Recent posts