12.4在Linux中编写阻塞模式的SPI控制器驱动

编写驱动程序步骤

  1. 实现SPI总线设置的函数setup,用于设置SPI总线,若片选采用GPIO编号模式还需要在这里将GPIO设置为输出
  2. 实现SPI总线数据传输的函数transfer,用于传输SPI的数据包,它通常将spi_message放入到控制器的链表中,然后触发工作队列,去执行真正的发送任务。
  3. 通过spi_alloc_master分配一个struct spi_master,分配struct spi_master时还可以额外分配一段存储私有数据的空间(通过函数spi_master_get_devdata可以得到这段私有数据空间的地址)
  4. 初始化struct spi_master,主要包含设备树节点、支持的模式、支持的最大频率和最小频率、片选引脚是GPIO编号模式还是描述符模式、setup函数(用于设置SPI总线)、transfer函数(用于传输spi_message,若提供了transfer函数则是阻塞模式的SPI控制器驱动)
  5. 若片选采用GPIO编号模式还需要对片选引脚进行request操作,若片选采用GPIO描述符模式则无该步骤
  6. 通过spi_register_master注册SPI控制器驱动
  7. 设备或驱动卸载时spi_unregister_master注销SPI控制器

编写驱动程

这里编写一个虚拟的SPI控制器驱动,通过printk来输出SPI控制器的工作状态。

设备树编写

在顶层设备树根节点中加入如下节点:

	virtual_spi_master {
   
			compatible = "atk,virtual_spi_master";
			status = "okay";
			//片选列表,一个spi_master至少有一个片选
			cs-gpios = <&gpioh 6 GPIO_ACTIVE_LOW>;
			//片选数量
			num-chipselects = <1>;
			//reg中地址字段的字数,必须为1
			#address-cells = <1>;
			//reg中地址空间大小的字数,必须为0
			#size-cells = <0>;

			//一个spidev的设备节点,以便在应用层通过spidev来测试SPI控制器驱动
			virtual_spi_dev: virtual_spi_dev@0 {
   
					compatible = "rohm,dh2228fv";
					reg = <0>;
					spi-max-frequency = <100000>;
			};
	};

用make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,用新的.dtb文件启动系统

驱动代码编写

完整的驱动代码如下所示:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/of_gpio.h>

struct virtual_spi_master{
   
	struct spi_master *spi_master;
	struct work_struct work_queue;
};

//工作队列函数,用于模拟SPI控制器的硬件中断
static void spi_virtual_work(struct work_struct *work)
{
   
	unsigned long flags;
	struct spi_message *mesg;
	struct spi_transfer *xfer;
	struct spi_statistics *statm;
	struct spi_statistics *stats;
	struct virtual_spi_master *virtual_master = container_of(work, struct virtual_spi_master, work_queue);
	struct spi_master *master = virtual_master->spi_master;

	//获取自旋锁
	spin_lock_irqsave(&master->queue_lock, flags);

	//便利存储mesg的队列
	while(!list_empty(&master->queue))
	{
   
		//从队列中取出一个mesg,并将其从队列中删除
		mesg = list_entry(master->queue.next, struct 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值