본문 바로가기
Linux BSP

[Ubuntu-RaspberryPi] C언어로 U-Boot gpio제어를 통한 led 동작시키는 명령어 추가

by TYB 2024. 2. 13.
반응형

 

Raspberry Pi UBoot 환경 명령어 추가

U-Boot에서 help를 치면 나오는 명령어 모음에 우리가 만든 명령어를 추가하고 싶다.

또한 명령어를 치면 실제로 동작되도록 만들고 싶음.

 


 

ubuntu@ubuntu8:~/pi_bsp/u-boot$ vi arch/arm/cpu/armv7/start.S

여기 127번 라인의 kcci_lcd_test i누르고 #해서 주석처리하고 저장

 

 

 

ubuntu@ubuntu8:~/pi_bsp/u-boot/common$ vi cmd_kcci_led.c


#include <common.h>
#include <command.h>
#include <asm/io.h>

#define BCM2711_GPIO_GPFSEL0 0xFE200000
#define BCM2711_GPIO_GPFSEL1 0xFE200004
#define BCM2711_GPIO_GPFSEL2 0xFE200008
#define BCM2711_GPIO_GPSET0  0xFE20001C
#define BCM2711_GPIO_GPCLR0  0xFE200028
#define BCM2711_GPIO_GPLEV0  0xFE200034

#define GPIO6_9_SIG_OUTPUT   0x09240000
#define GPIO10_13_SIG_OUTPUT 0x00012249

void led_init(void)
{
        writel(GPIO6_9_SIG_OUTPUT,BCM2711_GPIO_GPFSEL0);
        writel(GPIO10_13_SIG_OUTPUT,BCM2711_GPIO_GPFSEL1);
}

void led_write(unsigned long led_data)
{
        writel(0x3fc0, BCM2711_GPIO_GPCLR0);
        led_data = led_data << 6;
        writel(led_data, BCM2711_GPIO_GPSET0);
}
static int do_KCCI_LED(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
        unsigned long led_data;
        if(argc != 2)
        {
                cmd_usage(cmdtp);
                return 1;
        }
        printf("*LED TEST START\n");
        led_init();
        led_data = simple_strtoul(argv[1],NULL,16);
        led_write(led_data);
        printf("*LED TEST END(%s :%#04x)\n\n ", argv[0],(unsigned int)led_data);
        return 0;
}

U_BOOT_CMD(
                led,2,0,do_KCCI_LED,
                "led -kcci LED Test.",
                "number - Input argument is only one. (led [0x00~0xff])\n");

코드 봐보면 

 

 

U_BOOT_CMD(
                led,2,0,do_KCCI_LED,
                "led -kcci LED Test.",//help칠때 나오는 설명문
                "number - Input argument is only one. (led [0x00~0xff])\n");//usage에 나오는 설명

위 코드의 U_BOOT_CMD는 UBOOT에서 이미 구조체로 정의를 해놓은 거임. 저런식으로 적어놓으면 

do_KCCI_LED를 호출하기 전에 arg count를 확인해서 안맞으면 사용법을 알려주고 종료.

만약 맞다면 그대로 실행해서 argv[1] 대로 해당bit gpio write

static int do_KCCI_LED(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
        unsigned long led_data;
        if(argc != 2)
        {
                cmd_usage(cmdtp);
                return 1;
        }
        printf("*LED TEST START\n");
        led_init();
        led_data = simple_strtoul(argv[1],NULL,16);
        led_write(led_data);
        printf("*LED TEST END(%s :%#04x)\n\n ", argv[0],(unsigned int)led_data);
        return 0;
}

 

asm/io.h에writel, readl, write 등의 함수가 들어가있음.

 

 

simple_strtoul(simple string to unsigned long) 파일시스템이 마운트 되지 않은 그 전에 동작하는 코드이므로 표준 C가 아니라 U-Boot에서 사용하는 정적 라이브러리 제공 함수임.  

 

 

 U-Boot> led
led - led -kcci LED Test.

Usage:
led number - Input argument is only one. (led [0x00~0xff])

 

 

 

 

