리눅스 환경에서 SPI 통신을 사용하는 방법과 BMP280 센서에서 데이터를 읽어오는 과정을 알아보겠습니다. I2C 통신에 대한 개념과 SPI 통신의 기본 원리, 리눅스 커널에서의 SPI 지원 그리고 실제 BMP280 센서와의 연결 및 데이터 읽기에 대해 살펴보겠습니다.


| 모드 | CPOL (Idle 상태) | CPHA (샘플링 엣지) |
|---|---|---|
| 0 | Low | Rising (MOSI), Falling (MISO) |
| 1 | Low | Falling (MOSI), Rising (MISO) |
| 2 | High | Rising (MOSI), Falling (MISO) |
| 3 | High | Falling (MOSI), Rising (MISO) |
// drivers/spi/spi-bcm2835.c
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,
};
module_platform_driver(bcm2835_spi_driver);
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
int main() {
int fd;
unsigned char tx_buf[2] = {0x01, 0x00}; //예시 데이터
unsigned char rx_buf[2];
fd = open("/dev/spidev0.0", O_RDWR);
if (fd < 0) {
perror("Error opening SPI device");
return -1;
}
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx_buf,
.rx_buf = (unsigned long)rx_buf,
.len = 2, // 전송할 바이트 수
.speed_hz = 1000000, // SPI 클럭 속도 ex)1MHz
.bits_per_word = 8,
};
if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0) {
perror("Error sending SPI message");
close(fd);
return -1;
}
printf("Received: 0x%02x 0x%02x\\n", rx_buf[0], rx_buf[1]);
close(fd);
return 0;
}
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2708";
fragment@0 {
target = <&spi0>;
__overlay__ {
status = "disabled";
};
};
};
arch/arm/boot/dts/bcm2711-rpi-4-b.dts 에서 spidev 관련 설정
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#define BUF_SIZE 1024
#define DRIVER_NAME "k_spi_driver"
#define DRIVER_CLASS "k_spi_class"
#define TOY_SPI_BUS_NUM 0
static struct spi_device *bmp280_dev;
struct spi_board_info spi_device_info = {
.modalias = "bmp280",
.max_speed_hz = 1000000,
.bus_num = TOY_SPI_BUS_NUM,
.chip_select = 0,
.mode = 3,
};
static int __init k_module_init(void)
{
struct spi_master *master;
u8 id;
pr_info("k_spi_driver loaded\\n");
master = spi_busnum_to_master(TOY_SPI_BUS_NUM);
if (!master) {
pr_err("Error! spi bus with Nr. %d\\n", TOY_SPI_BUS_NUM);
return -ENODEV;
}
bmp280_dev = spi_new_device(master, &spi_device_info);
if (!bmp280_dev) {
pr_info("Could not create spi device!\\n");
return -ENODEV;
}
bmp280_dev->bits_per_word = 8;
if (spi_setup(bmp280_dev) != 0) {
pr_info("Could not change bus setup!\\n");
spi_unregister_device(bmp280_dev);
return -EFAULT;
}
// BMP280 ID 레지스터(0xD0) 읽기
id = spi_w8r8(bmp280_dev, 0xD0);
pr_info("ID: 0x%x\\n", id);
return 0;
}
static void __exit k_module_exit(void)
{
pr_info("k_spi_driver unloaded\\n");
if (bmp280_dev) {
spi_unregister_device(bmp280_dev);
}
}
module_init(k_module_init);
module_exit(k_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple SPI driver for BMP280");
코드 설명

/boot/config.txt 파일을 수정하여 dtoverlay=disable_spidev 를 추가
dtparam=spi=on 이 활성화되어 있는지 확인
변경 사항을 저장하고 재부팅
modprobe spi_bcm2835 명령어로 SPI 모듈을 로드
명령어로 핀 설정을 확인
mount -t debugfs debugfs /sys/kernel/debug
cat /sys/kernel/debug/pinctrl/pinctrl-maps