Subwayway Embedded DEV

NO.1 레이디그레이 400

  • 촬영 기간 : 2018~2021
  • 카메라 : 로모그래피 심플유즈
  • 필름 : 레이디그레이 400(Lady Grey 400)

메모리 설계, 익셉션 핸들러

개발 환경

  • Window 10, WSL1(Ubuntu 18.04)
  • QEMU(realview-pb-a8)
  • Graphic : VcXsrv Windows X Server

메모리는 크게 3가지 영역으로 나뉨.

  • text 영역 : 사용자 코드
  • data 영역 : 초기화된 전역, 정적 변수 저장 / 동작 모드별 스택, 태스크 스택
  • bss 영역 : 초기화 되지 않은 전역, 정적 변수 저장
  • stack, heap 영역 : 태스크, 동적할당

text 영역 메모리 설계

  • RTOS 펌웨어를 수십 kb로 가정하고 지정
할당
크기
주소
text 1MB 0X00000000~0X000FFFFF

동작모드 스택을 메모리에 설계

  • USR모드와 SYS모드는 메모리공간과 레지스터 공유
할당
크기
주소
USR,SYS 2MB 0X00100000~0X002FFFFF
SVC 1MB 0X00300000~0X003FFFFF
IRQ 1MB 0X00400000~0X004FFFFF
FIQ 1MB 0X00500000~0X005FFFFF
ABT 1MB 0X00600000~0X006FFFFF
UND 1MB 0X00700000~0X007FFFFF

테스크 스택, 전역변수, 동적 할당 영역 설계

  • RTOS의 동작 테스크를 1MB씩 64개 할당
할당
크기
주소
테스크 스택 64MB 0X00800000~0X047FFFFF
전역 변수 1MB 0X04800000~0X048FFFFF
동적 할당 55MB 0X04900000~0X07FFFFFF
1+2+1+1+1+1+1+64+1+55=128MB

Entry.S

익셉션 벡터 테이블

#include "ARMv7AR.h"	;동작모드 전환 비트 헤더파일
#include "MemoryMap.h"	;메모리맵 주소 헤더파일

.text
    .code 32

    .global vector_start
    .global vector_end

    vector_start:
      LDR PC, reset_handler_addr
      LDR PC, undef_handler_addr
      LDR PC, svc_handler_addr
      LDR PC, pfrch_abt_handler_addr
      LDR PC, data_abt_handler_addr
      B .
      LDR PC, irq_handler_addr
      LDR PC, fiq_handler_addr

      reset_handler_addr: .word reset_handler
      undef_handler_addr: .word reset_handler
      svc_handler_addr: .word reset_handler
      pfrch_abt_handler_addr: .word reset_handler
      data_abt_handler_addr: .word reset_handler
      irq_handler_addr: .word reset_handler
      fiq_handler_addr: .word reset_handler
    vector_end:

    reset_handler:
      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_SVC	;SVC 동작모드 비트
      MSR cpsr, r1	;cpsr에 동작모드 비트를 설정
      LDR sp, =SVC_STACK_TOP	;스택포인터에 SVC스택 탑 메모리 주소 설정

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_IRQ
      MSR cpsr, r1
      LDR sp, =IRQ_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_FIQ
      MSR cpsr, r1
      LDR sp, =FIQ_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_ABT
      MSR cpsr, r1
      LDR sp, =ABT_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_UND
      MSR cpsr, r1
      LDR sp, =UND_STACK_TOP

      MRS r0, cpsr
      BIC r1, r0, #0X1F
      ORR r1, r1, #ARM_MODE_BIT_SYS
      MSR cpsr, r1
      LDR sp, =USRSYS_STACK_TOP

      BL main
    dummy_handler:
      B .
.end

스택은 메모리를 반대방향으로 사용하기 때문에 스택의 꼭대기 주소를 스택포인터로 지정한다.


ARMv7AR.h

동작모드 전환 비트 헤더파일

#define ARM_MODE_BIT_USR  0X10
#define ARM_MODE_BIT_FIQ  0X11
#define ARM_MODE_BIT_IRQ  0X12
#define ARM_MODE_BIT_SVC  0X13
#define ARM_MODE_BIT_ABT  0X17
#define ARM_MODE_BIT_UND  0X1B
#define ARM_MODE_BIT_SYS  0X1F
#define ARM_MODE_BIT_MON  0X16

MemoryMap.h

메모리맵 주소, 사이즈, 스택 탑 위치 헤더파일

#define INST_ADDR_STATRT 0
#define USRSYS_STACK_START 0X00100000
#define SVC_STACK_START 0X00300000
#define IRQ_STACK_START 0X00400000
#define FIQ_STACK_START 0X00500000
#define ABT_STACK_START 0X00600000
#define UND_STACK_START 0X00700000
#define TASK_STACK_START 0X00800000
#define GLOBAL_STACK_START 0X04800000
#define DALLOC_STACK_START 0X04900000

#define INST_MEM_SIZE (USRSYS_STACK_START - INST_ADDR_STATRT)
#define USRSYS_STACK_SIZE (SVC_STACK_START - USRSYS_STACK_START)
#define SVC_STACK_SIZE (IRQ_STACK_START - SVC_STACK_START)
#define IRQ_STACK_SIZE (FIQ_STACK_START - IRQ_STACK_START)
#define FIQ_STACK_SIZE (ABT_STACK_START - FIQ_STACK_START)
#define ABT_STACK_SIZE (UND_STACK_START - ABT_STACK_START)
#define UND_STACK_SIZE (TASK_STACK_START - UND_STACK_START)
#define TASK_STACK_SIZE (GLOBAL_STACK_START - TASK_STACK_START)
#define DALLOC_MEM_SIZE (55*1024*1024)

