/** ****************************************************************************** * @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 "debug.h" /****************************************************** * Constants ******************************************************/ #define DMA_INTERRUPT_FLAGS ( DMA_IT_TC | DMA_IT_TE | DMA_IT_DME | DMA_IT_FE ) /****************************************************** * Enumerations ******************************************************/ /****************************************************** * Type Definitions ******************************************************/ /****************************************************** * Structures ******************************************************/ /****************************************************** * Variables Definitions ******************************************************/ /* UART alternate functions */ static const uint8_t uart_alternate_functions[NUMBER_OF_UART_PORTS] = { [0] = GPIO_AF_USART1, [1] = GPIO_AF_USART2, [2] = GPIO_AF_USART3, [3] = GPIO_AF_UART4, [4] = GPIO_AF_UART5, [5] = GPIO_AF_USART6, }; /* UART peripheral clock functions */ static const platform_peripheral_clock_function_t uart_peripheral_clock_functions[NUMBER_OF_UART_PORTS] = { [0] = RCC_APB2PeriphClockCmd, [1] = RCC_APB1PeriphClockCmd, [2] = RCC_APB1PeriphClockCmd, [3] = RCC_APB1PeriphClockCmd, [4] = RCC_APB1PeriphClockCmd, [5] = RCC_APB2PeriphClockCmd, }; /* UART peripheral clocks */ static const uint32_t uart_peripheral_clocks[NUMBER_OF_UART_PORTS] = { [0] = RCC_APB2Periph_USART1, [1] = RCC_APB1Periph_USART2, [2] = RCC_APB1Periph_USART3, [3] = RCC_APB1Periph_UART4, [4] = RCC_APB1Periph_UART5, [5] = RCC_APB2Periph_USART6, }; /* UART interrupt vectors */ #if defined (STM32F401xx) || defined (STM32F411xE) static const IRQn_Type uart_irq_vectors[NUMBER_OF_UART_PORTS] = { [0] = USART1_IRQn, [1] = USART2_IRQn, [2] = (IRQn_Type)0xFF, [3] = (IRQn_Type)0xFF, [4] = (IRQn_Type)0xFF, [5] = USART6_IRQn, }; #elif defined (STM32F412xG) static const IRQn_Type uart_irq_vectors[NUMBER_OF_UART_PORTS] = { [0] = USART1_IRQn, [1] = USART2_IRQn, [2] = USART3_IRQn, [3] = (IRQn_Type)0xFF, [4] = (IRQn_Type)0xFF, [5] = USART6_IRQn, }; #else static const IRQn_Type uart_irq_vectors[NUMBER_OF_UART_PORTS] = { [0] = USART1_IRQn, [1] = USART2_IRQn, [2] = USART3_IRQn, [3] = UART4_IRQn, [4] = UART5_IRQn, [5] = USART6_IRQn, }; #endif /****************************************************** * Static Function Declarations ******************************************************/ static OSStatus receive_bytes ( platform_uart_driver_t* driver, void* data, uint32_t size, uint32_t timeout ); static uint32_t get_dma_irq_status ( DMA_Stream_TypeDef* stream ); static void clear_dma_interrupts( DMA_Stream_TypeDef* stream, uint32_t flags ); /****************************************************** * 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 ) { DMA_InitTypeDef dma_init_structure; USART_InitTypeDef uart_init_structure; uint32_t uart_number; OSStatus err = kNoErr; platform_mcu_powersave_disable(); require_action_quiet( ( driver != NULL ) && ( peripheral != NULL ) && ( config != NULL ), exit, err = kParamErr); require_action_quiet( (optional_ring_buffer == NULL) || ((optional_ring_buffer->buffer != NULL ) && (optional_ring_buffer->size != 0)), exit, err = kParamErr); uart_number = platform_uart_get_port_number( peripheral->port ); driver->rx_size = 0; driver->tx_size = 0; driver->last_transmit_result = kNoErr; driver->last_receive_result = kNoErr; driver->peripheral = (platform_uart_t*)peripheral; if( driver->initialized == false ) { mico_rtos_init_semaphore( &driver->tx_complete, 1 ); mico_rtos_init_semaphore( &driver->rx_complete, 1 ); mico_rtos_init_mutex ( &driver->tx_mutex ); /* Configure TX and RX pin_mapping */ platform_gpio_set_alternate_function( peripheral->pin_tx->port, peripheral->pin_tx->pin_number, GPIO_OType_PP, GPIO_PuPd_UP, uart_alternate_functions[ uart_number ] ); platform_gpio_set_alternate_function( peripheral->pin_rx->port, peripheral->pin_rx->pin_number, GPIO_OType_PP, GPIO_PuPd_UP, uart_alternate_functions[ uart_number ] ); } if ( ( peripheral->pin_cts != NULL ) && ( config->flow_control == FLOW_CONTROL_CTS || config->flow_control == FLOW_CONTROL_CTS_RTS ) ) { platform_gpio_set_alternate_function( peripheral->pin_cts->port, peripheral->pin_cts->pin_number, GPIO_OType_PP, GPIO_PuPd_NOPULL, uart_alternate_functions[ uart_number ] ); } if ( ( peripheral->pin_rts != NULL ) && ( config->flow_control == FLOW_CONTROL_RTS || config->flow_control == FLOW_CONTROL_CTS_RTS ) ) { platform_gpio_set_alternate_function( peripheral->pin_rts->port, peripheral->pin_rts->pin_number, GPIO_OType_PP, GPIO_PuPd_NOPULL, uart_alternate_functions[ uart_number ] ); } /* Enable UART peripheral clock */ uart_peripheral_clock_functions[ uart_number ]( uart_peripheral_clocks[ uart_number ], ENABLE ); uart_init_structure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; uart_init_structure.USART_BaudRate = config->baud_rate; uart_init_structure.USART_WordLength = ( ( config->data_width == DATA_WIDTH_9BIT ) || ( ( config->data_width == DATA_WIDTH_8BIT ) && ( config->parity != NO_PARITY ) ) ) ? USART_WordLength_9b : USART_WordLength_8b; uart_init_structure.USART_StopBits = ( config->stop_bits == STOP_BITS_1 ) ? USART_StopBits_1 : USART_StopBits_2; switch ( config->parity ) { case NO_PARITY: uart_init_structure.USART_Parity = USART_Parity_No; break; case EVEN_PARITY: uart_init_structure.USART_Parity = USART_Parity_Even; break; case ODD_PARITY: uart_init_structure.USART_Parity = USART_Parity_Odd; break; default: err = kParamErr; goto exit; } switch ( config->flow_control ) { case FLOW_CONTROL_DISABLED: uart_init_structure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; break; case FLOW_CONTROL_CTS: uart_init_structure.USART_HardwareFlowControl = USART_HardwareFlowControl_CTS; break; case FLOW_CONTROL_RTS: uart_init_structure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS; driver->is_flow_control = true; break; case FLOW_CONTROL_CTS_RTS: uart_init_structure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS; driver->is_flow_control = true; break; default: err = kParamErr; goto exit; } /* Initialise USART peripheral */ USART_DeInit( peripheral->port ); USART_Init( peripheral->port, &uart_init_structure ); /************************************************************************** * Initialise STM32 DMA registers * Note: If DMA is used, USART interrupt isn't enabled. **************************************************************************/ /* Enable DMA peripheral clock */ if ( peripheral->tx_dma_config.controller == DMA1 ) { RCC->AHB1ENR |= RCC_AHB1Periph_DMA1; } else { RCC->AHB1ENR |= RCC_AHB1Periph_DMA2; } /* Fill init structure with common DMA settings */ dma_init_structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_init_structure.DMA_MemoryInc = DMA_MemoryInc_Enable; dma_init_structure.DMA_Priority = DMA_Priority_VeryHigh; dma_init_structure.DMA_FIFOMode = DMA_FIFOMode_Disable; dma_init_structure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; dma_init_structure.DMA_MemoryBurst = DMA_MemoryBurst_Single; dma_init_structure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; if ( config->data_width == DATA_WIDTH_9BIT ) { dma_init_structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dma_init_structure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; } else { dma_init_structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dma_init_structure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; } /* Initialise TX DMA */ DMA_DeInit( peripheral->tx_dma_config.stream ); dma_init_structure.DMA_Channel = peripheral->tx_dma_config.channel; dma_init_structure.DMA_PeripheralBaseAddr = (uint32_t) &peripheral->port->DR; dma_init_structure.DMA_Memory0BaseAddr = (uint32_t) 0; dma_init_structure.DMA_DIR = DMA_DIR_MemoryToPeripheral; dma_init_structure.DMA_BufferSize = 0xFFFF; // This parameter will be configured during communication dma_init_structure.DMA_Mode = DMA_Mode_Normal; DMA_Init( peripheral->tx_dma_config.stream, &dma_init_structure ); /* Initialise RX DMA */ DMA_DeInit( peripheral->rx_dma_config.stream ); dma_init_structure.DMA_Channel = peripheral->rx_dma_config.channel; dma_init_structure.DMA_PeripheralBaseAddr = (uint32_t) &peripheral->port->DR; dma_init_structure.DMA_Memory0BaseAddr = (uint32_t) 0; dma_init_structure.DMA_DIR = DMA_DIR_PeripheralToMemory; dma_init_structure.DMA_BufferSize = 0xFFFF; // This parameter will be configured during communication dma_init_structure.DMA_Mode = DMA_Mode_Normal; DMA_Init( peripheral->rx_dma_config.stream, &dma_init_structure ); /************************************************************************** * Initialise STM32 DMA interrupts **************************************************************************/ /* Configure TX DMA interrupt on Cortex-M3 */ NVIC_EnableIRQ( peripheral->tx_dma_config.irq_vector ); /* Enable TC (transfer complete) and TE (transfer error) interrupts on source */ clear_dma_interrupts( peripheral->tx_dma_config.stream, peripheral->tx_dma_config.complete_flags | peripheral->tx_dma_config.error_flags ); DMA_ITConfig( peripheral->tx_dma_config.stream, DMA_INTERRUPT_FLAGS, ENABLE ); /* Enable USART interrupt vector in Cortex-M3 */ NVIC_EnableIRQ( uart_irq_vectors[uart_number] ); USART_DMACmd( driver->peripheral->port, USART_DMAReq_Tx, DISABLE ); /* Enable USART */ USART_Cmd( peripheral->port, ENABLE ); /* Enable both transmit and receive */ peripheral->port->CR1 |= USART_CR1_TE; peripheral->port->CR1 |= USART_CR1_RE; /* Setup ring buffer */ if ( optional_ring_buffer != NULL ) { /* Note that the ring_buffer should've been initialised first */ driver->rx_buffer = optional_ring_buffer; driver->rx_size = 0; driver->peripheral->rx_dma_config.stream->CR |= DMA_SxCR_CIRC; // Enabled individual byte interrupts so progress can be updated USART_ClearITPendingBit( driver->peripheral->port, USART_IT_RXNE ); USART_ITConfig( driver->peripheral->port, USART_IT_RXNE, ENABLE ); } else { /* Not using ring buffer. Configure RX DMA interrupt on Cortex-M3 */ NVIC_EnableIRQ( peripheral->rx_dma_config.irq_vector ); /* Enable TC (transfer complete) and TE (transfer error) interrupts on source */ clear_dma_interrupts( peripheral->rx_dma_config.stream, peripheral->rx_dma_config.complete_flags | peripheral->rx_dma_config.error_flags ); DMA_ITConfig( peripheral->rx_dma_config.stream, DMA_INTERRUPT_FLAGS, ENABLE ); } driver->initialized = true; exit: platform_mcu_powersave_enable(); return err; } OSStatus platform_uart_deinit( platform_uart_driver_t* driver ) { uint8_t uart_number; OSStatus err = kNoErr; platform_mcu_powersave_disable(); require_action_quiet( ( driver != NULL ), exit, err = kParamErr); uart_number = platform_uart_get_port_number( driver->peripheral->port ); /* Disable USART */ USART_Cmd( driver->peripheral->port, DISABLE ); /* Deinitialise USART */ USART_DeInit( driver->peripheral->port ); /************************************************************************** * De-initialise STM32 DMA and interrupt **************************************************************************/ /* Deinitialise DMA streams */ DMA_DeInit( driver->peripheral->tx_dma_config.stream ); DMA_DeInit( driver->peripheral->rx_dma_config.stream ); /* Disable TC (transfer complete) interrupt at the source */ DMA_ITConfig( driver->peripheral->tx_dma_config.stream, DMA_INTERRUPT_FLAGS, DISABLE ); DMA_ITConfig( driver->peripheral->rx_dma_config.stream, DMA_INTERRUPT_FLAGS, DISABLE ); /* Disable transmit DMA interrupt at Cortex-M3 */ NVIC_DisableIRQ( driver->peripheral->tx_dma_config.irq_vector ); /************************************************************************** * De-initialise STM32 USART interrupt **************************************************************************/ USART_ITConfig( driver->peripheral->port, USART_IT_RXNE, DISABLE ); /* Disable UART interrupt vector on Cortex-M3 */ NVIC_DisableIRQ( driver->peripheral->rx_dma_config.irq_vector ); /* Disable registers clocks */ uart_peripheral_clock_functions[uart_number]( uart_peripheral_clocks[uart_number], DISABLE ); mico_rtos_deinit_semaphore( &driver->rx_complete ); mico_rtos_deinit_semaphore( &driver->tx_complete ); mico_rtos_deinit_mutex( &driver->tx_mutex ); driver->rx_size = 0; driver->tx_size = 0; driver->last_transmit_result = kNoErr; driver->last_receive_result = kNoErr; driver->initialized = false; 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; platform_mcu_powersave_disable(); mico_rtos_lock_mutex( &driver->tx_mutex ); require_action_quiet( ( driver != NULL ) && ( data_out != NULL ) && ( size != 0 ), exit, err = kParamErr); /* Clear interrupt status before enabling DMA otherwise error occurs immediately */ clear_dma_interrupts( driver->peripheral->tx_dma_config.stream, driver->peripheral->tx_dma_config.complete_flags | driver->peripheral->tx_dma_config.error_flags ); /* Init DMA parameters and variables */ driver->last_transmit_result = kGeneralErr; driver->tx_size = size; driver->peripheral->tx_dma_config.stream->CR &= ~(uint32_t) DMA_SxCR_CIRC; driver->peripheral->tx_dma_config.stream->NDTR = size; driver->peripheral->tx_dma_config.stream->M0AR = (uint32_t)data_out; USART_DMACmd( driver->peripheral->port, USART_DMAReq_Tx, ENABLE ); USART_ClearFlag( driver->peripheral->port, USART_FLAG_TC ); driver->peripheral->tx_dma_config.stream->CR |= DMA_SxCR_EN; /* Wait for transmission complete */ mico_rtos_get_semaphore( &driver->tx_complete, MICO_NEVER_TIMEOUT ); while ( ( driver->peripheral->port->SR & USART_SR_TC ) == 0 ) { } /* Disable DMA and clean up */ USART_DMACmd( driver->peripheral->port, USART_DMAReq_Tx, DISABLE ); driver->tx_size = 0; err = driver->last_transmit_result; exit: mico_rtos_unlock_mutex( &driver->tx_mutex ); 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; //platform_mcu_powersave_disable(); require_action_quiet( ( driver != NULL ) && ( data_in != NULL ) && ( expected_data_size != 0 ), exit, err = kParamErr); mico_rtos_get_semaphore( &driver->rx_complete, 0 ); if ( driver->rx_buffer != NULL) { if( ( driver->is_flow_control == true ) && ( driver->is_recv_over_flow == true ) ) { driver->is_recv_over_flow = false; USART_ITConfig( driver->peripheral->port, USART_IT_RXNE, ENABLE ); } while ( expected_data_size != 0 ) { uint32_t transfer_size = MIN( driver->rx_buffer->size / 2, expected_data_size ); /* Set rx_size and wait in rx_complete semaphore until data reaches rx_size or timeout occurs */ driver->last_receive_result = kNoErr; driver->rx_size = transfer_size; /* Check if ring buffer already contains the required amount of data. */ if ( transfer_size > ring_buffer_used_space( driver->rx_buffer ) ) { err = mico_rtos_get_semaphore( &driver->rx_complete, timeout_ms ); /* Reset rx_size to prevent semaphore being set while nothing waits for the data */ driver->rx_size = 0; if( err != kNoErr ) goto exit; }else { driver->rx_size = 0; } err = driver->last_receive_result; 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_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_buffer, bytes_available ); } while ( transfer_size != 0 ); } } else { err = receive_bytes( driver, data_in, expected_data_size, timeout_ms ); } exit: //platform_mcu_powersave_enable(); return err; } static OSStatus receive_bytes( platform_uart_driver_t* driver, void* data, uint32_t size, uint32_t timeout ) { OSStatus err = kNoErr; if ( driver->rx_buffer != NULL ) { driver->peripheral->rx_dma_config.stream->CR |= DMA_SxCR_CIRC; // Enabled individual byte interrupts so progress can be updated USART_ClearITPendingBit( driver->peripheral->port, USART_IT_RXNE ); USART_ITConfig( driver->peripheral->port, USART_IT_RXNE, ENABLE ); } else { driver->rx_size = size; driver->peripheral->rx_dma_config.stream->CR &= ~(uint32_t) DMA_SxCR_CIRC; } clear_dma_interrupts( driver->peripheral->rx_dma_config.stream, driver->peripheral->rx_dma_config.complete_flags | driver->peripheral->rx_dma_config.error_flags ); driver->peripheral->rx_dma_config.stream->NDTR = size; driver->peripheral->rx_dma_config.stream->M0AR = (uint32_t)data; driver->peripheral->rx_dma_config.stream->CR |= DMA_SxCR_EN; USART_DMACmd( driver->peripheral->port, USART_DMAReq_Rx, ENABLE ); if ( timeout > 0 ) { err = mico_rtos_get_semaphore( &driver->rx_complete, timeout ); } return err; } uint32_t platform_uart_get_length_in_buffer( platform_uart_driver_t* driver ) { return ring_buffer_used_space( driver->rx_buffer ); } static void clear_dma_interrupts( DMA_Stream_TypeDef* stream, uint32_t flags ) { if ( stream <= DMA1_Stream3 ) { DMA1->LIFCR |= flags; } else if ( stream <= DMA1_Stream7 ) { DMA1->HIFCR |= flags; } else if ( stream <= DMA2_Stream3 ) { DMA2->LIFCR |= flags; } else { DMA2->HIFCR |= flags; } } static uint32_t get_dma_irq_status( DMA_Stream_TypeDef* stream ) { if ( stream <= DMA1_Stream3 ) { return DMA1->LISR; } else if ( stream <= DMA1_Stream7 ) { return DMA1->HISR; } else if ( stream <= DMA2_Stream3 ) { return DMA2->LISR; } else { return DMA2->HISR; } } uint8_t platform_uart_get_port_number( USART_TypeDef* uart ) { if ( uart == USART1 ) { return 0; } else if ( uart == USART2 ) { return 1; } else if ( uart == USART3 ) { return 2; } else if ( uart == UART4 ) { return 3; } else if ( uart == UART5 ) { return 4; } else if ( uart == USART6 ) { return 5; } else { return 0xff; } } /****************************************************** * Interrupt Service Routines ******************************************************/ void platform_uart_irq( platform_uart_driver_t* driver ) { uint8_t recv_byte = 0; platform_uart_port_t* uart = (platform_uart_port_t*) driver->peripheral->port; /* Receive new data */ if ( USART_GetITStatus( uart, USART_IT_RXNE ) == SET ) { if ( ring_buffer_is_full( driver->rx_buffer ) == 0 ) { recv_byte = USART_ReceiveData( uart ); ring_buffer_write( driver->rx_buffer, &recv_byte, 1 ); if ( ( driver->rx_size > 0 ) && ( ring_buffer_used_space( driver->rx_buffer ) >= driver->rx_size ) ) { mico_rtos_set_semaphore( &driver->rx_complete ); driver->rx_size = 0; } } else { if( driver->is_flow_control == true ){ USART_ITConfig( driver->peripheral->port, USART_IT_RXNE, DISABLE ); driver->is_recv_over_flow = true; }else{ USART_ReceiveData( uart ); } } } } void platform_uart_tx_dma_irq( platform_uart_driver_t* driver ) { if ( ( get_dma_irq_status( driver->peripheral->tx_dma_config.stream ) & driver->peripheral->tx_dma_config.complete_flags ) != 0 ) { clear_dma_interrupts( driver->peripheral->tx_dma_config.stream, driver->peripheral->tx_dma_config.complete_flags ); driver->last_transmit_result = kNoErr; } if ( ( get_dma_irq_status( driver->peripheral->tx_dma_config.stream ) & driver->peripheral->tx_dma_config.error_flags ) != 0 ) { clear_dma_interrupts( driver->peripheral->tx_dma_config.stream, driver->peripheral->tx_dma_config.error_flags ); driver->last_transmit_result = kGeneralErr; } if ( driver->tx_size > 0 ) { /* Set semaphore regardless of result to prevent waiting thread from locking up */ mico_rtos_set_semaphore( &driver->tx_complete ); } } void platform_uart_rx_dma_irq( platform_uart_driver_t* driver ) { if ( ( get_dma_irq_status( driver->peripheral->rx_dma_config.stream ) & driver->peripheral->rx_dma_config.complete_flags ) != 0 ) { clear_dma_interrupts( driver->peripheral->rx_dma_config.stream, driver->peripheral->rx_dma_config.complete_flags ); driver->last_receive_result = kNoErr; } if ( ( get_dma_irq_status( driver->peripheral->rx_dma_config.stream ) & driver->peripheral->rx_dma_config.error_flags ) != 0 ) { clear_dma_interrupts( driver->peripheral->rx_dma_config.stream, driver->peripheral->rx_dma_config.error_flags ); driver->last_receive_result = kGeneralErr; } if ( driver->rx_size > 0 ) { /* Set semaphore regardless of result to prevent waiting thread from locking up */ mico_rtos_set_semaphore( &driver->rx_complete ); } }