ubuntu@ubuntu8:~/pi_bsp/u-boot/common$ vi Makefile

O눌러서 8번줄 위에 이거 추가

  8 obj-y += cmd_kcci_led.o

 

넣고

ubuntu@ubuntu8:~/pi_bsp/u-boot$ make clean


ubuntu@ubuntu8:~/pi_bsp/u-boot$ ./build.sh

Make할 때 CC    common/cmd_kcci_lcd.o 가 들어가있어야함.

cc common/cmd_kcci_led.o 나오죠?

 

 

nfs 서버로 파일 옮기고 라즈베리파이에서도 그걸 /boot/firmware/u-boot.bin으로 복사해주고 재부팅하면됨.

그리고 아무 키나 눌러서 U-Boot 으로 들어감.

그리고 나서 help치면

우리가 추가한 명령어가 나옴

 

U-Boot> led
led - led -kcci LED Test.

Usage:
led number - Input argument is only one. (led [0x00~0xff])

명령어를 잘 못  치면 사용방법을 알려주는 메시지가 나오고

led 명령어 뒤에 몇번 bit를 켤 것인지를 정해주면 됨.

led 0xff하면 8개 다 켜지고 0x00하면 다 꺼지고

 U-Boot> led 0xff
*LED TEST START
*LED TEST END(led :0xff)


U-Boot> led 0x00
*LED TEST START
*LED TEST END(led :0x00)

 

 

 

 


윗 코드에는 문제가 있긴함.

 

기존에 메모리에 있는 값을 싹다 0으로 바꿔버리고 write를 해버렸는데 시스템을 수정할 때는 좋은 방법이 아님.

 

시스템 메모리 값을 수정할 때는 반드시 3가지 과정을 거치는게 좋음

 

1. read

2. modify

    ㄱ.(mask를 만들어서 내가 write할 곳은 0으로 채우고 write안할 곳은 1로 채운 채로 read해온 값과 and연산)

    ㄴ.(and 연산 된 값을 내가 write할 값과 or연산을 하면 write안할 곳은 원래 데이터로 채워지고 내가 write할 부분만 건드           리는거임.)

3. write

 

코드를 수정해보면

 15 void led_init(void)
 16 {
 17     unsigned long temp;
 18     temp = readl(BCM2711_GPIO_GPFSEL0);
 19     temp = temp & 0x0003ffff;
 20     temp = temp | GPIO6_9_SIG_OUTPUT;
 21     writel(temp,BCM2711_GPIO_GPFSEL0);
 22
 23     writel(GPIO10_13_SIG_OUTPUT,BCM2711_GPIO_GPFSEL1);
 24 }

이렇게 됨.

 

근데 이게 어셈블리로 바뀌면 코드 양이 상당히 길어짐.

 

그래서 나오는 개념이 bit banding이라는 개념인데 

요약하면, 메모리가 남을 때, 특정 영역의 주소에 대해서 bit 하나마다 하나의 주소를 다 할당해놓은 거임. 1bit의 1개의주소를 할당.

그래서 그 주소를 통해 1bit만 수정이 가능하게 만들어 놓은 걸 비트 밴딩이라고 함.

 

그러면 속도가 훨씬 빠름. 

 

지원 되는 MCU가 있고 안되는 것도 있음.

 

자세한 건 아래 링크를 보고 공부..

 

Cortex M3 비트밴딩(Bit Banding)에 대해서

  AVR과 같은 MCU를 다루다가 Cortex로 와서 이해하기 쉽지 않은 내용입니다. 비트밴딩. 저번...

blog.naver.com

 


 26 void led_write(unsigned long led_data)
 27 {
 28     writel(0x3fc0, BCM2711_GPIO_GPCLR0);
 29     led_data = led_data << 6;
 30     writel(led_data, BCM2711_GPIO_GPSET0);
 31 }

 

0x3fc0은 gpio 6~13까지의 값을 1로 설정해놓은거고

led_data입력을 0xff로 주므로, led_data를 좌측으로 6번 시프트하면 사용자가 원하는 값이 각각의 led의 gpio에 맞춰서 정렬됨.

반응형