This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C123GH6PM:TM4C123GXL Launchpad 上的 SPI 从器件

Guru**** 2482225 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/726168/tm4c123gh6pm-spi-slave-on-tm4c123gxl-launchpad

器件型号:TM4C123GH6PM

您好!

我是一名学生、正在从事一个利用 Tiva tm4c123gh下午6 ARM 处理器的高级设计项目、并且正在努力使 SPI 从接口正常工作。

到目前为止、我已经使用了此表单文章 https://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/302734?SPI-Slave-problem 中的一个示例

并将其修改为使用 SSI0而不是 SSI2。 在测试代码时,我无法从主器件 Raspberry PI 读取多个字节,在下面显示的函数 SSIDataGet ()中的 Tiva 在无限循环中挂起等待数据之前。 主器件的时钟设置为122kHz。

void
SSIDataGet (uint32_t ui32Base、uint32_t * pui32Data)
{
//
//检查参数。
//
assert (_SSIBaseValid (ui32Base));

//
//等待直至有数据要读取。
//
while (!(HWREG (ui32Base + SSI_O_SR)& SSI_SR_RNE))//在这里永久挂起
{
}

//
//从 SSI 读取数据。
//
* pui32Data = HWREG (ui32Base + SSI_O_DR);
} 

示波器输出显示数据已正确发送、但 Tiva 似乎未处理数据、我不确定如何解决此问题。

通道1为 clk、2为 MOSI、3为 MISO、4为 CS。

如果有任何建议、我将非常感激、下面是我的完整代码。

//
//
//// spi_slave.c -演示如何在
// SPI 从模式中配置 RX 超时中断的示例。
//
//版权所有(c) 2013 Texas Instruments Incorporated。 保留所有权利。
// TI 信息-选择性披露
//
*********
#include 
#include 
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_ssi.h"
#include "inc/hw_types.h"
#include "driverlib/ssi.h"
#include "driverlib/interrupt.h"

#include "driverlib/driverlib"#driverlib/udio.h"#include "driverlib/us.trine.h





//
//! \addtogroup SSI_examples_list
//! 

SPI 从器件(SPI_SLAVE)

