SiLabs Pearl: LDMA support for serial_api

LDMA support for serial HAL. Adds callback support for emlib LDMA
code so that most of the old serial code can be reused.

Note: Serial shows some signs of life on the bus, but DMA mode
is completely untested.
pull/1501/head
Mikko Polojarvi 2015-10-15 13:33:05 +03:00 committed by Steven Cooreman
parent e3e385e16d
commit 7be6799638
5 changed files with 120 additions and 8 deletions

View File

@ -30,6 +30,7 @@
#include <stdint.h>
#include "dma_api_HAL.h"
#include "em_device.h"
#include "em_cmu.h"
#ifdef DMA_PRESENT

View File

@ -33,7 +33,15 @@
#include <stdint.h>
#include "dma_api.h"
#include "em_device.h"
#ifdef DMA_PRESENT
#include "em_dma.h"
#endif
#ifdef LDMA_PRESENT
#include "em_ldma.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -65,6 +73,8 @@ typedef struct {
int dmaChannel;
#ifndef LDMA_PRESENT
DMA_CB_TypeDef dmaCallback;
#else
LDMA_Callback_t dmaCallback;
#endif
} DMA_OPTIONS_t;

View File

@ -434,6 +434,13 @@ typedef struct
uint8_t ldmaLoopCnt; /**< Counter for looped transfers. */
} LDMA_TransferCfg_t;
typedef void (*LDMA_CBFunc_t)(unsigned int channel, bool primary, void *user);
typedef struct
{
LDMA_CBFunc_t cbFunc;
void *userPtr;
} LDMA_Callback_t;
/*******************************************************************************
************************** STRUCT INITIALIZERS ****************************
@ -1213,7 +1220,9 @@ void LDMA_DeInit( void );
void LDMA_Init( LDMA_Init_t *init );
void LDMA_StartTransfer( int ch,
LDMA_TransferCfg_t *transfer,
LDMA_Descriptor_t *descriptor );
LDMA_Descriptor_t *descriptor,
LDMA_CBFunc_t callback,
void *userData );
void LDMA_StopTransfer( int ch );
bool LDMA_TransferDone( int ch );
uint32_t LDMA_TransferRemainingCount( int ch );

View File

@ -202,7 +202,13 @@
@endverbatim
* @{ *************************************************************************/
#if defined( LDMA_IRQ_HANDLER_TEMPLATE )
typedef struct {
LDMA_CBFunc_t callback;
void *userdata;
} LDMA_InternCallback_t;
static LDMA_InternCallback_t ldmaCallback[DMA_CHAN_COUNT];
/***************************************************************************//**
* @brief
* Template for an LDMA IRQ handler.
@ -234,10 +240,13 @@ void LDMA_IRQHandler( void )
LDMA->IFC = chmask;
/* Do more stuff here, execute callbacks etc. */
if ( ldmaCallback[chnum].callback )
{
ldmaCallback[chnum].callback(chnum, false, ldmaCallback[chnum].userdata);
}
}
}
}
#endif
/***************************************************************************//**
* @brief
@ -308,7 +317,9 @@ void LDMA_Init( LDMA_Init_t *init )
******************************************************************************/
void LDMA_StartTransfer( int ch,
LDMA_TransferCfg_t *transfer,
LDMA_Descriptor_t *descriptor )
LDMA_Descriptor_t *descriptor,
LDMA_CBFunc_t cbFunc,
void *userData )
{
uint32_t tmp;
uint32_t chMask = 1 << ch;
@ -335,6 +346,9 @@ void LDMA_StartTransfer( int ch,
EFM_ASSERT( !( ( transfer->ldmaLoopCnt << _LDMA_CH_LOOP_LOOPCNT_SHIFT )
& ~_LDMA_CH_LOOP_LOOPCNT_MASK ) );
ldmaCallback[ch].callback = cbFunc;
ldmaCallback[ch].userdata = userData;
LDMA->CH[ ch ].REQSEL = transfer->ldmaReqSel;
LDMA->CH[ ch ].LOOP =

View File

@ -85,6 +85,7 @@ void serial_enable_pins(serial_t *obj, uint8_t enable);
IRQn_Type serial_get_rx_irq_index(serial_t *obj);
IRQn_Type serial_get_tx_irq_index(serial_t *obj);
CMU_Clock_TypeDef serial_get_clock(serial_t *obj);
static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx);
/* ISRs for RX and TX events */
#ifdef UART0
@ -964,6 +965,8 @@ static void serial_dmaTransferComplete(unsigned int channel, bool primary, void
}
}
#ifndef LDMA_PRESENT
/******************************************
* static void serial_setupDmaChannel(serial_t *obj, bool tx_nrx)
*
@ -1065,10 +1068,10 @@ static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx)
DMA_CfgChannel(obj->serial.dmaOptionsRX.dmaChannel, &channelConfig);
}
}
#endif /* LDMA_PRESENT */
/******************************************
* static void serial_dmaTrySetState(DMA_OPTIONS_t *obj, DMAUsage requestedState)
*
@ -1121,6 +1124,8 @@ static void serial_dmaTrySetState(DMA_OPTIONS_t *obj, DMAUsage requestedState, s
}
}
#ifndef LDMA_PRESENT
static void serial_dmaActivate(serial_t *obj, void* cb, void* buffer, int length, bool tx_nrx)
{
DMA_CfgDescr_TypeDef channelConfig;
@ -1183,6 +1188,55 @@ static void serial_dmaActivate(serial_t *obj, void* cb, void* buffer, int length
}
}
#endif
#ifdef LDMA_PRESENT
static void serial_dmaSetupChannel(serial_t *obj, bool tx_nrx)
{
}
static void serial_dmaActivate(serial_t *obj, void* cb, void* buffer, int length, bool tx_nrx)
{
uint32_t dma_periph;
obj->serial.dmaOptionsRX.dmaCallback.userPtr = cb;
if( tx_nrx ) {
switch((uint32_t)(obj->serial.periph.uart)) {
case USART_0: dma_periph = DMAREQ_USART0_TXBL; break;
case USART_1: dma_periph = DMAREQ_USART1_TXBL; break;
case LEUART_0: dma_periph = DMAREQ_LEUART0_TXBL; break;
default:
EFM_ASSERT(0);
while(1);
break;
}
LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph);
LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_M2P_BYTE(buffer, dma_periph, length);
LDMA_StartTransfer(obj->serial.dmaOptionsTX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete, cb);
} else {
switch((uint32_t)(obj->serial.periph.uart)) {
case USART_0: dma_periph = DMAREQ_USART0_RXDATAV; break;
case USART_1: dma_periph = DMAREQ_USART1_RXDATAV; break;
case LEUART_0: dma_periph = DMAREQ_LEUART0_RXDATAV; break;
default:
EFM_ASSERT(0);
while(1);
break;
}
LDMA_TransferCfg_t xferConf = LDMA_TRANSFER_CFG_PERIPHERAL(dma_periph);
LDMA_Descriptor_t desc = LDMA_DESCRIPTOR_SINGLE_P2M_BYTE(dma_periph, buffer, length);
LDMA_StartTransfer(obj->serial.dmaOptionsRX.dmaChannel, &xferConf, &desc, serial_dmaTransferComplete, cb);
}
}
#endif /* LDMA_PRESENT */
/************************************************************************************
* ASYNCHRONOUS HAL *
************************************************************************************/
@ -1463,7 +1517,11 @@ uint8_t serial_tx_active(serial_t *obj)
return 1;
case DMA_USAGE_ALLOCATED:
/* Check whether the allocated DMA channel is active by checking the DMA transfer */
return(DMA_ChannelEnabled(obj->serial.dmaOptionsTX.dmaChannel));
#ifndef LDMA_PRESENT
return DMA_ChannelEnabled(obj->serial.dmaOptionsTX.dmaChannel);
#else
return !LDMA_TransferDone(obj->serial.dmaOptionsTX.dmaChannel);
#endif
default:
/* Check whether interrupt for serial TX is enabled */
if(LEUART_REF_VALID(obj->serial.periph.leuart)) {
@ -1487,7 +1545,11 @@ uint8_t serial_rx_active(serial_t *obj)
return 1;
case DMA_USAGE_ALLOCATED:
/* Check whether the allocated DMA channel is active by checking the DMA transfer */
return(DMA_ChannelEnabled(obj->serial.dmaOptionsRX.dmaChannel));
#ifndef LDMA_PRESENT
return DMA_ChannelEnabled(obj->serial.dmaOptionsRX.dmaChannel);
#else
return !LDMA_TransferDone(obj->serial.dmaOptionsTX.dmaChannel);
#endif
default:
/* Check whether interrupt for serial TX is enabled */
if(LEUART_REF_VALID(obj->serial.periph.leuart)) {
@ -1764,11 +1826,19 @@ void serial_tx_abort_asynch(serial_t *obj)
switch(obj->serial.dmaOptionsTX.dmaUsageState) {
case DMA_USAGE_ALLOCATED:
/* stop DMA transfer */
#ifndef LDMA_PRESENT
DMA_ChannelEnable(obj->serial.dmaOptionsTX.dmaChannel, false);
#else
LDMA_StopTransfer(obj->serial.dmaOptionsTX.dmaChannel);
#endif
break;
case DMA_USAGE_TEMPORARY_ALLOCATED:
/* stop DMA transfer and release channel */
#ifndef LDMA_PRESENT
DMA_ChannelEnable(obj->serial.dmaOptionsTX.dmaChannel, false);
#else
LDMA_StopTransfer(obj->serial.dmaOptionsTX.dmaChannel);
#endif
dma_channel_free(obj->serial.dmaOptionsTX.dmaChannel);
obj->serial.dmaOptionsTX.dmaChannel = -1;
obj->serial.dmaOptionsTX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;
@ -1811,11 +1881,19 @@ void serial_rx_abort_asynch(serial_t *obj)
switch(obj->serial.dmaOptionsRX.dmaUsageState) {
case DMA_USAGE_ALLOCATED:
/* stop DMA transfer */
#ifndef LDMA_PRESENT
DMA_ChannelEnable(obj->serial.dmaOptionsRX.dmaChannel, false);
#else
LDMA_StopTransfer(obj->serial.dmaOptionsRX.dmaChannel);
#endif
break;
case DMA_USAGE_TEMPORARY_ALLOCATED:
/* stop DMA transfer and release channel */
#ifndef LDMA_PRESENT
DMA_ChannelEnable(obj->serial.dmaOptionsRX.dmaChannel, false);
#else
LDMA_StopTransfer(obj->serial.dmaOptionsRX.dmaChannel);
#endif
dma_channel_free(obj->serial.dmaOptionsRX.dmaChannel);
obj->serial.dmaOptionsRX.dmaChannel = -1;
obj->serial.dmaOptionsRX.dmaUsageState = DMA_USAGE_OPPORTUNISTIC;