目录
Introduction
在英飞凌MCU中,UART通信可以通过三种主要模式进行:Interrupt(中断)、Direct(直接)和DMA(直接存储器访问)。下面是这三种通讯模式的区别:
-
Interrupt(中断)模式:
- 在中断模式下,UART通过中断请求(IRQ)与CPU通信。当UART接收到数据或者发送数据时,它会生成一个中断信号给CPU,CPU响应中断并执行相应的中断服务程序(ISR)来处理数据。
- 这种模式下,CPU需要在每次数据传输时响应中断,这可能会导致CPU资源的频繁占用,尤其是在数据传输频繁时。
- 适用于数据传输速率不高,对实时性要求不是非常高的应用场景。
-
Direct(直接)模式:
- 直接模式下,UART的数据传输不通过中断或DMA,而是直接通过CPU编程控制数据的发送和接收。
- 这种方式下,CPU需要轮询UART的状态寄存器,检查是否可以发送或接收数据,然后进行相应的读写操作。
- 直接模式通常用于简单的串口通信,或者在系统资源允许的情况下,对实时性要求较高的场合。
-
DMA(直接存储器访问)模式:
- DMA模式允许UART通过DMA控制器直接将数据从外设传输到内存,或者从内存传输到外设,而不需要CPU的直接参与。
- 在这种模式下,DMA控制器负责管理数据的移动,减少了CPU的负担,使得CPU可以处理其他任务。
- DMA模式适用于高速数据传输,可以显著提高数据传输效率,尤其在数据量大且频繁传输的场景中。
总结来说,中断模式适合于数据传输速率较低的应用,直接模式适用于简单的串口通信,而DMA模式适合于高速且数据量大的传输场景,可以有效地减轻CPU负担。每种模式都有其适用的场景和优缺点,开发者可以根据具体的应用需求选择合适的通信模式。
实践
英飞凌的IDE DAVE(Digital Application Virtual Engineer)是一款专为XMC系列微控制器(MCU)设计的集成开发环境(IDE)。以下是DAVE的主要特点和功能:
-
基于Eclipse的IDE:DAVE基于Eclipse平台,提供了一个熟悉的开发环境,支持代码提示和快捷键自定义等功能。
-
DAVE APPs:DAVE提供了可配置的应用库(DAVE APPs),这些是预定义的模块,可以通过图形化界面进行配置,然后自动生成源代码库供开发者进行上层逻辑的开发。
-
图形化配置界面:DAVE提供了一个图形化的外设配置界面,用户可以通过拖放组件来配置外设(如GPIO、UART、SPI、PWM等),并自动生成相应的初始化代码。
-
代码生成工具:配置完外设后,用户可以点击“Project” -> “Generate Code”,DAVE会自动根据配置生成相应的初始化代码,包含了每个外设的初始化函数,自动配置了所有外设寄存器。
-
实时调试功能:DAVE支持实时调试功能,包括设置断点、单步调试、查看变量和调用栈等,以及实时查看外设状态的功能。
-
支持多种编译工具:DAVE支持多种编译工具,如GCC ARM,以及与硬件的无缝集成,使得开发者可以专注于应用逻辑的实现,而无需过多关注底层硬件的配置和管理。
-
性能分析工具:DAVE IDE支持性能分析工具,可以对代码的执行时间进行剖析,查找性能瓶颈,并提供了针对不同外设的低功耗配置,帮助实现节能优化。
-
固件包:DAVE IDE提供了固件包(如XMC-Device-FW),里面包含了XMC系列MCU的所有驱动程序和外设配置函数,便于快速开始开发。
-
免费使用:DAVE是一个免费提供给用户的开发工具,可以帮助用户减少开发时间和成本。
-
支持多种MCU系列:DAVE支持英飞凌的多种MCU系列,包括XMC系列、XC800、C166、XC166、XE166、XC2000和TriCore AUDO系列产品。
DAVE通过提供图形化的配置界面、强大的代码生成工具、实时调试功能以及对XMC MCU系列的全面支持,使得开发者能够高效地开发、调试和部署嵌入式应用。
New Project in DAVE
关于一个简单的串口程序,我原来写过一篇blog, 可以参考:【基于DAVE的英飞凌XMC4200的RS485通讯】
Interrupt mode
在UART APP configuration 界面的advanced settings页面中可以看到,默认模式是Interrupt模式。
generate code 之后我们可以去uart.c里面看具体怎么收发数据。
/*
* @brief Common function to transmit data.
*
* @param[in] handle UART APP handle pointer of type UART_t*
* @param[in] data_ptr Pointer to data of type uint8_t
* @param[in] count Number of uint8_t type bytes to be transmitted
*
* @return UART_STATUS_t
* UART_SUCCESS: If the data is put to transmit.<BR>
* UART_STATUS_BUSY : If the channel is busy.<BR>
* UART_STATUS_BUFFER_INVALID: Either if buffer is NULL or count is 0.<BR>
* UART_STATUS_MODE_MISMATCH: If the configured mode is invalid.<BR>
*
*/
UART_STATUS_t UART_Transmit(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
switch(handle->config->transmit_mode)
{
#ifdef UART_TX_INTERRUPT_USED
case UART_TRANSFER_MODE_INTERRUPT:
ret_stat = UART_StartTransmitIRQ(handle, data_ptr, count);
break;
#endif
#ifdef UART_TX_DMA_USED
case UART_TRANSFER_MODE_DMA:
ret_stat = UART_StartTransmitDMA(handle, data_ptr, count);
break;
#endif
#ifdef UART_TX_DIRECT_USED
case UART_TRANSFER_MODE_DIRECT:
ret_stat = UART_lStartTransmitPolling(handle, data_ptr, count);
break;
#endif
default:
break;
}
return ret_stat;
}
/*
* @brief Common function to receive data.
*
* @param[in] handle UART APP handle pointer of type UART_t*
* @param[in] data_ptr Pointer to data of type uint8_t
* @param[in] count Number of uint8_t type bytes to be received
*
* @return UART_STATUS_t
* UART_SUCCESS: If the data is put to transmit.<BR>
* UART_STATUS_BUSY : If the channel is busy.<BR>
* UART_STATUS_BUFFER_INVALID: Either if buffer is NULL or count is 0.<BR>
* UART_STATUS_MODE_MISMATCH: If the configured mode is invalid.<BR>
*
*/
UART_STATUS_t UART_Receive(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
switch(handle->config->receive_mode)
{
#ifdef UART_RX_INTERRUPT_USED
case UART_TRANSFER_MODE_INTERRUPT:
ret_stat = UART_StartReceiveIRQ(handle, data_ptr, count);
break;
#endif
#ifdef UART_RX_DMA_USED
case UART_TRANSFER_MODE_DMA:
ret_stat = UART_StartReceiveDMA(handle, data_ptr, count);
break;
#endif
#ifdef UART_RX_DIRECT_USED
case UART_TRANSFER_MODE_DIRECT:
ret_stat = UART_lStartReceivePolling(handle, data_ptr, count);
break;
#endif
default:
break;
}
return ret_stat;
}
/*
* @brief Registers a request for transmitting data over UART channel.
*
* @param[in] UART_t* UART APP handle pointer of type UART_t
* @param[in] uint8_t* Pointer to data
* @param[in] uint32_t Total no of words to be transmitted.
*
* @return UART_STATUS_t UART_STATUS_SUCCESS if the request is accepted.
* UART_STATUS_BUSY if a transmission is in progress.
* Details of function:
* The data transmission is accomplished using transmit interrupt. User can configure
* a callback function in the APP UI. When the data is fully transmitted, the callback
* function will be executed. If transmit FIFO is enabled, the trigger limit is set to 0.
* So the transmit interrupt will be generated when all the data in FIFO is moved from FIFO.
*
* <i>Imp Note:</i> Return value should be validated by user to ensure that the
* request is registered.
*
*
*/
UART_STATUS_t UART_StartTransmitIRQ(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
UART_RUNTIME_t * ptr_runtime = handle->runtime;
XMC_ASSERT("UART_StartTransmitIRQ: UART APP handle invalid", ((handle != NULL)&&
(handle->runtime != NULL)));
if (handle->config->transmit_mode == UART_TRANSFER_MODE_INTERRUPT)
{
ret_stat = UART_STATUS_BUSY;
if (ptr_runtime->tx_busy == false)
{
/*If there is no transmission in progress*/
if ((data_ptr != NULL) && (count > 0U))
{
/*Obtain the address of data, size of data*/
ptr_runtime->tx_data = data_ptr;
ptr_runtime->tx_data_count = count;
/*Initialize to first index and set the busy flag*/
ptr_runtime->tx_data_index = 0U;
ptr_runtime->tx_busy = true;
/*Enable the transmit buffer event*/
if (handle->config->tx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Clear the transmit FIFO*/
XMC_USIC_CH_TXFIFO_Flush(handle->channel);
/*Enable transmit buffer interrupt*/
XMC_USIC_CH_TXFIFO_EnableEvent(handle->channel,(uint32_t)XMC_USIC_CH_TXFIFO_EVENT_CONF_STANDARD);
}
else
{
XMC_USIC_CH_EnableEvent(handle->channel, (uint32_t)XMC_USIC_CH_EVENT_TRANSMIT_BUFFER);
}
ret_stat = UART_STATUS_SUCCESS;
/*Trigger the transmit buffer interrupt*/
XMC_USIC_CH_TriggerServiceRequest(handle->channel, (uint32_t)handle->config->tx_sr);
}
else
{
ret_stat = UART_STATUS_BUFFER_INVALID;
}
}
}
return ret_stat;
}
/*
* @brief Registers a request to receive data over UART channel.
*
* @param[in] UART_t* UART APP handle pointer of type UART_t
* @param[in] uint8_t* Pointer to data array
* @param[in] uint32_t Total no of bytes to be read.
*
* @return UART_STATUS_t UART_STATUS_SUCCESS if the request is accepted.
* UART_STATUS_BUSY if a reception is in progress.
* Details of function:
* This function registers the receive request by configuring the UART
* receive FIFO/Standard buffer (depending on the user configuration). The data
* is received asynchronously. When the requested number of data bytes are received,
* optionally, the user configured callback function will be executed. If a callback
* function is not configured on the APP UI, the user has to poll for the status of
* rx_busy variable of the APP handle structure.
*
* <i>Imp Note:</i> Return value should be validated by user to ensure that the
* request is registered.
*
*
*/
UART_STATUS_t UART_StartReceiveIRQ(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
UART_RUNTIME_t * ptr_runtime = handle->runtime;
XMC_ASSERT("UART_StartReceiveIRQ: UART APP handle invalid", ((handle != NULL)&&
(handle->runtime != NULL)));
if (handle->config->receive_mode == UART_TRANSFER_MODE_INTERRUPT)
{
ret_stat = UART_STATUS_BUSY;
if (ptr_runtime->rx_busy == false)
{
/*If no active reception in progress*/
if ((data_ptr != NULL) && (count > 0U))
{
/*Obtain the address of data buffer and
* number of data bytes to be received*/
ptr_runtime->rx_data = data_ptr;
ptr_runtime->rx_data_count = count;
ptr_runtime->rx_busy = true;
ptr_runtime->rx_data_index = 0U;
if (handle->config->rx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Clear the receive FIFO, configure the trigger lime
* and enable the receive events*/
XMC_USIC_CH_RXFIFO_Flush(handle->channel);
/*Configure the FIFO trigger limit based on the required data size*/
UART_lReconfigureRxFIFO(handle, count);
XMC_USIC_CH_RXFIFO_EnableEvent(handle->channel,
(uint32_t)((uint32_t)XMC_USIC_CH_RXFIFO_EVENT_CONF_STANDARD |
(uint32_t)XMC_USIC_CH_RXFIFO_EVENT_CONF_ALTERNATE));
}
else
{
XMC_USIC_CH_EnableEvent(handle->channel,
(uint32_t)((uint32_t)XMC_USIC_CH_EVENT_STANDARD_RECEIVE | (uint32_t)XMC_USIC_CH_EVENT_ALTERNATIVE_RECEIVE));
}
ret_stat = UART_STATUS_SUCCESS;
}
else
{
ret_stat = UART_STATUS_BUFFER_INVALID;
}
}
}
return ret_stat;
}
从上述代码中可以看到,在interrupt mode中是通过 XMC_USIC_CH_RXFIFO_EnableEvent
或XMC_USIC_CH_EnableEvent
来开始数据传输的,一般会选择使用FIFO,这样会降低CPU的操作频率。
Direct mode
generate code 后我们可以看到direct mode下是怎么工作的
/*
* Polling method to transmit data.
* @param[in] UART_t* handle UART APP handle pointer
* @param[in] uint8_t* Pointer to data array
* @param[in] uint32_t number of bytes to be transmitted.
*
* @return UART_STATUS_t Status of transmit request handling.
*
* Description:
* Transmits data by blocking the CPU until all data is sent. Transmission
* cannot be aborted since it is blocking implementation. Based on FIFO selection,
* either TBUF or IN register is updated with the data.
*
*/
static UART_STATUS_t UART_lStartTransmitPolling(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_BUFFER_INVALID;
uint32_t loc_index;
XMC_ASSERT("UART_Transmit: UART APP handle invalid", (((handle != NULL)&&
(handle->runtime != NULL))&&(handle->config != NULL)));
if ((data_ptr != NULL) && (count > 0U))
{
ret_stat = UART_STATUS_BUSY;
if (handle->runtime->tx_busy == false)
{
handle->runtime->tx_busy = true;
if (handle->config->tx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Clear the transmit FIFO*/
XMC_USIC_CH_TXFIFO_Flush(handle->channel);
}
/*Loop through each byte*/
for (loc_index = 0U; loc_index < count; loc_index++)
{
/*If FIFO is enabled, FIFO filling status should be checked
* to avoid overflow error*/
if (handle->config->tx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Wait if transmit FIFO is full*/
while (XMC_USIC_CH_TXFIFO_IsFull(handle->channel) == true)
{
}
}
XMC_UART_CH_Transmit(handle->channel, (uint16_t)data_ptr[loc_index]);
}
if (handle->config->tx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Wait till FIFO is empty*/
while (XMC_USIC_CH_TXFIFO_IsEmpty(handle->channel) == false)
{
}
}
ret_stat = UART_STATUS_SUCCESS;
handle->runtime->tx_busy = false;
}
}
return ret_stat;
}
/*
* Polling method to receive data.
* @param[in] UART_t* handle UART APP handle pointer
* @param[in] uint8_t* Pointer to data array
* @param[in] uint32_t number of bytes to be received.
*
* @return UART_STATUS_t Status of receive request handling.
*
* Description:
* Receives data by blocking the CPU until all data is received. Reception
* cannot be aborted since it is blocking implementation. Based on FIFO selection,
* either RBUF or OUT register will be read.
*
*/
static UART_STATUS_t UART_lStartReceivePolling(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_BUFFER_INVALID;
uint32_t loc_index;
uint32_t loc_status;
XMC_ASSERT("UART_Receive: UART APP handle invalid", ((handle != NULL)&&
(handle->runtime != NULL)));
if ((data_ptr != NULL) && (count > 0U))
{
ret_stat = UART_STATUS_BUSY;
if (handle->runtime->rx_busy == false)
{
handle->runtime->rx_busy = true;
if (handle->config->rx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Clear the receive FIFO, configure the trigger lime
* and enable the receive events*/
XMC_USIC_CH_RXFIFO_Flush(handle->channel);
}
for (loc_index = 0U; loc_index < count; loc_index++)
{
/*If receive FIFO is configured, wait for FIFO to get data.*/
if (handle->config->rx_fifo_size != XMC_USIC_CH_FIFO_DISABLED)
{
/*Wait if FIFO empty*/
while(XMC_USIC_CH_RXFIFO_IsEmpty(handle->channel) == true)
{
}
}
else
{
/*Wait for RIF or AIF flag update*/
loc_status = XMC_UART_CH_GetStatusFlag(handle->channel);
while (!(loc_status & ((uint32_t)XMC_UART_CH_STATUS_FLAG_ALTERNATIVE_RECEIVE_INDICATION |
(uint32_t)XMC_UART_CH_STATUS_FLAG_RECEIVE_INDICATION)))
{
loc_status = XMC_UART_CH_GetStatusFlag(handle->channel);
}
/*Clear the detected event.
* Both events should not be cleared at once, otherwise if 2 bytes are received, only
* one byte will be read.*/
XMC_UART_CH_ClearStatusFlag(handle->channel,
((uint32_t)XMC_UART_CH_STATUS_FLAG_RECEIVE_INDICATION | (uint32_t)XMC_UART_CH_STATUS_FLAG_ALTERNATIVE_RECEIVE_INDICATION));
}
data_ptr[loc_index] = (uint8_t)XMC_UART_CH_GetReceivedData(handle->channel);
}
ret_stat = UART_STATUS_SUCCESS;
handle->runtime->rx_busy = false;
}
}
return ret_stat;
}
可以看到上述代码中用了for 循环和while循环,这样会让CPU一直出于block的状态。一般不建议使用。
DMA mode
同样generate code
/*
* @brief Registers a request for transmitting data over UART channel using DMA.
*
* @param[in] UART_t* UART APP handle pointer of type UART_t
* @param[in] uint8_t* Pointer to data
* @param[in] uint32_t Total no of words to be transmitted.
*
* @return UART_STATUS_t UART_STATUS_SUCCESS if the request is accepted.
* UART_STATUS_BUSY if a transmission is in progress.
* Details of function:
* The data transmission is accomplished using a DMA channel. User can configure
* a callback function in the APP UI. When the data is fully transmitted, the callback
* function will be executed.
* <i>Imp Note:</i> Return value should be validated by user to ensure that the
* request is registered.
*
*
*/
UART_STATUS_t UART_StartTransmitDMA(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
UART_RUNTIME_t * ptr_runtime = handle->runtime;
const UART_DMA_CONFIG_t * ptr_dma_config = handle->config->transmit_dma_config;
XMC_DMA_t * ptr_gpdma = handle->config->global_dma->dma;
XMC_ASSERT("UART_StartTransmitDMA: UART APP handle invalid", (((handle != NULL)&&
(handle->runtime != NULL))&&(handle->config != NULL)));
if (handle->config->transmit_mode == UART_TRANSFER_MODE_DMA)
{
ret_stat = UART_STATUS_BUSY;
if (ptr_runtime->tx_busy == false)
{
/*If there is no transmission in progress*/
if ((data_ptr != NULL) && ((count > 0U) &&(count <= UART_DMA_MAXCOUNT)))
{
/*Obtain the address of data, size of data*/
ptr_runtime->tx_data = data_ptr;
ptr_runtime->tx_data_count = count;
/*Initialize to first index and set the busy flag*/
ptr_runtime->tx_data_index = 0U;
ptr_runtime->tx_busy = true;
/*Enable transmit event generation*/
XMC_UART_CH_EnableEvent(handle->channel, (uint32_t)XMC_UART_CH_EVENT_TRANSMIT_BUFFER);
ret_stat = UART_STATUS_SUCCESS;
/*Enable DMA channel*/
XMC_DMA_CH_SetBlockSize(ptr_gpdma, ptr_dma_config->dma_channel, count);
XMC_DMA_CH_SetSourceAddress(ptr_gpdma, ptr_dma_config->dma_channel, (uint32_t)data_ptr);
XMC_DMA_CH_SetDestinationAddress(ptr_gpdma, ptr_dma_config->dma_channel,
(uint32_t)&(handle->channel->TBUF[0]));
XMC_DMA_CH_Enable(ptr_gpdma, ptr_dma_config->dma_channel);
}
else
{
ret_stat = UART_STATUS_BUFFER_INVALID;
}
}
}
return ret_stat;
}
/*
* @brief Registers a request to receive data over UART channel using DMA.
*
* @param[in] UART_t* UART APP handle pointer of type UART_t
* @param[in] uint8_t* Pointer to data array
* @param[in] uint32_t Total no of bytes to be read.
*
* @return UART_STATUS_t UART_STATUS_SUCCESS if the request is accepted.
* UART_STATUS_BUSY if a reception is in progress.
* Details of function:
* This function registers the receive request by configuring the UART
* receive Standard buffer and the DMA channel. The data
* is received asynchronously. When the requested number of data bytes are received,
* optionally, the user configured callback function will be executed.
*
* <i>Imp Note:</i> Return value should be validated by user to ensure that the
* request is registered.
*
*
*/
UART_STATUS_t UART_StartReceiveDMA(const UART_t *const handle, uint8_t* data_ptr, uint32_t count)
{
UART_STATUS_t ret_stat = UART_STATUS_MODE_MISMATCH;
UART_RUNTIME_t * ptr_runtime = handle->runtime;
const UART_DMA_CONFIG_t * ptr_dma_config = handle->config->receive_dma_config;
XMC_DMA_t * ptr_gpdma = handle->config->global_dma->dma;
XMC_ASSERT("UART_StartReceiveDMA: UART APP handle invalid", (((handle != NULL)&&
(handle->runtime != NULL)) && (handle->config != NULL)));
if (handle->config->receive_mode == UART_TRANSFER_MODE_DMA)
{
ret_stat = UART_STATUS_BUSY;
if (ptr_runtime->rx_busy == false)
{
/*If no active reception in progress*/
if ((data_ptr != NULL) && ((count > 0U) && (count <= UART_DMA_MAXCOUNT)))
{
/*Obtain the address of data buffer and
* number of data bytes to be received*/
ptr_runtime->rx_data = data_ptr;
ptr_runtime->rx_data_count = count;
ptr_runtime->rx_busy = true;
ptr_runtime->rx_data_index = 0U;
XMC_USIC_CH_EnableEvent(handle->channel,
(uint32_t)((uint32_t)XMC_USIC_CH_EVENT_STANDARD_RECEIVE | (uint32_t)XMC_USIC_CH_EVENT_ALTERNATIVE_RECEIVE));
ret_stat = UART_STATUS_SUCCESS;
/*Enable DMA channel*/
XMC_DMA_CH_SetBlockSize(ptr_gpdma, ptr_dma_config->dma_channel, count);
XMC_DMA_CH_SetSourceAddress(ptr_gpdma, ptr_dma_config->dma_channel, (uint32_t)&(handle->channel->RBUF));
XMC_DMA_CH_SetDestinationAddress(ptr_gpdma, ptr_dma_config->dma_channel, (uint32_t)data_ptr);
XMC_DMA_CH_Enable(ptr_gpdma, ptr_dma_config->dma_channel);
}
else
{
ret_stat = UART_STATUS_BUFFER_INVALID;
}
}
}
return ret_stat;
}
可以看到函数开头的description中建议我们配置callback 函数,这样可以在收发所有数据结束后调用callback函数,进行后续操作。
这样有一个好处。比如说发送结束之后我要做一些操作,但这些操作必须发完所有数据之后才可以进行。而DMA模式下又不占用CPU资源,当软件中执行完UART_Transmit
后,不代表所有数据被发完了,我们必须计算发送这些数据的时间,创建相应的delay, delay结束后再进行相关操作。
在DAVE中我们可以用APP TIMER 和INTERRUPT完成。
比如我们要在9600下发送10个byte, 那么我们需要
t
=
1
s
/
9600
∗
10
b
i
t
∗
10
b
y
t
e
=
10416
u
s
.
t = 1s/9600 * 10bit *10byte = 10416us.
t=1s/9600∗10bit∗10byte=10416us.
那么TIMER和INTERRUPT可以如下图设置。
将time interval设为10500us, 不让TIMER start after initialization。
将Time interval event连接到INTERRUPT的ISR上,这样表明当时间走完之后,要调用interrupt。
将INTERRUPT handler改名为TxTimeout_ISR
同样generate code.
DMA without callback
接下来可以写一个简单的程序。发完数据后才开始收。
新建tesRXTX.h
#ifndef TESRXTX_H_
#define TESRXTX_H_
extern UART_STATUS_t UART_0_RxStatus;
extern UART_STATUS_t UART_0_TxStatus;
void TXfirst(void);
void initTIMER(uint32_t time_interval);
void checkRX(void);
新建tesRXTX.c
#include "DAVE.h"
#include "tesRXTX.h"
UART_STATUS_t UART_0_RxStatus;
UART_STATUS_t UART_0_TxStatus;
//data initialization
uint8_t TX_DATA[10] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
uint8_t RX_DATA[255];
void TXfirst(void)
{
//transmit data
UART_0_TxStatus = UART_Transmit(&UART_0, TX_DATA, 10);
initTIMER(10500);//10500us,
}
void initTIMER(uint32_t time_interval)
{
//init timer
TIMER_Stop(&TIMER_0);
TIMER_Clear(&TIMER_0);
TIMER_SetTimeInterval(&TIMER_0, time_interval * 100); //factor is 100
TIMER_Start(&TIMER_0);
}
void checkRX(void)
{
if ((UART_0_RxStatus == UART_STATUS_SUCCESS) && (RX_DATA[0] == 0x55))
{
//transmit data
UART_Transmit(&UART_0, RX_DATA, 10);
RX_DATA[0] = 0;
UART_AbortReceive(&UART_0);
}
}
//interrupt ISR
void TxTimeout_ISR(void)
{
//receive 10 byte
UART_0_RxStatus = UART_Receive(&UART_0, RX_DATA, 10);
}
main.c中调用
#include "DAVE.h" //Declarations from DAVE Code Generation (includes SFR declaration)
#include "tesRXTX.h"
/**
* @brief main() - Application entry point
*
* <b>Details of function</b><br>
* This routine is the application entry point. It is invoked by the device startup code. It is responsible for
* invoking the APP initialization dispatcher routine - DAVE_Init() and hosting the place-holder for user application
* code.
*/
int main(void)
{
DAVE_STATUS_t status;
status = DAVE_Init(); /* Initialization of DAVE APPs */
if (status != DAVE_STATUS_SUCCESS)
{
/* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
XMC_DEBUG("DAVE APPs initialization failed\n");
while(1U)
{
}
}
/* Placeholder for user application code. The while loop below can be replaced with user application code. */
TXfirst();
while(1U)
{
checkRX();
}
}
然后将程序下载到开发板中, 打开串口助手,查看收发的数据。
可以看到上电之后串口助手就会收到XMC4200发的数组TX_DATA[10] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
,这时手动用串口助手发送55 11 22 33 44 55 66 77 88 99
给XMC4200,XMC4200就会将收到的数据发出来。
DMA with callback
接下来使用callback, 去掉TIMER和INTERRUPT.
tesRXTX.h
#ifndef TESRXTX_H_
#define TESRXTX_H_
extern UART_STATUS_t UART_0_RxStatus;
extern UART_STATUS_t UART_0_TxStatus;
void TXfirst(void);
#endif /* TESRXTX_H_ */
tesRXTX.c
#include "DAVE.h"
#include "tesRXTX.h"
UART_STATUS_t UART_0_RxStatus;
UART_STATUS_t UART_0_TxStatus;
//data initialization
uint8_t TX_DATA[10] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
uint8_t RX_DATA[255];
void TXfirst(void)
{
//transmit data
UART_0_TxStatus = UART_Transmit(&UART_0, TX_DATA, 10);
}
//End of transmit ISR
void TxCallback(void)
{
//receive 10 byte
UART_0_RxStatus = UART_Receive(&UART_0, RX_DATA, 5);
}
//End of RECEIVE ISR
void RxCallback(void)
{
//transmit data
UART_Transmit(&UART_0, RX_DATA, 5);
}
main.c 中只需要调用一次 TXfirst();
就可以了
#include "DAVE.h" //Declarations from DAVE Code Generation (includes SFR declaration)
#include "tesRXTX.h"
/**
* @brief main() - Application entry point
*
* <b>Details of function</b><br>
* This routine is the application entry point. It is invoked by the device startup code. It is responsible for
* invoking the APP initialization dispatcher routine - DAVE_Init() and hosting the place-holder for user application
* code.
*/
int main(void)
{
DAVE_STATUS_t status;
status = DAVE_Init(); /* Initialization of DAVE APPs */
if (status != DAVE_STATUS_SUCCESS)
{
/* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
XMC_DEBUG("DAVE APPs initialization failed\n");
while(1U)
{
}
}
/* Placeholder for user application code. The while loop below can be replaced with user application code. */
TXfirst();
}
测试结果以及问题
test setup
test result
但是这里有个问题,收发的数据在串口助手上显示是不一致的,但是在示波器上是一致的。
将示波器探头接在串口助手的A,B端,发现收发太快,导致第一个下降沿在串口助手端没有识别。
为了解决这个问题,我们可以在XMC发之前加入delay。
#include "DAVE.h"
#include "tesRXTX.h"
UART_STATUS_t UART_0_RxStatus;
UART_STATUS_t UART_0_TxStatus;
//data initialization
uint8_t TX_DATA[10] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
uint8_t RX_DATA[255];
void TXfirst(void)
{
//transmit data
UART_0_TxStatus = UART_Transmit(&UART_0, TX_DATA, 10);
}
//End of transmit ISR
void TxCallback(void)
{
//receive 10 byte
UART_0_RxStatus = UART_Receive(&UART_0, RX_DATA, 5);
}
//End of RECEIVE ISR
void RxCallback(void)
{
custom_delay(1000);
//transmit data
UART_Transmit(&UART_0, RX_DATA, 5);
}
void custom_delay(int32_t delay_cyc)
{
while (delay_cyc--)
asm("NOP");
}
这样收发就一致了
后来查看英飞凌手册发现,之所以callback函数被过早调用,是因为interrupt flag在收到最后一个databit后就被置1了, 而这时还有stop bit 没有发完。
如果可以做到使用PSR.TFF和PSR.RFF而不是PSR.RIF, 那么上述问题应该可以解决。