From af931af9e56865fb991216f33300066fe54ee5d7 Mon Sep 17 00:00:00 2001 From: barthess Date: Tue, 13 Nov 2012 14:56:40 +0000 Subject: [PATCH] AT91SAM7A3. I2C basic support added. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@4819 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/AT91SAM7/i2c_lld.c | 399 ++++++++++++++++++++++++++ os/hal/platforms/AT91SAM7/i2c_lld.h | 208 ++++++++++++++ os/hal/platforms/AT91SAM7/platform.mk | 1 + readme.txt | 1 + 4 files changed, 609 insertions(+) create mode 100755 os/hal/platforms/AT91SAM7/i2c_lld.c create mode 100755 os/hal/platforms/AT91SAM7/i2c_lld.h 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 . +*/ +/* + 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 . +*/ +/* + 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 \ diff --git a/readme.txt b/readme.txt index 9d1020c99..b63beac99 100644 --- a/readme.txt +++ b/readme.txt @@ -133,6 +133,7 @@ (backported to 2.4.3). - FIX: Fixed STM8L, cosmic compiler: c_lreg not saved (bug 3566342)(backported to 2.2.10 and 2.4.3). +- NEW: AT91SAM7A3 I2C support. - NEW: AT91SAM7A3 basic support. - NEW: Unified the STM32F4xx and STM32F2xx platform code. The STM32F2xx now is only supported as an STM32F4xx variant and not tested separately.