树莓派 -- oled


硬件

SPI0,CE0
这里写图片描述

SPI Master Driver

设备树

arch\arm\boot\dts\bcm2710-rpi-3-b.dts

&gpio {
    spi0_pins: spi0_pins {
        brcm,pins = <9 10 11>;
        brcm,function = <4>; /* alt0 */
    };

    spi0_cs_pins: spi0_cs_pins {
        brcm,pins = <8 7>;
        brcm,function = <1>; /* output */
    };
}
&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>;
    };
};

spi0

arch\arm\boot\dts\bcm283x.dtsi

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;

        spi: spi@7e204000 {
            compatible = "brcm,bcm2835-spi";
            reg = <0x7e204000 0x1000>;
            interrupts = <2 22>;
            clocks = <&clocks BCM2835_CLOCK_VPU>;
            #address-cells = <1>;
            #size-cells = <0>;
            status = "disabled";
        };

    }

arch\arm\boot\dts\bcm270x.dtsi

    soc: soc {
        spi0: spi@7e204000 {
            /* Add alias */
            dmas = <&dma 6>, <&dma 7>;
            dma-names = "tx", "rx";
        };
    }

bcm2835-spi

drivers\spi\spi-bcm2835.c

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,
};

compatible

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

probe函数

static int bcm2835_spi_probe(struct platform_device *pdev)
{
    struct spi_master *master;
    struct bcm2835_spi *bs;
    struct resource *res;
    int err;

    master = spi_alloc_master(&pdev->dev, sizeof(*bs));
    if (!master) {
        dev_err(&pdev->dev, "spi_alloc_master() failed\n");
        return -ENOMEM;
    }

    platform_set_drvdata(pdev, master);

    master->mode_bits = BCM2835_SPI_MODE_BITS;
    master->bits_per_word_mask = SPI_BPW_MASK(8);
    master->num_chipselect = 3;
    master->setup = bcm2835_spi_setup;
    master->set_cs = bcm2835_spi_set_cs;
    master->transfer_one = bcm2835_spi_transfer_one;
    master->handle_err = bcm2835_spi_handle_err;
    master->prepare_message = bcm2835_spi_prepare_message;
    master->dev.of_node = pdev->dev.of_node;

    bs = spi_master_get_devdata(master);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    bs->regs = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(bs->regs)) {
        err = PTR_ERR(bs->regs);
        goto out_master_put;
    }

    bs->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(bs->clk)) {
        err = PTR_ERR(bs->clk);
        dev_err(&pdev->dev, "could not get clk: %d\n", err);
        goto out_master_put;
    }

    bs->irq = platform_get_irq(pdev, 0);
    if (bs->irq <= 0) {
        dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
        err = bs->irq ? bs->irq : -ENODEV;
        goto out_master_put;
    }

    clk_prepare_enable(bs->clk);

    bcm2835_dma_init(master, &pdev->dev);

    /* initialise the hardware with the default polarities */
    bcm2835_wr(bs, BCM2835_SPI_CS,
           BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);

    err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
                   dev_name(&pdev->dev), master);
    if (err) {
        dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
        goto out_clk_disable;
    }

    err = devm_spi_register_master(&pdev->dev, master);
    if (err) {
        dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
        goto out_clk_disable;
    }

    return 0;

out_clk_disable:
    clk_disable_unprepare(bs->clk);
out_master_put:
    spi_master_put(master);
    return err;
}

在probe函数中调用了devm_spi_register_master
spi device

#define devm_spi_register_master(_dev, _ctlr) \
    devm_spi_register_controller(_dev, _ctlr)

在drivers/spi/spi.c中,定义了devm_spi_register_controller

/**
 * devm_spi_register_controller - register managed SPI master or slave
 *  controller
 * @dev:    device managing SPI controller
 * @ctlr: initialized controller, originally from spi_alloc_master() or
 *  spi_alloc_slave()
 * Context: can sleep
 *
 * Register a SPI device as with spi_register_controller() which will
 * automatically be unregistered and freed.
 *
 * Return: zero on success, else a negative error code.
 */
