对于spi的只是请先阅读我的上一篇文章
http://blog.csdn.net/zlw1005/article/details/43731067
先上一个210 SPI工作流程图
首先上spi 初始化寄存器函数
static void spireg_init(void)
{
u32 val=0;
if(spi_message.mode&SPI_CPOL_MASK)
val |= ~SPI_CPOL;
else
val &=SPI_CPOL;
if(spi_message.mode&SPI_CPHA_MASK)
val |= ~SPI_CPHA;
else
val &=SPI_CPHA;
val |= SPI_MASTER;
writel(val,regs+S3C64XX_SPI_CH_CFG); //设置spi 控制器为主机
val = readl(regs+S3C64XX_SPI_CLK_CFG);
val |= ((clk_get_rate(srcclk) / spi_message.speed/ 2 - 1)/*datasheet有计算公式*/
& SPI_SPEED_MASK)|CLK_ENCLK|SPI_PCLK; //设置spi速率以及时钟源选择
DBG("val %d %d %d ",val,(int)(clk_get_rate(srcclk) / spi_message.speed/ 2 - 1),(int)clk_get_rate(srcclk) );
writel(val,regs+S3C64XX_SPI_CLK_CFG);
val = readl(regs+S3C64XX_SPI_MODE_CFG);
val |= CH_WIDTH_BYTE|BUS_WIDTH_BYTE; //设置传送数据宽度byte
writel(val,regs+S3C64XX_SPI_MODE_CFG);
writel(0, regs+S3C64XX_SPI_INT_EN);//不使用中断
val=readl(regs+S3C64XX_SPI_SLAVE_SEL);
writel(1,regs+S3C64XX_SPI_SLAVE_SEL);//关闭spi使能
val=readl(regs+S3C64XX_SPI_PACKET_CNT);
val |=0xffff;
writel(val,regs+S3C64XX_SPI_PACKET_CNT);//spi数据数目 bytes
}
设置控制器主从数据相位
```![](http://img.blog.csdn.net/20150304160703061)
<div class="se-preview-section-delimiter"></div>
if(spi_message.mode&SPI_CPOL_MASK)
val |= ~SPI_CPOL;
else
val &=SPI_CPOL;
if(spi_message.mode&SPI_CPHA_MASK)
val |= ~SPI_CPHA;
else
val &=SPI_CPHA;
val |= SPI_MASTER;
writel(val,regs+S3C64XX_SPI_CH_CFG); //设置spi 控制器为主机
时钟源选择,传输速率设置
![这里写图片描述](https://img-blog.csdn.net/20150304161028150)
<div class="se-preview-section-delimiter"></div>
val |= ((clk_get_rate(srcclk) / spi_message.speed/ 2 - 1)/datasheet有计算公式/
& SPI_SPEED_MASK)|CLK_ENCLK|SPI_PCLK; //设置spi速率以及时钟源选择
DBG(“val %d %d %d “,val,(int)(clk_get_rate(srcclk) / spi_message.speed/ 2 - 1),(int)clk_get_rate(srcclk) );
writel(val,regs+S3C64XX_SPI_CLK_CFG);
设置位宽
![这里写图片描述](https://img-blog.csdn.net/20150304161339620)
<div class="se-preview-section-delimiter"></div>
val |= CH_WIDTH_BYTE|BUS_WIDTH_BYTE; //设置传送数据宽度byte writel(val,regs+S3C64XX_SPI_MODE_CFG);
读写同步函数
<div class="se-preview-section-delimiter"></div>
static void transer_write_read(void)
{
int i=0,j=0,ms;
u32 val,loops;
writel(((spi_message.length* 8 / 8) & 0xffff)
| SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
for(i = 0;i<spi_message.length;i++)
DBG("write %d\n",spi_message.send[i]);
i=0;
while(i < spi_message.length)
writeb(spi_message.send[i++], regs + S3C64XX_SPI_TX_DATA);
val = readl(regs + S3C64XX_SPI_CH_CFG);
val =(RX_CH_ON | TX_CH_ON);
writel(val, regs + S3C64XX_SPI_CH_CFG);
writel(0, regs + S3C64XX_SPI_SLAVE_SEL);
gpio_set_value(S5PV210_GPB(5),1);
ms =spi_message.length* 8 * 1000 / spi_message.speed+20;
loops = msecs_to_loops(ms);
do{
val = readl(regs+S3C64XX_SPI_STATUS);
}while(loops-- && RX_FIFO_LVL(val)<spi_message.length);//wait for rx fifo receive spi_message.length
spi_readregs(regs);
while(j<spi_message.length)
{
spi_message.receive[j++] = readb(regs + S3C64XX_SPI_RX_DATA);
DBG(" %x\n ",spi_message.receive[j-1]);
}
DBG("read after\n");
spi_readregs(regs);
writel(1, regs + S3C64XX_SPI_SLAVE_SEL);
gpio_set_value(S5PV210_GPB(5),0);
spi_flush_fifo();
}
由于传输数据很频繁,这里采用mmap方式实现内核和用户态数据交互,提高效率
<div class="se-preview-section-delimiter"></div>
static int myspi_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
int spi_size = vma->vm_end - vma->vm_start;
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if(map_index++)//第一次映射send
{
ret = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(spi_message.send)>> PAGE_SHIFT,
spi_size, vma->vm_page_prot);
}
else
{
ret = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(spi_message.receive)>> PAGE_SHIFT,
spi_size, vma->vm_page_prot);
}
if (ret)
return -EAGAIN;
return 0;
}
注意这里要映射的kmalloc分配的spi_message.receive(逻辑地址在物理内存上是连续的和vmalloc有区别)内存必须是PAGE_SIZE*n才能保证映射以及后续操作成功
stm32全双工通信的实现,由于spi协议中规定从机和主机通信的成功与否依赖于主机的时钟,所以我们这里使用一个IO口向210发送个跳变,210监测到就发送一个异步信号给用户层,主机开始产生sck给让从机能够通信成功
<div class="se-preview-section-delimiter"></div>
static irqreturn_t myspi_slave_irq(int irq, void *dev_id)
{
kill_fasync (&myspi_async, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
“`
程序下载链接
http://download.csdn.net/detail/zlw1005/8472605
注意本程序只是个粗糙的测试程序,很多地方需要完善,请注意stm32芯片类型
还有210spi驱动我这边测试的时钟过高(远没有达到高速率)有问题,可能是我这边的硬件问题