转自:http://blog.csdn.net/stargui/archive/2008/05/08/2418367.aspx
2410 spi 与 DSP vc33 serial 的通讯:
因为需要2410 和 vc33通讯, 通讯速率要求 vc33->2410 大于1Mbyte/s , 2410->vc33速率要求较低, 所以主要解决vc33->2410的问题, vc33的串口波特率可以达到30Mbps, 2410的spi口可以达到25Mbps, 因此考虑用2410的spi和vc33的串口来交换数据。
过程如下:2410的spi设置为slave, vc33的串口发送作master。
接线如下: 2410 vc33
spinss1 <--> FSX
spiclk1 <--> CLKX
spimiso1 <--> DX
数据通讯模式考虑到通讯速率,不可能采用polling 或 interrupt模式,只能采用dma模式:
vc33方面的配置:
串口配置:
rSERIOX = 0x333;
rSERIOR = 0x111;
rSERTCON = 0x1EF;
rSERTCNT = 0x0002;
rSERTCMP = 0x0002;
rSERCON = 0xEBD0344;
中断配置:
asm(" LDI 0121H, IE");
传送: rDMACON = 0x0000; // reset DMA
rDMASRC = (unsigned int)src; // src of data
rDMADST = 0x808048; // serial dx
rDMACNT = cnt; // dma count
rDMACON = 0x0E13; // 配置并发送
rSERTXD = 0x55555555; // 触发发送
2410 spi dma的linux驱动编写:
因为以前只针对atmel 的 9260、 9261的dma写过驱动,2410下面的费了一番功夫。
2410的dma的实现代码在
linux-2.6.24.3/arch/arm/plat-s3c24xx/dma.c
linux-2.6.24.3/include/asm/arch/dma.h
看了一下代码: DMA对硬件进行了抽象, 维护一个等待队列, 队列中保存着每个DMA的请求, 通过几个简单的接口来管理 DMA通道申请、DMA中断申请、控制寄存器设置、挂入DMA等待队列、清除DMA中断、释放DMA通道。 下面详解整个DMA过程:
串口配置: __raw_writeb(0x51, spi_base + 0x00);
__raw_writeb(0x02, spi_base + 0x08);
__raw_writeb(0x01, spi_base + 0x0C);
s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPG5_SPIMISO1);
s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1);
s3c2410_gpio_cfgpin(S3C2410_GPG3, (3<<6));
分配DMA缓冲:
dmabuffer=dma_alloc_coherent(NULL, SPI_DMA_BUF_SIZE, &dmaphys, GFP_KERNEL|GFP_DMA);
配置DMA: ret = s3c2410_dma_request(DMACH_SPI1, &s3c2410spi1_dma_in, NULL);
/*
函数原型:
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev);
功能: 申请DMA资源, 申请DMA中断。
输入参数: channel 通道号(这里的通道号在dma.h中声明)
client 名称,同样在dma.h中声明。
dev 可以是NULL
*/
s3c2410_dma_setflags(DMACH_SPI1, S3C2410_DMAF_AUTOSTART);
/*
函数原型:
int s3c2410_dma_setflags(dmach_t channel, unsigned int flags);
功能: 设置DMA的标志。
输入参数: channel 通道号(这里的通道号在dma.h中声明)
flag 标志,这里设置为S3C2410_DMAF_AUTOSTART
*/
s3c2410_dma_devconfig(DMACH_SPI1, S3C2410_DMASRC_HW, 0x03, 0x59000034);
/*
函数原型:
int s3c2410_dma_devconfig(int channel,enum s3c2410_dmasrc source, int hwcfg,
unsigned long devaddr)
功能: 配置DMA的源/目的硬件类型和地址:
输入参数: channel 通道号(这里的通道号在dma.h中声明)
source: S3C2410_DMASRC_HW: src is hardware, S3C2410_DMASRC_MEM: src is memory
hwcfg: the value for xxxSTCn register,
bit 0: 0=increment pointer, 1=leave pointer bit 1: 0=source is AHB, 1=source is APB
devaddr: 源物理地址
*/
s3c2410_dma_config(DMACH_SPI1, 1, 0xa2800000);
/*
函数原型:
int s3c2410_dma_config(dmach_t channel, int xferunit, int dcon);
功能: 设置配置DMA寄存器。
输入参数: channel 通道号(这里的通道号在dma.h中声明)
xfersize: size of unit in bytes (1,2,4)
dcon: base value of the DCONx register */
s3c2410_dma_set_buffdone_fn(DMACH_SPI1, spi_dmain_done_callback);
/*函数原型:
int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn);
功能: 设置配置DMA寄存器。
输入参数: channel 通道号(这里的通道号在dma.h中声明)
rtn 回调函数, 在DMA完成时调用。
*/
通过以上过程,DMA通道已经申请并准备好,现面就可以开始DMA的传送了,需要下面的函数:
s3c2410_dma_enqueue(DMACH_SPI1, (void *)dmabuf, dmaphys, SPI_DMA_BUF_SIZE);
/*
函数原型:
int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size);
功能: 分配并初始化一个DMA内核缓冲区控制结构, 并将它插入DMA等待队列, 设置DMA控制寄存器内容, 等待DMA操作触发。
输入参数: channel 通道号(这里的通道号在dma.h中声明)
id the device driver's id information for this buffer
data the physical address of the buffer data
size the size of the buffer in bytes If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART is checked, and if set, the channel is started. If this flag isn't set, then an error will be returned. It is possible to queue more than one DMA buffer onto a channel at once, and the code will deal with the re-loading of the next buffer when necessary.
*/
DMA通道把以上请求放入等待队列,在合适的时候会开始请求传送。
用以下函数可以释放申请的DMA通道:
int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client);
回调函数: 因为回调函数在DMA中断中调用, 中断服务程序在执行时,会屏蔽其它中断。因此,中断服务程序应该执行得越快越好,以免延误其他中断, 而丢失信息。 因此此处用了Tasklet, 以便及时响应其他设备中断。实现如下:
static void
Serial_OK_Tasklet(unsigned long data) ...{
//add code do what you want
... ... ...
s3c2410_dma_enqueue(DMACH_SPI1, (void *)dmabuf, dmaphys, SPI_DMA_BUF_SIZE);
//DMA通道申请下一次传送
}
首先通过DECLARE_TASKLET(name, func, data)来创建tasklet。
这是一个宏定义: DECLARE_TASKLET(SerialOKTasklet, Serial_OK_Tasklet, (unsigned long)(0));
static void
spi_dmain_done_callback(struct s3c2410_dma_chan *ch, void *buf, int size, enum s3c2410_dma_buffresult result) ...{
tasklet_schedule(&SerialOKTasklet); //该tasklet就会在适当的时机得到执行。
}
通过以上操作,实现了spi的DMA数据交换。
在调试过程中,一开始DMA怎么也没有进行传送,费了很大事,后来仔细看了DMA.C的代码,发现了几处可疑的地方,
static struct
s3c2410_dma_chan *lookup_dma_channel(unsigned int channel) {
if (channel & DMACH_LOW_LEVEL)
return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL];
else return dma_chan_map[channel];
}
函数实现查找对应的DMA通道,其中s3c2410_chans对应的是4个物理通道 dma_chan_map[channel]对应的是映射的通道,但是在以后的DMA.C的调用中,出现了传入的是物理通道, 但是却没有DMACH_LOW_LEVEL标志位的错误,主要是这个函数 s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); 改正后 s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_START); 通讯正常,不知道是不是DMA的BUG。( 转载请著明出处)。
发表于 @ 2008年05月08日 19:15:00
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/stargui/archive/2008/05/08/2418367.aspx
文章评论(0条评论)
登录后参与讨论