基于Linux的ARM与FPGA SPI接口驱动设计

时间:2022-10-04 10:04:16

【摘要】1 接口电路设计 ARM工作在SPI主模式,FPGA工作在SPI从模式,FPGA与ARM间接口电路如图1所示。 图1 接口电路 ARM使用SPI0控制器,EINT13作为中断输入,GPG6用于复位FPGA中断。 ARM通...

基于Linux的ARM与FPGA SPI接口驱动设计

摘 要:本文结合具体的FPGA与ARM接口电路介绍了SPI接口驱动程序设计,并给出了部分程序的核心代码。本驱动编写的思路严格按照Linux下字符设备驱动程序的要求,并重点介绍了中断挂载及内存映射等关键技术,对其他SPI外设的驱动程序设计有较强的参考意义。

关键词:linux;SPI;驱动

中图分类号:TP336

在某项目的单板设计中,集成了Altera公司的STRATIX II EP2S180 FPGA和三星公司的S3C2440A ARM9处理器,FPGA用于处理原始数据,ARM用于配置FPGA参数并接收FPGA处理后数据,之间采用SPI串行接口通信。

SPI接口驱动基于linux系统开发,Linux内核中虽已集成了SPI驱动,但接口复杂且冗长,不适宜本项目采用,因此重新设计了SPI驱动。

1 接口电路设计

arm工作在SPI主模式,fpga工作在spi从模式,FPGA与ARM间接口电路如图1所示。

图1 接口电路

ARM使用SPI0控制器,EINT13作为中断输入,GPG6用于复位FPGA中断。

ARM通过SPIMOSI0接口对FPGA进行参数配置,而后启动FPGA进行数据处理。FPGA处理完数据后通过EINT13对ARM发送上升沿中断,触发ARM读取FPGA数据。ARM读取完毕后,通过INT_ACK信号线发上升沿信号对FPGA进行中断清除。

2 SPI驱动程序设计

SPI为串行接口,其驱动在Linux中沿用字符设备驱动格式,字符设备驱动接口函数数据结构如下:

static struct file_operations dev_fops = {

.owner = THIS_MODULE,

.open = s3c24xx_SPI_open,

.release = s3c24xx_SPI_close,

.read = s3c24xx_SPI_read,

};

驱动程序需要实现s3c24xx_SPI_open、s3c24xx_SPI_close、s3c24xx_SPI_read等几个关键函数。

s3c24xx_SPI_open函数用于初始化SPI控制器并对FPGA发送初始化配置参数并挂载中断处理函数。

s3c24xx_SPI_close函数用于关闭FPGA设备。

s3c24xx_SPI_read用于读取FPGA数据,该函数通过调用wait_event_interruptible工作在阻塞模式,由中断处理函数负责唤醒 。

中断描述结构如下:

struct spi_irq_desc {

int irq;

int pin;

int pin_setting;

int number;

char *name;

};

static struct spi_irq_desc spi_irqs [] = {

{IRQ_EINT13, S3C2410_GPG(5),S3C2410_GPG5_EINT13,2,“SPIINT”},

};

在s3c24xx_SPI_open函数中挂载中断处理函数spi_interrupt,具体如下:

request_irq(spi_irqs[i].irq, spi_interrupt, IRQ_TYPE_EDGE_RISING,

spi_irqs[i].name,(void *)&spi_irqs[i]);

request_irq将EINT13的处理函数设定为spi_interrupt,需要重点注意的是操作系统自动将EINT13引脚配置为中断模式,并设置为上升沿触发,无需用户再进行GPIO配置。

在中断处理函数中,涉及到对SPI的发送寄存器和接收寄存器的读写操作。在Linux环境中,由于采用了MMU及虚拟内存设计,无法直接读写外部寄存器,需将其映射到内存空间,而后采用iowrite32完成读写。

以对SPICON0寄存器写入0x18示例如下,该寄存器地址为0x59000000:

spicon0_phys = 0x59000000;

spi0_virt =(unsigned long)ioremap(spi0_phys, 0x20);

va_s3c2440_SPI_SPCON0 = (unsigned long *)(spi0_virt + 0x00);

iowrite32(0x18,va_s3c2440_SPI_SPCON0);

在spi_interrupt函数中实现SPI接口读取,spi字长为8位,spi的接口配置为时钟上升沿发送,下降沿读取。

因此为读取FPGA需要对发送寄存器SPTDAT0先写一个字节,而后判断状态寄存器SPSTA0是否完成发送,如果完成则读取接收寄存器SPRDAT0,从FPGA读取一个字节,以后循环读取定长字节,即可完成对FPGA数据的读取。

而后对GPG6依次写0、1、0,之间加入1us延时,即可产生上升沿中断以复位FPGA中断。

对FPGA的读定长数据操作示例如下:

int s3c2440SPIRead(char* buffer, int nbyte)

{

char* pChar = buffer;

int ix = nbyte;

for(; nbyte > 0; nbyte--)

{

*(volatile char*)va_s3c2440_SPI_SPTDAT0=0x55;

/*等待传输完成*/

while(!(*((volatile char*)va_s3c2440_SPI_SPSTA0) & 0x1));

*pChar=*(volatile char*)(va_s3c2440_SPI_SPRDAT0);

pChar++;

}

return(ix);

}

为避免编译器将va_s3c2440_SPI_SPTDAT0地址列入CACHE空间,必须对va_s3c2440_SPI_SPTDAT0进行volatile char*强制类型转换,否则将读到过期数据。

中断处理函数完成读数据后调用wake_up_interruptible(&spi_waitq)唤醒阻塞的读操作s3c24xx_SPI_read,该函数调用copy_to_user函数将数据复制到用户空间。

3 结束语

该接口程序已成功应用于某工程项目,本文是基于调试经验,重点介绍了SPI接口程序框架及内存映射和中断挂载等关键技术,可以作为开发类似驱动的参考。

参考文献:

[1]韦东山.嵌入式Linux应用开发[M].北京:人民邮电出版社,2010.

作者单位:中国船舶重工集团公司第七二二研究所,武汉 430079

上一篇:论英语教学中的情感渗透 下一篇:学习能力在化学教学中的培养