14.【linux驱动】spi(GPIO模拟)

spi系列文章:
1.spi 硬件、协议
2.spi(GPIO模拟)
3.spi驱动

GPIO模拟SPI

只实现一下主机发送,上升沿发送,空闲状态为低电平。需要注意CPU速度过快,GPIO电平设置会被合并,需要加入延时函数。
另外自己实现延时的话需要使用内存屏障关键字volatile 防止被优化。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <mach/soc.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 500000;
char * char_data;

#pragma pack(4)
static struct GPIO{
	volatile unsigned int out_put;
	unsigned int out_enb;
	unsigned int detect_md_0;
	unsigned int detect_md_1;
	unsigned int int_enb;
	unsigned int event_detect;
	unsigned int pad_state;
	unsigned int resv;
	unsigned int func0;
	unsigned int func1;
	unsigned int DETMODEEX;
	unsigned int DETENB;
	unsigned int SLEW;
	unsigned int SLEW_DISABLE_DEFAULT;
	unsigned int DRV1;
	unsigned int DRV1_DISABLE_DEFAULT;
	unsigned int DRV0;
	unsigned int DRV0_DISABLE_DEFAULT;
	unsigned int pull_sell;
	unsigned int PULLSEL_DISABLE_DEFAULT;
	unsigned int pull_enb;
}* gpio_b;
#pragma pack()

#define SCL_H (gpio_b->out_put |= (1 << 31))
#define SCL_L (gpio_b->out_put &= ~(1 << 31))

#define SDO_H (gpio_b->out_put |= (1 << 30))
#define SDO_L (gpio_b->out_put &= ~(1 << 30))

#define cycle_ns 	20L //spi周期

inline void Write_SPI_Byte(unsigned char spi_Byte)
{
	volatile unsigned char i = 0x80;
	volatile unsigned int j;
	while(i)
	{
		if(spi_Byte & i)
			SDO_H;
		else
			SDO_L;
		SCL_H;
   		for (j = 0; j < cycle_ns; ++j);
		SCL_L;
   		for (j = 0; j < cycle_ns; ++j);
		i>>=1;
	}
}

static ssize_t write(struct file * fl, const char __user * buf, size_t len, loff_t * offset){
	int ret = 0, copy_len, i, data_len = buffer_size, _offset, let;

	if(fl->f_pos + len > data_len)
		copy_len = data_len - fl->f_pos; //超过长度,复制剩余部分
	else
		copy_len = len;					 //没超过

	ret = copy_len;
	_offset = 0;
	while(ret){
		let = copy_from_user(char_data + fl->f_pos + _offset,buf + _offset,ret);
		_offset = _offset + ret - let;
		ret = let;
	}
	ret = copy_len - ret;
	for (i = 0; i < ret; ++i)
	{
		Write_SPI_Byte(* (char_data + fl->f_pos + i));
	}
	// *offset += ret;						 //移动文件指针
	return ret;
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	unsigned int dir,size;
	dir = _IOC_DIR(cmd);
	size = _IOC_SIZE(cmd);

	if(dir == _IOC_WRITE){

	}
	return 0;
}


struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = ioctl,
	.write = write
};

static int __init spi_init(void){
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = vzalloc(buffer_size);

	gpio_b = (struct GPIO *)ioremap(0xc001b000,sizeof(struct GPIO));			//映射地址

	gpio_b->func1 |= (1 << 30);
	gpio_b->func1 |= (1 << 28);

	gpio_b->out_enb |= (1 << 31);
	gpio_b->out_enb |= (1 << 30);
	gpio_b->pull_enb &= ~(1 << 30);
	SDO_L;
	SCL_L;

	printk("spi init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit spi_exit(void){
	SDO_L;
	SCL_L;
	iounmap(gpio_b);

	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("spi exit\n");
}

module_init(spi_init);
module_exit(spi_exit);

运行试一下

[root@minicoco spi_visual]# insmod spi.ko
[  364.136000] spi init
[root@minicoco spi_visual]# echo 123456 > /dev/char_test_dev_1 

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/106733956