edubrm/firmware/usbcomp_msd_cdc/src/serial.c
2011-04-01 21:55:31 +02:00

243 lines
8.6 KiB
C

/*----------------------------------------------------------------------------
* Name: serial.c
* Purpose: serial port handling for LPC134x
* Version: V1.10
*----------------------------------------------------------------------------
* This software is supplied "AS IS" without any warranties, express,
* implied or statutory, including but not limited to the implied
* warranties of fitness for purpose, satisfactory quality and
* noninfringement. Keil extends you a royalty-free right to reproduce
* and distribute executable files created using this software for use
* on NXP Semiconductors LPC microcontroller devices only. Nothing else
* gives you the right to use this software.
*
* Copyright (c) 2009 Keil - An ARM Company. All rights reserved.
*---------------------------------------------------------------------------*/
#include "LPC13xx.h" // LPC13xx definitions
#include "type.h"
#include "serial.h"
/*----------------------------------------------------------------------------
Defines for ring buffers
*---------------------------------------------------------------------------*/
#define SER_BUF_SIZE (128) // serial buffer in bytes (power 2)
#define SER_BUF_MASK (SER_BUF_SIZE-1ul) // buffer size mask
/* Buffer read / write macros */
#define SER_BUF_RESET(serBuf) (serBuf.rdIdx = serBuf.wrIdx = 0)
#define SER_BUF_WR(serBuf, dataIn) (serBuf.data[SER_BUF_MASK & serBuf.wrIdx++] = (dataIn))
#define SER_BUF_RD(serBuf) (serBuf.data[SER_BUF_MASK & serBuf.rdIdx++])
#define SER_BUF_EMPTY(serBuf) (serBuf.rdIdx == serBuf.wrIdx)
#define SER_BUF_FULL(serBuf) (serBuf.rdIdx == serBuf.wrIdx+1)
#define SER_BUF_COUNT(serBuf) (SER_BUF_MASK & (serBuf.wrIdx - serBuf.rdIdx))
// buffer type
typedef struct __SER_BUF_T {
unsigned char data[SER_BUF_SIZE];
unsigned int wrIdx;
unsigned int rdIdx;
} SER_BUF_T;
unsigned long ser_txRestart; // NZ if TX restart is required
unsigned short ser_lineState; // ((msr << 8) | (lsr))
SER_BUF_T ser_out; // Serial data buffers
SER_BUF_T ser_in;
/*----------------------------------------------------------------------------
open the serial port
*---------------------------------------------------------------------------*/
void ser_OpenPort (void) {
NVIC_DisableIRQ(UART_IRQn);
LPC_IOCON->PIO1_6 &= ~0x07; /* UART I/O config */
LPC_IOCON->PIO1_6 |= 0x01; /* UART RXD */
LPC_IOCON->PIO1_7 &= ~0x07;
LPC_IOCON->PIO1_7 |= 0x01; /* UART TXD */
/* Enable UART clock */
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<12);
LPC_SYSCON->UARTCLKDIV = 0x1; /* divided by 1 */
return;
}
/*----------------------------------------------------------------------------
close the serial port
*---------------------------------------------------------------------------*/
void ser_ClosePort (void) {
LPC_IOCON->PIO1_6 &= ~0x07; /* UART I/O config */
LPC_IOCON->PIO1_7 &= ~0x07;
/* Disable the interrupt in the VIC and UART controllers */
LPC_UART->IER = 0;
NVIC_DisableIRQ(UART_IRQn);
return;
}
/*----------------------------------------------------------------------------
initialize the serial port
*---------------------------------------------------------------------------*/
void ser_InitPort (unsigned long baudrate, unsigned int databits,
unsigned int parity, unsigned int stopbits) {
uint8_t lcr_p, lcr_s, lcr_d;
uint32_t dll;
uint32_t Fdiv;
switch (databits) {
case 5: // 5 Data bits
lcr_d = 0x00;
break;
case 6: // 6 Data bits
lcr_d = 0x01;
break;
case 7: // 7 Data bits
lcr_d = 0x02;
break;
case 8: // 8 Data bits
default:
lcr_d = 0x03;
break;
}
switch (stopbits) {
case 1: // 1,5 Stop bits
case 2: // 2 Stop bits
lcr_s = 0x04;
break;
case 0: // 1 Stop bit
default:
lcr_s = 0x00;
break;
}
switch (parity) {
case 1: // Parity Odd
lcr_p = 0x08;
break;
case 2: // Parity Even
lcr_p = 0x18;
break;
case 3: // Parity Mark
lcr_p = 0x28;
break;
case 4: // Parity Space
lcr_p = 0x38;
break;
case 0: // Parity None
default:
lcr_p = 0x00;
break;
}
SER_BUF_RESET(ser_out); // reset out buffer
SER_BUF_RESET(ser_in); // reset in buffer
/* Note that the pclk is 24,0 MHz. (48.0 MHz / 2) */
/* 24 MHz PCLK generates also rates for 115200, 57600 baud */
Fdiv = LPC_SYSCON->UARTCLKDIV;
dll = (((SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/Fdiv)/16)/baudrate ; /*baud rate */
LPC_UART->FDR = 0; // Fractional divider not used
LPC_UART->LCR = 0x80 | lcr_d | lcr_p | lcr_s; // Data bits, Parity, Stop bit
LPC_UART->DLL = dll; // Baud Rate depending on PCLK
LPC_UART->DLM = (dll >> 8); // High divisor latch
LPC_UART->LCR = 0x00 | lcr_d | lcr_p | lcr_s; // DLAB = 0
LPC_UART->IER = 0x03; // Enable TX/RX interrupts
LPC_UART->FCR = 0x07; /* Enable and reset TX and RX FIFO. */
ser_txRestart = 1; // TX fifo is empty
/* Enable the UART Interrupt */
NVIC_EnableIRQ(UART_IRQn);
return;
}
/*----------------------------------------------------------------------------
read data from serial port
*---------------------------------------------------------------------------*/
int ser_Read (char *buffer, const int *length) {
int bytesToRead, bytesRead;
/* Read *length bytes, block if *bytes are not avaialable */
bytesToRead = *length;
bytesToRead = (bytesToRead < (*length)) ? bytesToRead : (*length);
bytesRead = bytesToRead;
while (bytesToRead--) {
while (SER_BUF_EMPTY(ser_in)); // Block until data is available if none
*buffer++ = SER_BUF_RD(ser_in);
}
return (bytesRead);
}
/*----------------------------------------------------------------------------
write data to the serial port
*---------------------------------------------------------------------------*/
int ser_Write (const char *buffer, int *length) {
int bytesToWrite, bytesWritten;
// Write *length bytes
bytesToWrite = *length;
bytesWritten = bytesToWrite;
while (!SER_BUF_EMPTY(ser_out)); // Block until space is available if none
while (bytesToWrite) {
SER_BUF_WR(ser_out, *buffer++); // Read Rx FIFO to buffer
bytesToWrite--;
}
if (ser_txRestart) {
ser_txRestart = 0;
LPC_UART->THR = SER_BUF_RD(ser_out); // Write to the Tx Register
}
return (bytesWritten);
}
/*----------------------------------------------------------------------------
check if character(s) are available at the serial interface
*---------------------------------------------------------------------------*/
void ser_AvailChar (int *availChar) {
*availChar = SER_BUF_COUNT(ser_in);
}
/*----------------------------------------------------------------------------
read the line state of the serial port
*---------------------------------------------------------------------------*/
void ser_LineState (unsigned short *lineState) {
*lineState = ser_lineState;
ser_lineState = 0;
}
/*----------------------------------------------------------------------------
serial port 1 interrupt
*---------------------------------------------------------------------------*/
void UART_IRQHandler(void)
{
volatile unsigned long iir;
iir = LPC_UART->IIR;
if ((iir & 0x4) || (iir & 0xC)) { // RDA or CTI pending
while (LPC_UART->LSR & 0x01) { // Rx FIFO is not empty
SER_BUF_WR(ser_in, LPC_UART->RBR); // Read Rx FIFO to buffer
}
}
if ((iir & 0x2)) { // TXMIS pending
if (SER_BUF_COUNT(ser_out) != 0) {
LPC_UART->THR = SER_BUF_RD(ser_out); // Write to the Tx FIFO
ser_txRestart = 0;
}
else {
ser_txRestart = 1;
}
}
ser_lineState = LPC_UART->LSR & 0x1E; // update linestate
return;
}