SPI子系统之驱动SSD1306 OLED

接触Linux之前,曾以为读源码可以更快的学习软件,于是前几篇文章都是一边读源码一边添加注释(),甚至精读到每一行代码,实际上效果并不理想,看过之后就忘记了。主要原因是没理解透程序架构,各个模块之间的关系,如何联系在一起,再加上没有实例验证。后来逐渐发现,理解框架能达到事半功倍的效果,理解框架之后,反而代码也更容易看懂,甚至可以猜部分代码的作用,印象更加深刻。

理解SPI的驱动框架,还是从最基本的三个入口点触发,platform_device,platform_bus,platform_driver。

其中内核一提供给platform_bus,platform_driver在spi_s3c24xx_gpio.c和spi_s3c24xxc.c中,其中spi_s3c24xx_gpio.c用于IO模拟SPI (本例讨论的是IO模拟SPI),spi_s3c24xxc.c用于s3c24xx的硬件SPI。因此,我们需要动手写一个platform_device。

看看spi_s3c24xx_gpio.c做了些什么。

static int s3c2410_spigpio_probe(struct platform_device *dev)
{
    ... ...
    /* [cgw]: 分配一个SPI主机 */
    master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio));
    ... ...

sp = spi_master_get_devdata(master);

platform_set_drvdata(dev, sp);

/* [cgw]: 分配与spi硬件相关的配置,如指定哪些IO为MISO,MOSI,SCLK,CS,SPI工作模式,最大时钟等等 */
    /* copy in the plkatform data */
    sp->info = dev->dev.platform_data;

/* [cgw]: 提供实现SPI各种模式的时序的基本方法,和CS的激活方法 */
    /* setup spi bitbang adaptor */
    sp->bitbang.master = spi_master_get(master);
    sp->bitbang.chipselect = s3c2410_spigpio_chipselect;

sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
    sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1;
    sp->bitbang.txrx_word[SPI_MODE_2] = s3c2410_spigpio_txrx_mode2;
    sp->bitbang.txrx_word[SPI_MODE_3] = s3c2410_spigpio_txrx_mode3;

/* [cgw]: 配置相关io为输入输出 */
    /* set state of spi pins */
    s3c2410_gpio_setpin(sp->info->pin_clk, 0);
    s3c2410_gpio_setpin(sp->info->pin_mosi, 0);

s3c2410_gpio_cfgpin(sp->info->pin_clk, S3C2410_GPIO_OUTPUT);
    s3c2410_gpio_cfgpin(sp->info->pin_mosi, S3C2410_GPIO_OUTPUT);
    s3c2410_gpio_cfgpin(sp->info->pin_miso, S3C2410_GPIO_INPUT);

/* [cgw]: 设置spi的收发,如注册一个工作队列,收发时序的方法,8/16/32的spi数据等等 */
    ret = spi_bitbang_start(&sp->bitbang);
    ... ...

/* [cgw]: 注册sp->info->board_size个spi设备,这几个spi设备都是挂接在统一spi总线上的 */
    /* register the chips to go with the board */
    for (i = 0; i < sp->info->board_size; i++) {
        dev_info(&dev->dev, "registering %p: %s\n",
            &sp->info->board_info[i],
            sp->info->board_info[i].modalias);

sp->info->board_info[i].controller_data = sp;
        spi_new_device(master, sp->info->board_info + i);
    }
    ... ...
}

注册了一个platform_driver

在s3c2410_spigpio_probe中,分配并注册了一个spi主机,并注册了挂接在这个SPI主机上的所有spi设备

要想s3c2410_spigpio_probe得到调用,即探测到有效的platform_device,我们需要一个与platform同名("s3c24xx-spi-gpio")的platform_device。

static struct spi_board_info board_info[1] = {
    {
    .modalias = "spi_ssd1306",    /* [cgw]: spi设备名,和设备驱动名对应 */
    .bus_num = 0,                /* [cgw]: spi总线号,即spi0 */
    .chip_select = 2,            /* [cgw]: spi总线上的设备号,即spi0.2 */
    .max_speed_hz    = 50000,    /* [cgw]: spi时钟 */
    .mode = SPI_MODE_3,          /* [cgw]: spi数据模式 */
    },
};

static struct s3c2410_spigpio_info spi_dev = {
    .pin_clk = S3C2410_GPG7,
    .pin_mosi = S3C2410_GPG5,
    .pin_miso = S3C2410_GPG6,
    .board_size = 1,                    /* [cgw]: 设置板上spi接口数量为1 */
    .board_info = &board_info[0],
    .chip_select = ssd1306_chip_select
};

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f06f37a19007f6f04167f392c9581460.html