]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/twi.c
Add SAMA5D2 Xplained IAR demo.
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D2x_Xplained_IAR / AtmelFiles / drivers / peripherals / twi.c
diff --git a/FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/twi.c b/FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/twi.c
new file mode 100644 (file)
index 0000000..9177e14
--- /dev/null
@@ -0,0 +1,500 @@
+/* ----------------------------------------------------------------------------\r
+ *         SAM Software Package License\r
+ * ----------------------------------------------------------------------------\r
+ * Copyright (c) 2015, Atmel Corporation\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the disclaimer below.\r
+ *\r
+ * Atmel's name may not be used to endorse or promote products derived from\r
+ * this software without specific prior written permission.\r
+ *\r
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * ----------------------------------------------------------------------------\r
+ */\r
+\r
+/** \addtogroup twi_module Working with TWI\r
+ * \section Purpose\r
+ * The TWI driver provides the interface to configure and use the TWI\r
+ * peripheral.\r
+ *\r
+ * \section Usage\r
+ * <ul>\r
+ * <li> Configures a TWI peripheral to operate in master mode, at the given\r
+ * frequency (in Hz) using twi_configure(). </li>\r
+ * <li> Sends a STOP condition on the TWI using twi_stop().</li>\r
+ * <li> Starts a read operation on the TWI bus with the specified slave using\r
+ * twi_start_read(). Data must then be read using twi_read_byte() whenever\r
+ * a byte is available (poll using twi_is_byte_received()).</li>\r
+ * <li> Starts a write operation on the TWI to access the selected slave using\r
+ * twi_start_write(). A byte of data must be provided to start the write;\r
+ * other bytes are written next.</li>\r
+ * <li> Sends a byte of data to one of the TWI slaves on the bus using twi_write_byte().\r
+ * This function must be called once before twi_start_write() with the first byte of data\r
+ * to send, then it shall be called repeatedly after that to send the remaining bytes.</li>\r
+ * <li> Check if a byte has been received and can be read on the given TWI\r
+ * peripheral using twi_is_byte_received().<\r
+ * Check if a byte has been sent using twi_byte_sent().</li>\r
+ * <li> Check if the current transmission is complete (the STOP has been sent)\r
+ * using twi_is_transfer_complete().</li>\r
+ * <li> Enables & disable the selected interrupts sources on a TWI peripheral\r
+ * using twi_enable_it() and twi_enable_it().</li>\r
+ * <li> Get current status register of the given TWI peripheral using\r
+ * twi_get_status(). Get current status register of the given TWI peripheral, but\r
+ * masking interrupt sources which are not currently enabled using\r
+ * twi_get_masked_status().</li>\r
+ * </ul>\r
+ * For more accurate information, please look at the TWI section of the\r
+ * Datasheet.\r
+ *\r
+ * Related files :\n\r
+ * \ref twi.c\n\r
+ * \ref twi.h.\n\r
+*/\r
+/*@{*/\r
+/*@}*/\r
+\r
+/**\r
+ * \file\r
+ *\r
+ * Implementation of Two Wire Interface (TWI).\r
+ *\r
+ */\r
+\r
+/*----------------------------------------------------------------------------\r
+ *        Headers\r
+ *----------------------------------------------------------------------------*/\r
+\r
+#include "chip.h"\r
+#include "peripherals/twi.h"\r
+#include "peripherals/pmc.h"\r
+#include "trace.h"\r
+\r
+#include "timer.h"\r
+#include "io.h"\r
+\r
+#include <stddef.h>\r
+#include <assert.h>\r
+\r
+/*----------------------------------------------------------------------------\r
+ *        Exported functions\r
+ *----------------------------------------------------------------------------*/\r
+\r
+/**\r
+ * \brief Configures a TWI peripheral to operate in master mode, at the given\r
+ * frequency (in Hz). The duty cycle of the TWI clock is set to 50%.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param twi_clock  Desired TWI clock frequency.\r
+ */\r
+void twi_configure_master(Twi * pTwi, uint32_t twi_clock)\r
+{\r
+       uint32_t ck_div, cl_div, hold, ok, clock;\r
+       uint32_t id = get_twi_id_from_addr(pTwi);\r
+\r
+       trace_debug("twi_configure_master(%u)\n\r", (unsigned)twi_clock);\r
+       assert(pTwi);\r
+       assert(id < ID_PERIPH_COUNT);\r
+\r
+       /* SVEN: TWI Slave Mode Enabled */\r
+       pTwi->TWI_CR = TWI_CR_SVEN;\r
+\r
+       /* Reset the TWI */\r
+       pTwi->TWI_CR = TWI_CR_SWRST;\r
+       pTwi->TWI_RHR;\r
+       timer_sleep(10);\r
+\r
+       /* TWI Slave Mode Disabled, TWI Master Mode Disabled. */\r
+       pTwi->TWI_MMR = 0;\r
+       pTwi->TWI_CR = TWI_CR_SVDIS;\r
+       pTwi->TWI_CR = TWI_CR_MSDIS;\r
+       clock = pmc_get_peripheral_clock(id);\r
+\r
+       /* Compute clock */\r
+       ck_div = 0; ok = 0;\r
+       while (!ok) {\r
+               cl_div = ((clock / (2 * twi_clock)) - 3) >> ck_div;\r
+               if (cl_div <= 255)\r
+                       ok = 1;\r
+               else\r
+                       ck_div++;\r
+       }\r
+       twi_clock = ROUND_INT_DIV(clock, (((cl_div * 2) << ck_div) + 3));\r
+       assert(ck_div < 8);\r
+       trace_debug("twi: CKDIV=%u CLDIV=CHDIV=%u -> TWI Clock %uHz\n\r",\r
+                   (unsigned)ck_div, (unsigned)cl_div, (unsigned)twi_clock);\r
+\r
+       /* Compute holding time (I2C spec requires 300ns) */\r
+       hold = ROUND_INT_DIV((uint32_t)(0.3 * clock), 1000000) - 3;\r
+       trace_debug("twi: HOLD=%u -> Holding Time %uns\n\r",\r
+                   (unsigned)hold, (unsigned)((1000000 * (hold + 3)) / (clock / 1000)));\r
+\r
+       /* Configure clock */\r
+       pTwi->TWI_CWGR = 0;\r
+       pTwi->TWI_CWGR = TWI_CWGR_CKDIV(ck_div) | TWI_CWGR_CHDIV(cl_div) |\r
+               TWI_CWGR_CLDIV(cl_div) | TWI_CWGR_HOLD(hold);\r
+\r
+       /* Set master mode */\r
+       pTwi->TWI_CR = TWI_CR_MSEN;\r
+       timer_sleep(10);\r
+       assert((pTwi->TWI_CR & TWI_CR_SVDIS) != TWI_CR_MSDIS);\r
+\r
+}\r
+\r
+/**\r
+ * \brief Configures a TWI peripheral to operate in slave mode.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param slaveAddress Slave address.\r
+ */\r
+void twi_configure_slave(Twi * pTwi, uint8_t slave_address)\r
+{\r
+       trace_debug("twi_configure_slave()\n\r");\r
+       assert(pTwi);\r
+       /* TWI software reset */\r
+       pTwi->TWI_CR = TWI_CR_SWRST;\r
+       pTwi->TWI_RHR;\r
+       /* Wait at least 10 ms */\r
+       timer_sleep(10);\r
+       /* TWI Slave Mode Disabled, TWI Master Mode Disabled */\r
+       pTwi->TWI_CR = TWI_CR_SVDIS | TWI_CR_MSDIS;\r
+       /* Configure slave address. */\r
+       pTwi->TWI_SMR = 0;\r
+       pTwi->TWI_SMR = TWI_SMR_SADR(slave_address);\r
+       /* SVEN: TWI Slave Mode Enabled */\r
+       pTwi->TWI_CR = TWI_CR_SVEN;\r
+       /* Wait at least 10 ms */\r
+       timer_sleep(10);\r
+       assert((pTwi->TWI_CR & TWI_CR_SVDIS) != TWI_CR_SVDIS);\r
+}\r
+\r
+/**\r
+ * \brief Sends a STOP condition on the TWI.\r
+ * \param twi  Pointer to an Twi instance.\r
+ */\r
+void twi_stop(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       pTwi->TWI_CR = TWI_CR_STOP;\r
+}\r
+\r
+/**\r
+ * \brief Starts a read operation on the TWI bus with the specified slave, it returns\r
+ * immediately. Data must then be read using twi_read_byte() whenever a byte is\r
+ * available (poll using twi_is_byte_received()).\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param address  Slave address on the bus.\r
+ * \param iaddress  Optional internal address bytes.\r
+ * \param isize  Number of internal address bytes.\r
+ */\r
+void twi_start_read(Twi * pTwi, uint8_t address,\r
+                   uint32_t iaddress, uint8_t isize)\r
+{\r
+       assert(pTwi != NULL);\r
+       assert((address & 0x80) == 0);\r
+       assert((iaddress & 0xFF000000) == 0);\r
+       assert(isize < 4);\r
+       /* Set slave address and number of internal address bytes. */\r
+       pTwi->TWI_MMR = 0;\r
+       pTwi->TWI_MMR = (isize << 8) | TWI_MMR_MREAD | (address << 16);\r
+       /* Set internal address bytes */\r
+       pTwi->TWI_IADR = 0;\r
+       pTwi->TWI_IADR = iaddress;\r
+       /* Send START condition */\r
+       pTwi->TWI_CR = TWI_CR_START;\r
+}\r
+\r
+/**\r
+ * \brief Reads a byte from the TWI bus. The read operation must have been started\r
+ * using twi_start_read() and a byte must be available (check with twi_is_byte_received()).\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \return byte read.\r
+ */\r
+uint8_t twi_read_byte(Twi * twi)\r
+{\r
+       assert(twi != NULL);\r
+       uint8_t value;\r
+       readb(&twi->TWI_RHR, &value);\r
+       return value;\r
+}\r
+\r
+/**\r
+ * \brief Sends a byte of data to one of the TWI slaves on the bus.\r
+ * \note This function must be called once before twi_start_write() with\r
+ * the first byte of data  to send, then it shall be called repeatedly\r
+ * after that to send the remaining bytes.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param byte  Byte to send.\r
+ */\r
+void twi_write_byte(Twi * twi, uint8_t byte)\r
+{\r
+       assert(twi != NULL);\r
+       writeb(&twi->TWI_THR, byte);\r
+}\r
+\r
+/**\r
+ * \brief Starts a write operation on the TWI to access the selected slave, then\r
+ *  returns immediately. A byte of data must be provided to start the write;\r
+ * other bytes are written next.\r
+ * after that to send the remaining bytes.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param address  Address of slave to acccess on the bus.\r
+ * \param iaddress  Optional slave internal address.\r
+ * \param isize  Number of internal address bytes.\r
+ * \param byte  First byte to send.\r
+ */\r
+void twi_start_write(Twi * pTwi, uint8_t address, uint32_t iaddress,\r
+                    uint8_t isize, uint8_t byte)\r
+{\r
+       assert(pTwi != NULL);\r
+       assert((address & 0x80) == 0);\r
+       assert((iaddress & 0xFF000000) == 0);\r
+       assert(isize < 4);\r
+       /* Set slave address and number of internal address bytes. */\r
+       pTwi->TWI_MMR = 0;\r
+       pTwi->TWI_MMR = (isize << 8) | (address << 16);\r
+       /* Set internal address bytes. */\r
+       pTwi->TWI_IADR = 0;\r
+       pTwi->TWI_IADR = iaddress;\r
+       /* Write first byte to send. */\r
+       twi_write_byte(pTwi, byte);\r
+}\r
+\r
+/**\r
+ * \brief Check if a byte have been receiced from TWI.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \return 1 if a byte has been received and can be read on the given TWI\r
+ * peripheral; otherwise, returns 0. This function resets the status register.\r
+ */\r
+uint8_t twi_is_byte_received(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       return ((pTwi->TWI_SR & TWI_SR_RXRDY) == TWI_SR_RXRDY);\r
+}\r
+\r
+/**\r
+ * \brief Check if a byte have been sent to TWI.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \return 1 if a byte has been sent  so another one can be stored for\r
+ * transmission; otherwise returns 0. This function clears the status register.\r
+ */\r
+uint8_t twi_byte_sent(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       return ((pTwi->TWI_SR & TWI_SR_TXRDY) == TWI_SR_TXRDY);\r
+}\r
+\r
+/**\r
+ * \brief Check if current transmission is complet.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \return  1 if the current transmission is complete (the STOP has been sent);\r
+ * otherwise returns 0.\r
+ */\r
+uint8_t twi_is_transfer_complete(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       return ((pTwi->TWI_SR & TWI_SR_TXCOMP) == TWI_SR_TXCOMP);\r
+}\r
+\r
+/**\r
+ * \brief Enables the selected interrupts sources on a TWI peripheral.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param sources  Bitwise OR of selected interrupt sources.\r
+ */\r
+void twi_enable_it(Twi * pTwi, uint32_t sources)\r
+{\r
+       assert(pTwi != NULL);\r
+       pTwi->TWI_IER = sources;\r
+}\r
+\r
+/**\r
+ * \brief Disables the selected interrupts sources on a TWI peripheral.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \param sources  Bitwise OR of selected interrupt sources.\r
+ */\r
+void twi_disable_it(Twi * pTwi, uint32_t sources)\r
+{\r
+       assert(pTwi != NULL);\r
+       pTwi->TWI_IDR = sources;\r
+}\r
+\r
+/**\r
+ * \brief Get the current status register of the given TWI peripheral.\r
+ * \note This resets the internal value of the status register, so further\r
+ * read may yield different values.\r
+ * \param twi  Pointer to an Twi instance.\r
+ * \return  TWI status register.\r
+ */\r
+uint32_t twi_get_status(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       return pTwi->TWI_SR;\r
+}\r
+\r
+/**\r
+ * \brief Returns the current status register of the given TWI peripheral, but\r
+ * masking interrupt sources which are not currently enabled.\r
+ * \note This resets the internal value of the status register, so further\r
+ * read may yield different values.\r
+ * \param twi  Pointer to an Twi instance.\r
+ */\r
+uint32_t twi_get_masked_status(Twi * pTwi)\r
+{\r
+       uint32_t status;\r
+       assert(pTwi != NULL);\r
+       status = pTwi->TWI_SR;\r
+       status &= pTwi->TWI_IMR;\r
+       return status;\r
+}\r
+\r
+/**\r
+ * \brief  Sends a STOP condition. STOP Condition is sent just after completing\r
+ *  the current byte transmission in master read mode.\r
+ * \param twi  Pointer to an Twi instance.\r
+ */\r
+void twi_send_stop_condition(Twi * pTwi)\r
+{\r
+       assert(pTwi != NULL);\r
+       pTwi->TWI_CR |= TWI_CR_STOP;\r
+}\r
+\r
+#ifdef CONFIG_HAVE_TWI_ALTERNATE_CMD\r
+void twi_init_write_transfert(Twi * twi, uint8_t addr, uint32_t iaddress,\r
+                    uint8_t isize, uint8_t len)\r
+{\r
+       twi->TWI_RHR;\r
+       twi->TWI_CR = TWI_CR_MSDIS;\r
+       twi->TWI_CR = TWI_CR_MSEN;\r
+       twi->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS | TWI_CR_ACMEN;\r
+       twi->TWI_ACR = 0;\r
+       twi->TWI_ACR = TWI_ACR_DATAL(len);\r
+       twi->TWI_MMR = 0;\r
+       twi->TWI_MMR = TWI_MMR_DADR(addr) | TWI_MMR_IADRSZ(isize);\r
+       /* Set internal address bytes. */\r
+       twi->TWI_IADR = 0;\r
+       twi->TWI_IADR = iaddress;\r
+}\r
+\r
+void twi_init_read_transfert(Twi * twi, uint8_t addr, uint32_t iaddress,\r
+                    uint8_t isize, uint8_t len)\r
+{\r
+       twi->TWI_RHR;\r
+       twi->TWI_CR = TWI_CR_MSEN | TWI_CR_SVDIS | TWI_CR_ACMEN;\r
+       twi->TWI_ACR = 0;\r
+       twi->TWI_ACR = TWI_ACR_DATAL(len) | TWI_ACR_DIR;\r
+       twi->TWI_MMR = 0;\r
+       twi->TWI_MMR = TWI_MMR_DADR(addr) | TWI_MMR_MREAD\r
+               | TWI_MMR_IADRSZ(isize);\r
+       /* Set internal address bytes. */\r
+       twi->TWI_IADR = 0;\r
+       twi->TWI_IADR = iaddress;\r
+       twi->TWI_CR = TWI_CR_START;\r
+       while(twi->TWI_SR & TWI_SR_TXCOMP);\r
+}\r
+#endif\r
+\r
+#ifdef CONFIG_HAVE_TWI_FIFO\r
+void twi_fifo_configure(Twi* twi, uint8_t tx_thres,\r
+                       uint8_t rx_thres,\r
+                       uint32_t ready_modes)\r
+{\r
+       /* Disable TWI master and slave mode and activate FIFO */\r
+       twi->TWI_CR = TWI_CR_MSDIS | TWI_CR_SVDIS | TWI_CR_FIFOEN;\r
+\r
+       /* Configure FIFO */\r
+       twi->TWI_FMR = TWI_FMR_TXFTHRES(tx_thres) | TWI_FMR_RXFTHRES(rx_thres)\r
+               | ready_modes;\r
+}\r
+\r
+uint32_t twi_fifo_rx_size(Twi *twi)\r
+{\r
+       return (twi->TWI_FLR & TWI_FLR_RXFL_Msk) >> TWI_FLR_RXFL_Pos;\r
+}\r
+\r
+uint32_t twi_fifo_tx_size(Twi *twi)\r
+{\r
+       return (twi->TWI_FLR & TWI_FLR_TXFL_Msk) >> TWI_FLR_TXFL_Pos;\r
+}\r
+\r
+uint32_t twi_read_stream(Twi *twi, uint32_t addr, uint32_t iaddr,\r
+                         uint32_t isize, const void *stream, uint8_t len)\r
+{\r
+       const uint8_t* buffer = stream;\r
+       uint8_t left = len;\r
+\r
+       twi_init_read_transfert(twi, addr, iaddr, isize, len);\r
+       if (twi_get_status(twi) & TWI_SR_NACK) {\r
+               trace_error("twid2: command NACK!\r\n");\r
+               return 0;\r
+       }\r
+\r
+       while (left > 0) {\r
+               if ((twi->TWI_SR & TWI_SR_RXRDY) == 0) continue;\r
+\r
+               /* Get FIFO free size (int octet) and clamp it */\r
+               uint32_t buf_size = twi_fifo_rx_size(twi);\r
+               buf_size = buf_size > left ? left : buf_size;\r
+\r
+               /* Fill the FIFO as must as possible */\r
+               while (buf_size > sizeof(uint32_t)) {\r
+                       *(uint32_t*)buffer = twi->TWI_RHR;\r
+                       buffer += sizeof(uint32_t);\r
+                       left -= sizeof(uint32_t);\r
+                       buf_size -= sizeof(uint32_t);\r
+               }\r
+               while (buf_size >= sizeof(uint8_t)) {\r
+                       readb(&twi->TWI_RHR, (uint8_t*)buffer);\r
+                       buffer += sizeof(uint8_t);\r
+                       left -= sizeof(uint8_t);\r
+                       buf_size -= sizeof(uint8_t);\r
+               }\r
+       }\r
+       return len - left;\r
+}\r
+\r
+uint32_t twi_write_stream(Twi *twi, uint32_t addr, uint32_t iaddr,\r
+                         uint32_t isize, const void *stream, uint8_t len)\r
+{\r
+       const uint8_t* buffer = stream;\r
+       uint8_t left = len;\r
+\r
+       int32_t fifo_size = get_peripheral_fifo_depth(twi);\r
+       if (fifo_size < 0)\r
+               return 0;\r
+\r
+       twi_init_write_transfert(twi, addr, iaddr, isize, len);\r
+       if (twi_get_status(twi) & TWI_SR_NACK) {\r
+               trace_error("twid2: command NACK!\r\n");\r
+               return 0;\r
+       }\r
+       while (left > 0) {\r
+               if ((twi->TWI_SR & TWI_SR_TXRDY) == 0) continue;\r
+\r
+               /* Get FIFO free size (int octet) and clamp it */\r
+               uint32_t buf_size = fifo_size - twi_fifo_tx_size(twi);\r
+               buf_size = buf_size > left ? left : buf_size;\r
+\r
+               /* /\* Fill the FIFO as must as possible *\/ */\r
+               while (buf_size >= sizeof(uint8_t)) {\r
+                       writeb(&twi->TWI_THR,*buffer);\r
+                       buffer += sizeof(uint8_t);\r
+                       left -= sizeof(uint8_t);\r
+                       buf_size -= sizeof(uint8_t);\r
+               }\r
+       }\r
+       return len - left;\r
+}\r
+\r
+#endif\r