DSP(TI-C2000)---基于SPI通信的FIFO接收发送中断(主从模式)

首先,叠加环节:

1.才入门dsp,我的理解和代码可能会有错误,主要供作者本人参考!!!
2.如果您发现我的文章错误请联系我,谢谢。
3.基本所有资料都是从网上查找的,如有侵权,请联系我。

4.我是用的芯片TMS320F280039
 

目录

一、SPI主要结构

二、SPI配置

1.SPI外设时钟配置

2. GPIO初始化

2.1 主设备GPIO配置

2.2 从设备GPIO配置:

3.外设时钟分频配置

4.SPI初始化配置

5.FIFO中断

中断原理

FIFO阈值控制说明

SPI中断配置

中断启用逻辑(并行触发中断、并行执行中断服务函数)

 1)并行触发中断​编辑

 2)并行执行中断服务函数

附录一:相关资料

附录二:代码


一、SPI主要结构

SPI通信:高速、全双工、同步。

以上是SPI的主要硬件结构:
1.区分主机、从机 。主从不同,相关的配置也不一样。
2.接口:

        MOSI连接SIMO:主设备输出、从设备输入。

        MISO连接SOMI:主设备输入、从设备输出。

        SCLK:时钟
        CS(STE):片选信号

注意:当有数据从主机发出,那么必有数据从从机发出,也就是说在主机发出数据,从机没有发送数据的同时,主机这个时候去接收也会接收到一串数据,我遇到的情况就是接收到了主机接收到了主机发出的数据。
SPI 设备间的数据传输之所以又被称为数据交换是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)". 在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据(不管主设备好还是从设备), 相当于该设备有一个 bit 大小的数据被交换了。

具体看这篇文章:

https://blog.csdn.net/qq_41899480/article/details/100041428

二、SPI配置

1.SPI外设时钟配置

        首先使能SPI的外设时钟,我只使用了SPIA,SPIB可以不写:

 EALLOW;
 CpuSysRegs.PCLKCR8.bit.SPI_A = 1;
 CpuSysRegs.PCLKCR8.bit.SPI_B = 1;
 EDIS;

2. GPIO初始化

首先,查找芯片手册,找对应引脚数的芯片,然后找使用的功能的复用通道。

注意,每个功能可能有很多个GPIO可以使用,要找到自己想用的,或者开发板支持的引脚。我使用的是f280039。对应的引脚号:
        SPI SIMOA --> GPIO54
        SPI SOMIA --> GPIO55
        SPI CLKA  --> GPIO12
        SPI STEA --> GPIO57


然后是GPIO初始化:
SCLK:主设备推挽输出、从设备浮空输入,一般不上拉。
MOSI:主设备推挽输出、从设备浮空输入,一般不上拉。
MISO:若从设备推挽输出,则主设备浮空输入。
             若从设备开漏输出,则主设备上拉输入。
CS/STE:主设备推挽输出、从设备浮空输入。

2.1 主设备GPIO配置

2.2 从设备GPIO配置:

从设备这里只写了gpio,也需要加上EALLOW、EDIS、外设时钟使能

 

3.外设时钟分频配置

SPI的时钟源是LSPCLK。
时钟频率配置过程:
输入时钟信号--->refdiv分频-->imult倍频-->odiv分频-->SYSCLK(主时钟)-->LSPCLKDIV(低速外设时钟分频)-->LSPCLK(低速外设时钟)
(1)查看时钟源:SPI的时钟源是外设时钟,查看开发板原理图的外部晶振频率,我的外部晶振是20Mhz。
(2)查看倍频、分频:芯片手册查看不同的值对应的功能。


主时钟配置如下(使用的官方库函数  在f28003x_sysctrl.c文件的void InitSysCtrl(void) 中  ):
一般不用改这里,官方基本是配置好的

计算过程:20Mhz*48/2/4=120Mhz(主时钟频率)
                   

低速外设时钟配置如下:

这里赋值3是因为参考手册写的0x011的时候为6分频。

计算过程:120Mhz/6=20Mhz  



