时间:2022-10-04 10:04:16
【摘要】1 接口电路设计 ARM工作在SPI主模式,FPGA工作在SPI从模式,FPGA与ARM间接口电路如图1所示。 图1 接口电路 ARM使用SPI0控制器,EINT13作为中断输入,GPG6用于复位FPGA中断。 ARM通...
摘 要:本文结合具体的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进行中断清除。
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