4 * \brief SAM D20 SERCOM USART Asynchronous Driver
\r
6 * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved.
\r
12 * Redistribution and use in source and binary forms, with or without
\r
13 * modification, are permitted provided that the following conditions are met:
\r
15 * 1. Redistributions of source code must retain the above copyright notice,
\r
16 * this list of conditions and the following disclaimer.
\r
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
\r
19 * this list of conditions and the following disclaimer in the documentation
\r
20 * and/or other materials provided with the distribution.
\r
22 * 3. The name of Atmel may not be used to endorse or promote products derived
\r
23 * from this software without specific prior written permission.
\r
25 * 4. This software may only be redistributed and used in connection with an
\r
26 * Atmel microcontroller product.
\r
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
\r
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
\r
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
\r
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
\r
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
\r
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
\r
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
38 * POSSIBILITY OF SUCH DAMAGE.
\r
44 #include "usart_interrupt.h"
\r
48 * Asynchronous write of a buffer with a given length
\r
50 * \param[in] module Pointer to USART software instance struct
\r
51 * \param[in] tx_data Pointer to data to be transmitted
\r
52 * \param[in] length Length of data buffer
\r
55 void _usart_write_buffer(
\r
56 struct usart_module *const module,
\r
60 /* Sanity check arguments */
\r
64 /* Get a pointer to the hardware module instance */
\r
65 SercomUsart *const usart_hw = &(module->hw->USART);
\r
67 /* Write parameters to the device instance */
\r
68 module->remaining_tx_buffer_length = length;
\r
69 module->tx_buffer_ptr = tx_data;
\r
70 module->tx_status = STATUS_BUSY;
\r
72 /* Enable the Data Register Empty Interrupt */
\r
73 usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE;
\r
78 * Asynchronous read of a buffer with a given length
\r
80 * \param[in] module Pointer to USART software instance struct
\r
81 * \param[in] rx_data Pointer to data to be received
\r
82 * \param[in] length Length of data buffer
\r
85 void _usart_read_buffer(
\r
86 struct usart_module *const module,
\r
90 /* Sanity check arguments */
\r
94 /* Get a pointer to the hardware module instance */
\r
95 SercomUsart *const usart_hw = &(module->hw->USART);
\r
97 /* Set length for the buffer and the pointer, and let
\r
98 * the interrupt handler do the rest */
\r
99 module->remaining_rx_buffer_length = length;
\r
100 module->rx_buffer_ptr = rx_data;
\r
101 module->rx_status = STATUS_BUSY;
\r
103 /* Enable the RX Complete Interrupt */
\r
104 usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC;
\r
108 * \brief Registers a callback
\r
110 * Registers a callback function which is implemented by the user.
\r
112 * \note The callback must be enabled by \ref usart_enable_callback,
\r
113 * in order for the interrupt handler to call it when the conditions for
\r
114 * the callback type are met.
\r
116 * \param[in] module Pointer to USART software instance struct
\r
117 * \param[in] callback_func Pointer to callback function
\r
118 * \param[in] callback_type Callback type given by an enum
\r
121 void usart_register_callback(
\r
122 struct usart_module *const module,
\r
123 usart_callback_t callback_func,
\r
124 enum usart_callback callback_type)
\r
126 /* Sanity check arguments */
\r
128 Assert(callback_func);
\r
130 /* Register callback function */
\r
131 module->callback[callback_type] = callback_func;
\r
133 /* Set the bit corresponding to the callback_type */
\r
134 module->callback_reg_mask |= (1 << callback_type);
\r
138 * \brief Unregisters a callback
\r
140 * Unregisters a callback function which is implemented by the user.
\r
142 * \param[in,out] module Pointer to USART software instance struct
\r
143 * \param[in] callback_type Callback type given by an enum
\r
146 void usart_unregister_callback(
\r
147 struct usart_module *const module,
\r
148 enum usart_callback callback_type)
\r
150 /* Sanity check arguments */
\r
153 /* Unregister callback function */
\r
154 module->callback[callback_type] = NULL;
\r
156 /* Clear the bit corresponding to the callback_type */
\r
157 module->callback_reg_mask &= ~(1 << callback_type);
\r
161 * \brief Asynchronous write a single char
\r
163 * Sets up the driver to write the data given. If registered and enabled,
\r
164 * a callback function will be called when the transmit is completed.
\r
166 * \param[in] module Pointer to USART software instance struct
\r
167 * \param[in] tx_data Data to transfer
\r
169 * \returns Status of the operation
\r
170 * \retval STATUS_OK If operation was completed
\r
171 * \retval STATUS_BUSY If operation was not completed, due to the
\r
172 * USART module being busy
\r
173 * \retval STATUS_ERR_DENIED If the transmitter is not enabled
\r
175 enum status_code usart_write_job(
\r
176 struct usart_module *const module,
\r
177 const uint16_t tx_data)
\r
179 /* Sanity check arguments */
\r
181 Assert(module->hw);
\r
182 /* Check if the USART transmitter is busy */
\r
183 if (module->remaining_tx_buffer_length > 0) {
\r
184 return STATUS_BUSY;
\r
187 /* Check that the transmitter is enabled */
\r
188 if (!(module->transmitter_enabled)) {
\r
189 return STATUS_ERR_DENIED;
\r
192 /* Call internal write buffer function with length 1 */
\r
193 _usart_write_buffer(module, (uint8_t *)&tx_data, 1);
\r
199 * \brief Asynchronous read a single char
\r
201 * Sets up the driver to read data from the USART module to the data
\r
202 * pointer given. If registered and enabled, a callback will be called
\r
203 * when the receiving is completed.
\r
205 * \param[in] module Pointer to USART software instance struct
\r
206 * \param[out] rx_data Pointer to where received data should be put
\r
208 * \returns Status of the operation
\r
209 * \retval STATUS_OK If operation was completed
\r
210 * \retval STATUS_BUSY If operation was not completed,
\r
212 enum status_code usart_read_job(
\r
213 struct usart_module *const module,
\r
214 uint16_t *const rx_data)
\r
216 /* Sanity check arguments */
\r
219 /* Check if the USART receiver is busy */
\r
220 if (module->remaining_rx_buffer_length > 0) {
\r
221 return STATUS_BUSY;
\r
224 /* Call internal read buffer function with length 1 */
\r
225 _usart_read_buffer(module, (uint8_t *)rx_data, 1);
\r
231 * \brief Asynchronous buffer write
\r
233 * Sets up the driver to write a given buffer over the USART. If registered and
\r
234 * enabled, a callback function will be called.
\r
236 * \param[in] module Pointer to USART software instance struct
\r
237 * \param[in] tx_data Pointer do data buffer to transmit
\r
238 * \param[in] length Length of the data to transmit
\r
240 * \returns Status of the operation
\r
241 * \retval STATUS_OK If operation was completed successfully.
\r
242 * \retval STATUS_BUSY If operation was not completed, due to the
\r
243 * USART module being busy
\r
244 * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid
\r
246 * \retval STATUS_ERR_DENIED If the transmitter is not enabled
\r
248 enum status_code usart_write_buffer_job(
\r
249 struct usart_module *const module,
\r
253 /* Sanity check arguments */
\r
257 return STATUS_ERR_INVALID_ARG;
\r
260 /* Check if the USART transmitter is busy */
\r
261 if (module->remaining_tx_buffer_length > 0) {
\r
262 return STATUS_BUSY;
\r
265 /* Check that the receiver is enabled */
\r
266 if (!(module->transmitter_enabled)) {
\r
267 return STATUS_ERR_DENIED;
\r
270 /* Issue internal asynchronous write */
\r
271 _usart_write_buffer(module, tx_data, length);
\r
277 * \brief Asynchronous buffer read
\r
279 * Sets up the driver to read from the USART to a given buffer. If registered
\r
280 * and enabled, a callback function will be called.
\r
282 * \param[in] module Pointer to USART software instance struct
\r
283 * \param[out] rx_data Pointer to data buffer to receive
\r
284 * \param[in] length Data buffer length
\r
286 * \returns Status of the operation
\r
287 * \retval STATUS_OK If operation was completed
\r
288 * \retval STATUS_BUSY If operation was not completed, due to the
\r
289 * USART module being busy
\r
290 * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid
\r
292 * \retval STATUS_ERR_DENIED If the transmitter is not enabled
\r
294 enum status_code usart_read_buffer_job(
\r
295 struct usart_module *const module,
\r
299 /* Sanity check arguments */
\r
304 return STATUS_ERR_INVALID_ARG;
\r
307 /* Check that the receiver is enabled */
\r
308 if (!(module->receiver_enabled)) {
\r
309 return STATUS_ERR_DENIED;
\r
312 /* Check if the USART receiver is busy */
\r
313 if (module->remaining_rx_buffer_length > 0) {
\r
314 return STATUS_BUSY;
\r
317 /* Issue internal asynchronous read */
\r
318 _usart_read_buffer(module, rx_data, length);
\r
324 * \brief Cancels ongoing read/write operation
\r
326 * Cancels the ongoing read/write operation modifying parameters in the
\r
327 * USART software struct.
\r
329 * \param[in] module Pointer to USART software instance struct
\r
330 * \param[in] transceiver_type Transfer type to cancel
\r
332 void usart_abort_job(
\r
333 struct usart_module *const module,
\r
334 enum usart_transceiver_type transceiver_type)
\r
336 /* Sanity check arguments */
\r
338 Assert(module->hw);
\r
340 /* Get a pointer to the hardware module instance */
\r
341 SercomUsart *const usart_hw = &(module->hw->USART);
\r
343 switch(transceiver_type) {
\r
344 case USART_TRANSCEIVER_RX:
\r
345 /* Clear the interrupt flag in order to prevent the receive
\r
346 * complete callback to fire */
\r
347 usart_hw->INTFLAG.reg |= SERCOM_USART_INTFLAG_RXC;
\r
349 /* Clear the software reception buffer */
\r
350 module->remaining_rx_buffer_length = 0;
\r
354 case USART_TRANSCEIVER_TX:
\r
355 /* Clear the interrupt flag in order to prevent the receive
\r
356 * complete callback to fire */
\r
357 usart_hw->INTFLAG.reg |= SERCOM_USART_INTFLAG_TXC;
\r
359 /* Clear the software reception buffer */
\r
360 module->remaining_tx_buffer_length = 0;
\r
367 * \brief Get status from the ongoing or last asynchronous transfer operation
\r
369 * Returns the error from a given ongoing or last asynchronous transfer operation.
\r
370 * Either from a read or write transfer.
\r
372 * \param[in] module Pointer to USART software instance struct
\r
373 * \param[in] transceiver_type Transfer type to check
\r
375 * \return Status of the given job.
\r
376 * \retval STATUS_OK No error occurred during the last transfer
\r
377 * \retval STATUS_BUSY A transfer is ongoing
\r
378 * \retval STATUS_ERR_BAD_DATA The last operation was aborted due to a
\r
379 * parity error. The transfer could be affected
\r
380 * by external noise.
\r
381 * \retval STATUS_ERR_BAD_FORMAT The last operation was aborted due to a
\r
383 * \retval STATUS_ERR_OVERFLOW The last operation was aborted due to a
\r
385 * \retval STATUS_ERR_INVALID_ARG An invalid transceiver enum given.
\r
387 enum status_code usart_get_job_status(
\r
388 struct usart_module *const module,
\r
389 enum usart_transceiver_type transceiver_type)
\r
391 /* Sanity check arguments */
\r
394 /* Variable for status code */
\r
395 enum status_code status_code;
\r
397 switch(transceiver_type) {
\r
398 case USART_TRANSCEIVER_RX:
\r
399 status_code = module->rx_status;
\r
402 case USART_TRANSCEIVER_TX:
\r
403 status_code = module->tx_status;
\r
407 status_code = STATUS_ERR_INVALID_ARG;
\r
411 return status_code;
\r
416 * Handles interrupts as they occur, and it will run callback functions
\r
417 * which are registered and enabled.
\r
419 * \param[in] instance ID of the SERCOM instance calling the interrupt
\r
422 void _usart_interrupt_handler(
\r
425 /* Temporary variables */
\r
426 uint16_t interrupt_status;
\r
427 uint16_t callback_status;
\r
428 uint8_t error_code;
\r
431 /* Get device instance from the look-up table */
\r
432 struct usart_module *module
\r
433 = (struct usart_module *)_sercom_instances[instance];
\r
435 /* Pointer to the hardware module instance */
\r
436 SercomUsart *const usart_hw
\r
437 = &(module->hw->USART);
\r
439 /* Wait for the synchronization to complete */
\r
440 _usart_wait_for_sync(module);
\r
442 /* Read and mask interrupt flag register */
\r
443 interrupt_status = usart_hw->INTFLAG.reg;
\r
444 callback_status = module->callback_reg_mask
\r
445 &module->callback_enable_mask;
\r
447 /* Check if a DATA READY interrupt has occurred,
\r
448 * and if there is more to transfer */
\r
449 if (interrupt_status & SERCOM_USART_INTFLAG_DRE) {
\r
450 if (module->remaining_tx_buffer_length) {
\r
451 /* Write value will be at least 8-bits long */
\r
452 uint16_t data_to_send = *(module->tx_buffer_ptr);
\r
453 /* Increment 8-bit pointer */
\r
454 (module->tx_buffer_ptr)++;
\r
456 if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
\r
457 data_to_send = (*(module->tx_buffer_ptr) << 8);
\r
458 /* Increment 8-bit pointer */
\r
459 (module->tx_buffer_ptr)++;
\r
461 /* Write the data to send */
\r
462 usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK);
\r
464 if (--(module->remaining_tx_buffer_length) == 0) {
\r
465 /* Disable the Data Register Empty Interrupt */
\r
466 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
\r
467 /* Enable Transmission Complete interrupt */
\r
468 usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_TXC;
\r
472 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
\r
475 /* Check if the Transmission Complete interrupt has occurred and
\r
476 * that the transmit buffer is empty */
\r
478 if (interrupt_status & SERCOM_USART_INTFLAG_TXC) {
\r
480 /* Disable TX Complete Interrupt, and set STATUS_OK */
\r
481 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_TXC;
\r
482 module->tx_status = STATUS_OK;
\r
484 /* Run callback if registered and enabled */
\r
485 if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED)) {
\r
486 (*(module->callback[USART_CALLBACK_BUFFER_TRANSMITTED]))(module);
\r
489 /* Check if the Receive Complete interrupt has occurred, and that
\r
490 * there's more data to receive */
\r
492 if (interrupt_status & SERCOM_USART_INTFLAG_RXC) {
\r
494 if (module->remaining_rx_buffer_length) {
\r
495 /* Read out the status code and mask away all but the 4 LSBs*/
\r
496 error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK);
\r
498 /* Check if an error has occurred during the receiving */
\r
500 /* Check which error occurred */
\r
501 if (error_code & SERCOM_USART_STATUS_FERR) {
\r
502 /* Store the error code and clear flag by writing 1 to it */
\r
503 module->rx_status = STATUS_ERR_BAD_FORMAT;
\r
504 usart_hw->STATUS.reg |= SERCOM_USART_STATUS_FERR;
\r
505 } else if (error_code & SERCOM_USART_STATUS_BUFOVF) {
\r
506 /* Store the error code and clear flag by writing 1 to it */
\r
507 module->rx_status = STATUS_ERR_OVERFLOW;
\r
508 usart_hw->STATUS.reg |= SERCOM_USART_STATUS_BUFOVF;
\r
509 } else if (error_code & SERCOM_USART_STATUS_PERR) {
\r
510 /* Store the error code and clear flag by writing 1 to it */
\r
511 module->rx_status = STATUS_ERR_BAD_DATA;
\r
512 usart_hw->STATUS.reg |= SERCOM_USART_STATUS_PERR;
\r
515 /* Run callback if registered and enabled */
\r
516 if (callback_status
\r
517 & (1 << USART_CALLBACK_ERROR)) {
\r
518 (*(module->callback[USART_CALLBACK_ERROR]))(module);
\r
523 /* Read current packet from DATA register,
\r
524 * increment buffer pointer and decrement buffer length */
\r
525 uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK);
\r
527 /* Read value will be at least 8-bits long */
\r
528 *(module->rx_buffer_ptr) = received_data;
\r
529 /* Increment 8-bit pointer */
\r
530 module->rx_buffer_ptr += 1;
\r
532 if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
\r
533 /* 9-bit data, write next received byte to the buffer */
\r
534 *(module->rx_buffer_ptr) = (received_data >> 8);
\r
535 /* Increment 8-bit pointer */
\r
536 module->rx_buffer_ptr += 1;
\r
539 /* Check if the last character have been received */
\r
540 if(--(module->remaining_rx_buffer_length) == 0) {
\r
541 /* Disable RX Complete Interrupt,
\r
542 * and set STATUS_OK */
\r
543 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
\r
544 module->rx_status = STATUS_OK;
\r
546 /* Run callback if registered and enabled */
\r
547 if (callback_status
\r
548 & (1 << USART_CALLBACK_BUFFER_RECEIVED)) {
\r
549 (*(module->callback[USART_CALLBACK_BUFFER_RECEIVED]))(module);
\r
554 /* This should not happen. Disable Receive Complete interrupt. */
\r
555 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
\r