4.SPI初始化配置

 // SPI核心寄存器配置(复位状态下设置)
    SpiaRegs.SPICCR.bit.SPISWRESET = 0;  // 复位SPI模块(!!!必须先复位才能修改配置!!!)
    SpiaRegs.SPICCR.bit.CLKPOLARITY = 0;  // 时钟极性=0:上升沿发送数据,下降沿接收数据
    SpiaRegs.SPICCR.bit.SPILBK = 0;      // 禁用回环模式(正常外部通信)******我不确定这个可不可以不写******
    SpiaRegs.SPICCR.bit.SPICHAR = 0xF;   // 字符长度=16位(0xF + 1 = 16)

    SpiaRegs.SPICTL.bit.CLK_PHASE = 0;    // 时钟相位=0:无延迟(数据在时钟第一个边沿采样)
    SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; // 主模式(1=主机,0=从机)
    SpiaRegs.SPICTL.bit.TALK = 1;         // 使能数据传输(允许主机发送数据)******主机要写,从机应该为0******
    SpiaRegs.SPICTL.bit.SPIINTENA = 0;    // 禁用标准SPI中断(使用FIFO中断替代)

    SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x0031; // 波特率寄存器值=49,波特率=LSPCLK/(SPIBRR+1)=20MHz/50=400kHz
    // 高级控制:调试模式设置
    SpiaRegs.SPIPRI.bit.FREE = 1;  // 自由运行模式:调试断点不影响SPI传输******可以不写*****

    // FIFO发送配置
    SpiaRegs.SPIFFTX.bit.SPIRST = 1;      // 复位FIFO发送缓冲区
    SpiaRegs.SPIFFTX.bit.SPIFFENA = 1;    // 使能FIFO功能(替代标准SPI缓冲区)
    SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1;  // 清除发送FIFO中断标志(先设1后清0)
    SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 0;  // 完成清除操作
    SpiaRegs.SPIFFTX.bit.TXFFIENA = 0;    // 禁用发送FIFO中断*****一般写1来打开TXFFIENA 中断,我有特殊要求所以关闭****
    SpiaRegs.SPIFFTX.bit.TXFFIL = 4;      // 发送FIFO中断阈值=4

    // FIFO接收配置
    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 1;  // 清除接收FIFO溢出标志
    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 0;  // 完成清除操作
    SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1;  // 清除接收FIFO中断标志
    SpiaRegs.SPIFFRX.bit.RXFFIENA = 0;    // 禁用接收FIFO中断*****一般写1来打开RXFFIENA 中断,我有特殊要求所以关闭****
    SpiaRegs.SPIFFRX.bit.RXFFIL = 4;      // 接收FIFO中断阈值=4

    // FIFO传输延迟控制
    SpiaRegs.SPIFFCT.bit.TXDLY = 0;       // 发送字符间延迟=0(无额外延迟)
    SpiaRegs.SPIFFCT.all = 0x00;          // 整个延迟寄存器清零(确保默认状态)*****我不确定是不是必须写*****

    // 激活FIFO缓冲区
    SpiaRegs.SPIFFTX.bit.TXFIFO = 0;      // 复位发送FIFO
    SpiaRegs.SPIFFTX.bit.TXFIFO = 1;      // 使能发送FIFO
    SpiaRegs.SPIFFRX.bit.RXFIFORESET = 0; // 复位接收FIFO
    SpiaRegs.SPIFFRX.bit.RXFIFORESET = 1; // 使能接收FIFO

    // 最终使能SPI模块
    SpiaRegs.SPICCR.bit.SPISWRESET = 1;  // 释放SPI复位,模块开始工作

步骤说明(我的配置和下面的步骤差不多):

1.配置SPI_CR.MASTER=0;
2.主从模式选择,选择极性、相位、数据长度、波特率;
3.使能FIFO(SPIFFENA);
4.清除 FIFO 标志(TXFFINTCLR、RXFFOVFCLR 和 RXFFINTCLR);
5.使能接收、发送FIFO中断(TXFFIENA、RXFFIENA);
6.解锁发送和接收 FIFO 重置(TXFFIFO 和 RXFFIORESET);
7.配置SPI_CR.MASTER=1;

5.FIFO中断

中断原理

中断触发过程

外设级:

  • 外设事件触发中断标志位,若使能位开启,则向 PIE 提交请求。

