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.

[参考译文] EK-TM4C123GXL:从 Arduino [主控方]到 EK-TM4C123GXL 的 SPI 传输

Guru**** 664280 points
Other Parts Discussed in Thread: EK-TM4C123GXL, EK-TM4C1294XL
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1056339/ek-tm4c123gxl-spi-transfer-from-arduino-master-to-ek-tm4c123gxl

器件型号:EK-TM4C123GXL
主题中讨论的其他器件: TM4C123EK-TM4C1294XL

大家好、

我尝试在 Arduino Mega 2560和 TI 微控制器之间设置 SPI 通信。 我正在尝试将 Arduino Mega 2560设置为主控制器、将 TI 控制器设置为从控制器。 我希望主器件通过一次 SPI 传输连续地将2个字节从 Arduino 发送到 TI 微控制器。

在两个板之间、我有以下连接:

Arduino Mega 2560        EK-TM4C123GXL

引脚50                     =>    PA5

引脚51                     =>    PA4

引脚52                     =>    PA2

引脚53                     =>    PA3

看来、SPI 的中断被调用一次、然后不会再次被调用。 它正确传输发送的第一个字节、但该第一个字节同时出现在存储阵列的0和1索引中。

似乎不会多次调用中断。 有哪些因素阻碍 Arduino 正确重复传输两个字节到 EK-TM4C123GXL?

Arduino 代码:

#include<SPI.h>

byte direction_byte = 0b00001110; 
byte speed_byte =     0b01010101;
//SPISettings ti_settings(SPI_CLOCK_DIV4,MSBFIRST,SPI_MODE2);

bool flip = 0;
void setup (void)
{

  digitalWrite(SS, HIGH);  // ensure SS stays high for now
  SPI.setDataMode(SPI_MODE2);
  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  SPI.setDataMode(SPI_MODE2);
}  // end of setup


void loop (void)
{

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string


    SPI.transfer(direction_byte);


    SPI.transfer(speed_byte);

  
  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (1000);  // 1 seconds delay
}  // end of loop

EK-TM4C123GXL 的代码:

//*****************************************************************************
//
// spi_slave.c - Example demonstrating how to configure RX timeout interrupt in
// SPI slave mode.
//
// Copyright (c) 2013 Texas Instruments Incorporated.  All rights reserved.
// TI Information - Selective Disclosure
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/pwm.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "inc/hw_memmap.h"
#include "driverlib/qei.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "driverlib/fpu.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
// The error routine that is called if the driver library encounters an error.
#include "inc/hw_types.h"                   // Defines common types and macros
#include "inc/hw_gpio.h"                    // Defines Macros for GPIO hardware
#include "inc/hw_qei.h"
#include "driverlib/ssi.h"

#include "utils/uartstdio.h"

//*****************************************************************************
//
//! \addtogroup ssi_examples_list
//! <h1>SPI Slave (spi_slave)</h1>
//!
//! This example configures the SSI0 as SPI Master, SSI2 as SPI Slave on an
//! EK-LM4F232 evaluation board.  RX timeout interrupt is configured for SSI2.
//! Three characters are sent on the master TX, then SSI2 RX timeout interrupt
//! is enabled. The code then waits for the interrupt to fire.  Once the
//! interrupt is fired the data from slave RX FIFO is read and compared to the
//! transmitted packet and the appropriate status is displayed.  If everything
//! goes well you should see a "Test Passed." message on the terminal window.
//! The status messages are transmitted over UART0 at 115200 baud and 8-n-1
//! mode.
//!
//! This example uses the following peripherals and I/O signals on EK-LM4F232.
//! You must review these and change as needed for your own board:
//! - SSI0 peripheral
//! - GPIO Port A peripheral (for SSI0 pins) (available near the SD card slot)
//! - SSI0CLK - PA2
//! - SSI0Fss - PA3
//! - SSI0Rx  - PA4
//! - SSI0Tx  - PA5
//!
//! - SSI2 peripheral
//! - GPIO Port M peripheral (for SSI2 pins) (available right below the OLED)
//! - SSI2CLK - PH4
//! - SSI2Fss - PH5
//! - SSI2Rx  - PH6
//! - SSI2Tx  - PH7
//!
//! For this example to work, the following connections are needed on the
//! EK-LM4F232 evaluation board.
//! - SSI0CLK(PA2) - SSI2CLK(PH4)
//! - SSI0Fss(PA3) - SSI0Fss(PH5)
//! - SSI0Rx(PA4)  - SSI2Tx(PH7)
//! - SSI0Tx(PA5)  - SSI2Rx(PH6)
//!
//! The following UART signals are configured only for displaying console
//! messages for this example.  These are not required for operation of SSI0.
//! - UART0 peripheral
//! - GPIO Port A peripheral (for UART0 pins)
//! - UART0RX - PA0
//! - UART0TX - PA1
//!
//! This example uses the following interrupt handlers.  To use this example
//! in your own application you must add these interrupt handlers to your
//! vector table.
//! - SSI2IntHandler.
//!
//
//*****************************************************************************

