mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-17 23:48:13 +08:00
421 lines
14 KiB
C
421 lines
14 KiB
C
/**
|
|
******************************************************************************
|
|
* @file platform_uart.c
|
|
* @author William Xu
|
|
* @version V1.0.0
|
|
* @date 05-May-2014
|
|
* @brief This file provide UART driver functions.
|
|
******************************************************************************
|
|
* UNPUBLISHED PROPRIETARY SOURCE CODE
|
|
* Copyright (c) 2016 MXCHIP Inc.
|
|
*
|
|
* The contents of this file may not be disclosed to third parties, copied or
|
|
* duplicated in any form, in whole or in part, without the prior written
|
|
* permission of MXCHIP Corporation.
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#include "platform_peripheral.h"
|
|
#include "platform_logging.h"
|
|
|
|
/******************************************************
|
|
* Constants
|
|
******************************************************/
|
|
|
|
#define DMA_INTERRUPT_FLAGS ( DMA_IT_TC | DMA_IT_TE | DMA_IT_DME | DMA_IT_FE )
|
|
|
|
/******************************************************
|
|
* Enumerations
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Type Definitions
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Structures
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Variables Definitions
|
|
******************************************************/
|
|
|
|
static IRQn_Type platform_flexcom_irq_numbers[] =
|
|
{
|
|
[0] = FLEXCOM0_IRQn,
|
|
[1] = FLEXCOM1_IRQn,
|
|
[2] = FLEXCOM2_IRQn,
|
|
[3] = FLEXCOM3_IRQn,
|
|
[4] = FLEXCOM4_IRQn,
|
|
[5] = FLEXCOM5_IRQn,
|
|
[6] = FLEXCOM6_IRQn,
|
|
[7] = FLEXCOM7_IRQn,
|
|
};
|
|
|
|
/******************************************************
|
|
* Static Function Declarations
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Function Definitions
|
|
******************************************************/
|
|
|
|
OSStatus platform_uart_init( platform_uart_driver_t* driver, const platform_uart_t* peripheral, const platform_uart_config_t* config, ring_buffer_t* optional_ring_buffer )
|
|
{
|
|
pdc_packet_t pdc_uart_packet, pdc_uart_tx_packet;
|
|
OSStatus err = kNoErr;
|
|
sam_usart_opt_t settings;
|
|
bool hardware_shaking = false;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( ( driver != NULL ) && ( peripheral != NULL ) && ( config != NULL ), exit, err = kParamErr);
|
|
require_action_quiet( (optional_ring_buffer->buffer != NULL ) && (optional_ring_buffer->size != 0), exit, err = kUnsupportedErr);
|
|
|
|
driver->rx_size = 0;
|
|
driver->tx_size = 0;
|
|
driver->rx_ring_buffer = optional_ring_buffer;
|
|
driver->last_transmit_result = kNoErr;
|
|
driver->last_receive_result = kNoErr;
|
|
driver->peripheral = (platform_uart_t*)peripheral;
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_init_semaphore( &driver->tx_complete, 1 );
|
|
mico_rtos_init_semaphore( &driver->rx_complete, 1 );
|
|
mico_rtos_init_semaphore( &driver->sem_wakeup, 1 );
|
|
mico_rtos_init_mutex ( &driver->tx_mutex );
|
|
#else
|
|
driver->tx_complete = false;
|
|
driver->rx_complete = false;
|
|
#endif
|
|
|
|
/* Set Tx and Rx pin mode to UART peripheral */
|
|
platform_gpio_peripheral_pin_init( peripheral->tx_pin, ( peripheral->tx_pin_mux_mode | IOPORT_MODE_PULLUP ) );
|
|
platform_gpio_peripheral_pin_init( peripheral->rx_pin, ( peripheral->rx_pin_mux_mode | IOPORT_MODE_PULLUP ) );
|
|
|
|
/* Init CTS and RTS pins (if available) */
|
|
if ( peripheral->cts_pin != NULL && (config->flow_control == FLOW_CONTROL_CTS || config->flow_control == FLOW_CONTROL_CTS_RTS) )
|
|
{
|
|
hardware_shaking = true;
|
|
platform_gpio_peripheral_pin_init( peripheral->cts_pin, ( peripheral->cts_pin_mux_mode | IOPORT_MODE_PULLUP ) );
|
|
}
|
|
|
|
if ( peripheral->rts_pin != NULL && (config->flow_control == FLOW_CONTROL_CTS || config->flow_control == FLOW_CONTROL_CTS_RTS) )
|
|
{
|
|
hardware_shaking = true;
|
|
platform_gpio_peripheral_pin_init( peripheral->rts_pin, ( peripheral->rts_pin_mux_mode | IOPORT_MODE_PULLUP ) );
|
|
}
|
|
|
|
/* Enable the clock. */
|
|
if( pmc_is_periph_clk_enabled( peripheral->peripheral_id ) == 0 ){
|
|
flexcom_enable( peripheral->flexcom_base );
|
|
}
|
|
flexcom_set_opmode( peripheral->flexcom_base, FLEXCOM_USART );
|
|
|
|
/* Enable the receiver and transmitter. */
|
|
usart_reset_tx( peripheral->port );
|
|
usart_reset_rx( peripheral->port );
|
|
|
|
/* Disable all the interrupts. */
|
|
usart_disable_interrupt( peripheral->port, 0xffffffff );
|
|
|
|
switch ( config->parity ) {
|
|
case NO_PARITY:
|
|
settings.parity_type = US_MR_PAR_NO;
|
|
break;
|
|
case EVEN_PARITY:
|
|
settings.parity_type = US_MR_PAR_EVEN;
|
|
break;
|
|
case ODD_PARITY:
|
|
settings.parity_type = US_MR_PAR_ODD;
|
|
break;
|
|
default:
|
|
err = kParamErr;
|
|
goto exit;
|
|
}
|
|
switch ( config->data_width) {
|
|
case DATA_WIDTH_5BIT:
|
|
settings.char_length = US_MR_CHRL_5_BIT;
|
|
break;
|
|
case DATA_WIDTH_6BIT:
|
|
settings.char_length = US_MR_CHRL_6_BIT;
|
|
break;
|
|
case DATA_WIDTH_7BIT:
|
|
settings.char_length = US_MR_CHRL_7_BIT;
|
|
break;
|
|
case DATA_WIDTH_8BIT:
|
|
settings.char_length = US_MR_CHRL_8_BIT;
|
|
break;
|
|
case DATA_WIDTH_9BIT:
|
|
settings.char_length = US_MR_MODE9;
|
|
break;
|
|
default:
|
|
err = kParamErr;
|
|
goto exit;
|
|
}
|
|
settings.baudrate = config->baud_rate;
|
|
settings.stop_bits = ( config->stop_bits == STOP_BITS_1 ) ? US_MR_NBSTOP_1_BIT : US_MR_NBSTOP_2_BIT;
|
|
settings.channel_mode= US_MR_CHMODE_NORMAL;
|
|
|
|
/* Configure USART in serial mode. */
|
|
if (!hardware_shaking) {
|
|
usart_init_rs232( peripheral->port, &settings, sysclk_get_peripheral_hz());
|
|
} else {
|
|
usart_init_hw_handshaking( peripheral->port, &settings, sysclk_get_peripheral_hz());
|
|
}
|
|
|
|
/* Enable uart interrupt */
|
|
NVIC_SetPriority( platform_flexcom_irq_numbers[peripheral->uart_id], 0x06 );
|
|
NVIC_EnableIRQ( platform_flexcom_irq_numbers[peripheral->uart_id] );
|
|
|
|
/* Enable PDC transmit */
|
|
pdc_enable_transfer( usart_get_pdc_base( peripheral->port ), PERIPH_PTCR_TXTEN | PERIPH_PTCR_RXTEN );
|
|
pdc_disable_transfer( usart_get_pdc_base( driver->peripheral->port ), PERIPH_PTCR_TXTDIS );
|
|
|
|
pdc_uart_packet.ul_addr = (uint32_t)driver->rx_ring_buffer->buffer;
|
|
pdc_uart_packet.ul_size = (uint32_t)driver->rx_ring_buffer->size;
|
|
pdc_rx_init( usart_get_pdc_base( peripheral->port ), &pdc_uart_packet, &pdc_uart_packet );
|
|
|
|
pdc_uart_tx_packet.ul_addr = (uint32_t)0;
|
|
pdc_uart_tx_packet.ul_size = (uint32_t)1;
|
|
|
|
pdc_tx_init( usart_get_pdc_base( driver->peripheral->port ), &pdc_uart_tx_packet, NULL );
|
|
|
|
usart_enable_interrupt( peripheral->port, US_IER_ENDRX | US_IER_RXBUFF | US_IER_RXRDY | US_IER_ENDTX );
|
|
|
|
/* Enable the receiver and transmitter. */
|
|
usart_enable_tx( peripheral->port );
|
|
usart_enable_rx( peripheral->port );
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable( );
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_uart_deinit( platform_uart_driver_t* driver )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
require_action_quiet( ( driver != NULL ), exit, err = kParamErr);
|
|
|
|
usart_disable_interrupt( driver->peripheral->port, 0xffffffff );
|
|
|
|
NVIC_DisableIRQ( platform_flexcom_irq_numbers[driver->peripheral->uart_id] );
|
|
|
|
pdc_disable_transfer( usart_get_pdc_base( driver->peripheral->port ), PERIPH_PTCR_TXTDIS | PERIPH_PTCR_RXTDIS );
|
|
|
|
usart_disable_tx( driver->peripheral->port );
|
|
usart_disable_rx( driver->peripheral->port );
|
|
|
|
if( pmc_is_periph_clk_enabled( driver->peripheral->peripheral_id ) == 1 ){
|
|
flexcom_disable( driver->peripheral->flexcom_base );
|
|
}
|
|
|
|
platform_gpio_deinit( driver->peripheral->tx_pin );
|
|
platform_gpio_deinit( driver->peripheral->rx_pin );
|
|
|
|
if ( driver->peripheral->cts_pin != NULL )
|
|
{
|
|
platform_gpio_deinit( driver->peripheral->cts_pin );
|
|
}
|
|
|
|
if ( driver->peripheral->rts_pin != NULL )
|
|
{
|
|
platform_gpio_deinit( driver->peripheral->rts_pin );
|
|
}
|
|
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_deinit_semaphore(&driver->rx_complete);
|
|
mico_rtos_deinit_semaphore(&driver->tx_complete);
|
|
#endif
|
|
|
|
driver->peripheral = NULL;
|
|
memset( driver, 0, sizeof(platform_uart_driver_t) );
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_uart_transmit_bytes( platform_uart_driver_t* driver, const uint8_t* data_out, uint32_t size )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
pdc_packet_t pdc_uart_packet;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_lock_mutex( &driver->tx_mutex );
|
|
#endif
|
|
|
|
require_action_quiet( ( driver != NULL ) && ( data_out != NULL ) && ( size != 0 ), exit, err = kParamErr);
|
|
|
|
/* reset DMA transmission result. the result is assigned in interrupt handler */
|
|
driver->last_transmit_result = kGeneralErr;
|
|
driver->tx_size = size;
|
|
|
|
pdc_uart_packet.ul_addr = (uint32_t) data_out;
|
|
pdc_uart_packet.ul_size = size;
|
|
pdc_tx_init( usart_get_pdc_base( driver->peripheral->port ), &pdc_uart_packet, NULL);
|
|
|
|
/* Enable Tx DMA transmission */
|
|
pdc_enable_transfer( usart_get_pdc_base( driver->peripheral->port ), PERIPH_PTCR_TXTEN );
|
|
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_get_semaphore( &driver->tx_complete, MICO_NEVER_TIMEOUT );
|
|
#else
|
|
while( driver->tx_complete == false);
|
|
driver->tx_complete = false;
|
|
#endif
|
|
|
|
exit:
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_unlock_mutex( &driver->tx_mutex );
|
|
#endif
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_uart_receive_bytes( platform_uart_driver_t* driver, uint8_t* data_in, uint32_t expected_data_size, uint32_t timeout_ms )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
uint32_t transfer_size;
|
|
|
|
//platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( ( driver != NULL ) && ( data_in != NULL ) && ( expected_data_size != 0 ), exit, err = kParamErr);
|
|
require_action_quiet( driver->rx_ring_buffer != NULL , exit, err = kUnsupportedErr);
|
|
|
|
while ( expected_data_size != 0 )
|
|
{
|
|
transfer_size = MIN(driver->rx_ring_buffer->size / 2, expected_data_size);
|
|
|
|
/* Check if ring buffer already contains the required amount of data. */
|
|
if ( transfer_size > ring_buffer_used_space( driver->rx_ring_buffer ) )
|
|
{
|
|
/* Set rx_size and wait in rx_complete semaphore until data reaches rx_size or timeout occurs */
|
|
driver->rx_size = transfer_size;
|
|
|
|
#ifndef NO_MICO_RTOS
|
|
if ( mico_rtos_get_semaphore( &driver->rx_complete, timeout_ms ) != kNoErr )
|
|
{
|
|
driver->rx_size = 0;
|
|
err = kTimeoutErr;
|
|
goto exit;
|
|
}
|
|
|
|
/* Reset rx_size to prevent semaphore being set while nothing waits for the data */
|
|
driver->rx_size = 0;
|
|
#else
|
|
driver->rx_complete = false;
|
|
int delay_start = mico_rtos_get_time();
|
|
while(driver->rx_complete == false){
|
|
if(mico_rtos_get_time() >= delay_start + timeout_ms && timeout_ms != MICO_NEVER_TIMEOUT){
|
|
driver->rx_size = 0;
|
|
err = kTimeoutErr;
|
|
goto exit;
|
|
}
|
|
}
|
|
driver->rx_size = 0;
|
|
#endif
|
|
}
|
|
expected_data_size -= transfer_size;
|
|
|
|
// Grab data from the buffer
|
|
do
|
|
{
|
|
uint8_t* available_data;
|
|
uint32_t bytes_available;
|
|
|
|
ring_buffer_get_data( driver->rx_ring_buffer, &available_data, &bytes_available );
|
|
bytes_available = MIN( bytes_available, transfer_size );
|
|
memcpy( data_in, available_data, bytes_available );
|
|
transfer_size -= bytes_available;
|
|
data_in = ( (uint8_t*)data_in + bytes_available );
|
|
ring_buffer_consume( driver->rx_ring_buffer, bytes_available );
|
|
}
|
|
while ( transfer_size != 0 );
|
|
}
|
|
|
|
require_action( expected_data_size == 0, exit, err = kReadErr);
|
|
|
|
exit:
|
|
//platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
uint32_t platform_uart_get_length_in_buffer( platform_uart_driver_t* driver )
|
|
{
|
|
return ring_buffer_used_space( driver->rx_ring_buffer );
|
|
}
|
|
|
|
/******************************************************
|
|
* Interrupt Service Routines
|
|
******************************************************/
|
|
|
|
void platform_uart_irq( platform_uart_driver_t* driver )
|
|
{
|
|
uint32_t status = usart_get_status( driver->peripheral->port );
|
|
uint32_t mask = usart_get_interrupt_mask( driver->peripheral->port );
|
|
Pdc* pdc_register = usart_get_pdc_base( driver->peripheral->port );
|
|
|
|
/* ENDTX flag is set when Tx DMA transfer is done */
|
|
if ( ( mask & US_IMR_ENDTX ) && ( status & US_CSR_ENDTX ) )
|
|
{
|
|
pdc_packet_t dma_packet;
|
|
|
|
/* ENDTX is cleared when TCR or TNCR is set to a non-zero value, which effectively
|
|
* starts another Tx DMA transaction. To work around this, disable Tx before
|
|
* performing a dummy Tx init.
|
|
*/
|
|
pdc_disable_transfer( usart_get_pdc_base( driver->peripheral->port ), PERIPH_PTCR_TXTDIS );
|
|
|
|
dma_packet.ul_addr = (uint32_t)0;
|
|
dma_packet.ul_size = (uint32_t)1;
|
|
|
|
pdc_tx_init( usart_get_pdc_base( driver->peripheral->port ), &dma_packet, NULL );
|
|
|
|
/* Notifies waiting thread that Tx DMA transfer is complete */
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_set_semaphore( &driver->tx_complete );
|
|
#else
|
|
driver->tx_complete = true;
|
|
#endif
|
|
}
|
|
|
|
/* ENDRX flag is set when RCR is 0. RNPR and RNCR values are then copied into
|
|
* RPR and RCR, respectively, while the RX tranfer continues. We now need to
|
|
* prepare RNPR and RNCR for the next iteration.
|
|
*/
|
|
if ( ( mask & US_IMR_ENDRX ) && ( status & US_CSR_ENDRX ) )
|
|
{
|
|
pdc_register->PERIPH_RNPR = (uint32_t)driver->rx_ring_buffer->buffer;
|
|
pdc_register->PERIPH_RNCR = (uint32_t)driver->rx_ring_buffer->size;
|
|
}
|
|
|
|
/* RXRDY interrupt is triggered and flag is set when a new character has been
|
|
* received but not yet read from the US_RHR. When this interrupt executes,
|
|
* the DMA engine already read the character out from the US_RHR and RXRDY flag
|
|
* is no longer asserted. The code below updates the ring buffer parameters
|
|
* to keep them current
|
|
*/
|
|
if ( ( mask & US_IMR_RXRDY ) )
|
|
{
|
|
driver->rx_ring_buffer->tail = driver->rx_ring_buffer->size - pdc_register->PERIPH_RCR;
|
|
|
|
// Notify thread if sufficient data are available
|
|
if ( ( driver->rx_size > 0 ) && ( ring_buffer_used_space( driver->rx_ring_buffer ) >= driver->rx_size ) )
|
|
{
|
|
#ifndef NO_MICO_RTOS
|
|
mico_rtos_set_semaphore( &driver->rx_complete );
|
|
#else
|
|
driver->rx_complete = true;
|
|
#endif
|
|
driver->rx_size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|