본문 바로가기
Firmware Programming

[Ubuntu-RaspberryPi] C언어로 U-Boot gpio제어를 통한 led 동작 및 버튼 제어

by TYB 2024. 2. 14.
반응형

구현 목표는 led 0xff 명령어를 치면 이름 이니셜을 띄워주면서 led 작동시키고

button을 누르면 8개의 버튼 중 해당 버튼에 맞게 O X를 출력해주고

해당 버튼에 맞는 led만 on시켜주기

+ 8번 버튼 누르면 프로그램 종료시키기

 

#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 0x00000249
#define GPIO16_19_SIG_OUTPUT 0x00000000
#define GPIO20_23_SIG_OUTPUT 0x00000000

#define LED_6_9_SIG_MASK     0x0003ffff
#define LED_10_13_SIG_MASK   0x3ffff000
#define KEY_16_19_SIG_MASK   0x0003ffff
#define KEY_20_23_SIG_MASK   0x3ffff000

#define LED6_13_STATUS_MASK  0x00003fc0
#define KEY16_23_STATUS_MASK 0x00ff0000

void led_init(void)
{
        unsigned long led_data;
        led_data = readl(BCM2711_GPIO_GPFSEL0);
        led_data = led_data & LED_6_9_SIG_MASK;
        led_data = led_data | GPIO6_9_SIG_OUTPUT;
        writel(led_data,BCM2711_GPIO_GPFSEL0);


        led_data = readl(BCM2711_GPIO_GPFSEL1);
        led_data = led_data & LED_10_13_SIG_MASK;
        led_data = led_data | GPIO10_13_SIG_OUTPUT;
        writel(led_data,BCM2711_GPIO_GPFSEL1);
}

void key_init(void)
{
        unsigned long key_data;
        key_data = readl(BCM2711_GPIO_GPFSEL1);
        key_data = key_data & KEY_16_19_SIG_MASK;
        key_data = key_data | GPIO16_19_SIG_OUTPUT;
        writel(key_data, BCM2711_GPIO_GPFSEL1);

        key_data = readl(BCM2711_GPIO_GPFSEL2);
        key_data = key_data & KEY_20_23_SIG_MASK;
        key_data = key_data | GPIO20_23_SIG_OUTPUT;
        writel(key_data, BCM2711_GPIO_GPFSEL2);
}

void led_write(unsigned long led_data)
{
        writel(0x3fc0, BCM2711_GPIO_GPCLR0);

//      unsigned long original_data = readl(BCM2711_GPIO_GPCLR0) & LED6_13_STATUS_MASK;
        //writel(led_data, BCM2711_GPIO_GPCLR0);
//      led_data = original_data | led_data;
        writel(led_data, BCM2711_GPIO_GPSET0);
}

void key_read(unsigned long* key_data)
{
        *key_data = readl(BCM2711_GPIO_GPLEV0) & KEY16_23_STATUS_MASK;
}



