/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2015, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* 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 disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \addtogroup usart_module Working with USART_LIN | |
* \section Purpose | |
* The USART_LIN driver provides the interface to configure and use the USART peripheral in LIN mode.\n | |
* | |
* | |
* For more accurate information, please look at the USART LIN section of the | |
* Datasheet. | |
* | |
* Related files :\n | |
* \ref usart_lin.c\n | |
* \ref usart_lin.h\n | |
*/ | |
/** | |
* \file | |
* | |
* Implementation of USART in LIN mode (Universal Synchronous Asynchronous Receiver Transmitter) | |
* controller. | |
* | |
*/ | |
/*----------------------------------------------------------------------------- | |
* Headers | |
*---------------------------------------------------------------------------*/ | |
#include "chip.h" | |
#include "compiler.h" | |
#include "peripherals/pmc.h" | |
#include "peripherals/usart.h" | |
#include "peripherals/usart_lin.h" | |
#include "trace.h" | |
#include "io.h" | |
#include <assert.h> | |
#include <string.h> | |
/*----------------------------------------------------------------------------- | |
* | |
*---------------------------------------------------------------------------*/ | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
void usart_lin_reset_status_bits(Usart *usart) | |
{ | |
usart->US_CR = US_CR_RSTSTA; | |
} | |
uint32_t usart_lin_get_status_register(Usart *usart) | |
{ | |
return usart->US_CSR; | |
} | |
void usart_lin_set_mode_master_or_slave (Usart* usart, uint32_t mode_master_or_slave) | |
{ | |
/* Set LIN master or slave mode. */ | |
usart->US_MR = (usart->US_MR & ~US_MR_USART_MODE_Msk) | US_MR_USART_MODE(mode_master_or_slave); | |
} | |
/** | |
* \brief Abort the current LIN transmission. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_abort_tx(Usart *usart) | |
{ | |
usart->US_CR = US_CR_LINABT; | |
} | |
/** | |
* \brief Send a wakeup signal on the LIN bus. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_send_wakeup_signal(Usart *usart) | |
{ | |
usart->US_CR = US_CR_LINWKUP; | |
} | |
/** | |
* \brief Configure the LIN node action, which should be one of PUBLISH, | |
* SUBSCRIBE or IGNORE. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param action 0 for PUBLISH, 1 for SUBSCRIBE, 2 for IGNORE. | |
*/ | |
void usart_lin_set_node_action(Usart *usart, uint8_t action) | |
{ | |
usart->US_LINMR = (usart->US_LINMR & ~US_LINMR_NACT_Msk) | (action << US_LINMR_NACT_Pos); | |
} | |
/** | |
* \brief Disable the parity check during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_disable_parity(Usart *usart) | |
{ | |
usart->US_LINMR |= US_LINMR_PARDIS; | |
} | |
/** | |
* \brief Enable the parity check during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_enable_parity(Usart *usart) | |
{ | |
usart->US_LINMR &= ~US_LINMR_PARDIS; | |
} | |
/** | |
* \brief Disable the checksum during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_disable_checksum(Usart *usart) | |
{ | |
usart->US_LINMR |= US_LINMR_CHKDIS; | |
} | |
/** | |
* \brief Enable the checksum during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_enable_checksum(Usart *usart) | |
{ | |
usart->US_LINMR &= ~US_LINMR_CHKDIS; | |
} | |
/** | |
* \brief Configure the checksum type during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param type 0 for LIN 2.0 Enhanced checksum or 1 for LIN 1.3 Classic | |
* checksum. | |
*/ | |
void usart_lin_set_checksum_type(Usart *usart, uint8_t type) | |
{ | |
usart->US_LINMR = (usart->US_LINMR & ~US_LINMR_CHKTYP) | (type << 4); | |
} | |
/** | |
* \brief Configure the data length mode during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param mode Indicate the data length type: 0 if the data length is | |
* defined by the DLC of LIN mode register or 1 if the data length is defined | |
* by the bit 5 and 6 of the identifier. | |
*/ | |
void usart_lin_set_data_len_mode(Usart *usart, uint8_t mode) | |
{ | |
usart->US_LINMR = (usart->US_LINMR & ~US_LINMR_DLM) | (mode << 5); | |
} | |
/** | |
* \brief Disable the frame slot mode during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_disable_frame_slot(Usart *usart) | |
{ | |
usart->US_LINMR |= US_LINMR_FSDIS; | |
} | |
/** | |
* \brief Enable the frame slot mode during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_enable_frame_slot(Usart *usart) | |
{ | |
usart->US_LINMR &= ~US_LINMR_FSDIS; | |
} | |
/** | |
* \brief Configure the wakeup signal type during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param type Indicate the checksum type: 0 if the wakeup signal is a | |
* LIN 2.0 wakeup signal; 1 if the wakeup signal is a LIN 1.3 wakeup signal. | |
*/ | |
void usart_lin_set_wakeup_signal_type(Usart *usart, uint8_t type) | |
{ | |
usart->US_LINMR = (usart->US_LINMR & ~US_LINMR_WKUPTYP) | (type << 7); | |
} | |
/** | |
* \brief Configure the response data length if the data length is defined by | |
* the DLC field during the LIN communication. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param len Indicate the response data length. | |
*/ | |
void usart_lin_set_frame_data_len(Usart *usart, uint8_t len) | |
{ | |
usart->US_LINMR = (usart->US_LINMR & ~US_LINMR_DLC_Msk) | ((len-1) << US_LINMR_DLC_Pos); | |
} | |
/** | |
* \brief The LIN mode register is not written by the DMAC. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_disable_dmac_mode(Usart *usart) | |
{ | |
usart->US_LINMR &= ~US_LINMR_PDCM; | |
} | |
/** | |
* \brief The LIN mode register (except this flag) is written by the DMAC. | |
* | |
* \param usart Pointer to a USART instance. | |
*/ | |
void usart_lin_enable_dmac_mode(Usart *usart) | |
{ | |
usart->US_LINMR |= US_LINMR_PDCM; | |
} | |
/** | |
* \brief Configure the LIN identifier when USART works in LIN master mode. | |
* | |
* \param usart Pointer to a USART instance. | |
* \param id The identifier to be transmitted. | |
*/ | |
void usart_lin_set_tx_identifier(Usart *usart, uint8_t id) | |
{ | |
usart->US_LINIR = (usart->US_LINIR & ~US_LINIR_IDCHR_Msk) | US_LINIR_IDCHR(id); | |
} | |
/** | |
* \brief Read the identifier when USART works in LIN mode. | |
* | |
* \param usart Pointer to a USART instance. | |
* | |
* \return The last identifier received in LIN slave mode or the last | |
* identifier transmitted in LIN master mode. | |
*/ | |
uint8_t usart_lin_read_identifier(Usart *usart) | |
{ | |
return (usart->US_LINIR & US_LINIR_IDCHR_Msk); | |
} | |
/** | |
* \brief Get data length. | |
* | |
* \param usart Pointer to a USART instance. | |
* | |
* \return Data length. | |
*/ | |
uint8_t usart_lin_get_data_length(Usart *usart) | |
{ | |
if (usart->US_LINMR & US_LINMR_DLM) { | |
uint8_t data_length = 1 << ((usart->US_LINIR >> (US_LINIR_IDCHR_Pos + 4)) & 0x03); | |
return data_length; | |
} else { | |
return ((usart->US_LINMR & US_LINMR_DLC_Msk) >> US_LINMR_DLC_Pos) + 1; | |
} | |
} | |
/*----------------------------------------------------------------------------- | |
* Functions if FIFO are used | |
*---------------------------------------------------------------------------*/ | |
#ifdef CONFIG_HAVE_USART_FIFO | |
/** | |
* \brief Reads from USART device input channel until the specified length is | |
* reached. | |
* | |
* \param usart Pointer to an USART instance. | |
* \param stream Pointer to the receive buffer. | |
* \param len Size of the receive buffer, in octets. | |
* | |
* \return Number of read octets | |
* | |
* \warning WORKS ONLY IN LITTLE ENDIAN MODE! | |
* | |
* \note The FIFO must be configured before using this function. | |
* \note In case of a TIMEOUT or a BREAK, a null character is appended to the | |
* buffer and the returned value should be inferior to \ref len. | |
*/ | |
uint32_t usart_lin_read_stream(Usart *usart, uint8_t *stream, uint32_t len) | |
{ | |
uint8_t* buffer = stream; | |
uint32_t left = len; | |
while (left > 0) { | |
/* Stop reception if a timeout or break occur */ | |
if ((usart->US_CSR & (US_CSR_TIMEOUT | US_CSR_RXBRK)) != 0) { | |
*buffer = '\0'; | |
break; | |
} | |
if ((usart->US_CSR & (US_CSR_RXRDY | US_CSR_LINTC)) == 0) continue; | |
/* Get FIFO size (in octets) and clamp it */ | |
uint32_t buf_size = usart_fifo_rx_size(usart); | |
buf_size = buf_size > left ? left : buf_size; | |
/* Fill the buffer with data received */ | |
while (buf_size) { | |
readb(&usart->US_RHR, buffer); | |
buffer ++; | |
left -= sizeof(uint8_t); | |
buf_size -= sizeof(uint8_t); | |
} | |
} | |
return len - left; | |
} | |
/** | |
* \brief Writes given data to USART device output channel until the specified | |
* length is reached. | |
* | |
* \param usart Pointer to an USART instance. | |
* \param stream Pointer to the data to send. | |
* \param len Size of the data to send, in octets. | |
* | |
* \return Number of written octets | |
* | |
* \warning WORKS ONLY IN LITTLE ENDIAN MODE! | |
* | |
* \note The FIFO must be configured before using this function. | |
* \note This function do not wait for the FIFO to be empty. | |
* \note In case of a TIMEOUT the transmission is aborted and the returned value | |
* should be inferior to \ref len. | |
*/ | |
uint32_t usart_lin_write_stream(Usart *usart, uint8_t *stream, uint32_t len) | |
{ | |
uint8_t* buffer = stream; | |
uint32_t left = len; | |
int32_t fifo_size = get_peripheral_fifo_depth(usart); | |
if (fifo_size < 0) | |
return 0; | |
while (left > 0) { | |
if ((usart->US_CSR & US_CSR_TXRDY) == 0) continue; | |
/* Get FIFO free size (int octet) and clamp it */ | |
uint32_t buf_size = fifo_size - usart_fifo_tx_size(usart); | |
buf_size = buf_size > left ? left : buf_size; | |
/* Fill the FIFO with data to send */ | |
while (buf_size) { | |
writeb(&usart->US_THR, *buffer); | |
buffer ++ ; | |
left -= sizeof(uint8_t); | |
buf_size -= sizeof(uint8_t); | |
} | |
} | |
return len - left; | |
} | |
#endif | |