//*****************************************************************************
//
// Number of bytes to send and receive.
//
//*****************************************************************************
#define NUM_SSI_DATA 2

//*****************************************************************************
//
// Global variables used in interrupt handler and the main loop.
//
//*****************************************************************************
//volatile unsigned long g_ulSSI2RXTO = 0;
//unsigned long g_ulDataRx2[NUM_SSI_DATA];

volatile uint32_t g_ulSSI2RXTO = 0;
volatile uint32_t g_ulDataRx2[NUM_SSI_DATA];
//*****************************************************************************
//
// Interrupt handler for SSI2 peripheral in slave mode.  It reads the interrupt
// status and if the interrupt is fired by a RX time out interrupt it reads the
// SSI2 RX FIFO and increments a counter to tell the main loop that RX timeout
// interrupt was fired.
//
//*****************************************************************************
void
SSI0IntHandler(void)
{
    unsigned long ulStatus, ulIndex;

    //
    // Read interrupt status.
    //
    ulStatus = SSIIntStatus(SSI0_BASE, 1);

    //
    // Check the reason for the interrupt.
    //
    if(ulStatus & SSI_RXTO)
    {
        //
        // Interrupt is because of RX time out.  So increment counter to tell
        // main loop that RX timeout interrupt occurred.
        //
        g_ulSSI2RXTO++;

        //
        // Read NUM_SSI_DATA bytes of data from SSI2 RX FIFO.
        //
        for(ulIndex = 0; ulIndex < NUM_SSI_DATA; ulIndex++)
        {
            SSIDataGet(SSI0_BASE, &g_ulDataRx2[ulIndex]);
        }

        g_ulSSI2RXTO--;
    }

    //
    // Clear interrupts.
    //
    SSIIntClear(SSI2_BASE, ulStatus);
}

//*****************************************************************************
//
// This function sets up UART0 to be used for a console to display information
// as the example is running.
//
//*****************************************************************************