PIE 级:

  • 将 96 个外设中断分组为 12 组(INT1–INT12),每组 8 个中断(INTx.1–INTx.8)。
  • 通过 PIEIERx(组使能)、PIEIFRx(组标志)、PIEACKx(组应答)三级寄存器控制中断传递 。

CPU 级:

  • 接收 PIE 的 INTx 信号后,置位 IFR 标志位。
  •  IER 使能对应 INTx 且 INTM = 0(全局中断开启),则 CPU 响应该中断 。

中断优先级:

  • CPU 级优先级:INT1 > INT2 > ... > INT12(固定)

  • PIE 组内优先级:INTx.1 > INTx.2 > ... > INTx.8(硬件固定)

  • 若需调整 SPI 中断优先级,需通过 PIEIERx 的使能顺序实现。

FIFO 阈值:

  • TXFFIL:建议设为 FIFO 深度的 1/4(如 16 级 FIFO 设 TXFFIL=4

  • RXFFIL:建议略大于预期单次数据包大小(避免频繁中断)

SPI FIFO中断触发机:

以下是符合要求的表格格式,可直接插入文章:

FIFO阈值控制说明
寄存器功能描述触发条件设计目的
RXFFIL接收FIFO数据量 ≥ 阈值RXFFST ≥ RXFFIL避免频繁中断,数据积累到一定量再处理
TXFFIL发送FIFO数据量 ≤ 阈值TXFFST ≤ TXFFIL防止FIFO空转,维持连续传输
  1. 当接收FIFO中的数据量(RXFFST)大于或等于 RXFFIL设定的阈值时,若RXFFIENA=1(接收中断使能),则触发SPIRXINT中断 。
  2. 当发送FIFO中的数据量(TXFFST)小于或等于 TXFFIL设定的阈值时,若TXFFIENA=1(发送中断使能),则触发SPITXINT中断 。

具体触发中断的方法:

//发送中断触发 
for(i=0; i<4; i++)//深度是4,所以一次性填写4次,TXFFST=4等于TXFFIL=4。触发FIFO发送中断。填写的次数小于4也可以触发。
 {
    SpiaRegs.SPITXBUF = SpiFrame.TxBuf[TxIndex];
    TxIndex++;
 }
//接收中断触发
 for(i=0;i<Spi_Len;i++)//深度是4,所以一次性填写4次,RXFFST=4等于RXFFIL=4。触发FIFO接收中断。填写的次数大于4也可以触发。
 {
    SpiFrame.RxBuf[RxIndex]=SpiaRegs.SPIRXBUF;
    TxIndex++;
 }

注意:当在spi的中断配置时使能发送中断(SpiaRegs.SPIFFTX.bit.TXFFIENA = 1;)会导致spi初始化一结束就会触发发送中断,进入发送中断服务函数。

SPI中断配置
    // 中断配置:使能CPU级和PIE级中断
    EALLOW;
    IER |= M_INT6;  // 使能CPU中断组6(SPI中断通常映射到此组)
    EINT;                 // 开启全局中断(INTM = 0) 
    PieCtrlRegs.PIEIER6.bit.INTx1 = 1;  // 使能PIE组6的INTx1(接收中断)
    PieCtrlRegs.PIEIER6.bit.INTx2 = 1;  // 使能PIE组6的INTx2(发送中断)
    PieVectTable.SPIA_RX_INT = &SpiRxIsr;  // 绑定接收中断服务程序
    PieVectTable.SPIA_TX_INT = &SpiTxIsr;  // 绑定发送中断服务程序
    EDIS;

中断服务函数

interrupt void SpiTxIsr(void)
 {
    ......//处理发送数据

     
     SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1;  // 清除发送中断标志
     PieCtrlRegs.PIEACK.all = PIEACK_GROUP6;  //  中断返回前清楚该标志位,我的是GROUP6。在对应的GROUP6的bit上写1。     
 }


 interrupt void SpiRxIsr(void)
 {
    ......//处理发送数据


    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 1;  // 清除接收溢出中断标志
    SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1;  // 清除接收中断标志
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP6; 
 }


步骤说明:
1.需要写寄存器保护的解除、结束要写恢复。
2.配置外设中断,即初始化PIE向量表,将发送(SpiTxIsr)、接收(SpiRxIsr)中断服务函数绑定。
3.使能PIE中断。
4.使能CPU中断。
5.开启全局中断。
6.!!!!中断服务函数void前写interrupt 。
7.在中断服务函数结束时清除外设中断标志、打开闸门(PieCtrlRegs.PIEACK相应的bit写1)。

可能遇到的问题:

  •         同组高优先级中断阻塞低优先级:原因是PIEACKx 未及时清除,必须在 ISR 内清除 PIEACKx来解决。

当中断从PIE传播到CPU中断线时,该中断组的PIEACK位会被设置。这可以防止在处理第一个中断时,该组中的其他中断继续传播到CPU。向PIEACK位写入1可以清除该位,允许来自同一组的其他中断继续。


中断启用逻辑(并行触发中断、并行执行中断服务函数)

因为是全双工,所以软件层面来说可以同时发送、接收。主要看硬件层面是否支持。
如果硬件层面两个中断没有互斥逻辑就可以触发。

场景是否并行触发是否并行响应约束条件
FIFO阈值同时满足✅ 是✅ 是(硬件支持)无资源冲突
CPU单核处理✅ 是⚠️ 顺序响应优先级决定顺序(INT6.1先于INT6.2)
中断嵌套使能✅ 是✅ 是需配置 INTM=0 及嵌套优先级


 

F280039中SPITXINT、SPIRXINT是两个独立的外设中断源,映射到PIE的不同中断线上:INT6.1、INT6.2。

 1)并行触发中断