//! //! 此示例将 SSI0配置为 SPI 主器件、将 SSI2配置为 SPI 从器件、位于// ! EK-LM4F232评估板。 为 SSI2配置 RX 超时中断。 //! 在主器件 TX 上发送三个字符、然后 SSI2 RX 超时中断 //! 启用。 然后代码等待中断触发。 一次 //! 触发中断读取从器件 RX FIFO 中的数据并将其与 //! 传输的数据包、并显示相应的状态。 如果一切都好 //! 运行良好您应该会看到"Test Passed"。 终端窗口上的消息。 //! 状态消息通过 UART0以115200波特和8-n-1 //! 模式。 //! //! 此示例使用 EK-LM4F232上的以下外设和 I/O 信号。 //! 您必须查看这些内容并根据自己的板的需要进行更改: //! - SSI0外设 //! - GPIO 端口 A 外设(用于 SSI0引脚)(在 SD 卡插槽附近可用) //! - SSI0CLK - PA2 //! - SSI0Fss - PA3 //! - SSI0Rx - PA4 //! - SSI0Tx - PA5 //! //! - SSI2外设 //! - GPIO 端口 M 外设(用于 SSI2引脚)(在 OLED 的正下方提供) //! - SSI2CLK - PH4 //! - SSI2Fss - PH5 //! - SSI2Rx - PH6 //! - SSI2Tx - pH7 //! //! 为了使该示例正常工作、 //!上需要以下连接 EK-LM4F232评估板。 //! - SSI0CLK (PA2)- SSI2CLK (PH4) //! - SSI0Fss (PA3)- SSI0Fss (PH5) //! - SSI0Rx (PA4)- SSI2Tx (pH7) //! - SSI0Tx (PA5)- SSI2Rx (PH6) //! //! 以下 UART 信号仅配置为显示控制台 //! 消息。 SSI0的运行不需要这些。 //! - UART0外设 //! - GPIO 端口 A 外设(用于 UART0引脚) //! - UART0RX - PA0 //! - UART0TX - PA1 //! //! 此示例使用以下中断处理程序。 要使用此示例 //! 在您自己的应用程序中、您必须将这些中断处理程序添加到 您的//! 矢量表。 //! -SSI2IntHandler。 //! //// ***************** // // //要发送和接收的字节数。 //// ***************** #define NUM_SSI_DATA 7 //********* // ////中断处理程序和主循环中使用的全局变量。 //// ***************** volatile unsigned long g_ulSSI0RXTO = 0; unsigned long g_ulDataRx0[NUM_SSI_DATA]={0}; //********* // //从机模式中 SSI0外设的中断处理程序。 它读取中断 //状态、如果中断由 RX 超时中断触发、它读取 // SSI0 RX FIFO 并递增计数器以告知主循环 已触发 RX 超时//中断。 //// ***************** void SSI0IntHandler (void) { unsigned long ulStatus = 0、ulIndex = 0; // //读取中断状态。 // ulStatus = SSIIntStatus (SSI0_BASE、0); // //检查中断原因。 // IF (ulStatus & SSI_RXTO) { // //中断是因为 RX 超时。 因此、递增计数器会发出指示 //发生 RX 超时中断的主循环。 // G_ulSSI0RXTO++; // //从 SSI2 RX FIFO 读取 NUM_SSI_DATA 数据字节。 // for (int ulIndex = 0;ulIndex < NUM_SSI_DATA;ulIndex++){ SSIDataGet (SSI0_BASE、&g_ulDataRx0[ulIndex]); } } // //清除中断。 // SSIIntClear (SSI0_BASE、ulStatus); } //********* // //此函数将 SPI0设置为在 Freescale 模式下用作主机。 //// ***************** void InitSPI0 (void) { // //必须启用 SSI0外设才能使用。 // SysCtlPeripheralEnable (SYSCTL_Periph_SSI0); // //对于本示例,SSI0与 Porta[5:2]一起使用。 GPIO 端口 A 需要 //因此可以使用这些引脚。 // SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA); // //为端口 A2、A3、A4和 A5上的 SSI0功能配置引脚复用。 //如果您的器件不支持引脚复用、则无需执行此步骤。 // GPIOPinConfigure (GPIO_PA2_SSI0CLK); GPIOPinConfigure (GPIO_PA3_SSI0FSS); GPIOPinConfigure (GPIO_PA4_SSI0RX); GPIOPinConfigure (GPIO_PA5_SSI0TX); // //配置 SSI 引脚的 GPIO 设置。 该函数也会提供 将这些引脚的//控制到 SSI 硬件。 请参阅中的数据表 //查看每个引脚分配的函数。 //引脚分配如下: // PA5 - SSI0Tx // PA4 - SSI0Rx // PA3 - SSI0Fss // PA2 - SSI0CLK // GPIOPinTypeSSI (GPIO_Porta_base、GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2); // //为 SPI 从模式配置和启用 SSI0端口。 // SSIConfigSetExpClk (SSI0_BASE、SysCtlClockGet ()、SSI_FRF_MOTO_MODE_0、 SSI_MODE_SLAVE、122000、8); // //启用 SSI0模块。 // SSIEnable (SSI0_BASE); } int main (void) { SysCtlClockSet (SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHz); InitSPI0(); SSIDataPut (SSI0_BASE、0xED); // Rx 中断 SSIIntEnable (SSI0_BASE、SSI_RXTO); IntEnable (INT_SSI0); while (1);//在此处调试暂停并检查缓冲 区}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    在这里发布"第一次"—我们必须说、"干得好!"

    但是-(甚至)良好的工作可能会得到改进-让我们从您的示波器盖开始。   (否则-证明是最重要和最完整的)

    请注意、我已将"通道名称/标签"导入到 "CAP"中-在这里、它们"标记得更好!"  

    以下内容-旨在帮助您获得"SPI 从器件成功:"的评论

    • 您的代码有(许多)个元素-对您的问题"不重要/不重要"。  虽然(快速/简单)对您(复制/粘贴)-您已加大了对帮助者的努力(强迫)-从未如此好!
    • 过去的许多代码注释-尚未修改-以反映您的代码更改。   再次-使您的助手更加痛苦。
    • 为什么选择(奇怪的) 122KHz SPI 主时钟设置?   您的 TM4C 是否能够"直接"(具有足够的精度)达到该频率?  (可能是因为时钟错误会随着数据的进步而累积而导致第二个字节出现故障?)
    • 您的 SPI ISR 'SI0IntHandler'是否正确包含在您的'Start-up'文件中?
    • 请注意、你在处理程序中使用(最后一条)指令"清除该 ISR"。   MCU 手册和/或驱动程序用户指南-告知并告知此类"ISR 清除"证明"可能会失败"- 在 ISR 中定位为"太晚"时。
    • 您已选择飞思卡尔模式0 - MCU 手册注意:  
        • SPO = 0和 SPH = 0 然而、在连续背靠背传输的情况下、SSInFss 信号必须 在每次数据字传输之间输出高电平、因为从机选择引脚会冻结其串行 外设寄存器中的数据、如果 SPH 位清零、则不允许对其进行更改。 因此、主 器  件必须在每次数据传输(我假设这意味着字节)之间将从器件的 SSInFss 引脚拉高、以启用串行外设数据写入。   如果为 true -您选择的"Freescale Mode 0" -可能注定要...
    • "驱动程序用户指南"建议使用"测试"来确认外设(真正)就绪。"
        • SysCtlPeripheralEnable (SYSCTL_Periph_SSI0);
          //
          //等待 SSI0模块准备就绪。
          //
          while (!SysCtlPeripheralReady (SYSCTL_Periph_SSI0)  )// 您认为您的 SSI 外设'ucceeds'-但您的'GPIO_A 设置'违反了这一点(SAFE_实践)。

    • 通过 SPI 中断提供您的 SPI 接收-"生命与死亡"。   然而、MCU 手册注意到:"接收 FIFO 服务(当接收 FIFO 半满或更多时!)  然而、当您报告时、它似乎是用第一个接收到的字节触发的。
    • 您是否注意到您的 ISR "限定符"(用于读取 SPI 数据)已"锁定"至 "SI_RXTO"。    发生这种"SPI 超时"-在32个 SPI 时钟之后-"消失了!"   是否已检查并正确测量和/或验证过?   在主器件的初始 SPI 传输到达之前、是否会发生这样的"超时"(已发生)?   (这意味着您将获得第一个字节-但不再如此!)   (直到另一次超时)

    这应该证明是一个(合理)起点-不幸的是、很少(很少)供应商示例以(更复杂) SPI 从站为中心。

    应该注意-您必须确认"PI"和 LPAD 之间的牢固(并且始终存在)共享接地连接...

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    ISR 函数已正确添加到矢量表中。 我将 initSSI0开头的代码更改为以下内容

    void
    InitSPI0 (void)
    {
    //
    //必须启用 SSI0外设才能使用。
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);
    SysCtlPeripheralEnable (SYSCTL_Periph_SSI0);
    while (!SysCtlPeripheralReady (SYSCTL_Periph_SSI0)) 

    并按照建议在 ISR 例程中将中断清除上移。 此外、您还可以确定 Freescale Mode 0可能是一个问题、将其更改为 Freescale Mode 3似乎是一个问题、它可以使其在两个方向上正确读取数据。 作为测试、我向 TX 缓冲区添加了一些数据、看起来一切都正常。 感谢你的帮助。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    虽然"感谢您的帮助"是值得赞赏的、也是感谢您的帮助、但 不应该将绿色邮票附加到之前的那份上、而是仅是您自己的"问题识别帖子"?

    由于您之前发现并提出的所有三个 SPI 问题:

    • 正在等待外设(真正的)准备就绪
    • 重新定位 ISR "清除"-按顺序升高
    • 选择"更匹配"的飞思卡尔模式

    您向 SPI 管理报告了"令人难以置信"的情况-但这里有一个帖子-然后是一个快速而详细的回复-您现在是"空中!"

    "这一解决方案"的奖励似乎(超出预期)是相当有收入的...

    [编辑] 通过此类(温和)提示-正确的帖子(应答-不是确认帖子)(现在)已获得"正确"识别。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    如果您要描述 SPI_RX 中断的发生和频率、那么它将对(其他)此处"有用"。 MCU 手册注释"半填充 FIFO"(意味着4个 SPI 接收)-触发中断之前! 您是否"足够好"进行确认?   谢谢...

    此外(不要太贪婪)-您是否可以描述(考虑)"字符间"间隔(延迟)您的示波器上限详细信息?   ( 显示为蓝色-(顶部轨迹/主时钟)-您是否对该间距进行了"编程"?)  

    请注意-我们的"员工净化器"(从不是 Moi)注意到、您没有采纳"为示波器通道添加标签(超出) 1、2、3、4 -这证明了这是步行的-和时间/精力的饮食!   (帮助者被迫"向上滚动"-强化他们的"渠道识别!")   下游-这必然会"咬人"-渠道标签证明最有用-尤其是在"压力不足!"时

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我仍在使用 SSI_RXTO 中断。 未经修改、中断数量为1、因为函数 SSIDataGet 会在接收到数据之前阻止中断、并执行此操作7次。 当被修改为清除缓冲区时、下面显示的每个中断的值 g_ulSSI0RXTO 为7、这是有道理的、因为发送和接收的字节为7。

    while (SSIDataGetNonBlocking (SSI0_BASE、&g_ulDataRx0[ulIndex]))
    ulIndex++; 

    我的理解是、当 RX 超时时时、SSI_RXTO 中断将触发、正如您所说的、尽管我不知道"32个时钟"来自何处。  当有一个表示帧之间间隔的时钟缺失时、它看起来就会触发、尽管我在文档中没有对此进行验证。

    此外、我不确定"内部字符间隔"是什么意思、如果您是指每个字节帧内的脉冲之间的间隔、我认为这是从器件在每个时钟下降沿采样的位。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    [报价用户="Anthony speake"]虽然我不知道"32个时钟"来自何处、但您说过 RX 超时[/报价]

    • 32个时钟:简单易行: 第15.3.3节、第956页 您的 MCU 手册:

    "接收 FIFO 的超时周期为32个 SSInClk 时钟周期(不管 SSInClk 是否当前处于活动状态)、并且在 RX FIFO 从空变为非空时启动。  如果在32个时钟通过前 RX FIFO 被清空、超时周期被复位。"   

    • 字符间距 (不是"内部"字符!)   (如果您正在成为工程师-您需要"了解"语言... 错误引用另一个-证明(从不)好!)

    您的"PI"是否放置了这样的"起搏" -或者该可编程-并由您提供?  (请注意-再次说明-通道已(正确)标记!   BFD (我听到您的声音)-但我看到大家都在浪费时间-通过"将渠道编号承诺为"内存"、然后(稍后)发现他们是错误的!  这是一个非常好的 BFD!)

    • SSIDataGet (): 实际上-正如您(非常)注意到的-(我(完全)错过了)'SSIDataGet ()实施的阻止将(确实)-确保出现'SPI time-out'。  (您将"获得"七个中断。)   对于"现实世界使用"、"阻止"通常是不可接受的、然后您必须处理不太常规的 SPI RX 中断。

    干得好-谢谢-我们(现在)是否同意?  (我在"信道标记"上进行了强化、正如我看到的、大家都陷入了巨大的麻烦、"懒惰"。  我的尝试是鼓励(您)"良好习惯"。

    BTW -您是否注意到-就像在外科手术环境中使用"患者跳跃"一样-我从您的"治疗帽"上"移动了所有多余的东西"-这样"只有精髓"-这大大有助于"聚焦!"

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    抱歉、是的、Raspberry PI 的内部 SPI 硬件会放置间距、我只能通过此处记录的 API 函数调用"BCM2835_SPI_transfernb()"加载包含数据的 TX FIFO 缓冲区。 https://www.airspayce.com/mikem/bcm2835/group__spi.html

    下面提供的函数供参考。

    /*向 SPI 写入(和读取)多个字节*/
    void BCM2835_SPI_transfernb (char* tbuf、char* rbuf、uint32_t len)
    {
    volatile uint32_t* paddr = BCM2835_spi0 + BCM2835_SPI0_CS/4;
    易失性 uint32_t* fifo = BCM2835_spi0 + BCM2835_SPI0_FIFO/4;
    uint32_t TXCnt=0;
    uint32_t RXCnt=0;
    
    /*这是根据第10.6.1节进行轮询的传输
    //错误提示:如果我们在本节中与其他人发生中断,会发生什么情况
    //访问不同的外设?
    *
    
    /*清除 TX 和 RX FIFO */
    BCM2835_peri_set_Bits (paddr、BCM2835_SPI0_CS_CLEAR、BCM2835_SPI0_CS_CLEAR);
    
    /*设置 TA = 1 */
    BCM2835_peri_set_Bits (paddr、BCM2835_SPI0_CS_TA、BCM2835_SPI0_CS_TA);
    
    /*使用 FIFO 来减少字节间时间*/
    while (((TXCnt < len)||(RXCnt < len))
    {
    /* TX FIFO 未满、因此请添加更多字节*/
    while ((((BCM2835_peri_read (paddr)& BCM2835_SPI0_CS_TXD))&&(TXCnt < len))
    {
    BCM2835_peri_write_NB (fifo、BCM2835_correct_order (tbuf[TXCnt]);
    TXCnt++;
    }
    /* Rx FIFO 不为空、因此获取下一个接收到的字节*/
    while ((((BCM2835_peri_read (paddr)& BCM2835_SPI0_CS_RXD))&&(RXCnt < len)
    {
    rbuf[RXCnt]= BCM2835_correct_order (BCM2835_peri_read_NB (FIFO));
    RXCnt++;
    }
    }
    /*等待完成设置*/
    while (!(BCM2835_peri_read_NB (paddr)& BCM2835_SPI0_CS_DONE)
    );
    
    /*设置 TA = 0、同时设置屏障*/
    BCM2835_peri_set_Bits (paddr、0、BCM2835_SPI0_CS_TA);
    }