📄 tlc16c550.c
字号:
/* * Copyright (C) 2001-2003 by Cyber Integration, LLC. 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 CYBER INTEGRATION, LLC 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 CYBER * INTEGRATION, LLC 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. * * *//* * $Log: tlc16c550.c,v $ * Revision 1.1 2005/11/24 11:24:06 haraldkipp * Initial check-in. * Many thanks to William Basser for this code and also to Przemyslaw Rudy * for several enhancements. * */// system include files#include <string.h>#include <sys/atom.h>#include <sys/heap.h>#include <sys/event.h>#include <sys/timer.h>#include <sys/device.h>#include <dev/irqreg.h>#include <dev/tlc16c550.h>#include <fcntl.h>#include <stdio.h>/* * Not nice because stdio already defined them. But in order to save memory, * we do the whole buffering and including stdio here would be more weird. */#ifndef _IOFBF#define _IOFBF 0x00#define _IOLBF 0x01#define _IONBF 0x02#endif/*! * \ *//*@{*/// define the register offset// define the UART Register offsets#define ACE_DLSB_OFS 0#define ACE_DATA_OFS 0#define ACE_DMSB_OFS 1#define ACE_INTE_OFS 1#define ACE_FCTL_OFS 2#define ACE_INTI_OFS 2#define ACE_LCTL_OFS 3#define ACE_MCTL_OFS 4#define ACE_LSTS_OFS 5#define ACE_MSTS_OFS 6#define ACE_SCRG_OFS 7// define the interrupt enable masks#define INTE_RDA_MSK 0x01 // receiver data avaialbe#define INTE_THE_MSK 0x02 // transmit holding empty#define INTE_LST_MSK 0x04 // line status#define INTE_MST_MSK 0x08 // modem status// define the fifo control mask#define FCTL_ENABLE 0x01 //fifo enable#define FCTL_LEVEL_1 0x00 //receive trigger level 1#define FCTL_LEVEL_4 0x40 //receive trigger level 4#define FCTL_LEVEL_8 0x80 //receive trigger level 8#define FCTL_LEVEL_14 0xc0 //receive trigger level 14// define the interrupt id masks#define INTI_MST_MSK 0x00 // modem status interrupt#define INTI_TXE_MSK 0x02 // transmit buffer empty#define INTI_RDA_MSK 0x04 // receive data available#define INTI_TDA_MSK 0x0c // timeout receive data available#define INTI_LST_MSK 0x06 // line status interrupt#define INTI_NON_MSK 0x01 // no interrupt#define INTI_FIFO_MSK 0xc0 // mask to eliminate the fifo status// define the line control masks#define LCTL_WS0_MSK 0x01#define LCTL_WS1_MSK 0x02#define LCTL_STB_MSK 0x04#define LCTL_PEN_MSK 0x08#define LCTL_PRE_MSK 0x10#define LCTL_PRS_MSK 0x20#define LCTL_BRK_MSK 0x40#define LCTL_ENB_MSK 0x80// define the modem control masks#define MCTL_DTR_MSK 0x01#define MCTL_RTS_MSK 0x02#define MCTL_GP1_MSK 0x04#define MCTL_GP2_MSK 0x08#define MCTL_LOP_MSK 0x10// define the line status masks#define LSTS_RDR_MSK 0x01#define LSTS_OVR_MSK 0x02#define LSTS_PER_MSK 0x04#define LSTS_FER_MSK 0x08#define LSTS_BDT_MSK 0x10#define LSTS_THE_MSK 0x20#define LSTS_TXE_MSK 0x40// define the modem status masks#define MSTS_DCTS_MSK 0x01#define MSTS_DDSR_MSK 0x02#define MSTS_DRI_MSK 0x04#define MSTS_DDCD_MSK 0x08#define MSTS_CTS_MSK 0x10#define MSTS_DSR_MSK 0x20#define MSTS_RI_MSK 0x40#define MSTS_DCD_MSK 0x80u_char hackup[50] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };// define the irq structuretypedef struct tagIRQDEFS { IRQ_HANDLER *pvIrq; volatile u_char *pnIrqMskPort; u_char nMask;} IRQDEFS;// define the interrupt handlersstatic CONST PROGMEM IRQDEFS atIrqDefs[] = { {&sig_INTERRUPT0, &EICRA, 0x03}, {&sig_INTERRUPT1, &EICRA, 0x0C}, {&sig_INTERRUPT2, &EICRA, 0x30}, {&sig_INTERRUPT3, &EICRA, 0xc0}, {&sig_INTERRUPT4, &EICRB, 0x03}, {&sig_INTERRUPT5, &EICRB, 0x0C}, {&sig_INTERRUPT6, &EICRB, 0x30}, {&sig_INTERRUPT7, &EICRB, 0xC0},};// define the dcb's asigned to the interrupt to have more than one device on the same interrupt// NUT intrnal irq structure could be used instead but that would be a hackstatic ACEDCB *atIrqDcb[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };/* * Handle AVR ACE interrupts */static void AceIrqHandler(void *arg){ NUTDEVICE *dev = (NUTDEVICE *) arg; IFSTREAM *ifs; ACEDCB *dcb; volatile u_char event; u_char maxData; do { ifs = dev->dev_icb; dcb = dev->dev_dcb; // get the interrupt source while (((event = *(u_char *) (dev->dev_base + ACE_INTI_OFS)) & ~INTI_FIFO_MSK) != INTI_NON_MSK) { switch (event & ~INTI_FIFO_MSK) { case INTI_RDA_MSK: // receive data available case INTI_TDA_MSK: // timeout receive data available /* maxData can be avoided but it ensures that for slow system and fast uart we will not get stuck * reading incomming data all the time. */ maxData = (dcb->dcb_rfifo == 0) ? 1 : dcb->dcb_rfifo; for (; (*(u_char *) (dev->dev_base + ACE_LSTS_OFS) & LSTS_RDR_MSK) && (maxData > 0); --maxData) { // get the character and store it//increment count/adjust pointers ifs->if_rx_buf[ifs->if_rx_idx] = *(u_char *) (dev->dev_base + ACE_DATA_OFS); /* if we have just received a first byte into the empty buffer */ if (ifs->if_rd_idx == ifs->if_rx_idx) { NutEventPostAsync(&(dcb->dcb_rx_rdy)); } /* Late increment fixes ICCAVR bug on volatile variables. */ ifs->if_rx_idx++; } break; case INTI_TXE_MSK: // transmit buffer empty dcb->dcb_wfifo = (event & INTI_FIFO_MSK) ? 16 : 1; if (ifs->if_tx_idx != ifs->if_wr_idx) { for (; (ifs->if_tx_idx != ifs->if_wr_idx) && dcb->dcb_wfifo; --dcb->dcb_wfifo) { // send a character *(u_char *) (dev->dev_base + ACE_DATA_OFS) = ifs->if_tx_buf[ifs->if_tx_idx++]; } } else { ifs->if_tx_act = 0; NutEventPostAsync(&(dcb->dcb_tx_rdy)); } break; case INTI_MST_MSK: // modem status interrupt break; case INTI_LST_MSK: // line status interrupt break; } } /* get the next device assigned to this interrupt */ dev = dcb->dev_next; } while (dev != NULL);}/*! * \brief Wait for input. * * This function checks the input buffer for any data. If * the buffer is empty, the calling \ref xrThread "thread" * will be blocked until at least one new character is * received or a timeout occurs. * * \param dev Indicates the ACE device. * * \return 0 on success, -1 on timeout. */int AceInput(NUTDEVICE * dev){ int rc = 0; IFSTREAM *ifs = dev->dev_icb; ACEDCB *dcb = dev->dev_dcb; if (ifs->if_rd_idx == ifs->if_rx_idx) { /* * Changing if into a while loop fixes a serious bug: * Previous receiver events without any waiting thread * set the event handle to the signaled state. So the * wait returns immediately. Unfortunately the calling * routines rely on a filled buffer when we return 0. * Thanks to Mike Cornelius, who found this bug. */ do { rc = NutEventWait(&(dcb->dcb_rx_rdy), dcb->dcb_rtimeout); } while ((rc == 0) && (ifs->if_rd_idx == ifs->if_rx_idx)); } return rc;}/*! * \brief Initiate output. * * This function checks the output buffer for any data. If * the buffer contains at least one character, the transmitter * is started, if not already running. The function returns * immediately, without waiting for the character being * completely transmitted. Any remaining characters in the * output buffer are transmitted in the background. * * \param dev Indicates the ACE device. * * \return 0 on success, -1 otherwise. */int AceOutput(NUTDEVICE * dev){ IFSTREAM *ifs = dev->dev_icb; ACEDCB *dcb = dev->dev_dcb; if ((ifs->if_tx_act == 0) && (ifs->if_tx_idx != ifs->if_wr_idx)) { ifs->if_tx_act = 1; for (; (ifs->if_tx_idx != ifs->if_wr_idx) && (dcb->dcb_wfifo > 0); --dcb->dcb_wfifo) { // send a character *(u_char *) (dev->dev_base + ACE_DATA_OFS) = ifs->if_tx_buf[ifs->if_tx_idx++]; } // no need to enable an interrupt here as it should be enabled all the time } return 0;}/*! * \brief Wait for output buffer empty. * * If the output buffer contains any data, the calling * thread is suspended until all data has been transmitted. * * \param dev Indicates the ACE device. * * \return 0 on success, -1 otherwise. */int AceFlush(NUTDEVICE * dev){ IFSTREAM *ifs = dev->dev_icb; ACEDCB *dcb = dev->dev_dcb; /* * Start any pending output. */ if (AceOutput(dev)) return -1; /* * Wait until output buffer empty. */ while (ifs->if_tx_idx != ifs->if_wr_idx) NutEventWait(&dcb->dcb_tx_rdy, 100); return 0;}/* * * \param dev Indicates the ACE device. * * \return 0 on success, -1 otherwise. */static int AceGetStatus(NUTDEVICE * dev, u_long * status){ IFSTREAM *ifs = dev->dev_icb; u_char us; *status = 0; us = *(u_char *) (dev->dev_base + ACE_LSTS_OFS); if (us & LSTS_FER_MSK) *status |= ACE_FRAMINGERROR; if (us & LSTS_OVR_MSK) *status |= ACE_OVERRUNERROR; if (ifs->if_tx_idx == ifs->if_wr_idx) *status |= ACE_TXBUFFEREMPTY; if (ifs->if_rd_idx == ifs->if_rx_idx) *status |= ACE_RXBUFFEREMPTY; return 0;}/* * Carefully enable ACE functions. */static void AceEnable(u_short base){ /*volatile u_char* pnBase = *(volatile u_char* )base; */ /* * Enable ACE interrupts. */ NutEnterCritical(); *(u_char *) (base + ACE_INTE_OFS) = INTE_RDA_MSK | INTE_THE_MSK; NutExitCritical();}/* * Carefully disable ACE functions. */static void AceDisable(u_short base){ /*volatile u_char* pnBase = *(volatile u_char* )base; */ /* * Disable ACE interrupts. */ NutEnterCritical(); *(u_char *) (base + ACE_INTE_OFS) &= (u_char) ~ (INTE_RDA_MSK); NutExitCritical(); /* * Allow incoming or outgoing character to finish. */ NutDelay(10);}/*! * \brief Perform ACE control functions. * * \param dev Identifies the device that receives the device-control * function. * \param req Requested control function. May be set to one of the * following constants: * - ACE_SETSPEED, conf points to an u_long value containing the baudrate. * - ACE_GETSPEED, conf points to an u_long value receiving the current baudrate. * - ACE_SETDATABITS, conf points to an u_long value containing the number of data bits, 5, 6, 7 or 8. * - ACE_GETDATABITS, conf points to an u_long value receiving the number of data bits, 5, 6, 7 or 8. * - ACE_SETPARITY, conf points to an u_long value containing the parity, 0 (no), 1 (odd) or 2 (even). * - ACE_GETPARITY, conf points to an u_long value receiving the parity, 0 (no), 1 (odd) or 2 (even). * - ACE_SETSTOPBITS, conf points to an u_long value containing the number of stop bits 1 or 2. * - ACE_GETSTOPBITS, conf points to an u_long value receiving the number of stop bits 1 or 2. * - ACE_SETSTATUS * - ACE_GETSTATUS * - ACE_SETREADTIMEOUT, conf points to an u_long value containing the read timeout. * - ACE_GETREADTIMEOUT, conf points to an u_long value receiving the read timeout. * - ACE_SETWRITETIMEOUT, conf points to an u_long value containing the write timeout. * - ACE_GETWRITETIMEOUT, conf points to an u_long value receiving the write timeout. * - ACE_SETLOCALECHO, conf points to an u_long value containing 0 (off) or 1 (on). * - ACE_GETLOCALECHO, conf points to an u_long value receiving 0 (off) or 1 (on). * - ACE_SETFLOWCONTROL, conf points to an u_long value containing combined ACE_FCTL_ values. * - ACE_GETFLOWCONTROL, conf points to an u_long value containing receiving ACE_FCTL_ values. * - ACE_SETCOOKEDMODE, conf points to an u_long value containing 0 (off) or 1 (on). * - ACE_GETCOOKEDMODE, conf points to an u_long value receiving 0 (off) or 1 (on). * * \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. * * \warning Timeout values are given in milliseconds and are limited to * the granularity of the system timer. To disable timeout, * set the parameter to NUT_WAIT_INFINITE. * * */int AceIOCtl(NUTDEVICE * dev, int req, void *conf){ int rc = 0; ACEDCB *dcb; IFSTREAM *ifs; u_long *lvp = (u_long *) conf; u_long lv = *lvp; u_char bv = (u_char) lv; u_short sv; u_short devnum; u_char tv; if (dev == 0) { rc = -1; return rc; } devnum = dev->dev_base; dcb = dev->dev_dcb; switch (req) { case ACE_SETSPEED: AceDisable(devnum); sv = (u_short) (ACE_CLOCK / (lv * 16UL)); *(u_char *) (dev->dev_base + ACE_LCTL_OFS) |= LCTL_ENB_MSK; *(u_char *) (dev->dev_base + ACE_DLSB_OFS) = sv & 0xFF; *(u_char *) (dev->dev_base + ACE_DMSB_OFS) = (u_char) sv >> 8; *(u_char *) (dev->dev_base + ACE_LCTL_OFS) &= (u_char) ~ LCTL_ENB_MSK; AceEnable(devnum); break; case ACE_GETSPEED: *(u_char *) (dev->dev_base + ACE_LCTL_OFS) |= LCTL_ENB_MSK; sv = *(u_char *) (dev->dev_base + ACE_DLSB_OFS); sv |= *(u_char *) (dev->dev_base + ACE_DMSB_OFS) >> 8; *(u_char *) (dev->dev_base + ACE_LCTL_OFS) &= (u_char) ~ LCTL_ENB_MSK; *lvp = ACE_CLOCK / (16UL * (u_long) (sv)); break; case ACE_SETDATABITS: AceDisable(devnum); if ((bv >= 5) && (bv <= 8)) { bv -= 5;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -