]> git.sur5r.net Git - freertos/blob
e0dec61a03e3cec359fb0cd56b376e5e4e7eb960
[freertos] /
1 /**\r
2  * \file\r
3  *\r
4  * \brief SAM D20 SERCOM USART Asynchronous Driver\r
5  *\r
6  * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved.\r
7  *\r
8  * \asf_license_start\r
9  *\r
10  * \page License\r
11  *\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
14  *\r
15  * 1. Redistributions of source code must retain the above copyright notice,\r
16  *    this list of conditions and the following disclaimer.\r
17  *\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
21  *\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
24  *\r
25  * 4. This software may only be redistributed and used in connection with an\r
26  *    Atmel microcontroller product.\r
27  *\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
39  *\r
40  * \asf_license_stop\r
41  *\r
42  */\r
43 \r
44 #include "usart_interrupt.h"\r
45 \r
46 /**\r
47  * \internal\r
48  * Asynchronous write of a buffer with a given length\r
49  *\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
53  *\r
54  */\r
55 void _usart_write_buffer(\r
56                 struct usart_module *const module,\r
57                 uint8_t *tx_data,\r
58                 uint16_t length)\r
59 {\r
60         /* Sanity check arguments */\r
61         Assert(module);\r
62         Assert(module->hw);\r
63 \r
64         /* Get a pointer to the hardware module instance */\r
65         SercomUsart *const usart_hw = &(module->hw->USART);\r
66 \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
71 \r
72         /* Enable the Data Register Empty Interrupt */\r
73         usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE;\r
74 }\r
75 \r
76 /**\r
77  * \internal\r
78  * Asynchronous read of a buffer with a given length\r
79  *\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
83  *\r
84  */\r
85 void _usart_read_buffer(\r
86                 struct usart_module *const module,\r
87                 uint8_t *rx_data,\r
88                 uint16_t length)\r
89 {\r
90         /* Sanity check arguments */\r
91         Assert(module);\r
92         Assert(module->hw);\r
93 \r
94         /* Get a pointer to the hardware module instance */\r
95         SercomUsart *const usart_hw = &(module->hw->USART);\r
96 \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
102 \r
103         /* Enable the RX Complete Interrupt */\r
104         usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC;\r
105 }\r
106 \r
107 /**\r
108  * \brief Registers a callback\r
109  *\r
110  * Registers a callback function which is implemented by the user.\r
111  *\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
115  *\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
119  *\r
120  */\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
125 {\r
126         /* Sanity check arguments */\r
127         Assert(module);\r
128         Assert(callback_func);\r
129 \r
130         /* Register callback function */\r
131         module->callback[callback_type] = callback_func;\r
132 \r
133         /* Set the bit corresponding to the callback_type */\r
134         module->callback_reg_mask |= (1 << callback_type);\r
135 }\r
136 \r
137 /**\r
138  * \brief Unregisters a callback\r
139  *\r
140  * Unregisters a callback function which is implemented by the user.\r
141  *\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
144  *\r
145  */\r
146 void usart_unregister_callback(\r
147                 struct usart_module *const module,\r
148                 enum usart_callback callback_type)\r
149 {\r
150         /* Sanity check arguments */\r
151         Assert(module);\r
152 \r
153         /* Unregister callback function */\r
154         module->callback[callback_type] = NULL;\r
155 \r
156         /* Clear the bit corresponding to the callback_type */\r
157         module->callback_reg_mask &= ~(1 << callback_type);\r
158 }\r
159 \r
160 /**\r
161  * \brief Asynchronous write a single char\r
162  *\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
165  *\r
166  * \param[in]  module   Pointer to USART software instance struct\r
167  * \param[in]  tx_data  Data to transfer\r
168  *\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
174  */\r
175 enum status_code usart_write_job(\r
176                 struct usart_module *const module,\r
177                 const uint16_t tx_data)\r
178 {\r
179         /* Sanity check arguments */\r
180         Assert(module);\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
185         }\r
186 \r
187         /* Check that the transmitter is enabled */\r
188         if (!(module->transmitter_enabled)) {\r
189                 return STATUS_ERR_DENIED;\r
190         }\r
191 \r
192         /* Call internal write buffer function with length 1 */\r
193         _usart_write_buffer(module, (uint8_t *)&tx_data, 1);\r
194 \r
195         return STATUS_OK;\r
196 }\r
197 \r
198 /**\r
199  * \brief Asynchronous read a single char\r
200  *\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
204  *\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
207  *\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
211  */\r
212 enum status_code usart_read_job(\r
213                 struct usart_module *const module,\r
214                 uint16_t *const rx_data)\r
215 {\r
216         /* Sanity check arguments */\r
217         Assert(module);\r
218 \r
219         /* Check if the USART receiver is busy */\r
220         if (module->remaining_rx_buffer_length > 0) {\r
221                 return STATUS_BUSY;\r
222         }\r
223 \r
224         /* Call internal read buffer function with length 1 */\r
225         _usart_read_buffer(module, (uint8_t *)rx_data, 1);\r
226 \r
227         return STATUS_OK;\r
228 }\r
229 \r
230 /**\r
231  * \brief Asynchronous buffer write\r
232  *\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
235  *\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
239  *\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
245  *                                arguments\r
246  * \retval STATUS_ERR_DENIED      If the transmitter is not enabled\r
247  */\r
248 enum status_code usart_write_buffer_job(\r
249                 struct usart_module *const module,\r
250                 uint8_t *tx_data,\r
251                 uint16_t length)\r
252 {\r
253         /* Sanity check arguments */\r
254         Assert(module);\r
255 \r
256         if (length == 0) {\r
257                 return STATUS_ERR_INVALID_ARG;\r
258         }\r
259 \r
260         /* Check if the USART transmitter is busy */\r
261         if (module->remaining_tx_buffer_length > 0) {\r
262                 return STATUS_BUSY;\r
263         }\r
264         \r
265         /* Check that the receiver is enabled */\r
266         if (!(module->transmitter_enabled)) {\r
267                 return STATUS_ERR_DENIED;\r
268         }\r
269 \r
270         /* Issue internal asynchronous write */\r
271         _usart_write_buffer(module, tx_data, length);\r
272 \r
273         return STATUS_OK;\r
274 }\r
275 \r
276 /**\r
277  * \brief Asynchronous buffer read\r
278  *\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
281  *\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
285  *\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
291  *                                arguments\r
292  * \retval STATUS_ERR_DENIED      If the transmitter is not enabled\r
293  */\r
294 enum status_code usart_read_buffer_job(\r
295                 struct usart_module *const module,\r
296                 uint8_t *rx_data,\r
297                 uint16_t length)\r
298 {\r
299         /* Sanity check arguments */\r
300         Assert(module);\r
301         Assert(rx_data);\r
302 \r
303         if (length == 0) {\r
304                 return STATUS_ERR_INVALID_ARG;\r
305         }\r
306         \r
307         /* Check that the receiver is enabled */\r
308         if (!(module->receiver_enabled)) {\r
309                 return STATUS_ERR_DENIED;\r
310         }\r
311 \r
312         /* Check if the USART receiver is busy */\r
313         if (module->remaining_rx_buffer_length > 0) {\r
314                 return STATUS_BUSY;\r
315         }\r
316 \r
317         /* Issue internal asynchronous read */\r
318         _usart_read_buffer(module, rx_data, length);\r
319 \r
320         return STATUS_OK;\r
321 }\r
322 \r
323 /**\r
324  * \brief Cancels ongoing read/write operation\r
325  *\r
326  * Cancels the ongoing read/write operation modifying parameters in the\r
327  * USART software struct.\r
328  *\r
329  * \param[in]  module            Pointer to USART software instance struct\r
330  * \param[in]  transceiver_type  Transfer type to cancel\r
331  */\r
332 void usart_abort_job(\r
333                 struct usart_module *const module,\r
334                 enum usart_transceiver_type transceiver_type)\r
335 {\r
336         /* Sanity check arguments */\r
337         Assert(module);\r
338         Assert(module->hw);\r
339 \r
340         /* Get a pointer to the hardware module instance */\r
341         SercomUsart *const usart_hw = &(module->hw->USART);\r
342 \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
348 \r
349                         /* Clear the software reception buffer */\r
350                         module->remaining_rx_buffer_length = 0;\r
351 \r
352                         break;\r
353 \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
358 \r
359                         /* Clear the software reception buffer */\r
360                         module->remaining_tx_buffer_length = 0;\r
361 \r
362                         break;\r
363         }\r
364 }\r
365 \r
366 /**\r
367  * \brief Get status from the ongoing or last asynchronous transfer operation\r
368  *\r
369  * Returns the error from a given ongoing or last asynchronous transfer operation.\r
370  * Either from a read or write transfer.\r
371  *\r
372  * \param[in]  module            Pointer to USART software instance struct\r
373  * \param[in]  transceiver_type  Transfer type to check\r
374   *\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
382  *                                 frame error.\r
383  * \retval STATUS_ERR_OVERFLOW     The last operation was aborted due to a\r
384  *                                 buffer overflow.\r
385  * \retval STATUS_ERR_INVALID_ARG  An invalid transceiver enum given.\r
386  */\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
390 {\r
391         /* Sanity check arguments */\r
392         Assert(module);\r
393 \r
394         /* Variable for status code */\r
395         enum status_code status_code;\r
396 \r
397         switch(transceiver_type) {\r
398         case USART_TRANSCEIVER_RX:\r
399                         status_code = module->rx_status;\r
400                         break;\r
401 \r
402         case USART_TRANSCEIVER_TX:\r
403                         status_code = module->tx_status;\r
404                         break;\r
405 \r
406         default:\r
407                         status_code = STATUS_ERR_INVALID_ARG;\r
408                         break;\r
409         }\r
410 \r
411         return status_code;\r
412 }\r
413 \r
414 /**\r
415  * \internal\r
416  * Handles interrupts as they occur, and it will run callback functions\r
417  * which are registered and enabled.\r
418  *\r
419  * \param[in]  instance  ID of the SERCOM instance calling the interrupt\r
420  *                       handler.\r
421  */\r
422 void _usart_interrupt_handler(\r
423                 uint8_t instance)\r
424 {\r
425         /* Temporary variables */\r
426         uint16_t interrupt_status;\r
427         uint16_t callback_status;\r
428         uint8_t error_code;\r
429 \r
430 \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
434 \r
435         /* Pointer to the hardware module instance */\r
436         SercomUsart *const usart_hw\r
437                 = &(module->hw->USART);\r
438 \r
439         /* Wait for the synchronization to complete */\r
440         _usart_wait_for_sync(module);\r
441 \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
446 \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
455 \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
460                         }\r
461                         /* Write the data to send */\r
462                         usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK);\r
463 \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
469 \r
470                         }\r
471                 } else {\r
472                         usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;\r
473                 }\r
474 \r
475         /* Check if the Transmission Complete interrupt has occurred and\r
476          * that the transmit buffer is empty */\r
477         }\r
478         if (interrupt_status & SERCOM_USART_INTFLAG_TXC) {\r
479 \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
483 \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
487                 }\r
488 \r
489         /* Check if the Receive Complete interrupt has occurred, and that\r
490          * there's more data to receive */\r
491         }\r
492         if (interrupt_status & SERCOM_USART_INTFLAG_RXC) {\r
493 \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
497 \r
498                         /* Check if an error has occurred during the receiving */\r
499                         if (error_code) {\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
513                                 }\r
514 \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
519                                 }\r
520 \r
521                         } else {\r
522 \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
526 \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
531 \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
537                                 }\r
538 \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
545 \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
550                                         }\r
551                                 }\r
552                         }\r
553                 } else {\r
554                         /* This should not happen. Disable Receive Complete interrupt. */\r
555                         usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;\r
556                 }\r
557         }\r
558 }\r