spi方式以dma方式进行数据交互的主要思想:配置好SPI和DMA后,CPU只需启动传输,后续的数据收发全由DMA硬件自动完成,完成后通过中断通知CPU。这样CPU就可以腾出手来处理其他任务,或者在低功耗模式下等待。
一:软件的实现的基本思路:
为了实现SPI DMA读取数据的功能,关键在正确配置SPI与DMA外设参数信息。以下是基于AI8051U特性的操作步骤:
1.1 初始化SPI外设:
首先,SPI需要配置为主机模式,并设置好通信速率、数据格式(CPOL、CPHA)等。对于读取操作,通常还需要将SPI的片选(CS)引脚作为通用GPIO来控制,以便在读写操作前选中SPI设备。
AI8051U支持高速SPI,你可以在初始化时开启其FIFO(先进先出)缓冲区并调整时序参数,以优化传输效率。
1.2 配置DMA控制器:
设置源地址:指向SPI的数据接收寄存器。
设置目标地址:指向你在内存(建议使用 xdata 类型)中开辟的接收缓冲区。
设置传输长度:告诉DMA这次要读取多少个字节的数据。
设置传输模式:配置为“外设到内存”的单次传输模式,并开启传输完成中断。
1.3 启动DMA传输:
在需要读取数据时,先用软件拉低CS引脚,选中从机设备。
然后,向DMA控制寄存器中写入启动命令,DMA便会接管后续工作,自动产生SPI时钟并读取数据存入指定缓冲区。
AI8051U的DMA支持外设到外设(P2P)的直接传输,如果你想把从SPI读取的数据直接发给另一个外设(如显示屏的接口),可以进一步研究这种高级用法。
1.4 处理DMA完成中断:
当指定长度的数据读取完成后,DMA会触发中断。
在中断服务函数中,你应首先清除中断标志位,然后拉高CS引脚释放从设备。最后,你可以设置一个全局标志,通知主程序数据已经准备好,可以进行处理了
二:主要特色:
AI8051U为SPI DMA提供了强大的硬件支持,用好这些特性可以进一步提升效率:
调整传输间隔:AI8051U的DMA控制器中有专门的寄存器 DMA_SPI_ITVH 和 DMA_SPI_ITVL,它们用于控制SPI数据包之间的间隔时间。在某些高速场景下,将此值调小(甚至设为0),可以有效降低间隔,提升总线的利用率。
善用高速模式:配合AI8051U的主频(如40MHz),SPI通信速率可以达到较高水平,这对于需要快速读取大量数据(如从外部Flash加载图形、音频等)的应用非常关键。
三:软件代码
3.1 SPI初始化
void SPI_init(void)
{
SPI_S1 = 1; //00: P1.4 P1.5 P1.6 P1.7, 01: P2.4 P2.5 P2.6 P2.7, 10: P4.0 P4.1 P4.2 P4.3, 11: P3.5 P3.4 P3.3 P3.2
SPI_S0 = 0;
SSIG = 1; //忽略 SS 引脚功能,使用 MSTR 确定器件是主机还是从机
SPEN = 1; //使能 SPI 功能
DORD = 0; //先发送/接收数据的高位( MSB)
MSTR = 1; //设置主机模式
CPOL = 0; //SCLK 空闲时为低电平,SCLK 的前时钟沿为上升沿,后时钟沿为下降沿
CPHA = 0; //数据 SS 管脚为低电平驱动第一位数据并在 SCLK 的后时钟沿改变数据
SPCTL = (SPCTL & ~3) | 3; //SPI 时钟频率选择, 0: 4T, 1: 8T, 2: 16T, 3: 2T
HSCLKDIV = 0x08; //高速时钟8分频,默认2分频。开漏模式通过10K电阻上拉到3.3V,电平上升速度慢,需要降低SPI速率才能正常通信。
SPI_SCK = 0; // set clock to low initial state
SPI_SI = 1;
SPIF = 1; //清SPIF标志
WCOL = 1; //清WCOL标志
} 3.2 发送一个字节
void SPI_WriteByte(u8 out)
{
SPDAT = out;
while(SPIF == 0);
SPIF = 1; //清SPIF标志
WCOL = 1; //清WCOL标志
}3.3 读取一个字节
u8 SPI_ReadByte(void)
{
SPDAT = 0xff;
while(SPIF == 0);
SPIF = 1; //清SPIF标志
WCOL = 1; //清WCOL标志
return (SPDAT);
}3.4 使用dma方式写入多个字节
void SPI_Write_Nbytes(u32 addr, u8 size)
{
if(size == 0) return;
if(!B_FlashOK) return;
while(DmaFlag); //DMA忙检测
while(CheckFlashBusy() > 0); //Flash忙检测
FlashWriteEnable(); //使能Flash写命令
SPI_CE_Low(); // enable device
SPI_WriteByte(SFC_PAGEPROG); // 发送页编程命令
SPI_WriteByte(((u8 *)&addr)[1]); //设置起始地址
SPI_WriteByte(((u8 *)&addr)[2]);
SPI_WriteByte(((u8 *)&addr)[3]);
DmaFlag = 1;
DMA_SPI_AMT = size-1; //设置传输总字节数:n+1
DMA_SPI_CR |= 0x40; //开始SPI_DMA主模式操作
}3.5 使用dma方式读取多个字节
u8 SPI_Read_Compare(u16 size)
{
u8 j=0;
if(size == 0) return 2;
if(!B_FlashOK) return 2;
while(DmaFlag); //DMA忙检测
do
{
if(DmaRxBuffer[j] != DmaTxBuffer[j]) //receive byte and store at buffer
{
return 1;
}
j++;
}while(--size); //read until no_bytes is reached
return 0;
}3.6 spi的dma中断服务函数
void SPI_DMA_Interrupt(void) interrupt 13
{
DMA_SPI_STA = 0;
DmaFlag = 0;
SPI_CE_High();
}
我要赚赏金
