| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980 |
- /*
- * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
- * Copyright (C) 2012, Ole Reinhardt <ole.reinhardt@embedded-it.de>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- *
- */
- /*!
- * \file arch/avr/dev/twif.c
- * \brief AVR TWI support.
- *
- * \verbatim
- * $Id: twif.c 4937 2013-01-22 11:38:42Z haraldkipp $
- * \endverbatim
- */
- #include <string.h>
- #include <dev/irqreg.h>
- #include <sys/event.h>
- #include <sys/atom.h>
- #include <sys/timer.h>
- #include <sys/thread.h>
- #include <sys/heap.h>
- #include <dev/twif.h>
- #ifdef __AVR_ENHANCED__
- /*
- TWINT TWEA TWSTA TWSTO TWWC TWEN 0 TWIE
- C R R
- */
- #define TWGO (_BV(TWINT) | _BV(TWEN) | _BV(TWIE))
- /*
- * TWI interrupt handler.
- */
- static void TwInterrupt(void *arg)
- {
- NUTTWIBUS *bus = arg;
- NUTTWIICB *icb = bus->bus_icb;
- uint8_t twsr;
- register uint8_t twcr = inb(TWCR);
- /*
- * Read the status and interpret its contents.
- */
- twsr = inb(TWSR) & 0xF8;
- switch (twsr) {
- /*
- * 0x08: Start condition has been transmitted.
- * 0x10: Repeated start condition has been transmitted.
- */
- case TW_START:
- case TW_REP_START:
- /* We are entering the master mode. Mark the interface busy. */
- icb->tw_if_busy = 1;
- icb->tw_mm_txidx = 0;
- icb->tw_mm_rxidx = 0;
- /*
- * If outgoing data is available, transmit SLA+W. Logic is in
- * master transmit mode.
- */
- if (icb->tw_mm_txlen) {
- outb(TWDR, icb->tw_mm_sla);
- }
- /*
- * If outgoing data not available, transmit SLA+R. Logic will
- * switch to master receiver mode.
- */
- else {
- outb(TWDR, icb->tw_mm_sla | 1);
- }
- outb(TWCR, TWGO | (twcr & _BV(TWEA)));
- break;
- /*
- * 0x18: SLA+W has been transmitted and ACK has been received.
- * 0x28: Data byte has been transmitted and ACK has been received.
- */
- case TW_MT_SLA_ACK:
- case TW_MT_DATA_ACK:
- /*
- * If outgoing data left to send, put the next byte in the data
- * register.
- */
- if (icb->tw_mm_txidx < icb->tw_mm_txlen) {
- outb(TWDR, icb->tw_mm_txbuf[icb->tw_mm_txidx]);
- /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
- icb->tw_mm_txidx++;
- outb(TWCR, TWGO | (twcr & _BV(TWEA)));
- break;
- }
- /*
- * All outgoing data has been sent. If a response is expected,
- * transmit a repeated start condition.
- */
- icb->tw_mm_txlen = 0;
- if (icb->tw_mm_rxlen) {
- outb(TWCR, TWGO | (twcr & _BV(TWEA)) | _BV(TWSTA));
- break;
- }
- /*
- * 0x20: SLA+W has been transmitted, but not acknowledged.
- * 0x30: Data byte has been transmitted, but not acknowledged.
- * 0x48: SLA+R has been transmitted, but not acknowledged.
- */
- case TW_MT_SLA_NACK:
- case TW_MT_DATA_NACK:
- case TW_MR_SLA_NACK:
- /* Set unique error code. */
- if (twsr == TW_MT_SLA_NACK || twsr == TW_MR_SLA_NACK) {
- icb->tw_mm_err = TWERR_SLA_NACK;
- icb->tw_mm_txlen = 0;
- icb->tw_mm_rxlen = 0;
- }
- /* Wake up the application. */
- NutEventPostFromIrq(&icb->tw_mm_mtx);
- /*
- * Send a stop condition. If we have a listener, generate
- * an acknowlegde on an incoming address byte.
- */
- if(icb->tw_sm_rxlen) {
- outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO));
- }
- else {
- outb(TWCR, TWGO | _BV(TWSTO));
- }
- /* The interface is idle. */
- icb->tw_if_busy = 0;
- break;
- /*
- * 0x38: Arbitration lost while in master mode.
- */
- case TW_MT_ARB_LOST:
- /*
- * The start condition will be automatically resend after
- * the bus becomes available.
- */
- sbi(TWCR, TWSTA);
- /* The interface is idle. */
- icb->tw_if_busy = 0;
- break;
- /*
- * 0x50: Data byte has been received and acknowledged.
- */
- case TW_MR_DATA_ACK:
- /*
- * Store the data byte in the master receive buffer.
- */
- icb->tw_mm_rxbuf[icb->tw_mm_rxidx] = inb(TWDR);
- /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
- icb->tw_mm_rxidx++;
- /*
- * 0x40: SLA+R has been transmitted and ACK has been received.
- */
- case TW_MR_SLA_ACK:
- /*
- * Acknowledge next data bytes except the last one.
- */
- if (icb->tw_mm_rxidx + 1 < icb->tw_mm_rxlen) {
- outb(TWCR, TWGO | _BV(TWEA));
- }
- else {
- outb(TWCR, TWGO);
- }
- break;
- /*
- * 0x58: Data byte has been received, but not acknowledged.
- */
- case TW_MR_DATA_NACK:
- /*
- * Store the last data byte.
- */
- icb->tw_mm_rxbuf[icb->tw_mm_rxidx] = inb(TWDR);
- /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
- icb->tw_mm_rxidx++;
- icb->tw_mm_rxlen = 0;
- /* Wake up the application. */
- NutEventPostFromIrq(&icb->tw_mm_mtx);
- /*
- * Send a stop condition. If we have a listener, generate
- * an acknowlegde on an incoming address byte.
- */
- if(icb->tw_sm_rxlen) {
- outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO));
- }
- else {
- outb(TWCR, TWGO | _BV(TWSTO));
- }
- /* The interface is idle. */
- icb->tw_if_busy = 0;
- break;
- /*
- * 0x60: Own SLA+W has been received and acknowledged.
- * 0x68: Arbitration lost as master. Own SLA+W has been received
- * and acknowledged.
- * 0x70: General call address has been received and acknowledged.
- * 0x78: Arbitration lost as master. General call address has been
- * received and acknowledged.
- */
- case TW_SR_SLA_ACK:
- case TW_SR_ARB_LOST_SLA_ACK:
- case TW_SR_GCALL_ACK:
- case TW_SR_ARB_LOST_GCALL_ACK:
- /*
- * Do only acknowledge incoming data bytes, if we got receive
- * buffer space. Fetch the slave address from the data register
- * and reset the receive index.
- */
- if (icb->tw_sm_rxlen) {
- /* We are entering the slave receive mode. Mark the interface busy. */
- icb->tw_if_busy = 1;
- icb->tw_sm_sla = inb(TWDR);
- outb(TWCR, TWGO | _BV(TWEA));
- icb->tw_sm_rxidx = 0;
- }
- /*
- * Do not acknowledge incoming data.
- */
- else {
- outb(TWCR, TWGO);
- }
- break;
- /*
- * 0x80: Data byte for own SLA has been received and acknowledged.
- * 0x90: Data byte for general call address has been received and
- * acknowledged.
- */
- case TW_SR_DATA_ACK:
- case TW_SR_GCALL_DATA_ACK:
- /*
- * If the receive buffer isn't filled up, store data byte.
- */
- if (icb->tw_sm_rxidx < icb->tw_sm_rxlen) {
- icb->tw_sm_rxbuf[icb->tw_sm_rxidx] = inb(TWDR);
- /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
- icb->tw_sm_rxidx++;
- }
- else {
- icb->tw_sm_rxlen = 0;
- }
- /*
- * If more space is available for incoming data, then continue
- * receiving. Otherwise do not acknowledge new data bytes.
- */
- if (icb->tw_sm_rxlen) {
- outb(TWCR, TWGO | _BV(TWEA));
- break;
- }
- /*
- * 0x88: Data byte received, but not acknowledged.
- * 0x98: Data byte for general call address received, but not
- * acknowledged.
- */
- case TW_SR_DATA_NACK:
- case TW_SR_GCALL_DATA_NACK:
- /*
- * Continue not accepting more data.
- */
- if (icb->tw_mm_txlen || icb->tw_mm_rxlen) {
- outb(TWCR, inb(TWCR) | _BV(TWEA) | _BV(TWSTA));
- }
- else {
- outb(TWCR, inb(TWCR) | _BV(TWEA));
- }
- break;
- /*
- * 0xA0: Stop condition or repeated start condition received.
- */
- case TW_SR_STOP:
- /*
- * Wake up the application. If successful, do nothing. This
- * will keep SCL low and thus block the bus. The application
- * must now setup the transmit buffer and re-enable the
- * interface.
- */
- if (icb->tw_sm_rxmtx == 0 || icb->tw_sm_err) {
- /*
- * If no one has been waiting on the queue, the application
- * probably gave up waiting. So we continue on our own, either
- * in idle mode or switching to master mode if a master
- * request is waiting.
- */
- if (icb->tw_mm_txlen || icb->tw_mm_rxlen) {
- outb(TWCR, TWGO | _BV(TWSTA));
- }
- else {
- outb(TWCR, TWGO);
- }
- icb->tw_if_busy = 0;
- }
- else {
- NutEventPostFromIrq(&icb->tw_sm_rxmtx);
- icb->tw_sm_rxlen = 0;
- outb(TWCR, twcr & ~(_BV(TWINT) | _BV(TWIE)));
- }
- break;
- /*
- * 0xA8: Own SLA+R has been received and acknowledged.
- * 0xB0: Arbitration lost in master mode. Own SLA has been received
- * and acknowledged.
- */
- case TW_ST_SLA_ACK:
- case TW_ST_ARB_LOST_SLA_ACK:
- /* Not idle. */
- icb->tw_if_busy = 1;
- /* Reset transmit index and fall through for outgoing data. */
- icb->tw_sm_txidx = 0;
- /*
- * 0xB8: Data bytes has been transmitted and acknowledged.
- */
- case TW_ST_DATA_ACK:
- /*
- * If outgoing data left to send, put the next byte in the
- * data register. Otherwise transmit a dummy byte.
- */
- if (icb->tw_sm_txidx < icb->tw_sm_txlen) {
- outb(TWDR, icb->tw_sm_txbuf[icb->tw_sm_txidx]);
- /* Do not set acknowledge on the last data byte. */
- /* Early increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */
- ++icb->tw_sm_txidx;
- if (icb->tw_sm_txidx < icb->tw_sm_txlen) {
- outb(TWCR, TWGO | _BV(TWEA));
- }
- else {
- icb->tw_sm_txlen = 0;
- outb(TWCR, TWGO);
- }
- break;
- }
- /* No more data. Continue sending dummies. */
- outb(TWDR, 0);
- outb(TWCR, TWGO);
- break;
- /*
- * 0xC0: Data byte has been transmitted, but not acknowledged.
- * 0xC8: Last data byte has been transmitted and acknowledged.
- */
- case TW_ST_DATA_NACK:
- case TW_ST_LAST_DATA:
- NutEventPostFromIrq(&icb->tw_sm_txmtx);
- /* Transmit start condition, if a master transfer is waiting. */
- if (icb->tw_mm_txlen || icb->tw_mm_rxlen) {
- outb(TWCR, TWGO | _BV(TWSTA) | /**/ _BV(TWEA));
- }
- /* Otherwise enter idle state. */
- else {
- outb(TWCR, TWGO | _BV(TWEA));
- }
- icb->tw_if_busy = 0;
- break;
- /*
- * 0x00: Bus error.
- */
- case TW_BUS_ERROR:
- outb(TWCR, inb(TWCR) | _BV(TWSTO));
- #if 1
- icb->tw_if_busy = 0;
- icb->tw_mm_err = TWERR_BUS;
- icb->tw_sm_err = TWERR_BUS;
- NutEventPostFromIrq(&icb->tw_sm_rxmtx);
- NutEventPostFromIrq(&icb->tw_sm_txmtx);
- NutEventPostFromIrq(&icb->tw_mm_mtx);
- #endif
- break;
- }
- }
- #endif /* __AVR_ENHANCED__ */
- /*!
- * \brief Transmit and/or receive data as a master.
- *
- * The two-wire serial interface must have been initialized by calling
- * TwInit() before this function can be used.
- *
- * \note This function is only available on ATmega128 systems.
- *
- * \param sla Slave address of the destination. This slave address
- * must be specified as a 7-bit address. For example, the
- * PCF8574A may be configured to slave addresses from 0x38
- * to 0x3F.
- * \param txdata Points to the data to transmit. Ignored, if the number
- * of data bytes to transmit is zero.
- * \param txlen Number of data bytes to transmit. If zero, then the
- * interface will not send any data to the slave device
- * and will directly enter the master receive mode.
- * \param rxdata Points to a buffer, where the received data will be
- * stored. Ignored, if the maximum number of bytes to
- * receive is zero.
- * \param rxsiz Maximum number of bytes to receive. Set to zero, if
- * no bytes are expected from the slave device.
- * \param tmo Timeout in milliseconds. To disable timeout, set this
- * parameter to NUT_WAIT_INFINITE.
- *
- * \return The number of bytes received, -1 in case of an error or timeout.
- */
- int NutTwiMasterTranceive(NUTTWIBUS *bus, uint8_t sla, const void *txdata, uint16_t txlen, void *rxdata, uint16_t rxsiz, uint32_t tmo)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- NUTTWIICB *icb = bus->bus_icb;
- int rc = -1;
- /* This routine is marked reentrant, so lock the interface. */
- if(NutEventWait(&bus->bus_mutex, tmo)) {
- icb->tw_mm_err = TWERR_IF_LOCKED;
- NutEventPost(&bus->bus_mutex);
- return -1;
- }
- if (icb->tw_if_busy)
- NutSleep(1);
- while(icb->tw_if_busy) {
- if (tmo == 1)
- return -1;
- NutSleep(1);
- tmo --;
- }
- NutEnterCritical();
- /*
- * Set all parameters for master mode.
- */
- icb->tw_mm_sla = sla << 1;
- icb->tw_mm_err = 0;
- icb->tw_mm_txlen = txlen;
- icb->tw_mm_txbuf = txdata;
- icb->tw_mm_rxlen = rxsiz;
- icb->tw_mm_rxbuf = rxdata;
- /*
- * Send a start condition if the interface is idle. If busy, then
- * the interrupt routine will automatically initiate the transfer
- * as soon as the interface becomes ready again.
- */
- if(icb->tw_if_busy == 0) {
- uint8_t twcr = inb(TWCR);
- uint8_t twsr = inb(TWSR);
- if((twsr & 0xF8) == TW_NO_INFO) {
- if(icb->tw_sm_rxlen) {
- outb(TWCR, _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWSTA) | (twcr & _BV(TWSTO)));
- }
- else {
- outb(TWCR, _BV(TWEN) | _BV(TWIE) | _BV(TWSTA) | (twcr & _BV(TWSTO)));
- }
- }
- }
- /* Clear the queue. */
- //*broken?! NutEventBroadcastAsync(&tw_mm_mtx);
- if (icb->tw_mm_mtx == SIGNALED) {
- icb->tw_mm_mtx = 0;
- }
- NutExitCritical();
- /*
- * Wait for master transmission done.
- */
- rc = -1;
- if (NutEventWait(&icb->tw_mm_mtx, tmo)) {
- icb->tw_mm_error = TWERR_TIMEOUT;
- } else {
- NutEnterCritical();
- if (icb->tw_mm_err) {
- icb->tw_mm_error = icb->tw_mm_err;
- } else {
- rc = icb->tw_mm_rxidx;
- }
- NutExitCritical();
- }
- /*
- * Release the interface.
- */
- NutEventPost(&bus->bus_mutex);
- return rc;
- #endif /* __AVR_ENHANCED__ */
- }
- /*!
- * \brief Get last master mode error.
- *
- * You may call this function to determine the specific cause
- * of an error after TwMasterTransact() failed.
- *
- * \note This function is only available on ATmega128 systems.
- *
- */
- int NutTwiMasterError(NUTTWIBUS *bus)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = bus->bus_icb->tw_mm_error;
- bus->bus_icb->tw_mm_error = 0;
- return rc;
- #endif
- }
- /*!
- * \brief Listen for incoming data from a master.
- *
- * If this function returns without error, the bus is blocked. The caller
- * must immediately process the request and return a response by calling
- * TwSlaveRespond().
- *
- * \note This function is only available on ATmega128 systems. The
- * function is not reentrant.
- *
- * \param sla Points to a byte variable, which receives the slave
- * address sent by the master. This can be used by the
- * caller to determine whether the the interface has been
- * addressed by a general call or its individual address.
- * \param rxdata Points to a data buffer where the received data bytes
- * are stored.
- * \param rxsiz Specifies the maximum number of data bytes to receive.
- * \param tmo Timeout in milliseconds. To disable timeout,
- * set this parameter to NUT_WAIT_INFINITE.
- *
- * \return The number of bytes received, -1 in case of an error or timeout.
- */
- int NutTwiSlaveListen(NUTTWIBUS *bus, uint8_t *sla, void *rxdata, uint16_t rxsiz, uint32_t tmo)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = -1;
- NUTTWIICB *icb = bus->bus_icb;
- NutEnterCritical();
- /* Initialize parameters for slave receive. */
- icb->tw_sm_err = 0;
- icb->tw_sm_rxlen = rxsiz;
- icb->tw_sm_rxbuf = rxdata;
- /*
- * If the interface is currently not busy then enable it for
- * address recognition.
- */
- if(icb->tw_if_busy == 0) {
- uint8_t twsr = inb(TWSR);
- if((twsr & 0xF8) == TW_NO_INFO) {
- if(icb->tw_mm_txlen || icb->tw_mm_rxlen)
- outb(TWCR, _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA));
- else
- outb(TWCR, _BV(TWEA) | _BV(TWEN) | _BV(TWIE));
- }
- }
- /* Clear the queue. */
- //*broken?! NutEventBroadcastAsync(&tw_sm_rxmtx);
- if (icb->tw_sm_rxmtx == SIGNALED) {
- icb->tw_sm_rxmtx = 0;
- }
- NutExitCritical();
- /* Wait for a frame on the slave mode queue. */
- if (NutEventWait(&icb->tw_sm_rxmtx, tmo)) {
- NutEnterCritical();
- icb->tw_sm_err = TWERR_TIMEOUT;
- icb->tw_sm_rxlen = 0;
- NutExitCritical();
- }
- /*
- * Return the number of bytes received and the destination slave
- * address, if no slave error occured. In this case the bus is
- * blocked.
- */
- if(icb->tw_sm_err == 0) {
- rc = icb->tw_sm_rxidx;
- *sla = icb->tw_sm_sla;
- }
- return rc;
- #endif /* __AVR_ENHANCED__ */
- }
- /*!
- * \brief Send response to a master.
- *
- * This function must be called as soon as possible after TwSlaveListen()
- * returned successfully, even if no data needs to be returned. Not doing
- * so will completely block the bus.
- *
- * \note This function is only available on ATmega128 systems.
- *
- * \param txdata Points to the data to transmit. Ignored, if the
- * number of bytes to transmit is zero.
- * \param txlen Number of data bytes to transmit.
- * \param tmo Timeout in milliseconds. To disable timeout,
- * set this parameter to NUT_WAIT_INFINITE.
- *
- * \return The number of bytes transmitted, -1 in case of an error or timeout.
- */
- int NutTwiSlaveRespond(NUTTWIBUS *bus, void *txdata, uint16_t txlen, uint32_t tmo)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = -1;
- NUTTWIICB *icb = bus->bus_icb;
- /* The bus is blocked. No critical section required. */
- icb->tw_sm_txbuf = txdata;
- icb->tw_sm_txlen = txlen;
- /*
- * If there is anything to transmit, start the interface.
- */
- if (txlen) {
- NutEnterCritical();
- /* Clear the queue. */
- //*broken?! NutEventBroadcastAsync(&tw_sm_txmtx);
- if (icb->tw_sm_txmtx == SIGNALED) {
- icb->tw_sm_txmtx = 0;
- }
- /* Release the bus, accepting SLA+R. */
- outb(TWCR, TWGO | _BV(TWEA));
- NutExitCritical();
- if (NutEventWait(&icb->tw_sm_txmtx, tmo)) {
- icb->tw_sm_err = TWERR_TIMEOUT;
- }
- NutEnterCritical();
- icb->tw_sm_txlen = 0;
- if (icb->tw_sm_err) {
- icb->tw_sm_error = icb->tw_sm_err;
- } else {
- rc = icb->tw_sm_txidx;
- }
- NutExitCritical();
- }
- /*
- * Nothing to transmit.
- */
- else {
- rc = 0;
- /* Release the bus, not accepting SLA+R. */
- NutEnterCritical();
- inb(TWCR);
- inb(TWSR);
- /* Transmit start condition, if a master transfer is waiting. */
- if (icb->tw_mm_txlen || icb->tw_mm_rxlen) {
- outb(TWCR, TWGO | _BV(TWSTA));
- }
- /* Otherwise enter idle state. */
- else {
- icb->tw_if_busy = 0;
- outb(TWCR, TWGO);
- }
- NutExitCritical();
- }
- return rc;
- #endif /* __AVR_ENHANCED__ */
- }
- /*!
- * \brief Get last slave mode error.
- *
- * You may call this function to determine the specific cause
- * of an error after TwSlaveListen() or TwSlaveRespond() failed.
- *
- * \note This function is only available on ATmega128 systems.
- *
- */
- int NutTwiSlaveError(NUTTWIBUS *bus)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = bus->bus_icb->tw_sm_error;
- bus->bus_icb->tw_sm_error = 0;
- return rc;
- #endif
- }
- int NutTwiSetSpeed( NUTTWIBUS *bus, uint32_t speed)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = -1;
- uint32_t lval;
- if (speed > 400000) {
- /* Speed out of range */
- return rc;
- }
- if (bus==NULL) {
- /* No bus selected */
- return rc;
- }
- lval = ((2UL * NutGetCpuClock() / speed + 1UL) / 2UL - 16UL) / 2UL;
- if (lval > 1020UL) {
- lval /= 16UL;
- sbi(TWSR, TWPS1);
- } else {
- cbi(TWSR, TWPS1);
- }
- if (lval > 255UL) {
- lval /= 4UL;
- sbi(TWSR, TWPS0);
- } else {
- cbi(TWSR, TWPS0);
- }
- if (lval > 9UL && lval < 256UL) {
- outb(TWBR, (uint8_t) lval);
- rc = 0;
- } else {
- rc = -1;
- }
- return rc;
- #endif
- }
- /*!
- * \brief Request Current Speed of I2C Interface.
- *
- * \return 0..400000 for speed, -1 in case of error.
- */
- int NutTwiGetSpeed( NUTTWIBUS *bus, uint32_t *speed)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- uint32_t lval;
- if (bus) {
- lval = 2UL;
- if (bit_is_set(TWSR, TWPS0)) {
- lval *= 4UL;
- }
- if (bit_is_set(TWSR, TWPS1)) {
- lval *= 16UL;
- }
- *speed = NutGetCpuClock() / (16UL + lval * (uint32_t) inb(TWBR));
- }
- return 0;
- #endif
- }
- /*!
- * \brief Perform TWI control functions.
- *
- * This function is only available on ATmega128 systems.
- *
- * \param req Requested control function. May be set to one of the
- * following constants:
- * - TWI_SETSPEED, if conf points to an uint32_t value containing the bitrate.
- * - TWI_GETSPEED, if conf points to an uint32_t value receiving the current bitrate.
- * \param conf Points to a buffer that contains any data required for
- * the given control function or receives data from that
- * function.
- * \return 0 on success, -1 otherwise.
- *
- * \note Timeout is limited to the granularity of the system timer.
- *
- */
- int NutTwiIOCtl( NUTTWIBUS *bus, int req, void *conf )
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = 0;
- NUTTWIICB *icb = bus->bus_icb;
- switch (req) {
- case TWI_SETSPEED:
- rc = NutTwiSetSpeed(bus, *((uint32_t *)conf));
- break;
- case TWI_GETSPEED:
- rc = NutTwiGetSpeed(bus, ((uint32_t *)conf));
- break;
- case TWI_GETSTATUS:
- rc = 0;
- break;
- case TWI_SETSTATUS:
- rc = 0;
- break;
- case TWI_GETSLAVEADDRESS:
- // TODO: Slave handling
- *((uint8_t *)conf) = TWAR;
- break;
- case TWI_SETSLAVEADDRESS:
- // TODO: Slave handling
- TWAR = (*((uint8_t *) conf) << 1) | 1;
- icb->tw_mm_sla = *((uint16_t*)conf);
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- #endif /* __AVR_ENHANCED__ */
- }
- /*!
- * \brief Initialize TWI interface bus.
- *
- * The specified slave address is used only, if the local system
- * is running as a slave. Anyway, care must be taken that it doesn't
- * conflict with another connected device.
- *
- * \note This function is only available on ATmega128 systems.
- *
- * \param sla Slave address, must be specified as a 7-bit address,
- * always lower than 128.
- */
- int NutRegisterTwiBus( NUTTWIBUS *bus, uint8_t sla )
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- int rc = 0;
- uint32_t speed = 2400;
- NUTTWIICB *icb = NULL;
- /* Check if bus was already registered */
- if( bus->bus_icb) {
- return 0;
- }
- /* Allocate ICB for this bus */
- icb = NutHeapAlloc(sizeof(NUTTWIICB));
- if( icb == NULL) {
- return rc;
- }
- memset( icb, 0, sizeof(NUTTWIICB));
- /* Link bus and ICB */
- bus->bus_icb = icb;
- /* Initialize interface hardware */
- if (bus->bus_initbus) {
- rc = bus->bus_initbus();
- }
- /*
- * Set address register, enable general call address, set transfer
- * speed and enable interface.
- */
- outb(TWAR, (sla << 1) | 1);
- #ifdef I2C_DEFAULT_SPEED
- speed = I2C_DEFAULT_SPEED*1000UL;
- #endif
- /* Set initial rate. */
- if( (rc = NutTwiSetSpeed( bus, speed))) {
- return rc;
- }
- /* Register IRQ Handler */
- if( (rc = NutRegisterIrqHandler( bus->bus_sig_ev, TwInterrupt, bus))) {
- return rc;
- }
- /* Enable level triggered interrupts. */
- NutIrqEnable(bus->bus_sig_ev);
- outb(TWCR, _BV(TWINT));
- outb(TWCR, _BV(TWEN) | _BV(TWIE));
- /* Initialize mutex semaphores. */
- NutEventPost(&bus->bus_mutex);
- return rc;
- #endif
- }
- int NutDestroyTwiBus( NUTTWIBUS *bus)
- {
- #ifndef __AVR_ENHANCED__
- return -1;
- #else
- if (bus->bus_icb) {
- NutIrqDisable(bus->bus_sig_ev);
- NutHeapFree( bus->bus_icb);
- }
- return 0;
- #endif
- }
- /*!
- * \brief TWI/I2C bus structure.
- */
- NUTTWIBUS AVRTwiBus = {
- /*.bus_base = */ 0, /* Bus base address. */
- #ifndef __AVR_ENHANCED__
- /*.bus_sig_ev = */ NULL, /* Bus data and event interrupt handler. */
- #else
- /*.bus_sig_ev = */ &sig_2WIRE_SERIAL, /* Bus data and event interrupt handler. */
- #endif
- /*.bus_sig_er = */ NULL, /* Bus error interrupt handler. */
- /*.bus_mutex = */ NULL, /* Bus lock queue. */
- /*.bus_icb = */ NULL, /* Bus Runtime Data Pointer */
- /*.bus_dma_tx = */ 0, /* DMA channel for TX direction. */
- /*.bus_dma_rx = */ 0, /* DMA channel for RX direction. */
- /*.bus_initbus =*/ NULL, /* Initialize bus controller. */
- /*.bus_recover =*/ NULL, /* Recover bus controller */
- };
|