登录 注册
购物车0
TOP
Imgs 行业资讯

0

基于ARM9芯片的S3C2440和Linux操作系统设计SPI驱动程序

2021-03-02 18:00:36
在嵌入式开发过程中,很多系统通常使用串口驱动来满足通信要求,但在实际应用中,SPI通信更高效更快捷[2]。SPI接口是一种高速高效的串行接口技术,因此SPI设备在数据通信应用中非常方便[3]。本文基于ARM9芯片的S3C2440和Linux操作系统,设计了一种可靠、灵活、易于移植的SPI驱动程序,可应用于各种嵌入式平台,实现ARM与设备之间的通信。
1硬件描述
1.1S3C2440开发平台
1.2SPI硬件模块
S3C2440有两个SPI,每个SPI有两个8位移位寄存器,用于独立发送和接收数据。与SPIver.2.11协议兼容,支持8位逻辑预分频。系统可以通过轮询、中断和直接存储器存取来判断SPI的发送和接收状态。该SPI模块包含以下信号线[5]:
(1)SCK:数据同步时钟信号,由主设备驱动并输出到从设备,以便从设备可以根据同步时钟接收或发送数据。
(2)nCS(用户指定的GPIO):slave  select信号线(SS)由主设备发送,用于选择和激活一个从设备,在低电平时有效。
(3)MISO(SPIMISO0):主输入/输出信号线,表示该信号在主设备中用作输入,在从设备中用作输出。
(4)MOSI(SPIMOSI0):主输出和从输入信号线,表示信号在主设备中输出,在从设备中输入。
(5)/SS(nSS):多主检错。
2Linux下SPI设备驱动程序的设计
2.1SPI初始化
(1)申请中断。在这个驱动程序设计中,需要申请与SPI0相关的中断,并注册相应的中断处理功能。该驱动程序的中断处理函数声明如下:
staTIcirqreturn  _ ts3c  2440 _ ISR  _ SPI(IntRq,void*dev_id,structpt_regs*reg)
使用request_irq向内核申请一个中断号,并注册一个中断处理函数:
request_irq(IRQ_SPI0,s3c2440_isr_spi,SA_INTERRUPT,DEVICE_NAME,s  3c  2440 _ ISR  _ SPI);
(2)虚拟地址映射。驱动可以通过访问内核中的虚拟地址,直接访问设备物理地址对应的寄存器。SPI器件的地址映射过程如下:
request  _ mem  _ region(s3c  2440 _ PA  _ SPI,0x30,“s3c  2440-SPI”);
base  _ addr=iore  map(s3c  2440 _ PA  _ SPI,0x  30);
S3C2440_PA_SPI是SPI的物理地址(在/asm-arch/arch-s3c2440/map.h中定义),0x30的内存区域从S3C2440_PA_SPI分配,然后移动到内核空间。
(3)设置相关寄存器。通过配置SPI功能寄存器设置SPI工作模式。ioremap返回的虚拟地址作为基地址,通过增加不同的偏移量来访问相应的寄存器。在本设计中,本地SPI设置为主设备,SCK信号使能,CPOL和CPHA设置为0,SPI工作在正常模式。将波特率预分频寄存器(SPPRE)中的分频比设置为8。具体设计如下:
_ _ raw  _ writel((s3c  2440 _ SPCON  _ SMOD  _ INT  | s3c  2440 _ SPCON  _ ENSCK  | s3c  2440 _ SPCON  _ MSTR),s3c  2440 _ SPCON);
DPRINTK(DEVICE  _ NAME  " SPCONiniTIalizen  ");
_ _ raw  _ writel((s3c  2440 _ SPPIN  _ ENMUL  | s3c  2440 _ SPPIN  _ KEEP),s3c  2440 _ SPPIN);
DPRINTK(DEVICE  _ NAME  " SPPINiniTIalizen  ");
__raw_writel(0x07,s3c  2440 _ SPPRE);
DPRINTK(DEVICE  _ NAME  " SPPREinitializen  ");
(4)初始化发送和接收数据缓冲区。数据缓冲区采用环形缓冲区结构,通过头尾指针的循环移动实现缓冲区的动态管理。其定义如下:typedefstruct
spi_bufbuf[MAX_SPI_BUF];
unsignedinthead,tail;
wait_queue_head_twq;
}SPI_BUF;staticSPI_BUFspi_Tx_buf;staticSPI_BUFspi_Rec_buf;
其中spi_buf表示char型,MAX_SPI_BUF为缓冲区大小,设为1024B。head、tail分别表示头尾数组下标,wq为等待队列头。此结构依靠以下宏进行管理:
#defineSPI_Tx_BUF_HEAD(spi_Tx_buf.buf[spi_Tx_buf.head])
#defineSPI_Tx_BUF_TAIL(spi_Tx_buf.buf[spi_Tx_buf.tail])
#defineINCBUF(x,mod)((++(x))&((mod)-1))
前两个宏用于引用缓冲区中的元素,最后一个宏用于对头尾下标进行前移,并保证头尾下标数值可循环变化,不发生溢出。
在初始化时,分别对接收和发送缓冲区的头尾指针进行清零操作,具体如下:
spi_Tx_buf.head=spi_Tx_buf.tail=0;spi_Rec_buf.head=spi_Rec_buf.tail=0;
(5)内核机制相关的数据结构初始化。本设计所使用的内核机制包括了中断上下半部的操作和睡眠等待机制,因此需要对发送、接收等待队列以及tasklet结构进行初始化,并注册tasklet处理函数。初始化过程如下:
init_waitqueue_head(&(spi_Tx_buf.wq));
init_waitqueue_head(&(spi_Rec_buf.wq));
tasklet_init(&spi_tasklet,spi_tasklet_handler,data);
(6)初始化相应端口。根据S3C2440外部管脚配置,将与SPI功能引脚复用的GPIO设定为SPI相应功能。具体操作如下:
s3c2440_gpio_cfgpin
(S3C2440_GPE11,S3C2440_GPE11_SPIMISO0);
s3c2440_gpio_cfgpin
(S3C2440_GPE12,S3C2440_GPE12_SPIMOSI0);
s3c2440_gpio_cfgpin
(S3C2440_GPE13,S3C2440_GPE13_SPICLK0);
s3c2440_gpio_cfgpin
(S3C2440_GPG2,S3C2440_GPG2_INP);//设置nSS
s3c2440_gpio_cfgpin(S3C2440_GPB10,
S3C2440_GPB10_OUTP);//设置片选信号
s3c2440_gpio_setpin(S3C2440_GPB10,1);
2.2SPI写操作
写操作主要是将上层应用部分的用户空间中的数据拷贝到内核空间中的环形缓冲区中,此后将缓冲区的数据送到SPI发送寄存器中,在SPI发送完一个数据后,系统产生中断,中断例程中的下半部将调用tasklet判断缓冲区状态。若缓冲区中有相应的空间,可以将下一数据填入SPI发送寄存器中,直至将缓冲区数据全部发送完毕。
本设计的写操作实现了环形缓冲区的动态管理,即在缓冲区删除数据、尾指针前移的情况下,允许向缓冲区添加数据,头指针前移。此设计可以使用户空间任务与内核空间的数据发送同时进行,提高了用户空间任务执行效率,并且当利用copy_from_user函数将数据从用户空间拷贝至内核空间时,数据发送仍在进行,即数据从用户空间至内核空间拷贝过程与数据发送过程并发,提高了驱动程序效率。
为了实现环形缓冲区动态管理,定义了copy_to_Tx_buf_init和copy_to_Tx_buf两个函数完成数据向缓冲区的复制操作。
(1)copy_to_Tx_buf_init函数。本函数主要用于两种情况:
①如果缓冲区为空,当有一组数据到来且此数据的大小小于缓冲区的空间大小时,直接将此数据放到缓冲区中。
②如果发送数据的大小大于剩余缓冲区的空间,则只复制缓冲区大小的数据到缓冲区。
缓冲区满,该进程进行睡眠操作,直到缓冲区所有数据发送完毕,缓冲区再次为空,当前进程被唤醒,将此组用户数据的未发送部分复制到缓冲区,继续发送。
(2)copy_to_Tx_buf函数。此函数主要用于缓冲区正在发送且未发送完毕的情况,将新一组用户数据copy至缓冲区。首先计算缓冲区剩余空间,若剩余空间大于本组用户数据大小,则直接将用户数据全部copy至缓冲区;若剩余空间小于本组数据大小,则copy与剩余空间大小相同的用户数据至缓冲区。
写操作的具体流程如图1所示,首先用户数据从空间态转换到内核态,并设置相应的接收标志位。此后判断数据大小。若数据大于缓冲区空间,数据发生溢出,写操作结束;若没有溢出,为了保证进程间的数据,使得该进程获得自旋锁,此时判断缓冲区是否为空。根据上面两个函数的介绍,在不同情况下分别调用不同的函数,在数据写入环形缓冲区后,将数据发送到SPI的发送寄存器。当SPI发送寄存器发送数据时,环形缓冲区依旧接收数据,如果此时缓冲区为满,则释放自旋锁,并设置进程等待标志位(wait_Tx_done),将此进程休眠,直到发送寄存器中的数据发送完毕,再唤醒进程,判断数据是否全部发送完毕。若仍有数据等待发送,则调用copy_to_Tx_buf_int;若数据已全部发送完毕,则写操作结束。若缓冲区不为满,则判断数据是否发送完毕。数据全部发送完毕,发送操作结束。

高都电子,为客户创造价值!

双面板免费加费,四层板加急打样,厚铜电路板打样

Xcm