/* * File : serial.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006, RT-Thread Development Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://openlab.rt-thread.com/license/LICENSE * * Change Logs: * Date Author Notes * 2006-08-23 Bernard first version * 2009-05-14 Bernard add RT-THread device interface * 2010-03-14 MingBai US_IMR is read-only. * 2010-03-16 MingBai Changed interrupt source mode to level sensitive. */ #include #include #include "AT91SAM7X.h" #include "serial.h" /** * @addtogroup AT91SAM7X256 */ /*@{*/ typedef volatile rt_uint32_t REG32; struct rt_at91serial_hw { REG32 US_CR; // Control Register REG32 US_MR; // Mode Register REG32 US_IER; // Interrupt Enable Register REG32 US_IDR; // Interrupt Disable Register REG32 US_IMR; // Interrupt Mask Register REG32 US_CSR; // Channel Status Register REG32 US_RHR; // Receiver Holding Register REG32 US_THR; // Transmitter Holding Register REG32 US_BRGR; // Baud Rate Generator Register REG32 US_RTOR; // Receiver Time-out Register REG32 US_TTGR; // Transmitter Time-guard Register REG32 Reserved0[5]; // REG32 US_FIDI; // FI_DI_Ratio Register REG32 US_NER; // Nb Errors Register REG32 Reserved1[1]; // REG32 US_IF; // IRDA_FILTER Register REG32 Reserved2[44]; // REG32 US_RPR; // Receive Pointer Register REG32 US_RCR; // Receive Counter Register REG32 US_TPR; // Transmit Pointer Register REG32 US_TCR; // Transmit Counter Register REG32 US_RNPR; // Receive Next Pointer Register REG32 US_RNCR; // Receive Next Counter Register REG32 US_TNPR; // Transmit Next Pointer Register REG32 US_TNCR; // Transmit Next Counter Register REG32 US_PTCR; // PDC Transfer Control Register REG32 US_PTSR; // PDC Transfer Status Register }; struct rt_at91serial { struct rt_device parent; struct rt_at91serial_hw* hw_base; rt_uint16_t peripheral_id; rt_uint32_t baudrate; /* reception field */ rt_uint16_t save_index, read_index; rt_uint8_t rx_buffer[RT_UART_RX_BUFFER_SIZE]; }; #ifdef RT_USING_UART1 struct rt_at91serial serial1; #endif #ifdef RT_USING_UART2 struct rt_at91serial serial2; #endif static void rt_hw_serial_isr(int irqno, void* param) { rt_base_t level; struct rt_device* device; struct rt_at91serial* serial = (struct rt_at91serial*)param; RT_ASSERT(serial != RT_NULL); /* get generic device object */ device = (rt_device_t)serial; /* disable interrupt */ level = rt_hw_interrupt_disable(); /* get received character */ serial->rx_buffer[serial->save_index] = serial->hw_base->US_RHR; /* move to next position */ serial->save_index ++; if (serial->save_index >= RT_UART_RX_BUFFER_SIZE) serial->save_index = 0; /* if the next position is read index, discard this 'read char' */ if (serial->save_index == serial->read_index) { serial->read_index ++; if (serial->read_index >= RT_UART_RX_BUFFER_SIZE) serial->read_index = 0; } /* enable interrupt */ rt_hw_interrupt_enable(level); /* indicate to upper layer application */ if (device->rx_indicate != RT_NULL) device->rx_indicate(device, 1); /* ack interrupt */ AT91C_AIC_EOICR = 1; } static rt_err_t rt_serial_init (rt_device_t dev) { rt_uint32_t bd; struct rt_at91serial* serial = (struct rt_at91serial*) dev; RT_ASSERT(serial != RT_NULL); /* must be US0 or US1 */ RT_ASSERT((serial->peripheral_id == AT91C_ID_US0) || (serial->peripheral_id == AT91C_ID_US1)); /* Enable Clock for USART */ AT91C_PMC_PCER = 1 << serial->peripheral_id; /* Enable RxD0 and TxDO Pin */ if (serial->peripheral_id == AT91C_ID_US0) { /* set pinmux */ //AT91C_PIO_PDR = (1 << 5) | (1 << 6); AT91C_PIO_PDR = 1 | (1 << 1); //fix bug 2010-3-9 } else if (serial->peripheral_id == AT91C_ID_US1) { /* set pinmux */ //AT91C_PIO_PDR = (1 << 21) | (1 << 22); AT91C_PIO_PDR = (1 << 5) | (1 << 6); //fix bug 2010-3-9 } serial->hw_base->US_CR = AT91C_US_RSTRX | /* Reset Receiver */ AT91C_US_RSTTX | /* Reset Transmitter */ AT91C_US_RXDIS | /* Receiver Disable */ AT91C_US_TXDIS; /* Transmitter Disable */ serial->hw_base->US_MR = AT91C_US_USMODE_NORMAL | /* Normal Mode */ AT91C_US_CLKS_CLOCK | /* Clock = MCK */ AT91C_US_CHRL_8_BITS | /* 8-bit Data */ AT91C_US_PAR_NONE | /* No Parity */ AT91C_US_NBSTOP_1_BIT; /* 1 Stop Bit */ /* set baud rate divisor */ bd = ((MCK*10)/(serial->baudrate * 16)); if ((bd % 10) >= 5) bd = (bd / 10) + 1; else bd /= 10; serial->hw_base->US_BRGR = bd; serial->hw_base->US_CR = AT91C_US_RXEN | /* Receiver Enable */ AT91C_US_TXEN; /* Transmitter Enable */ /* reset rx index */ serial->save_index = 0; serial->read_index = 0; /* reset rx buffer */ rt_memset(serial->rx_buffer, 0, RT_UART_RX_BUFFER_SIZE); return RT_EOK; } static rt_err_t rt_serial_open(rt_device_t dev, rt_uint16_t oflag) { struct rt_at91serial *serial = (struct rt_at91serial*)dev; RT_ASSERT(serial != RT_NULL); if (dev->flag & RT_DEVICE_FLAG_INT_RX) { /* enable UART rx interrupt */ serial->hw_base->US_IER = 1 << 0; /* RxReady interrupt */ // US_IMR is a READ-ONLY register! //serial->hw_base->US_IMR |= 1 << 0; /* umask RxReady interrupt */ /* install UART handler */ rt_hw_interrupt_install(serial->peripheral_id, rt_hw_serial_isr, serial, "uart"); // SAM7X Datasheet 30.5.3: // It is notrecommended to use the USART interrupt line in edge sensitive mode //AT91C_AIC_SMR(serial->peripheral_id) = 5 | (0x01 << 5); AT91C_AIC_SMR(serial->peripheral_id) = 5; rt_hw_interrupt_umask(serial->peripheral_id); } return RT_EOK; } static rt_err_t rt_serial_close(rt_device_t dev) { struct rt_at91serial *serial = (struct rt_at91serial*)dev; RT_ASSERT(serial != RT_NULL); if (dev->flag & RT_DEVICE_FLAG_INT_RX) { /* disable interrupt */ serial->hw_base->US_IDR = 1 << 0; /* RxReady interrupt */ //serial->hw_base->US_IMR &= ~(1 << 0); /* mask RxReady interrupt */ } serial->hw_base->US_CR = AT91C_US_RSTRX | /* Reset Receiver */ AT91C_US_RSTTX | /* Reset Transmitter */ AT91C_US_RXDIS | /* Receiver Disable */ AT91C_US_TXDIS; /* Transmitter Disable */ return RT_EOK; } static rt_ssize_t rt_serial_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) { rt_uint8_t* ptr; struct rt_at91serial *serial = (struct rt_at91serial*)dev; RT_ASSERT(serial != RT_NULL); /* point to buffer */ ptr = (rt_uint8_t*) buffer; if (dev->flag & RT_DEVICE_FLAG_INT_RX) { while (size) { /* interrupt receive */ rt_base_t level; /* disable interrupt */ level = rt_hw_interrupt_disable(); if (serial->read_index != serial->save_index) { *ptr = serial->rx_buffer[serial->read_index]; serial->read_index ++; if (serial->read_index >= RT_UART_RX_BUFFER_SIZE) serial->read_index = 0; } else { /* no data in rx buffer */ /* enable interrupt */ rt_hw_interrupt_enable(level); break; } /* enable interrupt */ rt_hw_interrupt_enable(level); ptr ++; size --; } return (rt_uint32_t)ptr - (rt_uint32_t)buffer; } else if (dev->flag & RT_DEVICE_FLAG_DMA_RX) { /* not support right now */ RT_ASSERT(0); } else { /* poll mode */ while (size) { /* Wait for Full Rx Buffer */ while (!(serial->hw_base->US_CSR & AT91C_US_RXRDY)); /* Read Character */ *ptr = serial->hw_base->US_RHR; ptr ++; size --; } return (rt_size_t)ptr - (rt_size_t)buffer; } return 0; } static rt_ssize_t rt_serial_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) { rt_uint8_t* ptr; struct rt_at91serial *serial = (struct rt_at91serial*)dev; RT_ASSERT(serial != RT_NULL); ptr = (rt_uint8_t*) buffer; if (dev->open_flag & RT_DEVICE_OFLAG_WRONLY) { if (dev->flag & RT_DEVICE_FLAG_STREAM) { /* it's a stream mode device */ while (size) { /* stream mode */ if (*ptr == '\n') { while (!(serial->hw_base->US_CSR & AT91C_US_TXRDY)); serial->hw_base->US_THR = '\r'; } /* Wait for Empty Tx Buffer */ while (!(serial->hw_base->US_CSR & AT91C_US_TXRDY)); /* Transmit Character */ serial->hw_base->US_THR = *ptr; ptr ++; size --; } } else { while (size) { /* Wait for Empty Tx Buffer */ while (!(serial->hw_base->US_CSR & AT91C_US_TXRDY)); /* Transmit Character */ serial->hw_base->US_THR = *ptr; ptr ++; size --; } } } return (rt_size_t)ptr - (rt_size_t)buffer; } static rt_err_t rt_serial_control (rt_device_t dev, int cmd, void *args) { return RT_EOK; } rt_err_t rt_hw_serial_init() { rt_device_t device; #ifdef RT_USING_UART1 device = (rt_device_t) &serial1; /* init serial device private data */ serial1.hw_base = (struct rt_at91serial_hw*)AT91C_BASE_US0; serial1.peripheral_id = AT91C_ID_US0; serial1.baudrate = 115200; /* set device virtual interface */ device->init = rt_serial_init; device->open = rt_serial_open; device->close = rt_serial_close; device->read = rt_serial_read; device->write = rt_serial_write; device->control = rt_serial_control; /* register uart1 on device subsystem */ rt_device_register(device, "uart1", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX); #endif #ifdef RT_USING_UART2 device = (rt_device_t) &serial2; serial2.hw_base = (struct rt_at91serial_hw*)AT91C_BASE_US1; serial2.peripheral_id = AT91C_ID_US1; serial2.baudrate = 115200; /* set device virtual interface */ device->init = rt_serial_init; device->open = rt_serial_open; device->close = rt_serial_close; device->read = rt_serial_read; device->write = rt_serial_write; device->control = rt_serial_control; /* register uart2 on device subsystem */ rt_device_register(device, "uart2", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX); #endif return RT_EOK; } /*@}*/