请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
器件型号:TM4C129XNCZAD 尊敬的专家:
希望您在感恩节期间和家人度过美好时光。 最近、我正在帮助 TM4C 客户使用 TM4C129X 构建 DMA 演示代码、因为他们对成熟的项目有更严格的时序要求。 目标是向 SSI0发送/接收添加 DMA 操作。 我尝试构建启用了 DMA 的 SSI0操作代码、但发现该代码将滞留在外设 DMA 中断处理程序中、并且无法正常运行。 我使用的参考代码是 DK-TM4C129X 中的 udmademo、我使用的电路板是 DK-TM4C129X。 请帮助您查看我遗漏的内容吗? 这是由于总线的 CPU 和 DMA 竞争造成的吗? 因为我没有在代码中添加 CPU 睡眠。 BTW、从分步调试中、我发现 MCU 始终在执行 SSI 中断处理程序、但我尝试清除标志位、但不起作用。 我随附了我正在使用的代码、在此代码中、我将外设从 SSI 更改为 UART、因为我认为逻辑将相似、但我遇到了相同的故障。
Eric
-//*****************************************************************************
//
// spi_master.c - Example demonstrating how to configure SSI0 in SPI master
// mode.
//
// Copyright (c) 2010-2020 Texas Instruments Incorporated. All rights reserved.
// Software License Agreement
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.2.0.295 of the Tiva Firmware Development Package.
//
//*****************************************************************************
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "driverlib/udma.h"
#include "driverlib/uart.h"
#include "inc/hw_ssi.h"
#include "driverlib/interrupt.h"
#include "inc/hw_ints.h"
#include "inc/hw_types.h"
#include "inc/hw_udma.h"
#include "inc/hw_uart.h"
//*****************************************************************************
//
//! \addtogroup ssi_examples_list
//! <h1>SPI Master (spi_master)</h1>
//!
//! This example shows how to configure the SSI0 as SPI Master. The code will
//! send three characters on the master Tx then polls the receive FIFO until
//! 3 characters are received on the master Rx.
//!
//! This example uses the following peripherals and I/O signals. You must
//! review these and change as needed for your own board:
//! - SSI0 peripheral
//! - GPIO Port A peripheral (for SSI0 pins)
//! - SSI0Clk - PA2
//! - SSI0Fss - PA3
//! - SSI0Rx (TM4C123x) / SSI0XDAT0 (TM4C129x) - PA4
//! - SSI0Tx (TM4C123x) / SSI0XDAT1 (TM4C129x) - PA5
//!
//! 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.
//! - None.
//
//*****************************************************************************
//*****************************************************************************
//
// Number of bytes to send and receive.
//
//*****************************************************************************
#define NUM_SSI_DATA 3
#define SSI_TX 0x00000800
#define SSI_RX 0x00000400
uint32_t EEG_dataready = 0;
uint32_t g_ui32uDMAErrCount = 0;
uint8_t EEG_TXBuffer[132];
uint8_t EEG_RXBufferA[132];
uint8_t EEG_RXBufferB[132];
#pragma DATA_ALIGN(ui8ControlTable, 1024)
uint8_t ui8ControlTable[1024];
//*****************************************************************************
//
// The interrupt handler for SSI0. This interrupt will occur when a DMA
// transfer is complete using the SSI0 uDMA channel. It will also be
// triggered if the peripheral signals an error.
//
//*****************************************************************************
void SSI0IntHandler(void)
{
uint32_t ui32Status;
//
//
// Read the interrupt status of the SSI0.
//
ui32Status = HWREG(SSI0_BASE + SSI_O_MIS);
HWREG(SSI0_BASE + SSI_O_ICR) = ui32Status;
//
// Clear any pending status, even though there should be none since no UART
// interrupts were enabled. If UART error interrupts were enabled, then
// those interrupts could occur here and should be handled. Since uDMA is
// used for both the RX and TX, then neither of those interrupts should be
// enabled.
//
//SSIIntClear(SSI0_BASE, ui32Status);
ui32Status = HWREG(UDMA_CHIS);
HWREG(UDMA_CHIS) = ui32Status;
//
// If the SSI0 DMA RX channel is disabled, that means the RX DMA transfer
// is done.
//
//if(!uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0RX))
//
// Notify main that EEG data is ready.
//
if(!uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX))
{
//
EEG_dataready = 1;
}
}
void uDMAErrorHandler(void)
{
uint32_t ui32Status;
//
// Check for uDMA error bit
//
ui32Status = uDMAErrorStatusGet();
//
// If there is a uDMA error, then clear the error and increment
// the error counter.
//
if(ui32Status)
{
uDMAErrorStatusClear();
g_ui32uDMAErrCount++;
}
}
//*****************************************************************************
//
// The interrupt handler for UART0. This interrupt will occur when a DMA
// transfer is complete using the UART0 uDMA channel. It will also be
// triggered if the peripheral signals an error. This interrupt handler will
// switch between receive ping-pong buffers A and B. It will also restart a TX
// uDMA transfer if the prior transfer is complete. This will keep the UART
// running continuously (looping TX data back to RX).
//
//*****************************************************************************
void
UART0IntHandler(void)
{
uint32_t ui32Status;
uint32_t ui32Mode;
//
// Read the interrupt status of the UART.
//
ui32Status = UARTIntStatus(UART0_BASE, 1);
//
// Clear any pending status, even though there should be none since no UART
// interrupts were enabled. If UART error interrupts were enabled, then
// those interrupts could occur here and should be handled. Since uDMA is
// used for both the RX and TX, then neither of those interrupts should be
// enabled.
//
UARTIntClear(UART0_BASE, ui32Status);
//
// Check the DMA control table to see if the ping-pong "A" transfer is
// complete. The "A" transfer uses receive buffer "A", and the primary
// control structure.
//
ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT);
//
// If the primary control structure indicates stop, that means the "A"
// receive buffer is done. The uDMA controller should still be receiving
// data into the "B" buffer.
//
if(ui32Mode == UDMA_MODE_STOP)
{
//
// Set up the next transfer for the "A" buffer, using the primary
// control structure. When the ongoing receive into the "B" buffer is
// done, the uDMA controller will switch back to this one. This
// example re-uses buffer A, but a more sophisticated application could
// use a rotating set of buffers to increase the amount of time that
// the main thread has to process the data in the buffer before it is
// reused.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(UART0_BASE + UART_O_DR),
EEG_RXBufferA, sizeof(EEG_RXBufferA));
}
//
// Check the DMA control table to see if the ping-pong "B" transfer is
// complete. The "B" transfer uses receive buffer "B", and the alternate
// control structure.
//
ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT);
//
// If the alternate control structure indicates stop, that means the "B"
// receive buffer is done. The uDMA controller should still be receiving
// data into the "A" buffer.
//
if(ui32Mode == UDMA_MODE_STOP)
{
//
// Set up the next transfer for the "B" buffer, using the alternate
// control structure. When the ongoing receive into the "A" buffer is
// done, the uDMA controller will switch back to this one. This
// example re-uses buffer B, but a more sophisticated application could
// use a rotating set of buffers to increase the amount of time that
// the main thread has to process the data in the buffer before it is
// reused.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *)(UART0_BASE + UART_O_DR),
EEG_RXBufferB, sizeof(EEG_RXBufferA));
}
//
// If the UART0 DMA TX channel is disabled, that means the TX DMA transfer
// is done.
//
if(!uDMAChannelIsEnabled(UDMA_CHANNEL_UART0TX))
{
//
// Start another DMA transfer to UART0 TX.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, EEG_TXBuffer,
(void *)(UART0_BASE + UART_O_DR),
sizeof(EEG_TXBuffer));
EEG_dataready = 1;
//
// The uDMA TX channel must be re-enabled.
//
uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
}
}
//*****************************************************************************
//
// Configure SSI0 in master Freescale (SPI) mode. This example will send out
// 3 bytes of data, then wait for 3 bytes of data to come in. This will all be
// done using the polling method.
//
//*****************************************************************************
int
main(void)
{
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
uint32_t ui32SysClock;
#endif
uint32_t pui32DataTx[NUM_SSI_DATA];
uint32_t pui32DataRx[NUM_SSI_DATA];
uint32_t ui32Index;
//
// Set the clocking to run directly from the external crystal/oscillator.
// TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
// crystal on your board.
//
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN |
SYSCTL_USE_OSC), 25000000);
#else
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
#endif
//
// The SSI0 peripheral must be enabled for use.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//
// For this example SSI0 is used with PortA[5:2]. The actual port and pins
// used may be different on your part, consult the data sheet for more
// information. GPIO port A needs to be enabled so these pins can be used.
// TODO: change this to whichever GPIO port you are using.
//
SysCtlPeripheralEnable(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.
// TODO: change this to select the port/pin you are using.
//
GPIOPinConfigure(GPIO_PA2_SSI0CLK);
GPIOPinConfigure(GPIO_PA3_SSI0FSS);
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
GPIOPinConfigure(GPIO_PA4_SSI0XDAT0);
GPIOPinConfigure(GPIO_PA5_SSI0XDAT1);
#else
GPIOPinConfigure(GPIO_PA4_SSI0RX);
GPIOPinConfigure(GPIO_PA5_SSI0TX);
#endif
//
// PA0-1 are used for UART0.
//
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//
// 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 (TM4C123x) / SSI0XDAT1 (TM4C129x)
// PA4 - SSI0Rx (TM4C123x) / SSI0XDAT0 (TM4C129x)
// PA3 - SSI0Fss
// PA2 - SSI0CLK
// TODO: change this to select the port/pin you are using.
//
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
GPIO_PIN_2);
//
// Configure and enable the SSI port for SPI master mode. Use SSI0,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.
// For SPI mode, you can set the polarity of the SSI clock when the SSI
// unit is idle. You can also configure what clock edge you want to
// capture data on. Please reference the datasheet for more information on
// the different SPI modes.
//
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
SSIConfigSetExpClk(SSI0_BASE, ui32SysClock, SSI_FRF_MOTO_MODE_1,
SSI_MODE_MASTER, 1000000, 8);
#else
SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
SSI_MODE_MASTER, 1000000, 8);
#endif
UARTConfigSetExpClk(UART0_BASE, ui32SysClock, 115200,
UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE);
//
// Enable peripherals to operate when CPU is in sleep.
//
SysCtlPeripheralClockGating(true);
//
//
//
// Enable the UDMA peripheral
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
//
// Wait for the UDMA module to be ready.
//
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UDMA))
{
}
//
// Enable the uDMA controller.
//
uDMAEnable();
//
// Set the base for the channel control table.
//
uDMAControlBaseSet(&ui8ControlTable[0]);
//
// Set both the TX and RX trigger thresholds to 4. This will be used by
// the uDMA controller to signal when more data should be transferred. The
// uDMA TX and RX channels will be configured so that it can transfer 4
// bytes in a burst when the UART is ready to transfer more data.
//
UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
//
// Enable the UART for operation, and enable the uDMA interface for both TX
// and RX channels.
//
UARTEnable(UART0_BASE);
UARTDMAEnable(UART0_BASE, UART_DMA_TX | UART_DMA_RX);
//
// This register write will set the UART to operate in loopback mode. Any
// data sent on the TX output will be received on the RX input.
//
HWREG(UART0_BASE + UART_O_CTL) |= UART_CTL_LBE;
//
// Put the attributes in a known state for the uDMA UART0RX channel. These
// should already be disabled by default.
//
uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0RX,
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
//
// Configure the control parameters for the primary control structure for
// the UART RX channel. The primary contol structure is used for the "A"
// part of the ping-pong receive. The transfer data size is 8 bits, the
// source address does not increment since it will be reading from a
// register. The destination address increment is byte 8-bit bytes. The
// arbitration size is set to 4 to match the RX FIFO trigger threshold.
// The uDMA controller will use a 4 byte burst transfer if possible. This
// will be somewhat more effecient that single byte transfers.
//
uDMAChannelControlSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT,
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
UDMA_ARB_4);
//
// Configure the control parameters for the alternate control structure for
// the UART RX channel. The alternate contol structure is used for the "B"
// part of the ping-pong receive. The configuration is identical to the
// primary/A control structure.
//
uDMAChannelControlSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT,
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
UDMA_ARB_4);
//
// Set up the transfer parameters for the UART RX primary control
// structure. The mode is set to ping-pong, the transfer source is the
// UART data register, and the destination is the receive "A" buffer. The
// transfer size is set to match the size of the buffer.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG,
(void *)(UART0_BASE + UART_O_DR),
EEG_RXBufferA, sizeof(EEG_RXBufferA));
//
// Set up the transfer parameters for the UART RX alternate control
// structure. The mode is set to ping-pong, the transfer source is the
// UART data register, and the destination is the receive "B" buffer. The
// transfer size is set to match the size of the buffer.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG,
(void *)(UART0_BASE + UART_O_DR),
EEG_RXBufferB, sizeof(EEG_RXBufferB));
//
// Put the attributes in a known state for the uDMA UART0TX channel. These
// should already be disabled by default.
//
uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX,
UDMA_ATTR_ALTSELECT |
UDMA_ATTR_HIGH_PRIORITY |
UDMA_ATTR_REQMASK);
//
// Set the USEBURST attribute for the uDMA UART TX channel. This will
// force the controller to always use a burst when transferring data from
// the TX buffer to the UART. This is somewhat more effecient bus usage
// than the default which allows single or burst transfers.
//
uDMAChannelAttributeEnable(UDMA_CHANNEL_UART0TX, UDMA_ATTR_USEBURST);
//
// Configure the control parameters for the UART TX. The uDMA UART TX
// channel is used to transfer a block of data from a buffer to the UART.
// The data size is 8 bits. The source address increment is 8-bit bytes
// since the data is coming from a buffer. The destination increment is
// none since the data is to be written to the UART data register. The
// arbitration size is set to 4, which matches the UART TX FIFO trigger
// threshold.
//
uDMAChannelControlSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
UDMA_SIZE_8 | UDMA_SRC_INC_8 |
UDMA_DST_INC_NONE |
UDMA_ARB_4);
//
// Set up the transfer parameters for the uDMA UART TX channel. This will
// configure the transfer source and destination and the transfer size.
// Basic mode is used because the peripheral is making the uDMA transfer
// request. The source is the TX buffer and the destination is the UART
// data register.
//
uDMAChannelTransferSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC, EEG_TXBuffer,
(void *)(UART0_BASE + UART_O_DR),
sizeof(EEG_TXBuffer));
//
// Now both the uDMA UART TX and RX channels are primed to start a
// transfer. As soon as the channels are enabled, the peripheral will
// issue a transfer request and the data transfers will begin.
//
uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
uDMAChannelEnable(UDMA_CHANNEL_UART0RX);
//
// Enable the UART DMA TX/RX interrupts.
//
UARTIntEnable(UART0_BASE, UART_INT_DMATX | UART_INT_DMARX);
//
// Enable the UART peripheral interrupts.
//
IntEnable(INT_UART0);
while(1)
{
if(EEG_dataready)
{
//
EEG_dataready = 0;
//SysCtlDelay(240);
uint32_t i = 0;
for(i = 0; i < 132; i++)
{
//
EEG_TXBuffer[i] = 0x55;
EEG_RXBufferA[i] = 0x00;
EEG_RXBufferB[i] = 0x00;
}
uDMAChannelEnable(UDMA_CHANNEL_UART0TX);
uDMAChannelEnable(UDMA_CHANNEL_UART0RX);
}
}
//
// Return no errors
//
return(0);
}