SPI驱动模型---Oled

本文详细介绍了SPI驱动的实现过程,包括添加板级信息、构造注册spi_master及设备驱动等内容。通过具体实例展示了SPI驱动中各部分的作用及编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、对于SPI驱动,添加板级信息

       添加这个板级信息有两种方法:   1 直接在arch/arm/mach-s3c24xx/mach-xxxx文件里边修改,添加上一个spi_board_info结构体,这个结构体包括SPI控制器序号、片选引脚、数据比特率、传输方式等等,改完后编译内核,下载到开发板。        另一种方法:当然是比较方便的方法,我们可以像加载驱动的方法添加这些信息到内核,如下代码:

#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/io.h>

#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
/* 需要这个向内核里边添加SPI的信息,当有一个oled同名的驱动后,执行驱动程序probe函数会传入这个spi_dev */
static struct spi_board_info spi_info_jz2440[] = {
	{
    	 .modalias = "oled",  
    	 .max_speed_hz = 10000000,	/* max spi clock (SCK) speed in HZ */
    	 .bus_num = 1,     
    	 .mode    = SPI_MODE_0,
    	 .chip_select   = S3C2410_GPF(1), 
    	 .platform_data = (const void *)S3C2410_GPG(4) ,     	 
	 },
	 {
    	 .modalias = "100ask_spi_flash",  /* 对应的spi_driver名字也是"oled" */
    	 .max_speed_hz = 80000000,	/* max spi clock (SCK) speed in HZ */
    	 .bus_num = 1,     
    	 .mode    = SPI_MODE_0,
    	 .chip_select   = S3C2410_GPG(2), 
	 }
};

static int spi_info_jz2440_init(void)
{
    return spi_register_board_info(spi_info_jz2440, ARRAY_SIZE(spi_info_jz2440));
}

module_init(spi_info_jz2440_init);
MODULE_DESCRIPTION("OLED SPI Driver");
MODULE_LICENSE("GPL");
我是使用的开发板是JZ2440,开发板上有两个SPI接口,那么这板级信息有什么用,比如我们写好了一个驱动程序,然后加载到内核,写过驱动的都知道,在这个驱动程序里边有一个某某驱动结构体,有一个成员是name,没错,其实大家都知道,这个名字在平台驱动程序里边是干什么的:一般在驱动程序里边,我们需要向内核添加一个设备,然后添加一个驱动,那么这个设备之怎么与这个驱动匹配的,没错,就是上边的这个name。   一旦我们加载这个平台驱动后,就根据双方的名字匹配,然后就执行驱动里边的probe函数,-------重点来了,上面的这个SPI板级信息就要用上了,probe探测函数有一些参数,这些参数就是我们想内核添加设备时的信息。。。。这里SPI设备驱动里边的probe函数参数就是上面的结构体成员。。。。。。。

然后我们需要编译这个,然后insmod xxx.ko完事,但是目前我有一个疑问,那就是,卧槽,如何加载后我想重新修改这个板级信息或者想板级信息里边添加一下其他的设备信息,不会是重启开发板吧,那太low了!有没有其他办法,当然这里rmmod xxx是肯定不行的吧,连exit函数都没有,肯定有方法呀,以后补充

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二、构造注册spi_master

        这个spi_master是什么鬼?这是另一个需要insmod xxx.ko的驱动,就是SPI控制器的驱动,当初看SPI驱动时候,总是分不清SPI控制器驱动,还有设备驱动,到底是什么鬼?到底又有什么区别,好吧似乎写linux驱动就是要我们在linux这个大框架下,遵守他的“规章制度”,文字再怎么写的好,还是没有代码明显。

        好吧,接着说哦,JZ2440上有两个SPI控制器,所以我们在写master驱动时候需要注册两个spi_master,当然也要分配两个这个结构体,然后分别添加结构体成员:

不忙写,卧槽,困了////////////////////////////////////////////////////2016/11/7

1.首先,这个master驱动的功能是什么

   一个控制器要分配一个spi_master,然后去填充这个结构体:

struct spi_master {
	struct device	dev;

	struct list_head list;

	/* other than negative (== assign one dynamically), bus_num is fully
	 * board-specific.  usually that simplifies to being SOC-specific.
	 * example:  one SOC has three SPI controllers, numbered 0..2,
	 * and one board's schematics might show it using SPI-2.  software
	 * would normally use bus_num=2 for that controller.
	 */
	s16			bus_num;  /* 该SPI控制器对应的SPI总线号 */

	/* chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			num_chipselect;  /* 支持的设备数 */

	/* some SPI controllers pose alignment requirements on DMAable
	 * buffers; let protocol drivers know about these requirements.
	 */
	u16			dma_alignment;

	/* spi_device.mode flags understood by this controller driver */
	u16			mode_bits;

	/* other constraints relevant to this driver */
	u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* can't do full duplex */
#define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
#define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */

	/* lock and mutex for SPI bus locking */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* flag indicating that the SPI bus is locked for exclusive use */
	bool			bus_lock_flag;

	/* Setup mode and clock, etc (spi driver may call many times).
	 *
	 * IMPORTANT:  this may be called when transfers to another
	 * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
	 * which could break those transfers.
	 */
	int			(*setup)(struct spi_device *spi);