//*****************************************************************************
//
// This function sets up SPI0 to be used as Master in freescale mode.
//
//*****************************************************************************
void
InitSPI0(void)
{
    //
    // The SSI0 peripheral must be enabled for use.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);

    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0))
    {
    }
    //
    // For this example SSI2 is used with PortH[7:4].  GPIO port H needs to be
    // enabled so these pins can be used.
    //
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA))
    {
    }
    //
    // Configure the pin muxing for SSI2 functions on port H4, H5, H6 and H7.
    // This step is not necessary if your part does not support pin muxing.
    //
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);

    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);

    SSIDisable(SSI0_BASE);
    //
    // Configure and enable the SSI0 port for SPI master mode.
    //
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2,
                       SSI_MODE_SLAVE, 660000, 8);

    SSIDisable(SSI0_BASE);

    //
    // Enable the SSI2 module.
    //
    SSIEnable(SSI0_BASE);

    IntEnable(INT_SSI0);

    SSIIntEnable(SSI0_BASE, SSI_RXTO);

    SSIIntRegister(SSI0_BASE, SSI0IntHandler);
}
//*****************************************************************************
//
// This function sets up SPI2 to be used as slave in freescale mode.
//
//*****************************************************************************
//*************************************************************************
int
main(void)
{
    /*
    unsigned long ulDataTx0[NUM_SSI_DATA];
    unsigned long ulDataRx0[NUM_SSI_DATA];
    unsigned long ulindex;
    */
    uint32_t ulDataTx0[NUM_SSI_DATA];
    uint32_t ulDataRx0[NUM_SSI_DATA];
    uint32_t ulindex;

    //
    // Set the clocking to run directly from the external crystal/oscillator.
    //
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);


    //
    // Init SPI0 as master.
    //
    InitSPI0();

    //
    // Read any residual data from the SSI port.  This makes sure the receive
    // FIFOs are empty, so we don't read any unwanted junk.  This is done here
    // because the SPI SSI mode is full-duplex, which allows you to send and
    // receive at the same time.  The SSIDataGetNonBlocking function returns
    // "true" when data was returned, and "false" when no data was returned.
    // The "non-blocking" function checks if there is any data in the receive
    // FIFO and does not "hang" if there isn't.  This might not be needed here.
    //
    while(SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx0[0]))
    {
    }


    //
    // Clear any pending interrupt
    //
    SSIIntClear(SSI0_BASE, SSI_RXTO);



    while(1)
    {
    }
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    [引用 userid="503859" URL"~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1056339/ek-tm4c123gxl-spi-transfer-from-arduino-master-to-ek-tm4c123gxl "]似乎是一次调用 SPI 中断,然后不会再次调用[/引用]

    您提到的只有一个中断是谁? Arduino 或 TM4C123? 如果您是指 Arduino、那么我无法提供帮助和支持。 这不是 TI 产品。 如果您是指 TM4C123、那么您只能在写入 SSIIntEnable (SSI0_BASE、SSI_RXTO)时为超时启用中断。 您未启用接收 FIFO 中断。 您希望在接收到数据时而不是超时过期时生成中断。 如果主器件向从器件稳定发送数据、但从未触发超时、该怎么办? 在这种情况下、不会生成中断、对吧? 请参阅以下中断源。 请注意、只有当接收 FIFO 至少为满的一半时才会产生中断。 RXFIFO 中有8个单元。 因此、RXFIFO 中至少会产生4个或更多数据中断。  

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

    我认为我已经纠正了这个问题、但我仍然面临同样的问题。 我仍在接收存储阵列所有四个位置的第一个字节、而不是第一个位置出现的第一个字节、第二个位置出现的第二个字节等

    EK-TM4C123GXL 代码:

    //*****************************************************************************
    //
    // project0.c - Example to demonstrate minimal TivaWare setup
    //
    // Copyright (c) 2012-2020 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.fs
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    // This is part of revision 2.2.0.295 of the EK-TM4C123GXL Firmware Package.
    //
    //*****************************************************************************
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/pwm.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/hw_memmap.h"
    #include "driverlib/qei.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    #include "driverlib/fpu.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    // The error routine that is called if the driver library encounters an error.
    #include "inc/hw_types.h"                   // Defines common types and macros
    #include "inc/hw_gpio.h"                    // Defines Macros for GPIO hardware
    #include "inc/hw_qei.h"
    #include "driverlib/timer.h"
    #include "driverlib/ssi.h"
    #include "motor.h"
    
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    // Definitions
    #define NUM_SSI_DATA 4
    
    
    
    volatile uint32_t g_ulSSI2RXTO = 0;
    uint32_t g_ulDataRx2[NUM_SSI_DATA];
    
    
    void
    SSI0IntHandler(void)
    {
        unsigned long ulStatus, ulIndex;
    
        //
        // Read interrupt status.
        //
        ulStatus = SSIIntStatus(SSI0_BASE, 1);
    
        //
        // Check the reason for the interrupt.
        //
        if(ulStatus & SSI_RXFF); //SSI_RXTO
        {
            //
            // Interrupt is because of RX time out.  So increment counter to tell
            // main loop that RX timeout interrupt occurred.
            //
            g_ulSSI2RXTO++;
    
            //
            // Read NUM_SSI_DATA bytes of data from SSI2 RX FIFO.
            //
            for(ulIndex = 0; ulIndex < NUM_SSI_DATA; ulIndex++)
            {
                SSIDataGet(SSI0_BASE, &g_ulDataRx2[ulIndex]);
            }
    
        }
    
        //
        // Clear interrupts.
        //
        SSIIntClear(SSI0_BASE, ulStatus);
    }
    
    //*****************************************************************************
    //
    // This function sets up SPI2 to be used as slave in freescale mode.
    //
    //*****************************************************************************
    void
    InitSPI0(void)
    {
        //
        // The SSI0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
        while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0))
        {
        }
        //
        // For this example SSI0 is used with PortA[5:2].  GPIO port A needs to be
        // enabled so these pins can be used.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA))
        {
        }
        //
        // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
        // This step is not necessary if your part does not support pin muxing.
        //
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);
    
        //
        // Configure the GPIO settings for the SSI pins.  This function also gives
        // control of these pins to the SSI hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // The pins are assigned as follows:
        //      PA5 - SSI0Tx
        //      PA4 - SSI0Rx
        //      PA3 - SSI0Fss
        //      PA2 - SSI0CLK
        //
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);
    
        SSIDisable(SSI0_BASE);
            //
        //
        // Configure and enable the SSI2 port for SPI slave mode.
        //
        SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2,
                           SSI_MODE_SLAVE, 660000, 8);
    
        //
        // Enable the SSI2 module.
        //
        SSIEnable(SSI0_BASE);
    
        IntEnable(INT_SSI0);
    
        SSIIntEnable(SSI0_BASE, SSI_RXFF); //SSI_RXTO
    
        SSIIntRegister(SSI0_BASE, SSI0IntHandler);
    }
    //*****************************************************************************
    //
    // This example will send out 3 bytes of data from master, then waits for slave
    // RX timeout interrupt to fire (where these 3 bytes are read).  Then the sent
    // and returned data are compared to give out appropriate status messages on
    // UART0.
    //
    //*****************************************************************************
    
    int main(void)
    {
        //g_ulDataRx2[0] = 0b00001001;
        //g_ulDataRx2[1] = 0b11110110;
        ROM_FPULazyStackingEnable();
    
        //uint32_t ulDataRx0[NUM_SSI_DATA];
        uint32_t ulindex;
    
    
    
        //
        // Setup the system clock to run at 50 Mhz from PLL with crystal reference
        //
        SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |   SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
        //
        InitSPI0();
    
    
        while(SSIDataGetNonBlocking(SSI0_BASE, &g_ulDataRx2[0]))
        {
        }
    
        //
        // Clear any pending interrupt
        //
        SSIIntClear(SSI0_BASE, SSI_RXFF); //SSI_RXTO
    
    
    
        while(1)
        {
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    

    Arduino 代码:

    #include<SPI.h>
    
    byte send_byte1 = 0b00000001;
    byte send_byte2 = 0b00000010;
    byte send_byte3 = 0b00000011;
    byte send_byte4 = 0b00000100;
    byte receive;
    //SPISettings ti_settings(SPI_CLOCK_DIV4,MSBFIRST,SPI_MODE2);
    
    bool flip = 0;
    void setup (void)
    {
    
      digitalWrite(SS, HIGH);  // ensure SS stays high for now
      SPI.setDataMode(SPI_MODE2);
      // Put SCK, MOSI, SS pins into output mode
      // also put SCK, MOSI into LOW state, and SS into HIGH state.
      // Then put SPI hardware into Master mode and turn SPI on
      SPI.begin ();
    
      // Slow down the master a bit
      SPI.setClockDivider(SPI_CLOCK_DIV8);
      SPI.setDataMode(SPI_MODE2);
    }  // end of setup
    
    
    void loop (void)
    {
    
      // enable Slave Select
      digitalWrite(SS, LOW);    // SS is pin 10
    
      // send test string
    
    
        receive = SPI.transfer(send_byte1);
        receive = SPI.transfer(send_byte2);
        receive = SPI.transfer(send_byte3);
        receive = SPI.transfer(send_byte4);
        
      // disable Slave Select
      digitalWrite(SS, HIGH);
    
      delay (100);  // 1 seconds delay
    }  // end of loop

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

    您是否有逻辑分析仪或示波器来显示主器件输出的数据。

    主器件的波特率是多少?

     主器件是仅发送四个字节还是重复发送四个字节?

     这是美国的假期,反应将会拖延。

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

    主器件重复发送四个字节。 主器件以4Ghz 运行 SPI。 我无法访问逻辑分析仪。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    [引用 userid="503859" URL"~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1056339/ek-tm4c123gxl-spi-transfer-from-arduino-master-to-ek-tm4c123gxl/3910840 #3910840"]主器件以4Ghz 运行 SPI

    您确定是4Ghz、而不是4MHz 吗?  以下是一些注释:

     - TM4C123作为从器件运行的最大波特率为6.6Mhz (150ns 周期)。 请参见下面的。  

     -主器件和从器件具有相同的波特率。 这意味着您的主器件不能在4Mhz 频率下运行、更不用说4Ghz 了、这是不可能的。 我建议您首先在两侧尝试简单的通用波特率、如4Mhz 或1MHz。 工作后、您可以按照数据表中的说明将波特率提高到更高、但不超过6.6Mhz。  

     -主器件和从器件必须具有相同的极性和相位。 它们之间的极性和相位不匹配肯定不起作用。

     -尝试仅从主器件传输4个字节。 工作后、增加到更多字节。 从机接收4个字节后、将产生一个 RXFF 中断。 您不想在总线还不工作时堵塞总线。

     -检查从设备是否检测到任何错误。 有人在听吗?

     -如果您没有逻辑分析仪,请使用示波器。 如果您没有、我真的不知道建议什么。 它是一个有助于调试的基本工具。 您可以轻松地找出总线上的错误。  

      

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

    我认为我已经将主设备和从设备设置为相同的波特率,但我仍然得到相同的结果。 我已经尝试将两者都设置为4Mhz、如下代码所示。

    我只尝试传输四个字节,但也不幸运。 我已更改 Arduino 代码、每五秒发送四个字节。 在 CCS 中使用调试、我可以看到中断不是每5秒触发一次、而是大约20秒触发一次。

    -不确定如何检查从设备上的错误。 请提供更多的解释。

    -我将在明天访问示波器、然后查看波形。

    我对可能出错的问题没有想法。 如果您在查看我的代码后有任何其他建议、我将不胜感激。

    Arduino 代码

    #include<SPI.h>
    
    byte send_byte1 = 0b00000001;
    byte send_byte2 = 0b00000010;
    byte send_byte3 = 0b00000011;
    byte send_byte4 = 0b00000100;
    byte receive;
    //SPISettings ti_settings(SPI_CLOCK_DIV4,MSBFIRST,SPI_MODE2);
    
    bool flip = 0;
    void setup (void)
    {
    
      digitalWrite(SS, HIGH);  // ensure SS stays high for now
      SPI.setDataMode(SPI_MODE2);
      // Put SCK, MOSI, SS pins into output mode
      // also put SCK, MOSI into LOW state, and SS into HIGH state.
      // Then put SPI hardware into Master mode and turn SPI on
      SPI.begin ();
    
      // Slow down the master a bit
      SPI.setClockDivider(SPI_CLOCK_DIV4);
      SPI.setDataMode(SPI_MODE2);
    }  // end of setup
    
    
    void loop (void)
    {
    
      // enable Slave Select
      digitalWrite(SS, LOW);    // SS is pin 10
    
      // send test string
    
    
        receive = SPI.transfer(send_byte1);
        receive = SPI.transfer(send_byte2);
        receive = SPI.transfer(send_byte3);
        receive = SPI.transfer(send_byte4);
        
      // disable Slave Select
      digitalWrite(SS, HIGH);
    
      delay (5000);  // 1 seconds delay
    
    
    }  // end of loop

    TI 控制器代码

    //*****************************************************************************
    //
    // project0.c - Example to demonstrate minimal TivaWare setup
    //
    // Copyright (c) 2012-2020 Texas Instruments Incorporated.  All rights reserved.
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.fs
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    // This is part of revision 2.2.0.295 of the EK-TM4C123GXL Firmware Package.
    //
    //*****************************************************************************
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/pwm.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/hw_memmap.h"
    #include "driverlib/qei.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"
    #include "driverlib/fpu.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    // The error routine that is called if the driver library encounters an error.
    #include "inc/hw_types.h"                   // Defines common types and macros
    #include "inc/hw_gpio.h"                    // Defines Macros for GPIO hardware
    #include "inc/hw_qei.h"
    #include "driverlib/timer.h"
    #include "driverlib/ssi.h"
    #include "motor.h"
    
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif
    
    // Definitions
    #define NUM_SSI_DATA 4
    
    #define RIGHT_DIRECTION_MASK 0b00000011;
    #define LEFT_DIRECTION_MASK  0b00001100;
    
    #define RIGHT_SPEED_MASK 0b00001111;
    #define LEFT_SPEED_MASK  0b11110000;
    
    // Global Variables
    volatile uint32_t g_ulSSI2RXTO = 0;
    uint32_t g_ulDataRx2[NUM_SSI_DATA];
    
    volatile int count = 0;
    
    uint32_t g_ui32Flags;
    
    
    bool cw = true;
    bool ccw = false;
    
    uint8_t hi = 255;
    uint8_t lo = 0;
    
    uint32_t period = 5000; //20ms (16Mhz / 64pwm_divider / 50)
    uint32_t duty = 2500;    //1.5ms pulse width
    
    
    
    //volatile int qeiPosition;
    
    volatile float leftcurrentRPM;
    volatile float rightcurrentRPM;
    
    volatile float leftdesiredRPM = 0;
    volatile float rightdesiredRPM = 0;
    
    volatile float left_error = 0;
    volatile float right_error = 0;
    
    volatile float left_integrated_error_float = 0;
    volatile float right_integrated_error_float = 0;
    
    volatile float left_integrated_error = 0;
    volatile float right_integrated_error = 0;
    
    volatile float left_send_value_float = 1;
    volatile float right_send_value_float = 1;
    
    float left_kp = 0.1;
    float right_kp = 0.1;
    
    float left_ki = 0.02;
    float right_ki = 0.02;
    
    volatile int left_send_value_int = 0;
    volatile int right_send_value_int = 0;
    
    
    
    // Function Declarations
    
    void ConfigureTimers(void);
    void Timer0IntHandler(void);
    void Timer1IntHandler(void);
    void delayMS(int ms);
    
    void initMotorControl(uint32_t period, uint32_t duty);
    void ConfigureEncoder(void);
    void left_motor(bool direction, uint8_t percent);
    void right_motor(bool direction, uint8_t percent);
    
    void
    SSI0IntHandler(void)
    {
        unsigned long ulStatus, ulIndex;
    
        //
        // Read interrupt status.
        //
        ulStatus = SSIIntStatus(SSI0_BASE, 1);
    
        //
        // Check the reason for the interrupt.
        //
        if(ulStatus & SSI_RXFF); //SSI_RXTO
        {
            //
            // Interrupt is because of RX time out.  So increment counter to tell
            // main loop that RX timeout interrupt occurred.
            //
            g_ulSSI2RXTO++;
    
            //
            // Read NUM_SSI_DATA bytes of data from SSI2 RX FIFO.
            //
            for(ulIndex = 0; ulIndex < NUM_SSI_DATA; ulIndex++)
            {
                SSIDataGet(SSI0_BASE, &g_ulDataRx2[ulIndex]);
            }
    
        }
    
        //
        // Clear interrupts.
        //
        SSIIntClear(SSI0_BASE, ulStatus);
    }
    
    //*****************************************************************************
    //
    // This function sets up SPI2 to be used as slave in freescale mode.
    //
    //*****************************************************************************
    void
    InitSPI0(void)
    {
        //
        // The SSI0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    
        while (!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0))
        {
        }
        //
        // For this example SSI0 is used with PortA[5:2].  GPIO port A needs to be
        // enabled so these pins can be used.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    
        while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA))
        {
        }
        //
        // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
        // This step is not necessary if your part does not support pin muxing.
        //
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);
    
        //
        // Configure the GPIO settings for the SSI pins.  This function also gives
        // control of these pins to the SSI hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // The pins are assigned as follows:
        //      PA5 - SSI0Tx
        //      PA4 - SSI0Rx
        //      PA3 - SSI0Fss
        //      PA2 - SSI0CLK
        //
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2);
    
        SSIDisable(SSI0_BASE);
            //
        //
        // Configure and enable the SSI2 port for SPI slave mode.
        //
        SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_2,
                           SSI_MODE_SLAVE, 400000, 8); // 660000
    
        //
        // Enable the SSI2 module.
        //
        SSIEnable(SSI0_BASE);
    
        IntEnable(INT_SSI0);
    
        SSIIntEnable(SSI0_BASE, SSI_RXFF); //SSI_RXTO
    
        SSIIntRegister(SSI0_BASE, SSI0IntHandler);
    }
    //*****************************************************************************
    //
    // This example will send out 3 bytes of data from master, then waits for slave
    // RX timeout interrupt to fire (where these 3 bytes are read).  Then the sent
    // and returned data are compared to give out appropriate status messages on
    // UART0.
    //
    //*****************************************************************************
    
    int main(void)
    {
        //g_ulDataRx2[0] = 0b00001001;
        //g_ulDataRx2[1] = 0b11110110;
        ROM_FPULazyStackingEnable();
    
        //uint32_t ulDataRx0[NUM_SSI_DATA];
        uint32_t ulindex;
    
    
    
        //
        // Setup the system clock to run at 16 Mhz from PLL with crystal reference
        //
        SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |   SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    
    
    
        ConfigureTimers();
        ConfigureEncoder();
    
        initMotorControl(period, duty);
        enableHbridge();
        //
        // Init SPI2 as slave.
        //
        InitSPI0();
    
        //
        // Enable RX timeout interrupt.
        //
        //SSIIntEnable(SSI2_BASE, SSI_RXTO);
        //IntEnable(INT_SSI2);
        //
        // Read any residual data from the SSI port.  This makes sure the receive
        // FIFOs are empty, so we don't read any unwanted junk.  This is done here
        // because the SPI SSI mode is full-duplex, which allows you to send and
        // receive at the same time.  The SSIDataGetNonBlocking function returns
        // "true" when data was returned, and "false" when no data was returned.
        // The "non-blocking" function checks if there is any data in the receive
        // FIFO and does not "hang" if there isn't.
        //
        while(SSIDataGetNonBlocking(SSI0_BASE, &g_ulDataRx2[0]))
        {
        }
    
        //
        // Clear any pending interrupt
        //
        SSIIntClear(SSI0_BASE, SSI_RXFF); //SSI_RXTO
    
    // clock divider was 4
    
    
        UARTprintf("SSI ->\n");
        UARTprintf("  Mode: SPI\n");
        UARTprintf("  Data: 16-bit\n\n");
    
    
    
    
       QEIPositionSet(QEI0_BASE,170);
       QEIPositionSet(QEI1_BASE,170);
    
    
       //GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 255);
    
       // left_motor(cw, 75);
       // right_motor(cw, 75);
    
        while(1)
        {
    
    
            // Right Motor Control:
            uint8_t right_dir_mask_result = g_ulDataRx2[0] & RIGHT_DIRECTION_MASK;
            uint8_t right_spd_mask_result = g_ulDataRx2[1] & RIGHT_SPEED_MASK;
    
            right_spd_mask_result = 14 * right_spd_mask_result;
    
            if( (right_dir_mask_result == 0) || (right_dir_mask_result == 3) )
            {
               // right_motor(ccw,0);
            }
            if(right_dir_mask_result == 1)
            {
               // right_motor(ccw,right_spd_mask_result);
            }
            if(right_dir_mask_result == 2)
            {
               // right_motor(cw,right_spd_mask_result);
            }
    
            // Left Motor Control
            uint8_t left_dir_mask_result = g_ulDataRx2[0] & LEFT_DIRECTION_MASK;
            uint8_t left_spd_mask_result = g_ulDataRx2[1] & LEFT_SPEED_MASK;
    
            if(left_spd_mask_result > 0)
            {
               // left_spd_mask_result = (left_spd_mask_result >> 4);
            }
    
            left_spd_mask_result = 14* left_spd_mask_result;
    
            if( (left_dir_mask_result == 0b00000000) || (left_dir_mask_result == 0b00001100) )
            {
               // left_motor(ccw,0);
            }
            if(left_dir_mask_result == 0b00000100)
            {
               // left_motor(ccw,left_spd_mask_result);
            }
            if(left_dir_mask_result == 0b00001000)
            {
              //  left_motor(cw,left_spd_mask_result);
            }
    
        }
    }
    
    
    
    
    
    
    
    
    
    
    void ConfigureTimers(void){
        //
        // Enable the GPIO port that is used for the on-board LED.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
        //
        // Enable the GPIO pins for the LED (PF1 & PF2).
        //
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_1);
    
    
        //
        // Enable the peripherals used by this example.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    
        //
        // Enable processor interrupts.
        //
        ROM_IntMasterEnable();
    
        //
        // Configure the two 32-bit periodic timers.
        //
        ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
        ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);
        ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, ROM_SysCtlClockGet());
        ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, ROM_SysCtlClockGet() / 10); // was ROM_SysCtlClockGet() / 2
    
        //
        // Setup the interrupts for the timer timeouts.
        //
        ROM_IntEnable(INT_TIMER0A);
        ROM_IntEnable(INT_TIMER1A);
        ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
        ROM_TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    
        //
        // Enable the timers.
        //
        ROM_TimerEnable(TIMER0_BASE, TIMER_A);
        //ROM_TimerEnable(TIMER1_BASE, TIMER_A);
    
        //
        // Loop forever while the timers run.
        //
    
    }
    
    void Timer0IntHandler(void)
    {
    
        // LEFT MOTOR
        leftcurrentRPM = (QEIVelocityGet(QEI0_BASE) * 60)/(374*4);
            // Calculate error and proportional value.
            left_error = leftdesiredRPM - leftcurrentRPM;
            // Calculate error and integral value.
            left_integrated_error_float += left_error;
            // Return the PID controlled duty cycle value.
            left_send_value_float += ((left_kp*left_error + left_ki*left_integrated_error_float));
    
            left_send_value_int = (int) left_send_value_float;
    
            if(left_send_value_int < 99 && left_send_value_int > 0)
            {
                PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, ((period* left_send_value_int)/100) );
            }
            if(left_send_value_int <= 0){
                PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, ((period* 0)/100) );
            }
            if(left_send_value_int >= 99){
                        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, ((period* 99)/100) );
                    }
    
    
         // RIGHT MOTOR
         rightcurrentRPM = (QEIVelocityGet(QEI1_BASE) * 60)/(374*4);
             // Calculate error and proportional value.
             right_error = rightdesiredRPM - rightcurrentRPM;
             right_integrated_error_float += right_error;
             // Return the PID controlled duty cycle value.
             right_send_value_float += ((right_kp*right_error + right_ki*right_integrated_error_float));
             right_send_value_int = (int) right_send_value_float;
             if(right_send_value_int < 99 && right_send_value_int > 0)
             {
                 //motor(ccw,send_value_int);
                 PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, ((period* right_send_value_int)/100) );
             }
             if(right_send_value_int <= 0){
                 PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, ((period* 0)/100) );
             }
             if(right_send_value_int >= 99){
                         PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, ((period* 99)/100) );
                     }
    
    
    
        count = count + 1;
        //
    
    
    
    
        // Clear the timer interrupt.
        //
        ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    
        //
        // Toggle the flag for the first timer.
        //
        HWREGBITW(&g_ui32Flags, 0) ^= 1;
    
        //
        // Use the flags to Toggle the LED for this timer
        //
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, g_ui32Flags << 1);
    
        ROM_IntMasterDisable();
    
        ROM_IntMasterEnable();
    }
    
    
    void
    Timer1IntHandler(void)
    {
        //char cOne, cTwo;
    
        //
        // Clear the timer interrupt.
        //
        ROM_TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
    
        //
        // Toggle the flag for the second timer.
        //
        HWREGBITW(&g_ui32Flags, 1) ^= 1;
    
        //
        // Use the flags to Toggle the LED for this timer
        //
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, g_ui32Flags << 1);
        //count = count + 1;
    
        //
        ROM_IntMasterDisable();
    
        ROM_IntMasterEnable();
    
    }
    
    void delayMS(int ms) {
        SysCtlDelay( (SysCtlClockGet()/(3*1000))*ms ) ;
    }
    
    
    
    
    

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

    我将等待您的示波器波形进一步评论。 另请注意 、C:\ti\TivaWare_C_Series-2.2.0.295\examples\boards\ek-tm4c1294xl\ssi_master_slave_xfer 中有一个 SSI 示例、您可以参考。 此示例将一个 SSI 模块设置为主机、将另一个 SSI 设置为从机。 您将需要在板上的它们之间建立连接。 尽管此示例适用于 TM4C129、但您可以修改或只读取 TM4C123的示例。