int gpioLedInit(void);
void gpioLedSet(long val);
void gpioLedFree(void);
int gpioKeyInit(void);
int gpioKeyGet(void);
void gpioKeyFree(void);
int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
void gpio_set_value(unsigned gpio, int value);
int gpio_get_value(unsigned gpio);
int gpio_to_irq(unsigned gpio);
void free_irq(unsigned int irq, void* dev_id);
이 함수명들은 커널 도큐먼트에서 가져온 함수명들이다.
참고한 도큐먼트의 경로는 아래와 같다.
ubuntu@ubuntu14:~/pi_bsp/kernel/linux/Documentation/driver-api/gpio$ vi legacy.rst
해당 파일 안에 있는 내용은 리눅스 "man"(manual page, 메뉴얼페이지)에 대응하는 내용이다.
디바이스 드라이버는 커널에서 제공해주는 표준 시스템 콜 함수 API(open, read, write, close, ...)를 이용해서 만들 것이다.
int gpio_request(unsigned gpio, const char *label)
내가 사용하려는 gpio핀이 사용중이라면 -1(불가능)을 리턴, 사용중이 아니라면 0(가능)을 리턴한다. 라벨은 사용할 gpio에 등록할 문자열 라벨을 설정하는 것이다.
free()는 다 사용한 gpio자원을 해제하는 것이다.
gpio_direction_input()으로 gpio를 input모드로 사용할 수 있다.
gpio_direction_output()으로 gpio를 output모드로 사용할 수 있다. (파라미터로 쓰인 value는 초깃값을 보통 설정한다. 0= low, 1= high)
gpio_set_value()로 output 모드 상태의 gpio에 값을 쓴다.(0= CLR레지스터에 1을 쓴다. 1= SET레지스터에 1을 쓴다.)
gpio_get_value()로 input 모드 상태의 gpio에 값을 쓴다.(LEV레지스터의 값을 읽어온다.)
gpio_to_irq()는 스위치에 인터럽트 기능을 활성화 하고 싶을 때 사용한다.
free_irq()는 인터럽트 기능을 비활성화 하고 싶을 때 사용한다.
위 API는 그냥 우리가 STM32Cube에서 썼던 HAL 라이브러리 function들이 커널에서는 어떻게 구현되어있는지의 느낌으로 이해하면 되겠다.
[[ LED 하나만 껏다켰다 하기 ]]
## 시스템 콜 함수 코드
- 주의할 점: gpio_request() 했으면 gpio_free() 해줘야, 다음 시스템 콜 함수에서도 다시 gpio를 사용할 수 있다.
## 어플리케이션 코드
[[ LED 여러개 껐다 켰다 하기 ]]
## 시스템 콜 함수 코드
- 주의할 점: "led0\0"라는 gpio라벨을 저장하기 위한 char gpioName[6];
- C에서 가장 조심해야 하는 것은 buffer overflow이다. C에서 버퍼 크기를 넘어서 값을 다른 변수가 잡고 있는 공간을 침범해서 써버리기 때문에 디버깅이 정말 힘들어진다.
- 메모리를 어떻게든 아끼기 위해서 배열을 완전 필요에 딱 맞게 사용하다보니까, 숙련자도 버퍼 오버플로우를 범하는 실수를 범하곤 한다.
- 버퍼 오버플로우가 일어나면 프로그램이 이상하게 동작하고 디버깅이 정말정말 힘들어진다.
## 어플리케이션 코드
## 어플리케이션 실행화면
85를 input값으로 준 이유는 85가 16진수로 하면 0x55라서 8개의 LED를 "0101 0101" - "1010 1010"의 형태로 반짝반짝하게 이쁘게 반복점멸해주기 때문이다.
void gpioLedSet(long val);
void gpioLedFree(void);
int gpioKeyInit(void);
int gpioKeyGet(void);
void gpioKeyFree(void);
asmlinkage long sys_mysyscall(long val)
{
// printk(KERN_INFO "Welcome to KCCI's Embedded System!! app value=%ld\n", val);
int ret;
// LED 관련
ret = gpioLedInit();
if (ret < 0)
return ret;
gpioLedSet(val);
gpioLedFree();
// KEY 관련
ret = gpioKeyInit();
if (ret < 0)
return ret;
ret = gpioKeyGet();
gpioKeyFree();
return ret;
}
int gpioKeyInit(void)
{
int i;
int ret = 0; // 정상종료 초기값 // 리턴 값이 있는 함수가 모인 코드 블럭을 함수화 할대는 그 형을 맞춰주자
char gpioName[10];
for (i = 0; i < GPIOKEYCNT; i++)
{
sprintf(gpioName, "key%d", gpioKey[i]);
ret = gpio_request(gpioKey[i], gpioName);
if (ret < 0)
{
printk("Failed gpio_request() gpio%d error\n", i);
return ret;
}
ret = gpio_direction_input(gpioKey[i]);
if (ret < 0)
{
printk("Failed gpio_direction_input() gpio%d error\n", i);
return ret;
}
}
return ret;
}
int gpioKeyGet(void)
{
// 여기서 for문 돌면서 순회해서 찾는거네
int ret;
int i;
int keyData = 0x00;
for (i = 0; i < GPIOKEYCNT; i++)
{
ret = gpio_get_value(gpioKey[i]);
keyData = keyData | (ret << i);
}
return keyData;
}
void gpioKeyFree(void)
{
int i;
for (i = 0; i < GPIOKEYCNT; i++)
{
gpio_free(gpioKey[i]);
}
}
int gpioLedInit(void)
{
int i;
int ret = 0; // 정상종료 초기값 // 리턴 값이 있는 함수가 모인 코드 블럭을 함수화 할대는 그 형을 맞춰주자
char gpioName[10];
for (i = 0; i < GPIOLEDCNT; i++)
{
sprintf(gpioName, "led%d", i);
ret = gpio_request(gpioLed[i], gpioName);
if (ret < 0)
{
printk("Failed gpio_request() gpio%d error\n", i);
return ret;
}
ret = gpio_direction_output(gpioLed[i], OFF);
if (ret < 0)
{
printk("Failed gpio_direction_output() gpio%d error\n", i);
return ret;
}
}
return ret;
}
void gpioLedSet(long val)
{
int i;
for (i = 0; i < GPIOLEDCNT; i++)
{
gpio_set_value(gpioLed[i], (val >> i) & 0x01);
}
}
void gpioLedFree(void)
{
int i;
for (i = 0; i < GPIOLEDCNT; i++)
{
gpio_free(gpioLed[i]);
}
}
프로그램의 데이터 영역의 read only영역에 들어가는 폰트 데이터 같은 것들은 2kb밖에 안되는 SRAM에 기본적으로 올라가게 되는데, 그러면 SRAM이 너무 부족해서 변수를 많이 못써서 기능 구현을 못하는 경우가 생길 수가 있다. 그럴 경우는 어떤 라이브러리를 사용해서 그나마 32kb로 좀 여유가 있는 플래시 메모리에 옮겨놓을 수 있다.
cpu가 한 클럭에 한번에 32비트 데이터 버스로 꽝꽝 메모리 접근이 가능하기 때문이다. 만약 4의 배수고 aligned 되어있지 않다면, 해당 위치에 접근하기 위해서 or|and같은 연산들이 추가되어서 속도가 느려진다.
블루투스 모듈은 0.1~0.2와트 정도의 소비전력만 먹는다.
하지만 와이파이 모듈은 1~2와트 정도의 소비전력을 먹는다.
요즘 집에 들어오는 built-in IOT 가구들은 대부분 와이파이 모듈을 통해서 제어된다.
그래서 그런 IOT 장비들이 쌓이고 쌓이면 전기를 꽤 많이 먹는다.
그래서 그런거를 전부 블루투스를 통해서 제어하도록 하면 대기전력을 많이 아낄 수 있다.
블루투스로도 핑이 된다(?)