int devm_spi_register_controller(struct device *dev,
                 struct spi_controller *ctlr)
{
    struct spi_controller **ptr;
    int ret;
    ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
    if (!ptr)
        return -ENOMEM;
    ret = spi_register_controller(ctlr);
    if (!ret) {
        *ptr = ctlr;
        devres_add(dev, ptr);
    } else {
        devres_free(ptr);
    }
    return ret;
}

在spi_register_controller中调用

    status = device_add(&ctlr->dev);

spi device的file operations在drivers/spi/spidev.c中定义

static const struct file_operations spidev_fops = {
    .owner =    THIS_MODULE,
    /* REVISIT switch to aio primitives, so that userspace
     * gets more complete API coverage.  It'll simplify things
     * too, except for the locking.
     */
    .write =    spidev_write,
    .read =     spidev_read,
    .unlocked_ioctl = spidev_ioctl,
    .compat_ioctl = spidev_compat_ioctl,
    .open =     spidev_open,
    .release =  spidev_release,
    .llseek =   no_llseek,
};

wiringPi oled例程

wiringPi在用户空间通过SPI master device的API, open, release, ioctl, read, write来驱动oled.

static const char       *spiDev0  = "/dev/spidev0.0" ;
static const char       *spiDev1  = "/dev/spidev0.1" ;
/*
 * wiringPiSPISetupMode:
 *  Open the SPI device, and set it up, with the mode, etc.
 *********************************************************************************
 */

int wiringPiSPISetupMode (int channel, int speed, int mode)
{
  int fd ;

  mode    &= 3 ;    // Mode is 0, 1, 2 or 3
  channel &= 1 ;    // Channel is 0 or 1

  if ((fd = open (channel == 0 ? spiDev0 : spiDev1, O_RDWR)) < 0)
    return wiringPiFailure (WPI_ALMOST, "Unable to open SPI device: %s\n", strerror (errno)) ;

  spiSpeeds [channel] = speed ;
  spiFds    [channel] = fd ;

// Set SPI parameters.

  if (ioctl (fd, SPI_IOC_WR_MODE, &mode)            < 0)
    return wiringPiFailure (WPI_ALMOST, "SPI Mode Change failure: %s\n", strerror (errno)) ;

  if (ioctl (fd, SPI_IOC_WR_BITS_PER_WORD, &spiBPW) < 0)
    return wiringPiFailure (WPI_ALMOST, "SPI BPW Change failure: %s\n", strerror (errno)) ;

  if (ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed)   < 0)
    return wiringPiFailure (WPI_ALMOST, "SPI Speed Change failure: %s\n", strerror (errno)) ;

  return fd ;
}

树莓派 devfs sysfs中SPI设备

查看/dev

pi@raspberrypi:~ $ ls /dev | grep spi
spidev0.0
spidev0.1

查看/sys/class

pi@raspberrypi:/sys/class/spi_master $ ls
spi0
pi@raspberrypi:/sys/class/spi_master $ cd spi0
pi@raspberrypi:/sys/class/spi_master/spi0 $ ls
device  of_node  power  spi0.0  spi0.1  statistics  subsystem  uevent
pi@raspberrypi:/sys/class/spi_master/spi0 $ cd of_node
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ ls
#address-cells  cs-gpios   interrupts  pinctrl-0      #size-cells  status
clocks          dma-names  name        pinctrl-names  spidev@0
compatible      dmas       phandle     reg            spidev@1
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ cat compatible 
brcm,bcm2835-spi
pi@raspberrypi:/sys/class/spi_master/spi0/of_node $ cd spidev@0
pi@raspberrypi:/sys/class/spi_master/spi0/of_node/spidev@0 $ ls
#address-cells  compatible  name  phandle  reg  #size-cells  spi-max-frequency
pi@raspberrypi:/sys/class/spi_master/spi0/of_node/spidev@0 $ cat compatible 
spidev
posted @ 2018-07-21 20:57  feiwatson  阅读(462)  评论(0编辑  收藏  举报