📄 twif.c
字号:
/* * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved. * * 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 EGNITE SOFTWARE GMBH 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 EGNITE * SOFTWARE GMBH 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/ * *//* * $Log: twif.c,v $ * Revision 1.3 2005/10/24 10:56:30 haraldkipp * Added const modifier to transmit data pointer in TwMasterTransact(). * * Revision 1.2 2005/10/07 22:03:29 hwmaier * Using __AVR_ENHANCED__ macro instead of __AVR_ATmega128__ to support also AT90CAN128 MCU * * Revision 1.1 2005/07/26 18:02:40 haraldkipp * Moved from dev. * * Revision 1.9 2005/01/24 21:12:05 freckle * renamed NutEventPostFromIRQ into NutEventPostFromIrq * * Revision 1.8 2005/01/21 16:49:46 freckle * Seperated calls to NutEventPostAsync between Threads and IRQs * * Revision 1.7 2004/12/16 08:40:35 haraldkipp * Late increment fixes ICCAVR bug. * * Revision 1.6 2004/11/08 18:12:59 haraldkipp * Soooo many fixes, but I'm tired...really. * * Revision 1.5 2004/09/08 10:19:14 haraldkipp * *** empty log message *** * * Revision 1.4 2003/11/03 17:03:53 haraldkipp * Many new comments added * * Revision 1.3 2003/07/20 18:28:10 haraldkipp * Explain how to disable timeout. * * Revision 1.2 2003/07/17 09:38:07 haraldkipp * Setting different speeds is now supported. * * Revision 1.1.1.1 2003/05/09 14:40:53 haraldkipp * Initial using 3.2.1 * * Revision 1.2 2003/03/31 14:53:08 harald * Prepare release 3.1 * * Revision 1.1 2003/02/04 17:54:59 harald * *** empty log message *** * */#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__static volatile u_char tw_if_bsy; /* Set while interface is busy. */HANDLE tw_mm_mutex; /* Exclusive master access. */HANDLE tw_mm_que; /* Threads waiting for master transfer done. */HANDLE tw_sr_que; /* Threads waiting for slave receive. */HANDLE tw_st_que; /* Threads waiting for slave transmit done. */static u_char tw_mm_sla; /* Destination slave address. */static volatile u_char tw_mm_err; /* Current master mode error. */static u_char tw_mm_error; /* Last master mode error. */static CONST u_char *tw_mt_buf; /* Pointer to the master transmit buffer. */static volatile u_short tw_mt_len; /* Number of bytes to transmit in master mode. */static volatile u_short tw_mt_idx; /* Current master transmit buffer index. */static u_char *tw_mr_buf; /* Pointer to the master receive buffer. */static volatile u_short tw_mr_siz; /* Size of the master receive buffer. */static volatile u_short tw_mr_idx; /* Current master receive buffer index. */static volatile u_char tw_sm_sla; /* Slave address received. */static volatile u_char tw_sm_err; /* Current slave mode error. */static u_char tw_sm_error; /* Last slave mode error. */static u_char *tw_st_buf; /* Pointer to the slave transmit buffer. */static volatile u_short tw_st_len; /* Number of bytes to transmit in slave mode. */static volatile u_short tw_st_idx; /* Current slave transmit buffer index. */static u_char *tw_sr_buf; /* Pointer to the slave receive buffer. */static volatile u_short tw_sr_siz; /* Size of the master receive buffer. */static volatile u_short tw_sr_idx; /* Current slave receive buffer index. *//*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){ u_char twsr; register u_char 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. */ tw_if_bsy = 1; tw_mt_idx = 0; tw_mr_idx = 0; /* * If outgoing data is available, transmit SLA+W. Logic is in * master transmit mode. */ if (tw_mt_len) { outb(TWDR, tw_mm_sla); } /* * If outgoing data not available, transmit SLA+R. Logic will * switch to master receiver mode. */ else { outb(TWDR, 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 (tw_mt_idx < tw_mt_len) { outb(TWDR, tw_mt_buf[tw_mt_idx]); /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */ tw_mt_idx++; outb(TWCR, TWGO | (twcr & _BV(TWEA))); break; } /* * All outgoing data has been sent. If a response is expected, * transmit a repeated start condition. */ tw_mt_len = 0; if (tw_mr_siz) { 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) { tw_mm_err = TWERR_SLA_NACK; tw_mt_len = 0; tw_mr_siz = 0; } /* Wake up the application. */ NutEventPostFromIrq(&tw_mm_que); /* * Send a stop condition. If we have a listener, generate * an acknowlegde on an incoming address byte. */ if(tw_sr_siz) { outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO)); } else { outb(TWCR, TWGO | _BV(TWSTO)); } /* The interface is idle. */ tw_if_bsy = 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. */ tw_if_bsy = 0; break; /* * 0x50: Data byte has been received and acknowledged. */ case TW_MR_DATA_ACK: /* * Store the data byte in the master receive buffer. */ tw_mr_buf[tw_mr_idx] = inb(TWDR); /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */ tw_mr_idx++; /* * 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 (tw_mr_idx + 1 < tw_mr_siz) { 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. */ tw_mr_buf[tw_mr_idx] = inb(TWDR); /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */ tw_mr_idx++; tw_mr_siz = 0; /* Wake up the application. */ NutEventPostFromIrq(&tw_mm_que); /* * Send a stop condition. If we have a listener, generate * an acknowlegde on an incoming address byte. */ if(tw_sr_siz) { outb(TWCR, TWGO | _BV(TWEA) | _BV(TWSTO)); } else { outb(TWCR, TWGO | _BV(TWSTO)); } /* The interface is idle. */ tw_if_bsy = 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 (tw_sr_siz) { /* We are entering the slave receive mode. Mark the interface busy. */ tw_if_bsy = 1; tw_sm_sla = inb(TWDR); outb(TWCR, TWGO | _BV(TWEA)); tw_sr_idx = 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 (tw_sr_idx < tw_sr_siz) { tw_sr_buf[tw_sr_idx] = inb(TWDR); /* Late increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */ tw_sr_idx++; } else { tw_sr_siz = 0; } /* * If more space is available for incoming data, then continue * receiving. Otherwise do not acknowledge new data bytes. */ if (tw_sr_siz) { 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 (tw_mt_len || tw_mr_siz) { 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 (NutEventPostFromIrq(&tw_sr_que) == 0 || 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 (tw_mt_len || tw_mr_siz) { outb(TWCR, TWGO | _BV(TWSTA)); } else { outb(TWCR, TWGO); } tw_if_bsy = 0; } else { tw_sr_siz = 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. */ tw_if_bsy = 1; /* Reset transmit index and fall through for outgoing data. */ tw_st_idx = 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 (tw_st_idx < tw_st_len) { outb(TWDR, tw_st_buf[tw_st_idx]); /* Do not set acknowledge on the last data byte. */ /* Early increment fixes ICCAVR bug. Thanks to Andreas Siebert and Michael Fischer. */ ++tw_st_idx; if (tw_st_idx < tw_st_len) { outb(TWCR, TWGO | _BV(TWEA)); } else { tw_st_len = 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -