구현 목표는 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단에는 이러한 코드들이 돌아가고 있다. 정도 알면 될 듯
'Firmware Programming' 카테고리의 다른 글
| [ARM] ARM 어셈블리어 분석 (0) | 2024.02.13 | 
|---|---|
| [ARM] ARM 프로세서 기초 (0) | 2024.02.13 | 
| [Firmware Programming] C++ ESP32-CAM 보드 UDP camera frame 패킷 순서 제어 및 실시간 전송 (2) | 2024.02.10 | 
| [Firmware Programming] ESP32-CAM 보드 UDP camera frame 패킷 순서 제어 및 실시간 전송 (1) | 2024.02.10 | 
| [Firmware Programming] ESP32-CAM 보드 usb로 upload 하기 (1) | 2024.02.08 | 

