linux spi驱动

扬帆eric 2014-06-23 05:37:26
硬件平台:三星s5pv210
软件环境:linux系统2.6.32.9内核

在下使用的是a8开发板,linux2.6.32。正在使用spi0驱动一个温度模块(tmp122 半双工),本人没有用iomap的方法直接配置spi寄存器,而是想利用linux自己的spi驱动体系来编写设备驱动。
(1)已经在加上了spi0总线,在/sys/device/platform/下有了spi目录,
(2)将spidev.h,spidev.c抠出来加以修改,编译.ko驱动模块,insmod模块。然后短接MISO-MOSI,在测试的应用程序中使用 ioctl (fd, SPI_IOC_MESSAGE (1), &tr)进行IO同步的方法可以读写数据。应用层的测试代码:
static void
transfer (int fd)
{
int ret;
uint8_t tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
};
uint8_t rx[ARRAY_SIZE (tx)] = { 0, };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long) tx,
.rx_buf = (unsigned long) rx,
.len = ARRAY_SIZE (tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};

ret = ioctl (fd, SPI_IOC_MESSAGE (1), &tr);
if (ret == 1)
pabort ("can't send spi message");

for (ret = 0; ret < ARRAY_SIZE (tx); ret++)
{
if (!(ret % 6))
puts ("");
printf ("%.2X ", rx[ret]);
}
puts ("");
}


(3)问题来了!为了在内核使用spidev_read,spidev_write等接口,我在spidev_test.c中加入其测试代码后发现spidev_read,spidev_write根本没有读写成功!spidev.c添加的测试代码如下:
static int spidev_probe(struct spi_device *spi)
{
。。。省略。。。
/*test code*/
printk("test 3 start!\n");
if(write(spidev->spi,tx_buf,8)< 0){
printk("spi_write write error!\n");
}
if(read (spidev->spi,rx_buf,8)){
printk("spi_write read error!\n");
}
for(i = 0; i < 8; i++){
printk("{%2x}\n",rx_buf);
}
printk("test 4 start!\n");
for(i = 0; i < 8; i++){
printk("{%2x}\n",spi_w8r8(spidev->spi,i));
}
printk("test end!\n");
。。。省略。。。
return status;
}

小弟就不明白了,由于(2)测试成功,说明spi总线加上了,硬件层的配置也没问题了,而使用probe中传入的spi_device设备在内核进行spidev_read,spidev_write操作却没有成功?难道是那个地方没有设置的原因?
因为是半双工操作,有人提出是需要忽略cs,我不知道是什么意思?
...全文
774 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
guojianxun17953 2015-06-02
  • 打赏
  • 举报
回复
一个简单的spi,被linux驱动架构搞得那么复杂,真觉得完全没有必要,这个架构出来就是祸害人的,还有iic,也是一样。 我觉得做这两个驱动,要么直接新写一个驱动模块,直接把寄存器映射到虚拟地址空间,然后直接操作,照样可以编写文件系统接口,方便的多了。 要么就直接在供应商那里找到现成的驱动,更方便。
扬帆eric 2014-06-25
  • 打赏
  • 举报
回复
引用 13 楼 Huntercao 的回复:
[quote=引用 12 楼 bessandoscar 的回复:] 版主大哥加QQ直接聊聊吧! 531441986
Sorry. 没有QQ。 另外,我对SPI本身并不是太了解。这个坛子里面有好多人对SPI和SPI子系统比较了解。希望那些大牛能帮你分析分析。 你说的DMA模块能够再详细介绍一下么?是不是在用ioctl时候没用到DMA,而read/write时依赖于DMA,你的DMA控制器没设置好?[/quote] 谢谢了,DMA我本身也不是很了解 我再去看看吧!
曹大夯 2014-06-25
  • 打赏
  • 举报
回复
引用 12 楼 bessandoscar 的回复:
版主大哥加QQ直接聊聊吧! 531441986
Sorry. 没有QQ。 另外,我对SPI本身并不是太了解。这个坛子里面有好多人对SPI和SPI子系统比较了解。希望那些大牛能帮你分析分析。 你说的DMA模块能够再详细介绍一下么?是不是在用ioctl时候没用到DMA,而read/write时依赖于DMA,你的DMA控制器没设置好?
扬帆eric 2014-06-25
  • 打赏
  • 举报
回复
版主大哥加QQ直接聊聊吧! 531441986
扬帆eric 2014-06-25
  • 打赏
  • 举报
回复
引用 10 楼 Huntercao 的回复:
会不会是时序,时钟的问题。 你的case 2,测试程序发ioctl (fd, SPI_IOC_MESSAGE (1), &tr)是成功的。 这个方法的call stack和read/write有什么区别,最后是不是也到handle_msg里,试着比较一下。
你好,你可以看下我上面的逻辑分析仪测出来的结果,数据从tmp122以经发出来了,数据也是对的,可以证明我的时钟与时序没错。但就是我这边的接收spi_transfer->rx_buf一直为0x00.猜测他的API函数并没有将数据结果放到spi_transfer->rx_buf. 说实话,这个SPI子系统有点奇怪,我在case 2当中也跟进去过,到handle_msg当是,在还没有启动DMA发送前,数据以经在spi_transfer->rx_buf中,关键是数据都还没启动发送,那又怎么证明在全双工模式下测试方法是对的,比较不好理解,
曹大夯 2014-06-25
  • 打赏
  • 举报