	/* bidirectional bulk transfers
	 *
	 * + The transfer() method may not sleep; its main role is
	 *   just to add the message to the queue.
	 * + For now there's no remove-from-queue operation, or
	 *   any other request management
	 * + To a given spi_device, message queueing is pure fifo
	 *
	 * + The master's main job is to process its message queue,
	 *   selecting a chip then transferring data
	 * + If there are multiple spi_device children, the i/o queue
	 *   arbitration algorithm is unspecified (round robin, fifo,
	 *   priority, reservations, preemption, etc)
	 *
	 * + Chipselect stays active during the entire message
	 *   (unless modified by spi_transfer.cs_change != 0).
	 * + The message transfers use clock and SPI mode parameters
	 *   previously established by setup() for this device
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* called on release() to free memory provided by spi_master */
	void			(*cleanup)(struct spi_device *spi);

	/*
	 * These hooks are for drivers that want to use the generic
	 * master transfer queueing mechanism. If these are used, the
	 * transfer() function above must NOT be specified by the driver.
	 * Over time we expect SPI drivers to be phased over to this API.
	 */
	bool				queued;
	struct kthread_worker		kworker;
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	bool				busy;
	bool				running;
	bool				rt;

	int (*prepare_transfer_hardware)(struct spi_master *master);
	int (*transfer_one_message)(struct spi_master *master,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_master *master);
};
这个结构体很大呀,但是我们要完成一些基本功能,不需要填充所有成员,那么我们需要填充的有哪些?

    master = spi_alloc_master(&pdev->dev, sizeof(struct s3c_spi_info));
    master->bus_num = bus_num;
    master->num_chipselect = 0xffff;
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

    master->setup    = s3c2440_spi_setup;
    master->transfer = s3c2440_spi_transfer;
填充的是不是感觉很少,需要填充的大概有:总线号、这个SPI控制器支持的设备数、工作模式、还有成员setup、transfer函数需要指定--------

                          setup函数是什么时候使用:上面第一个是我们在加载一个向内核添加板级SPI信息的驱动,就是这个驱动里边,我们需要向内核注册这个板级信息,就是我们向内核注册这个板级信息的时候使用到了这个setup函数,似乎我们加载两个驱动顺序搞错了,好吧,还要调查一下是不是搞错了,要严谨!

                         transfer函数:这个函数就不用说了吧,一看transfer,那估计我们就靠它往我们的SPI相关传输寄存器上写数据了,似乎终于接地气了,写过51、STM32或者430的SPI程序的都会想,要传输的数据在哪写到相关寄存器上的,终于看到了直接对硬件的编程,而不是调用一层层函数。

2.在这个master驱动里边,还有一些疑问:

    第一个:我们需要分配一个平台设备,就像下边的代码

                                                              pdev = platform_device_alloc("s3c2440_spi", bus_num);
                                                              platform_device_add(pdev);

                     那么为什么要这么做,原来我一直认为这个平台设备就是我们上边我们的spi_board_info就是,但是好像不对,他们的名字不一样,一个是oled,一个是s3c2440_spi,到底是什么情况,先贴上分配master、填充master的全部代码:

static struct spi_master *create_spi_master_s3c2440(int bus_num, unsigned int reg_base_phy, int irq)
{
    int ret;
    struct spi_master *master;
    struct s3c_spi_info *info;
    struct platform_device *pdev;
    

    pdev = platform_device_alloc("s3c2440_spi", bus_num);
    platform_device_add(pdev);

    master = spi_alloc_master(&pdev->dev, sizeof(struct s3c_spi_info));
    master->bus_num = bus_num;
    master->num_chipselect = 0xffff;
    master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

    master->setup    = s3c2440_spi_setup;
    master->transfer = s3c2440_spi_transfer;

    info = spi_master_get_devdata(master);
    info->reg_base = (unsigned int)ioremap(reg_base_phy, 0x18);
    info->irq = irq;
    info->pdev = pdev;

    /* 硬件初始化 */
    s3c2440_spi_controler_init(bus_num);

    ret = request_irq(irq, s3c2440_spi_irq, 0, "s3c2440_spi", master);

    spi_register_master(master);

    return master;
}
好吧,接着扯吧----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看了看源码,似乎这创建的两个master都是s3c2440_spi,就是名字都是一样的,两个SPI的总线bus_number不一样,还有一点:

master = spi_alloc_master(&pdev->dev, sizeof(struct s3c_spi_info));
这行代码,我们深入到里边:

struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
	struct spi_master	*master;

	if (!dev)
		return NULL;

	master = kzalloc(size + sizeof *master, GFP_KERNEL);
	if (!master)
		return NULL;

	device_initialize(&master->dev);
	master->dev.class = &spi_master_class;
	master->dev.parent = get_device(dev);
	spi_master_set_devdata(master, &master[1]);

	return master;
}
看见master->dev.parent = get_device(dev),就是这里,这个s3c2400_spi平台设备是我们的OLED设备的上层设备,如果用文件的方式,就是我们的s3c2440_spi目录下有两个master。。。。。。。

三、设备驱动
             接下来,就是我们喜欢做的对硬件的编程工作了,但是还是得遵守linux提供的函数使用方法
 1.啥都不要说,肯定是我们要在init函数里边注册spi_driver,这个结构体如下:

struct spi_driver {
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	int			(*suspend)(struct spi_device *spi, pm_message_t mesg);
	int			(*resume)(struct spi_device *spi);
	struct device_driver	driver;
};
没错,我们第一步就是先填充这个结构体。

2.考虑我们要在probe函数里边干些什么事,想想你想为应用层提供怎样的接口

  比如是read、write、ioctl等等,那么就要在probe函数里边,我们要分配一个字符设备,就像以前的led驱动程序一样

3.最后考虑read、、、、ioctl里边干什么,当然记得完成remove函数

4.写过测试程序,调试呀


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值