si32171 做从,mt7628做主,si32171的spi时序要求:When using a hardware SPI, the user should configure the port for CPOL=1(SCLK/SPICLK is high when not clocking) and CPHA=1 (SDI/MOSI data change onSCLK/SPICLK falling and SDO/MISO is latched on SCLK /SPICLK rising). 当不需要时钟时,sclk电平为高。当主机发送数据时,在时钟的下降沿开始改变数据,在时钟的上升沿锁存数据,即si32171接收数据。当si32171发送数据时,在sclk低电平时数据可以变化,在sclk的上升沿把数据发送出去。
直接上源码:
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/crash_dump.h>
#include <linux/backing-dev.h>
#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/aio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#define _DBUG_
#undef _DBUG_
#define DEVICE_NAME "spidev" //加载模式后,执行”cat /proc/devices”命令看到的设备名称
#define SPI_MAJOR 0 //主设备号
//#define delays 300
#define delays 100
#define delay 100
#define LA_GAO 1
#define LA_DI 0
volatile unsigned long *GPIO_CTRL_1;
volatile unsigned long *GPIO_CTRL_0; //输出方向控制
volatile unsigned long *GPIO_POL_0; //数据是否翻转
volatile unsigned long *GPIO_DATA_0; // 什么数据
volatile unsigned long *GPIO_DATA_1;
volatile unsigned long *GPIOMODE;
volatile unsigned long *AGPIO_CFG;
#define _BUF_
#undef _BUF_
#ifdef _BUF_
char *buf=NULL;
#define COUNT 20
#endif
static struct class *spi_drv_class;
/*
cs: gpio14
clk: gpio15
miso: gpio45
mosi: gpio17
<span style="white-space:pre"> </span>slic reset:gpio 5
*/
static int spi_drv_open(struct inode *inode, struct file *file)
{
printk("%s:Hello spi\n", __FUNCTION__); // printk用于驱动中添加打印,用法和应用程序中的printf一样
return 0;
}
static char readByte(void)
{
char ch = 0;
int i = 7;
char mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
*GPIO_DATA_0&=~(1<<14);// 拉低片选
ndelay(delays);
for(i=7;i>=0;i--)
{
*GPIO_DATA_0 &=~(1<<15);// clk 拉低
if((*GPIO_DATA_1>>13)&0x1)// 读第16位的数据
ch|=mask[i];
else
ch&=~mask[i];
ndelay(delays);
*GPIO_DATA_0|=(1<<15);// clk 拉高
ndelay(delays);//延时delaysns
}
*GPIO_DATA_0|=(1<<14);// 拉高取消片选
#ifdef _DBUG_
printk("spi read 0x%02x\n",ch);
#endif
return ch;
}
static ssize_t spi_drv_read(struct file *file, char __user *buff, size_t size, loff_t *ppos)
{
#ifndef _BUF_
char *buf=NULL;
buf=(char *)kmalloc(size, GFP_KERNEL);
#else
memset(buf,0x0,COUNT);
#endif
int i=0;
char j=0;
for(i=0;i<size;i++)
{
buf[i]=readByte();
}
j=copy_to_user((char *)buff,buf,size);
if(j)
printk("copy error\n");
#ifndef _BUF_
kfree(buf);
#endif
return size;
}
static void writeByte(const char c)
{
int i = 7;
char mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
*GPIO_DATA_0&=~(1<<14);// 拉低片选
ndelay(delays);
for(i=7;i>=0;i--)
{
*GPIO_DATA_0&=~(1<<15);// clk 拉低
if(c&mask[i])
{
*GPIO_DATA_0|=(1<<17);// 往里面写入1
}
else
{
*GPIO_DATA_0&=~(1<<17);// 往里面写入0
}
ndelay(delays);//延时delaysns
*GPIO_DATA_0|=(1<<15);// clk 拉高
ndelay(delays);//延时delaysns
}
*GPIO_DATA_0|=(1<<14);// 拉高取消片选
return ;
}
static ssize_t spi_drv_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
// printk("%s:Hello spi\n", __FUNCTION__); // printk用于驱动中添加打印,用法和应用程序中的printf一样
int i=0;
#ifndef _BUF_
char *buf=NULL;
buf=(char *)kmalloc(count,GFP_KERNEL);
#else
memset(buf,0x0,COUNT);
#endif
if(copy_from_user(buf,(char *)ubuf,count))
{
printk("no enough memory!\n");
return -1;
}
for(i=0;i<count;i++)
{
#ifdef _DBUG_
printk("spi write 0x%02X!\n",*buf);
#endif
writeByte(buf[i]);
//buf++;
}
#ifndef _BUF_
kfree(buf);
#endif
return count;
}
static long gpio_rst__drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case LA_GAO:
*GPIO_DATA_0 |=(1<<5);
printk("%s: LA GAO\n", __FUNCTION__);
break;
case LA_DI:
*GPIO_DATA_0 &=~(1<<5);
printk("%s:LA DI\n", __FUNCTION__);
break;
default:
break;
}
return 0;
}
static struct file_operations spi_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = spi_drv_open,
.read = spi_drv_read,
.write = spi_drv_write,
.unlocked_ioctl = gpio_rst__drv_ioctl,
};
int major;
static int __init spi_drv_init(void)
{
major = register_chrdev(SPI_MAJOR, DEVICE_NAME, &spi_drv_fops);
if (major < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return major;
}
spi_drv_class = class_create(THIS_MODULE, "spi");
device_create(spi_drv_class, NULL, MKDEV(major, 0), NULL, "spi"); // /dev/spi
printk("%s:Hello spi\n", __FUNCTION__); // printk用于驱动中添加打印,用法和应用程序中的printf一样
AGPIO_CFG=(volatile unsigned long *)ioremap(0x1000003c,4);
GPIOMODE=(volatile unsigned long *)ioremap(0x10000060,4);
GPIO_CTRL_0=(volatile unsigned long *)ioremap(0x10000600,4);
GPIO_CTRL_1=(volatile unsigned long *)ioremap(0x10000604,4);
GPIO_DATA_0=(volatile unsigned long *)ioremap(0x10000620,4);
GPIO_DATA_1=(volatile unsigned long *)ioremap(0x10000624,4);
*AGPIO_CFG|=(1<<20)|(1<<19)|(1<<18)|(1<<17);//全部设置为1
*GPIOMODE&=~(0x3<<2);
*GPIOMODE&=~(0x3<<24);
*GPIOMODE|=(1<<2)|(1<<24);// 设置GPIO 模式功能
*GPIOMODE&=~(0x3<<20);// set gpio 5 rest
*GPIOMODE|=(1<<20);//
*GPIO_CTRL_0|=(1<<14)|(1<<15)|(1<<17);// GPIO 14 15 17 为输出
//*GPIO_CTRL_0&=~(1<<16);// GPIO 16 为输入 经过测试该引脚不行,换了gpio45
*GPIO_CTRL_1&=~(1<<13);// 45
*GPIO_DATA_0|=(1<<15);
*GPIO_DATA_0|=(1<<14);
*GPIO_CTRL_0|=(1<<5);// set gpio 5 output
#ifdef _BUF_
buf=(char *)kmalloc(COUNT,GFP_KERNEL);
#endif _BUF_
return 0;
}
static void __exit spi_drv_exit(void)
{
unregister_chrdev(major, "spi"); // 与入口函数的register_chrdev函数配对使用
device_destroy(spi_drv_class, MKDEV(major, 0)); // 与入口函数的device_create函数配对使用
class_destroy(spi_drv_class); // 与入口函数的class_create函数配对使用
iounmap(GPIOMODE);
iounmap(AGPIO_CFG);
iounmap(GPIO_CTRL_0);
iounmap(GPIO_CTRL_1);
iounmap(GPIO_DATA_1);
iounmap(GPIO_DATA_0);
printk("%s:Hello spi\n", __FUNCTION__); // printk用于驱动中添加打印,用法和应用程序中的printf一样
#ifdef _BUF_
kfree(buf);
#endif
}
module_init(spi_drv_init);
module_exit(spi_drv_exit);
MODULE_AUTHOR("http://www.spi.com");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("RT5350 FIRST Driver");
MODULE_LICENSE("GPL");
注意:在驱动中的write_byte(),read_byte()中不要调用printk函数,这样会严重影响正常时序,分配的内存用完一定要free掉,否则内存会造成内存
不足导致系统崩溃,一定要确保硬件是可行的,检查引脚是否输出正确。