前一阶段由于不熟悉linux的设备驱动程序的编写又找不到资料。所以就暂时把SPI设备的实验给搁置了。前几天看到网友的测评很受启发就又开始了解SPI的驱动。初步的对linux驱动有了一些认识后,开始SPI设备的实验。如果想使用SPI设备就要从设备树的配置开始。linux的设备树其实就是一种设备信息的配置文件。有点象JSON、XML文件的那种。设备树文件的主要思想就是让设备驱动和设备配置分开。这样编写设备驱动就和具体设备的BPS没有关系了。驱动程序只针对这类设备编写就可以。不同要求的具体板卡如果想使用某种外设,只要在设备的“配置文件”把这种外设的信息配置一下,驱动程序就可以工作了。就象我们测试的STM32MP157A-DK1板卡默认是不打开SPI5设备的,SPI5的PIN默认为GPIO。你的设备如果需要默认使用SPI5,打开SPI5的配置就可以。不需要针对具体设备编写驱动程序。设备树这种机制又可以将同类型的设备抽象成一种驱动,通过配置修改一下寄存器地址就可以驱动不同的同类设备。比如SPI、I2C、CAN、Uart等等字符设备。又如Uart设备的驱动操作方法是一样的,只是配置不一样而已。这样就没必要写多个驱动程序了。不同的uart使用同一个驱动只是各自的地址不一样而已。
如果想使用STM32MP157A-DK1的SPI5设备。首先需要在arch/arm/boot/dts/stm32mp157a-dk1.dts中进行修改配置。
&spi5 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi5_pins_a>;
pinctrl-1 = <&spi5_sleep_pins_a>;
cs-gpios = <&gpiog 15 GPIO_ACTIVE_LOW>;
cd-gpios = <&gpiod 7 GPIO_ACTIVE_LOW>;
status = "okay";
spidev0: spidev@0 {
compatible = "rohm,dh2228fv";
reg = <0>;
spi-max-frequency = <50000000>;
};
};
spi5引脚配置项pinctrl-0和pinctrl-1在包含文件.dtsi中 ,这些是SPI的引脚配置。我的SD1306还需要CS和CD两个控制引脚。同时也在这次声明中配置。其实cs-gpios和cd-gpios这两个引脚使用默认配置也可以。arch/arm/boot/dts/stm32mp157-pinctrl.dtsi
spi5_sleep_pins_a: spi5-sleep-0 {
pins {
pinmux = <STM32_PINMUX('F', 7, ANALOG)>, /* SPI5_SCK */
<STM32_PINMUX('F', 8, ANALOG)>, /* SPI5_MISO */
<STM32_PINMUX('F', 9, ANALOG)>; /* SPI5_MOSI */
};
};
gpiod: gpio@50005000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x3000 0x400>;
clocks = <&rcc GPIOD>;
st,bank-name = "GPIOD";
status = "disabled";
};
gpiog: gpio@50008000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x6000 0x400>;
clocks = <&rcc GPIOG>;
st,bank-name = "GPIOG";
status = "disabled";
};
可以看到SPI5的引脚配置(spi5_sleep_pins_a)与资料上的一致。为PF7、 PF8、PF9这些是芯片引脚功能编号。设备树还配置了其它参数。这些配置可以在spidev.c驱动中找到。如:spidev0: spidev@0设置了驱动文件spidev.c和驱动中的功能compatible 参考下面的代码
static struct class *spidev_class;
#ifdef CONFIG_OF
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
#endif
#ifdef CONFIG_ACPI
驱动程序中并没有看到使用这些引脚的代码,驱动只是使用了配置中的寄存器而已。SPI4和SPI5的驱动没有什么差别。只要配置好SPI5的设置,SPI5功能就可以使用了,且SPI的三个主要功能引脚全部设置完成。驱动程序配置完成后只需要编写应用层驱动程序了,也可以说只要文件向/dev/spidev0.0里面灌数据了就可以驱动SPI5了。我用的是SD1306 OLED SPI串口屏,除了SPI引脚还需要两个控制引脚(CS和CD),注意CS引脚默认为"低",使能打开。CD引脚的功能是命令选择。程序主要参考了tools\spi\spidev_test.c文件,其中主要的API有两个,
#include <linux/gpio.h> 其中的API函数 gpio_data.values用来设置cs-gpios引脚的状态
#include <linux/spi/spidev.h>主要使用了其中的宏定义,如:ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);当中的SPI_IOC_WR_MAX_SPEED_HZ等定义。
主要的文件两个
const char gpio_dev_cs = "/dev/gpiochip3";
const char gpio_dev_ds = "/dev/gpiochip5";
const char spidev_name = "/dev/spidev0.0";
int cd_gpio_set(void)
{
int ret;
ret = ioctl(cd_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &gpio_data);
if (ret == -1) {
ret = -errno;
fprintf(stderr, "Failed to issue %s (%d)\n",
"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
}
return ret;
}
void send_byte_spi(unsigned char data)
{
int status;
spi_tx[0] = data;
status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
if (status < 0)
{
perror("SPI_IOC_MESSAGE");
return;
}
}
用户程序参考SD1306的程序就可以。