这一部分我也不太清楚,大概是这个意思:F280039硬件支持并行触发中断,所以可以并行触发中断.

 2)并行执行中断服务函数

在CPU中断使能的情况下,它们可以同时被挂起,但由于CPU单核的限制,中断服务函数(ISR)不能真正同时执行(除非使用中断嵌套),让高优先级中断打断低优先级中断。

但是DSP\F280039好像是支持并行处理的


一下是AI生成的解释(有没有大佬帮我看看这部分对不对)

  • 发送和接收中断分别映射到INT6.1和INT6.2(假设接收为INT6.1,发送为INT6.2),组内优先级接收高于发送。
  • 如果接收中断服务函数正在执行,此时发生发送中断,那么发送中断会等待,直到接收中断执行完毕(除非在接收中断中打开了全局中断,允许嵌套)。

因此,要实现“同时进行”的效果,可以在接收中断服务函数中开启全局中断(EINT),这样发送中断就可以打断接收中断,从而形成嵌套。这样,发送中断服务函数就可以在接收中断服务函数执行过程中被执行。


附录一:相关资料

TI的spi配置文档:

https://coecsl.ece.illinois.edu/me461/Labs/SPICondensed_TechRef.pdf

和我一起学习dsp开发的人写的SPI的链接,这一篇从机的配置多一些可以参考:
https://blog.csdn.net/2301_76477834/article/details/148395050?spm=1001.2014.3001.5502

附录二:代码
 

void InitSpiAGpio(void)
{
   EALLOW;
   CpuSysRegs.PCLKCR8.bit.SPI_A = 1;       // SPI-A 外设时钟使能

   GPIO_SetupPinMux(GPIONUM_54, GPIO_MUX_CPU1, 1);
   GPIO_SetupPinOptions(GPIONUM_54, GPIO_OUTPUT, GPIO_OUTPUT, GPIO_PUSHPULL);

   GPIO_SetupPinMux(GPIONUM_55, GPIO_MUX_CPU1, 1);
   GPIO_SetupPinOptions(GPIONUM_55, GPIO_INPUT, GPIO_PULLUP);

   GPIO_SetupPinMux(GPIONUM_12, GPIO_MUX_CPU1, 11);
   GPIO_SetupPinOptions(GPIONUM_12, GPIO_OUTPUT, GPIO_PUSHPULL);

   GPIO_SetupPinMux(GPIONUM_57, GPIO_MUX_CPU1, 1);
   GPIO_SetupPinOptions(GPIONUM_57, GPIO_OUTPUT, GPIO_PUSHPULL);
   EDIS;
}