回复
会不会是时序,时钟的问题。 你的case 2,测试程序发ioctl (fd, SPI_IOC_MESSAGE (1), &tr)是成功的。 这个方法的call stack和read/write有什么区别,最后是不是也到handle_msg里,试着比较一下。
扬帆eric 2014-06-25
  • 打赏
  • 举报
回复
引用 8 楼 Huntercao 的回复:
handle_msg是哪个模块的例程?
现在的问题是调用了驱动的read/write例程,但接收不到数据。 spidev_read->spidev_sync_read->spidev_sync->spi_async->(控制器层)s3c_spi_transfer->(工作队列)s3c_spi_work->handle_msg
曹大夯 2014-06-24
  • 打赏
  • 举报
回复
handle_msg是哪个模块的例程?
曹大夯 2014-06-24
  • 打赏
  • 举报
回复
那你现在碰到的问题是什么? spidev_test.c中write(fd,tmp,2); 没写到驱动模块里,没有调用你驱动的read/write例程? 还是spidev_probe中Read/Write没生效?
扬帆eric 2014-06-24
  • 打赏
  • 举报
回复
以下是我用逻辑分析仪测出来的,MISO是tmp122(三线SPI元件)回复给我的,但在handle_msg中打印出现rx_buf数据都是0x00.
扬帆eric 2014-06-24
  • 打赏
  • 举报
回复
以下是我用逻辑分析仪测出来的,MISO是tmp122(三线SPI元件)回复给我的,但在handle_msg中打印出现rx_buf数据都是0x00.
扬帆eric 2014-06-24
  • 打赏
  • 举报
回复
引用 3 楼 Huntercao 的回复:
Read/Write函数的第一个参数spidev->spi内容是什么,打印出来看看?
以下是测试程序,spidev_test.c拿过来用的,上面write(spidev->spi,tx_buf,8)其实是 write(fd,tmp,2); int main(int argc, char *argv[]) { int ret = 0; int fd; uint8_t tmp[2]={0x80,0x00}; uint8_t temp[2]={0x00,0x00}; parse_opts(argc, argv); mode |=SPI_MODE_3; mode &=~SPI_READY; //mode |= SPI_3WIRE; //mode &= ~SPI_CS_HIGH; fd = open(device, O_RDWR); if (fd < 0) pabort("can't open device"); /* * spi mode */ ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); if (ret == -1) pabort("can't set spi mode"); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); if (ret == -1) pabort("can't get spi mode"); /* * spi LSB_FIRST */ ret = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &LSB_FIRST); if (ret == -1) pabort("can't set spi LSB_FIRST"); ret = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &LSB_FIRST); if (ret == -1) pabort("can't get spi LSB_FIRST"); /* * bits per word */ ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't set bits per word"); ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); if (ret == -1) pabort("can't get bits per word"); /* * max speed hz */ ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't set max speed hz"); ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); if (ret == -1) pabort("can't get max speed hz"); printf("spi mode: %d\n", mode); printf("spi LSB_FIRST: %d\n", LSB_FIRST); printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); //read/write的话有大小的限制,读写的大小默认不能超过4096字节。这个大小是一个模块加载参数,可以修改。 while(1) { write(fd,tmp,2); read(fd,temp,2); //printf("temp[0]= 0x.2%x temp[1]= 0x%.2x\n",temp[0],temp[1]); sleep(1); } close(fd); return ret; }
曹大夯 2014-06-24
  • 打赏
  • 举报
回复
Read/Write函数的第一个参数spidev->spi内容是什么,打印出来看看?
扬帆eric 2014-06-24
  • 打赏
  • 举报
回复
引用 1 楼 Huntercao 的回复:
spidev_probe是驱动模块的代码吧? 一般情况下,驱动使用Probe例程来初始化设备,所以,在Probe例程被调用的时候,设备初始化应该没有完全好。 你想触发spidev_read, spidev_write,应该在测试程序里掉read/write才可以吧?
我是这么理解的,首先加载的SPI总线驱动,然后是SPI控制器驱动扫描平台SPI设备,发现设备后创建设备,将其挂到SPI总线,最后是加载的SPI驱动。spidev_probe主要在创建SPI在/dev下面的符号连接,并提供应用层的读写接口。被调用时,控制器驱动在之前以经初使化好了,static const struct file_operations spidev_fops = { .owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things * too, except for the locking. */ .write = spidev_write, .read = spidev_read, .unlocked_ioctl = spidev_ioctl, .open = spidev_open, .release = spidev_release, }; 现在我用逻辑分析仪发现数据以经发出来了,但在handle_msg 里通过打印发现spi_transfer结构的rx_buf全是0,按道理这个层的函数应该是三星提供,不用修改的,但的确是逻辑分析仪发现数据发出来了,但接收缓存为空?不知道怎么处理。
曹大夯 2014-06-24
  • 打赏
  • 举报
回复
spidev_probe是驱动模块的代码吧? 一般情况下,驱动使用Probe例程来初始化设备,所以,在Probe例程被调用的时候,设备初始化应该没有完全好。 你想触发spidev_read, spidev_write,应该在测试程序里掉read/write才可以吧?

21,597

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