static int do_KCCI_LED(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
        unsigned long led_data;
        unsigned long key_data;
        uint16_t curr_key=0xff;
        uint16_t prev_key=0x00;
        bool end_flag = false;
        if(argc != 2)
        {
                cmd_usage(cmdtp);
                return 1;
        }
        printf("*LED TEST START(WCM)\n");
        led_init();
        led_data = simple_strtoul(argv[1],NULL,16);
        led_write(led_data << 6);

        key_init();

        do{
                key_read(&key_data);
                curr_key = (key_data >> 16) & 0xFF;
                if((curr_key != prev_key) && (curr_key != 0x00))
                {
                        printf("0:1:2:3:4:5:6:7\n");
                        prev_key = curr_key;
                //      printf("\n\n*(curr key : %#02x) ", (unsigned int)curr_key);
                        for(int i = 0; i < 8; i++)
                        {
                                if(curr_key & (1 << i)) {
                                        printf("O ");
                                        if(i==7){
                                                end_flag = true;
                                        }
                //                      printf("*(led_data : %#lx) ", (unsigned long)key_data >> 10);
                                        led_write(key_data >> 10);
                                }
                                else
                                {
                                        printf("X ");
                                }
                        }
                        printf("\n");
                }
        }while(!end_flag);

        printf("*LED TEST END(key : %#02x)\n\n ", (unsigned int)curr_key);
        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");

 

비트를 왜 그렇게 부여했는지 보고싶다면 아래 링크랑 사진 참고

 

[Linux BSP] U-Boot 어셈블리어로 메모리 read/write로 led on/off 하기

data sheet를 보면 gpio의 주소 및 입출력 주소가 모두 나옴. 아래 주소에서 라즈베리파이4를 찾아서 data sheet를 받으면 됨. https://datasheets.raspberrypi.com/ Raspberry Pi Datasheets datasheets.raspberrypi.com 매뉴얼 65

program-developers-story.tistory.com

 

 

 

실제 동작 영상임.

 

 


아래는 좀 더 펌웨어적으로 구현한 코드임.

 

구현 중점은

1. printf의 지연시간을 줄이기 위해 putc나 puts 사용

2. 코드에는 적용안되어 있지만 가독성 향상을 위해 마스크 연산자는 사용자가 건드릴 비트를 1 해놓고 사용할 때 ~로 not해주기

3. 불필요한 변수는 최대한 생략

4. for 문 안에 index 같은 경우도 for문과 함께 선언하지 말고 지역변수로 선언할 것.

#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 	0x00000249  

struct cmd_tbl_t;
void led_init(void)
{
	unsigned long temp;
	temp = readl(BCM2711_GPIO_GPFSEL0);
	temp = temp & 0x0003ffff;
//	temp = temp & ~0xfffc0000;
	temp = temp | GPIO6_9_SIG_OUTPUT;
	writel(temp,BCM2711_GPIO_GPFSEL0);  	
//	writel(GPIO6_9_SIG_OUTPUT,BCM2711_GPIO_GPFSEL0);  	

	temp = readl(BCM2711_GPIO_GPFSEL1);	//gpio10~13 read
	temp = temp & 0x3ffff000; 			//gpio10~13 clear
	temp = temp | GPIO10_13_SIG_OUTPUT;//gpio10~13 output
	writel(temp,BCM2711_GPIO_GPFSEL1);  //gpio10~13 write	
//	writel(GPIO10_13_SIG_OUTPUT,BCM2711_GPIO_GPFSEL1);  
}
void led_write(unsigned long led_data)
{
	writel(0x3fc0,BCM2711_GPIO_GPCLR0); 	//led all off
	led_data = led_data << 6;
	writel(led_data,BCM2711_GPIO_GPSET0);  //ledX on
}
void key_init(void)
{
	unsigned long temp;
	temp = readl(BCM2711_GPIO_GPFSEL1);
	temp = temp & 0x0003ffff;			//gpio16~20 input
	writel(temp,BCM2711_GPIO_GPFSEL1);  	

	temp = readl(BCM2711_GPIO_GPFSEL2);
	temp = temp & 0x3ffff000;			//gpio21~23 input
	writel(temp,BCM2711_GPIO_GPFSEL2);
}
void key_read(unsigned long * key_data)
{
	*key_data =(readl(BCM2711_GPIO_GPLEV0) >> 16) & 0x000000ff;
}
static int do_KCCI_LED(struct cmd_tbl *cmdtp,int flag, int argc, char * const argv[])
{
	unsigned long led_data;
	unsigned long key_data;
	unsigned long key_data_old=0;
	unsigned long i;
  	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);

	key_init();
	do {
		key_read(&key_data);
		if(key_data != key_data_old)
		{
			if(key_data)
			{
				led_write(key_data);
				puts("0:1:2:3:4:5:6:7\n");
				for(i=0;i<8;i++)
				{
					if(key_data & (0x01 << i))
						putc('O');
					else
						putc('X');
					if(i !=7 )
						putc(':');
					else
						putc('\n');
				}
				putc('\n');
			}
			key_data_old = key_data;
			if(key_data == 0x80)
				break;
		}
	} while(1);

	printf("*LED TEST END(%s : %#04x)\n\n ",argv[0],(unsigned int)key_data);
	return 0;
}
U_BOOT_CMD(
	led,2,0,do_KCCI_LED,
	"kcci LED Test.(WCM)",
	"number - Input argument is only one.(led [0x00~0xff])\n");

 

 


번외로 아래 명령어 쳐보면

ubuntu@ubuntu8:~/pi_bsp/u-boot$ vi u-boot.map

각각의 파일(좌측)과 메모리주소(우측)가 저장되어 있는 것을 볼 수 있음.

 

 

 

 


여기까지 하면서 배울건 우리가 평소에 주변 입출력장치, GPIO를 다루면서 편하게 썼던 라이브러리들의 LOW LEVEL단에는 이러한 코드들이 돌아가고 있다. 정도 알면 될 듯

반응형