void SpiAInit(void)
{
    InitSpiAGpio();//GPIO
    EALLOW;
    ClkCfgRegs.LOSPCP.bit.LSPCLKDIV = 3;       // 120mhs/6=20mhs
    EDIS;

    EALLOW;
    IER |= M_INT6;  //  Enable interrupts:
    PieCtrlRegs.PIEIER6.bit.INTx1 = 1;
    PieCtrlRegs.PIEIER6.bit.INTx2 = 1;
    PieVectTable.SPIA_RX_INT = &SpiRxIsr;    //
    PieVectTable.SPIA_TX_INT = &SpiTxIsr;    //
    EDIS;

 // SPI核心寄存器配置(复位状态下设置)
    SpiaRegs.SPICCR.bit.SPISWRESET = 0;  // 复位SPI模块(!!!必须先复位才能修改配置!!!)
    SpiaRegs.SPICCR.bit.CLKPOLARITY = 0;  // 时钟极性=0:上升沿发送数据,下降沿接收数据
    SpiaRegs.SPICCR.bit.SPILBK = 0;      // 禁用回环模式(正常外部通信)******我不确定这个可不可以不写******
    SpiaRegs.SPICCR.bit.SPICHAR = 0xF;   // 字符长度=16位(0xF + 1 = 16)

    SpiaRegs.SPICTL.bit.CLK_PHASE = 0;    // 时钟相位=0:无延迟(数据在时钟第一个边沿采样)
    SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; // 主模式(1=主机,0=从机)
    SpiaRegs.SPICTL.bit.TALK = 1;         // 使能数据传输(允许主机发送数据)******主机要写,从机应该为0******
    SpiaRegs.SPICTL.bit.SPIINTENA = 0;    // 禁用标准SPI中断(使用FIFO中断替代)

    SpiaRegs.SPIBRR.bit.SPI_BIT_RATE = 0x0031; // 波特率寄存器值=49,波特率=LSPCLK/(SPIBRR+1)=20MHz/50=400kHz
    // 高级控制:调试模式设置
    SpiaRegs.SPIPRI.bit.FREE = 1;  // 自由运行模式:调试断点不影响SPI传输******可以不写*****

    // FIFO发送配置
    SpiaRegs.SPIFFTX.bit.SPIRST = 1;      // 复位FIFO发送缓冲区
    SpiaRegs.SPIFFTX.bit.SPIFFENA = 1;    // 使能FIFO功能(替代标准SPI缓冲区)
    SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1;  // 清除发送FIFO中断标志(先设1后清0)
    SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 0;  // 完成清除操作
    SpiaRegs.SPIFFTX.bit.TXFFIENA = 0;    // 禁用发送FIFO中断*****一般写1来打开TXFFIENA 中断,我有特殊要求所以关闭****
    SpiaRegs.SPIFFTX.bit.TXFFIL = 4;      // 发送FIFO中断阈值=4

    // FIFO接收配置
    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 1;  // 清除接收FIFO溢出标志
    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 0;  // 完成清除操作
    SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1;  // 清除接收FIFO中断标志
    SpiaRegs.SPIFFRX.bit.RXFFIENA = 0;    // 禁用接收FIFO中断*****一般写1来打开RXFFIENA 中断,我有特殊要求所以关闭****
    SpiaRegs.SPIFFRX.bit.RXFFIL = 4;      // 接收FIFO中断阈值=4

    // FIFO传输延迟控制
    SpiaRegs.SPIFFCT.bit.TXDLY = 0;       // 发送字符间延迟=0(无额外延迟)
    SpiaRegs.SPIFFCT.all = 0x00;          // 整个延迟寄存器清零(确保默认状态)*****我不确定是不是必须写*****

    // 激活FIFO缓冲区
    SpiaRegs.SPIFFTX.bit.TXFIFO = 0;      // 复位发送FIFO
    SpiaRegs.SPIFFTX.bit.TXFIFO = 1;      // 使能发送FIFO
    SpiaRegs.SPIFFRX.bit.RXFIFORESET = 0; // 复位接收FIFO
    SpiaRegs.SPIFFRX.bit.RXFIFORESET = 1; // 使能接收FIFO

    // 最终使能SPI模块
    SpiaRegs.SPICCR.bit.SPISWRESET = 1;  // 释放SPI复位,模块开始工作

}


