aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/platforms/AT91SAM7
diff options
context:
space:
mode:
authorbarthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-11-13 14:56:40 +0000
committerbarthess <barthess@35acf78f-673a-0410-8e92-d51de3d6d3f4>2012-11-13 14:56:40 +0000
commitaf931af9e56865fb991216f33300066fe54ee5d7 (patch)
tree8e973b3a1ccf4418855e2b68b43b372c02bb5615 /os/hal/platforms/AT91SAM7
parent5649879b0ae63a5623a41bfcc43705002de36cd3 (diff)
downloadChibiOS-af931af9e56865fb991216f33300066fe54ee5d7.tar.gz
ChibiOS-af931af9e56865fb991216f33300066fe54ee5d7.tar.bz2
ChibiOS-af931af9e56865fb991216f33300066fe54ee5d7.zip
AT91SAM7A3. I2C basic support added.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4819 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/hal/platforms/AT91SAM7')
-rwxr-xr-xos/hal/platforms/AT91SAM7/i2c_lld.c399
-rwxr-xr-xos/hal/platforms/AT91SAM7/i2c_lld.h208
-rw-r--r--os/hal/platforms/AT91SAM7/platform.mk1
3 files changed, 608 insertions, 0 deletions
diff --git a/os/hal/platforms/AT91SAM7/i2c_lld.c b/os/hal/platforms/AT91SAM7/i2c_lld.c
new file mode 100755
index 000000000..e9a48d295
--- /dev/null
+++ b/os/hal/platforms/AT91SAM7/i2c_lld.c
@@ -0,0 +1,399 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file SAM7/i2c_lld.c
+ * @brief SAM7 I2C subsystem low level driver source.
+ * @note I2C peripheral interrupts on SAM7 platform must have highest
+ * priority in system.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief I2C1 driver identifier.*/
+#if SAM7_I2C_USE_I2C1 || defined(__DOXYGEN__)
+I2CDriver I2CD1;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Wakes up the waiting thread.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] msg wakeup message
+ *
+ * @notapi
+ */
+#define wakeup_isr(i2cp, msg) { \
+ chSysLockFromIsr(); \
+ if ((i2cp)->thread != NULL) { \
+ Thread *tp = (i2cp)->thread; \
+ (i2cp)->thread = NULL; \
+ tp->p_u.rdymsg = (msg); \
+ chSchReadyI(tp); \
+ } \
+ chSysUnlockFromIsr(); \
+}
+
+/**
+ * @brief Helper function.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void _i2c_lld_serve_rx_interrupt(I2CDriver *i2cp){
+ if (i2cp->rxbytes == 1)
+ AT91C_BASE_TWI->TWI_CR |= AT91C_TWI_STOP;
+
+ *(i2cp->rxbuf) = AT91C_BASE_TWI->TWI_RHR;
+ i2cp->rxbuf++;
+ i2cp->rxbytes--;
+ if (i2cp->rxbytes == 0){
+ AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_RXRDY;
+ AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXCOMP;
+ }
+}
+
+/**
+ * @brief Helper function.
+ * @note During write operation you do not need to set STOP manually.
+ * It sets automatically when THR and shift registers becomes empty.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void _i2c_lld_serve_tx_interrupt(I2CDriver *i2cp){
+
+ if (i2cp->txbytes == 0){
+ AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXRDY;
+ AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXCOMP;
+ }
+ else{
+ AT91C_BASE_TWI->TWI_THR = *(i2cp->txbuf);
+ i2cp->txbuf++;
+ i2cp->txbytes--;
+ }
+}
+
+/**
+ * @brief I2C shared ISR code.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void i2c_lld_serve_interrupt(I2CDriver *i2cp) {
+
+ uint32_t sr;
+ sr = AT91C_BASE_TWI->TWI_SR;
+ /* this masking doing in official Atmel driver. Is it needed ??? */
+ sr &= AT91C_BASE_TWI->TWI_IMR;
+
+ if (sr & AT91C_TWI_NACK){
+ i2cp->errors |= I2CD_ACK_FAILURE;
+ wakeup_isr(i2cp, RDY_RESET);
+ return;
+ }
+ if (sr & AT91C_TWI_RXRDY){
+ _i2c_lld_serve_rx_interrupt(i2cp);
+ }
+ else if (sr & AT91C_TWI_TXRDY){
+ _i2c_lld_serve_tx_interrupt(i2cp);
+ }
+ else if (sr & AT91C_TWI_TXCOMP){
+ AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXCOMP;
+ wakeup_isr(i2cp, RDY_OK);
+ }
+ else
+ chDbgPanic("Invalid value");
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SAM7_I2C_USE_I2C1 || defined(__DOXYGEN__)
+/**
+ * @brief I2C1 event interrupt handler.
+ *
+ * @notapi
+ */
+CH_IRQ_HANDLER(TWI_IRQHandler) {
+
+ CH_IRQ_PROLOGUE();
+ i2c_lld_serve_interrupt(&I2CD1);
+ AT91C_BASE_AIC->AIC_EOICR = 0;
+ CH_IRQ_EPILOGUE();
+}
+#endif /* STM32_I2C_USE_I2C1 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level I2C driver initialization.
+ *
+ * @notapi
+ */
+void i2c_lld_init(void) {
+
+#if SAM7_I2C_USE_I2C1
+ i2cObjectInit(&I2CD1);
+ I2CD1.thread = NULL;
+ I2CD1.txbuf = NULL;
+ I2CD1.rxbuf = NULL;
+ I2CD1.txbytes = 0;
+ I2CD1.rxbytes = 0;
+
+ AT91C_BASE_PIOA->PIO_PDR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
+ AT91C_BASE_PIOA->PIO_ASR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
+ AT91C_BASE_PIOA->PIO_MDER = AT91C_PA0_TWD | AT91C_PA1_TWCK;
+ AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
+
+ AIC_ConfigureIT(AT91C_ID_TWI,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, TWI_IRQHandler);
+#endif /* STM32_I2C_USE_I2C1 */
+}
+
+/**
+ * @brief Configures and activates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_start(I2CDriver *i2cp) {
+
+ volatile uint32_t fake;
+
+ /* If in stopped state then enables the I2C clocks.*/
+ if (i2cp->state == I2C_STOP) {
+
+#if SAM7_I2C_USE_I2C1
+ if (&I2CD1 == i2cp) {
+ /* enable peripheral clock */
+ AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TWI);
+
+ /* Enables associated interrupt vector.*/
+ AIC_EnableIT(AT91C_ID_TWI);
+
+ /* Reset */
+ AT91C_BASE_TWI->TWI_CR = AT91C_TWI_SWRST;
+ fake = AT91C_BASE_TWI->TWI_RHR;
+
+ /* Set master mode */
+ AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSDIS;
+ AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSEN;
+
+ /* Setup I2C parameters. */
+ AT91C_BASE_TWI->TWI_CWGR = i2cp->config->cwgr;
+ }
+#endif /* STM32_I2C_USE_I2C1 */
+ }
+
+ (void)fake;
+}
+
+/**
+ * @brief Deactivates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_stop(I2CDriver *i2cp) {
+
+ /* If not in stopped state then disables the I2C clock.*/
+ if (i2cp->state != I2C_STOP) {
+
+#if SAM7_I2C_USE_I2C1
+ if (&I2CD1 == i2cp) {
+ AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY |
+ AT91C_TWI_TXRDY | AT91C_TWI_NACK;
+ AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_TWI);
+ AIC_DisableIT(AT91C_ID_TWI);
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Receives data via the I2C bus as master.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received
+ * @param[in] timeout this value is ignored on SAM7 platform.
+ * .
+ * @return The operation status.
+ * @retval RDY_OK if the function succeeded.
+ * @retval RDY_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout) {
+ (void)timeout;
+
+ /* delete trash from RHR*/
+ volatile uint32_t fake;
+ fake = AT91C_BASE_TWI->TWI_RHR;
+ (void)fake;
+
+ /* Initializes driver fields.*/
+ i2cp->rxbuf = rxbuf;
+ i2cp->rxbytes = rxbytes;
+
+ i2cp->txbuf = NULL;
+ i2cp->txbytes = 0;
+
+ /* tune master mode register */
+ AT91C_BASE_TWI->TWI_MMR = 0;
+ AT91C_BASE_TWI->TWI_MMR |= (addr << 16) | AT91C_TWI_MREAD;
+
+ /* enable just needed interrupts */
+ AT91C_BASE_TWI->TWI_IER = AT91C_TWI_RXRDY | AT91C_TWI_NACK;
+
+ /* In single data byte master read or write, the START and STOP must both be set. */
+ uint32_t cr = AT91C_TWI_START;
+ if (rxbytes == 1)
+ cr |= AT91C_TWI_STOP;
+ AT91C_BASE_TWI->TWI_CR = cr;
+
+ /* Waits for the operation completion.*/
+ i2cp->thread = chThdSelf();
+ chSchGoSleepS(THD_STATE_SUSPENDED);
+
+ return chThdSelf()->p_u.rdymsg;
+}
+
+/**
+ * @brief Transmits data via the I2C bus as master.
+ * @details When performing reading through write you can not write more than
+ * 3 bytes of data to I2C slave. This is SAM7 platform limitation.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address
+ * @param[in] txbuf pointer to the transmit buffer
+ * @param[in] txbytes number of bytes to be transmitted
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received
+ * @param[in] timeout this value is ignored on SAM7 platform.
+ * .
+ * @return The operation status.
+ * @retval RDY_OK if the function succeeded.
+ * @retval RDY_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout) {
+ (void)timeout;
+
+ /* SAM7 specific check */
+ chDbgCheck(((rxbytes == 0) || ((txbytes > 0) && (txbytes < 4) && (rxbuf != NULL))),
+ "i2c_lld_master_transmit_timeout");
+
+ /* prepare to read through write operation */
+ if (rxbytes > 0){
+ AT91C_BASE_TWI->TWI_MMR |= txbytes << 8;
+
+ /* store internal slave address in TWI_IADR registers */
+ AT91C_BASE_TWI->TWI_IADR = 0;
+ while (txbytes > 0){
+ AT91C_BASE_TWI->TWI_IADR = (AT91C_BASE_TWI->TWI_IADR << 8);
+ AT91C_BASE_TWI->TWI_IADR |= *(txbuf++);
+ txbytes--;
+ }
+ /* Internal address of I2C slave was set in special Atmel registers.
+ * Now we must call read function. The I2C cell automatically sends
+ * bytes from IADR register to bus and issues repeated start. */
+ return i2c_lld_master_receive_timeout(i2cp, addr, rxbuf, rxbytes, timeout);
+ }
+ else{
+ if (txbytes == 1){
+ /* In single data byte master read or write, the START and STOP
+ * must both be set. */
+ AT91C_BASE_TWI->TWI_CR |= AT91C_TWI_STOP;
+ }
+ AT91C_BASE_TWI->TWI_MMR = 0;
+ AT91C_BASE_TWI->TWI_MMR |= addr << 16;
+
+ /* enable just needed interrupts */
+ AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
+
+ /* correct size and pointer because first byte will be written
+ * for issue start condition */
+ i2cp->txbuf = txbuf + 1;
+ i2cp->txbytes = txbytes - 1;
+
+ /* According to datasheet there is no need to set START manually
+ * we just need to write first byte in THR */
+ AT91C_BASE_TWI->TWI_THR = txbuf[0];
+
+ /* Waits for the operation completion.*/
+ i2cp->thread = chThdSelf();
+ chSchGoSleepS(THD_STATE_SUSPENDED);
+
+ return chThdSelf()->p_u.rdymsg;
+ }
+}
+
+#endif /* HAL_USE_I2C */
+
+/** @} */
diff --git a/os/hal/platforms/AT91SAM7/i2c_lld.h b/os/hal/platforms/AT91SAM7/i2c_lld.h
new file mode 100755
index 000000000..cf7d2705c
--- /dev/null
+++ b/os/hal/platforms/AT91SAM7/i2c_lld.h
@@ -0,0 +1,208 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
+ 2011,2012 Giovanni Di Sirio.
+
+ This file is part of ChibiOS/RT.
+
+ ChibiOS/RT is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS/RT is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/**
+ * @file SAM72/i2c_lld.h
+ * @brief SAM72 I2C subsystem low level driver header.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#ifndef _I2C_LLD_H_
+#define _I2C_LLD_H_
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Peripheral clock frequency.
+ */
+#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000)
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief I2C1 driver enable switch.
+ * @details If set to @p TRUE the support for I2C1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SAM7_I2C_USE_I2C1) || defined(__DOXYGEN__)
+#define SAM7_I2C_USE_I2C1 FALSE
+#endif
+
+/**
+ * @brief I2C1 interrupt priority level setting.
+ */
+#if !defined(SAM7_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SAM7_I2C_I2C1_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/** @brief error checks */
+#if !SAM7_I2C_USE_I2C1
+#error "I2C driver activated but no I2C peripheral assigned"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type representing I2C address.
+ */
+typedef uint16_t i2caddr_t;
+
+/**
+ * @brief I2C Driver condition flags type.
+ */
+typedef uint32_t i2cflags_t;
+
+/**
+ * @brief Driver configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief CWGR regitster content.
+ */
+ uint32_t cwgr;
+} I2CConfig;
+
+/**
+ * @brief Type of a structure representing an I2C driver.
+ */
+typedef struct I2CDriver I2CDriver;
+
+/**
+ * @brief Structure representing an I2C driver.
+ */
+struct I2CDriver{
+ /**
+ * @brief Driver state.
+ */
+ i2cstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const I2CConfig *config;
+ /**
+ * @brief Error flags.
+ */
+ i2cflags_t errors;
+ /**
+ * @brief Pointer to receive buffer.
+ */
+ uint8_t *rxbuf;
+ /**
+ * @brief Pointer to transmit buffer.
+ */
+ const uint8_t *txbuf;
+ /**
+ * @brief Bytes count to be received.
+ */
+ size_t rxbytes;
+ /**
+ * @brief Bytes count to be transmitted.
+ */
+ size_t txbytes;
+#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+#if CH_USE_MUTEXES || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the bus.
+ */
+ Mutex mutex;
+#elif CH_USE_SEMAPHORES
+ Semaphore semaphore;
+#endif
+#endif /* I2C_USE_MUTUAL_EXCLUSION */
+#if defined(I2C_DRIVER_EXT_FIELDS)
+ I2C_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Thread waiting for I/O completion.
+ */
+ Thread *thread;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Get errors from I2C driver.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(__DOXYGEN__)
+#if SAM7_I2C_USE_I2C1
+extern I2CDriver I2CD1;
+#endif
+
+#endif /* !defined(__DOXYGEN__) */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void i2c_lld_init(void);
+ void i2c_lld_start(I2CDriver *i2cp);
+ void i2c_lld_stop(I2CDriver *i2cp);
+ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout);
+ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_I2C */
+
+#endif /* _I2C_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/platforms/AT91SAM7/platform.mk b/os/hal/platforms/AT91SAM7/platform.mk
index 83b53491a..a460b8532 100644
--- a/os/hal/platforms/AT91SAM7/platform.mk
+++ b/os/hal/platforms/AT91SAM7/platform.mk
@@ -1,6 +1,7 @@
# List of all the AT91SAM7 platform files.
PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/AT91SAM7/hal_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/pal_lld.c \
+ ${CHIBIOS}/os/hal/platforms/AT91SAM7/i2c_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/ext_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/serial_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/spi_lld.c \