//스택 사이의 패딩(padding)을 위한 4바이트 비우기
#define USRSYS_STACK_TOP (USRSYS_STACK_START + USRSYS_STACK_SIZE - 4)
#define SVC_STACK_TOP (SVC_STACK_START + SVC_STACK_SIZE - 4)
#define IRQ_STACK_TOP (IRQ_STACK_START + IRQ_STACK_SIZE - 4)
#define FIQ_STACK_TOP (FIQ_STACK_START + FIQ_STACK_SIZE - 4)
#define ABT_STACK_TOP (ABT_STACK_START + ABT_STACK_SIZE - 4)
#define UND_STACK_TOP (UND_STACK_START + UND_STACK_SIZE - 4)

Makefile

ARCH = armv7-a
MCPU = cortex-a8

CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
OC = arm-none-eabi-objcopy

LINKER_SCRIPT = ./milktea.ld
MAP_FILE = build/milktea.map

ASM_SRCS = $(wildcard boot/*.S)
ASM_OBJS = $(patsubst boot/%.S, build/%.os, $(ASM_SRCS))

C_SRCS = $(wildcard boot/*.c)
C_OBJS = $(patsubst boot/%.c, build/%.o, $(C_SRCS))


INC_DIRS = include

milktea = build/milktea.axf
milktea_bin = build/milktea.bin

.PHONY: all clean run debug gdb

all: $(milktea)

clean:
	@rm -fr build

run: $(milktea)
	qemu-system-arm -M realview-pb-a8 -kernel $(milktea) -nographic

debug: $(milktea)
	qemu-system-arm -M realview-pb-a8 -kernel $(milktea) -S -gdb tcp::1234,ipv4

gdb:
	arm-none-eabi-gdb

$(milktea): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
	$(LD) -n -T $(LINKER_SCRIPT) -o $(milktea) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)
	$(OC) -O binary $(milktea) $(milktea_bin)

build/%.os: $(ASM_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<

build/%.o: $(C_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<

QR코드 생성기(QR-Maker)

여러개의 QR코드를 동시에 생성하고 출력하기 쉽도록 한장으로 편집이 되게끔 하는 목적으로 진행. ***

선택메뉴

  1. QR코드 한개 생성
  2. QR코드 여러개 생성
  3. 텍스트 파일로부터 QR코드 생성

QR코드 한개 생성

QR코드 데이터 입력 ex) hello

결과물

  • 실행파일 폴더내에 /qr_out/(출력시간)/one_qr에 QR코드 저장

QR코드 여러개 생성

QR코드 여러개 데이터 입력

결과물

  • 실행파일 폴더내에 /qr_out/(출력시간)/multi_qr에 QR코드,데이터 내용이 편집되어 한장으로 저장

텍스트 파일로부터 QR코드 생성

실행폴더내에 QR코드 데이터가 입력된 텍스트 파일 저장

읽으려는 텍스트 파일명을 입력(‘.txt’ 제외하여 입력)

결과물

  • 실행파일 폴더내에 /qr_out/(출력시간)/multi_qr에 QR코드,데이터 내용이 편집되어 한장으로 저장

GitHub QR-Maker

월페이퍼 엔진 스크립트(Button, fade in/out)

각종 보드의 핀맵이나 정의들을 항상 찾기 번거로워 배경화면에서 바로 이용할 수 있는 방안을 고민해서 만들어 보았다.


드래그 앤 드랍으로 화면을 구성해준다.

속성창을 통해 스크립트 편집을 할 수 있다.


메인 배경(wallpaper) 스크립트

'use strict';
let timer=0;
shared.work_state=0;  //shared=외부 변수 참조
shared.bt_state=0;
/**
 * @param {Boolean} value - for property 'visible'
 */
//레이어 메인 반복문
export function update(value) {
	switch(shared.bt_state)
	{
		case 'ardu on':
			fade_in("UNO");
			fade_in("NANO");
			break;
		case 'ardu off':
			fade_out("UNO");
			fade_out("NANO");
			break;
		default:
			console.log('break');
			break;
	}
	return value;
}

//fade in
export function fade_in(value) {
	if(thisScene.getLayer(value).alpha<=1) //alpha=투명도
	{
		timer+= engine.frametime; //월페이퍼 엔진 프레임타임
		thisScene.getLayer(value).alpha=timer*0.7;
	}
	console.log('fade_in');  //콘솔 확인용
	console.log(value);
}

//fade out
export function fade_out(value) {
	if(thisScene.getLayer(value).alpha>=0)
	{
		shared.work_state=1;
		timer-= engine.frametime;
		thisScene.getLayer(value).alpha=timer*0.7;
	}
	else
	{
		shared.work_state=0;
		shared.bt_state=0;
	}
	console.log('fade_out');
	console.log(value);
}

버튼(arduino_bt) 스크립트

'use strict';

/**
 * @param {Boolean} value - for property 'visible'
 */
export function update(value) {
	return value;
}

/**
 * @param {Boolean} value - for property 'visible'
 */
//레이어 초기화
export function init(value) {
	thisScene.getLayer("UNO").alpha=0; //투명도 0
	thisScene.getLayer("NANO").alpha=0;
	thisScene.getLayer("UNO").visible=true;  //보이기 허용
	thisScene.getLayer("NANO").visible=true;
	return value;
}

/**
 * @param {ICursorEvent} event
 */
//레이어 클릭 이벤트 함수
export function cursorClick(event) {
	if(shared.work_state==0){
		if(shared.bt_state=='ardu on')  //외부 변수에 클릭 레이어 저장
			shared.bt_state='ardu off';
		else if(shared.bt_state==0)
			shared.bt_state='ardu on';
		console.log("ardu on");
	}
}

스크립트 실행