interrupt void SpiTxIsr(void)
 {
    ......//处理发送数据

     
     SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1;  // 清除发送中断标志
     PieCtrlRegs.PIEACK.all = PIEACK_GROUP6;  //  中断返回前清楚该标志位,我的是GROUP6。在对应的GROUP6的bit上写1。     
 }


 interrupt void SpiRxIsr(void)
 {
    ......//处理发送数据


    SpiaRegs.SPIFFRX.bit.RXFFOVFCLR = 1;  // 清除接收溢出中断标志
    SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1;  // 清除接收中断标志
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP6; 
 }



#include "F28x_Project.h" #define BURST (FIFO_LVL-1) #define TRANSFER 15 #define FIFO_LVL 8 #pragma DATA_SECTION(sdata, "ramgs0"); #pragma DATA_SECTION(rdata, "ramgs1"); Uint16 sdata[128]; Uint16 rdata[128]; Uint16 rdata_point; volatile Uint16 *DMADest; volatile Uint16 *DMASource; volatile Uint16 done; __interrupt void local_D_INTCH5_ISR(void); __interrupt void local_D_INTCH6_ISR(void); void delay_loop(void); void dma_init(void); void spi_fifo_init(void); void error(); void main(void) { Uint16 i; InitSysCtrl(); DINT; IER = 0x0000; IFR = 0x0000; InitPieCtrl(); InitPieVectTable(); EALLOW; PieVectTable.DMA_CH5_INT= &local_D_INTCH5_ISR; PieVectTable.DMA_CH6_INT= &local_D_INTCH6_ISR; EDIS; dma_init(); spi_fifo_init(); EALLOW; CpuSysRegs.SECMSEL.bit.PF2SEL = 1; EDIS; for(i=0; i<128; i++) { sdata[i] = i; rdata[i]= 0; } rdata_point = 0; PieCtrlRegs.PIECTRL.bit.ENPIE = 1; PieCtrlRegs.PIEIER7.bit.INTx5 = 1; PieCtrlRegs.PIEIER7.bit.INTx6 = 1; IER= M_INT7; EINT; StartDMACH6(); StartDMACH5(); done = 0; while(!done); ESTOP0; } void error(void) { asm(" ESTOP0"); for (;;); } void spi_fifo_init() { SpiaRegs.SPIFFRX.all=0x2040; SpiaRegs.SPIFFRX.bit.RXFFIL = FIFO_LVL; SpiaRegs.SPIFFTX.all=0xE040; SpiaRegs.SPIFFTX.bit.TXFFIL = FIFO_LVL; InitSpi(); } void dma_init() { DMAInitialize(); DMASource = (volatile Uint16 *)sdata; DMADest = (volatile Uint16 *)rdata; DMACH5AddrConfig(&SpiaRegs.SPITXBUF,DMASource); DMACH5BurstConfig(BURST,1,0); DMACH5TransferConfig(TRANSFER,1,0); DMACH5ModeConfig(DMA_SPIATX,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE, SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,SIXTEEN_BIT, CHINT_END,CHINT_ENABLE); DMACH6AddrConfig(DMADest,&SpiaRegs.SPIRXBUF); DMACH6BurstConfig(BURST,0,1); DMACH6TransferConfig(TRANSFER,0,1); DMACH6ModeConfig(DMA_SPIARX,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE, SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,SIXTEEN_BIT, CHINT_END,CHINT_ENABLE); } __interrupt void local_D_INTCH5_ISR(void) { EALLOW; DmaRegs.CH5.CONTROL.bit.HALT=1; PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; EDIS; return; } __interrupt void local_D_INTCH6_ISR(void) { Uint16 i; EALLOW; DmaRegs.CH6.CONTROL.bit.HALT = 1; PieCtrlRegs.PIEACK.all = PIEACK_GROUP7; EDIS; for( i = 0; i<128; i++ ) { if(rdata[i] != i) { error(); } } done = 1; return; } 这段代码是什么时候启动SPI发送功能的?
03-08
<think>好的,我现在需要分析用户提供的这段C代码,找出SPI发送功能是在什么时候启动的。首先,我应该回顾一下代码的结构,特别是main函数和相关初始化函数。 用户的问题是关于这段代码中SPI发送功能何时启动的。首先,我需要理解代码的整体流程。代码是针对TI的F28x系列DSP,使用了DMA和SPI外设。主函数中调用了InitSysCtrl()来初始化系统控制,然后禁用中断,初始化Pie控制器和向量表。接下来,设置了DMA和SPI FIFO的初始化函数:dma_init()spi_fifo_init()。然后配置了数据数组sdata和rdata,并启用了中断。 接下来,主函数中有两行代码:StartDMACH6()和StartDMACH5()。这可能涉及到启动DMA通道5和6。然后设置done标志为0,进入while循环等待done变为1。当DMA传输完成时,中断服务例程可能会设置done为1,结束循环。 接下来,我需要查看dma_init()函数中的配置。DMA通道5的初始化中,DMACH5AddrConfig设置了源地址为sdata,目标地址为SPI发送缓冲区SPITXBUF。DMACH5ModeConfig中的模式设置为DMA_SPIATX,这表示DMA通道5用于SPI发送传输。同时,DMA通道6被配置为从SPI接收缓冲区SPIRXBUF读取数据到rdata。 SPI发送的启动可能与DMA通道5的启动有关。在main函数中,StartDMACH5()可能被用来启动DMA通道5,从而开始将数据从sdata传输到SPI发送缓冲区,触发SPI发送功能。而SPI本身的初始化在spi_fifo_init()中,配置SPIFIFO寄存器,可能启用了FIFO并设置了触发级别。 此外,DMA通道5的模式配置中,PERINT_ENABLE表示外设中断使能,可能当SPI的TX FIFO需要数据时,DMA会自动传输数据,从而启动SPI发送。当DMA开始传输数据到SPITXBUF时,SPI外设会自动将这些数据发送出去,因此StartDMACH5()应该是触发SPI发送的关键点。 需要确认StartDMACH5()这个函数的作用。在TIDSP库中,类似的函数通常是用来使能DMA通道,开始数据传输。所以当调用StartDMACH5()时,DMA通道5开始工作,将sdata中的数据搬运到SPI发送缓冲区,从而启动SPI发送过程。 总结来说,SPI发送功能的启动是在main函数中调用StartDMACH5()之后,DMA通道5开始将数据从sdata传输到SPI的TXBUF,从而触发SPI外设开始发送数据。此时,SPI发送功能被启动,数据通过SPI接口发送出去。</think>这段代码中SPI发送功能的启动是通过DMA触发的,具体发生在以下两个关键步骤: 1. **DMA通道5配置**(发送通道): ```c DMACH5AddrConfig(&SpiaRegs.SPITXBUF,DMASource); // 配置DMA将sdata写入SPI发送缓冲区 DMACH5ModeConfig(DMA_SPIATX...); // 设置DMA模式为SPI发送模式 ``` 2. **DMA通道5启动**: ```c StartDMACH5(); // 在main()函数中触发DMA传输 ``` **完整触发过程**: 1. 当执行`StartDMACH5()`后,DMA开始将`sdata`数组中的数据以突发传输模式搬运到SPI发送缓冲区`SPITXBUF` 2. SPI外设在FIFO阈值(`FIFO_LVL=8`)触发下,自动将FIFO中的数据通过MOSI引脚发送 3. 同时DMA通道6会通过`StartDMACH6()`启动接收流程,将SPI接收数据存入`rdata` **关键设计特点**: - 使用双DMA通道(CH5发送/CH6接收)实现全双工通信 - 突发传输长度`BURST=7`(8-1),每次传输15个元素(`TRANSFER=15`) - 通过SPI FIFO阈值触发DMA传输(TXFFIL/RXFFIL=8) - 中断服务程序`local_D_INTCH6_ISR`会在接收完成后验证数据正确性 因此,SPI发送功能的实质启动时刻是执行`StartDMACH5()`函数调用时,此时DMA开始将内存数据推送到SPI发送硬件单元。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值