리눅스 SPI 드라이버

EEEFFEE·2024년 1월 8일
0

임베디드 리눅스

목록 보기
9/14

24.01.08 최초 작성

1. SPI

  • 동기식 통신 방법으로 마스터와 슬레이브 모드로 동작

  • 속도 제한이 없어 빠름

  • CS를 통해 subnode 구분

SPI CLK, SCLKCSMOSIMISO
ClockChip SelectMain out, Subnode inMain in, Subnode out

1.1 SPI Mode

  • 타켓 디바이스에 맞는 모드로 설정해야 함

  • CS에 전원이 인가되지 않을 때 동작

SPI Mode 0SPI Mode 1SPI Mode 2SPI Mode 3
clock idlelowlowhighhigh
Rising edge일 때
data 읽음
MOSIMISOMOSIMISO
Falling edge일 때
data 읽음
MISOMOSIMISOMOSI

2. 리눅스에서 SPI

2.1 SPI bus Driver

  • /driver/spi에 위치하며 보드마다 다른 코드 사용

  • spi_controller표준 인터페이스의 함수 포인터에 등록해 사용

  • .dtsi파일이 존재해 부팅 시 SOC에 맞는 드라이버가 등록됨

2.2 SPI의 디바이스 트리 구조

  • /arch/arm/boot/dts/bcm2711.dtsi : brcm,bcm2835-spi와 매칭되는 디바이스 드라이버를 찾아 .probe함수 실행 시킴

...	
		spi3: spi@7e204600 {
			compatible = "brcm,bcm2835-spi";
			reg = <0x7e204600 0x0200>;
			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&clocks BCM2835_CLOCK_VPU>;
			#address-cells = <1>;
			#size-cells = <0>;
			status = "disabled";
		};
        
...

  • /drivers/spi/spi-bcm2835.c : brcm,bcm2835-spi와 매칭되는 디바이스 드라이버


...

static const struct of_device_id bcm2835_spi_match[] = {
	{ .compatible = "brcm,bcm2835-spi", },
	{}
};
MODULE_DEVICE_TABLE(of, bcm2835_spi_match);

static struct platform_driver bcm2835_spi_driver = {
	.driver		= {
		.name		= DRV_NAME,
		.of_match_table	= bcm2835_spi_match,
	},
	.probe		= bcm2835_spi_probe,
	.remove		= bcm2835_spi_remove,
	.shutdown	= bcm2835_spi_shutdown,
};

2.3 User SPI Interface

  • user레벨에서 SPI통신을 조작(속도 조절 등)할 수 있는 인터페이스 존재

  • /dev/spidev0.0을 통해 조작 가능하며 해당 파일을 통해 조작할지 디바이스 드라이버를 조작할지 디바이스 트리에서 결정 가능

예제

dts파일 수정

  • /arch/arm/boot/dts/bcm2711.dtsi에 설정된 spi 디바이스 드라이버의 속성을 무시하기 위해 disable_spidev.dts파일을 boot파티션의 overlay에 저장

...
&spi0 {
	pinctrl-names = "default";
	pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
	cs-gpios = <&gpio 8 1>, <&gpio 7 1>;

	spidev0: spidev@0{
		compatible = "spidev";
		reg = <0>;	/* CE0 */
		#address-cells = <1>;
		#size-cells = <0>;
		spi-max-frequency = <125000000>;
	};

	spidev1: spidev@1{
		compatible = "spidev";
		reg = <1>;	/* CE1 */
		#address-cells = <1>;
		#size-cells = <0>;
		spi-max-frequency = <125000000>;
	};
};
...

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2708";

    fragment@0 {
        target = <&spidev0>;

        __overlay__ {
            status = "disabled";
        };
    };
};

디바이스 드라이버 작성

  • k_spi.c : 디바이스 ID를 출력

  • 코드에서 디바이스 정보 등록

#define TOY_SPI_BUS_NUM 0

struct spi_board_info spi_device_info = {
	.modalias = "bmp280",							//장치 이름
	.max_speed_hz = 1000000,						//통신 속도
	.bus_num = TOY_SPI_BUS_NUM,						//0번 spi interface 사용
	.chip_select = 0,								//cs번호 지정
	.mode = 3,										//spi 통신 모드 설정
};

  • k_module_init() :

static int __init k_module_init(void)
{
	...
    
    master = spi_busnum_to_master(TOY_SPI_BUS_NUM);				//spi통신에서 master의 정보 가져옴
	if (!master) {
		pr_err("Error! spi bus with Nr. %d\n", TOY_SPI_BUS_NUM);
		goto reg_error;
	}

	bmp280_dev = spi_new_device(master, &spi_device_info);		//디바이스 정보를 입력받아 장치를 조작할 
    															//수 있는 인터페이스 생성
	if(!bmp280_dev) {
		pr_info("Could not spi create device!\n");
		goto reg_error;
	}

	bmp280_dev->bits_per_word = 8;								//word 설정

	if(spi_setup(bmp280_dev) != 0){								//디바이스 인터페이스 초기화
		pr_info("Could not change bus setup!\n");
		spi_unregister_device(bmp280_dev);
		goto spi_error;
	}

	id = spi_w8r8(bmp280_dev, 0xD0);							//디바이스의 ID 읽어옴
	pr_info("ID: 0x%x\n", id);

	return 0;
    
    ...
}

타겟에 업로드

  • disable_spidev.dtbo, k_spi.ko 생성

  • boot 디렉토리 마운트하고 disable_spidev.dtbo/boot/overlays에 옮김

  • /boot/config.txtdtoverlay=disable_spidev, dtparam=spi=on 입력, 저장 후 재부팅

  • modprobe spi_bcm2835 실행

  • insmod k_spi.ko로 디바이스 드라이버 등록

  • 핀 설정 확인

    • mount -t debugfs debugfs /sys/kernel/debug
    • cd /sys/kernel/debug
    • cat pinctrl/pinctrl-maps

0개의 댓글

관련 채용 정보