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掉,否则内存会造成内存
不足导致系统崩溃,一定要确保硬件是可行的,检查引脚是否输出正确。