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