/*******************************************************
   SERIAL.C
   Unit für V24-Ansteuerung, Philips LPC2220
 *******************************************************/

#include "LPC22xx.h"
#include "StdTypes.h"
#include "interrupts.h"
#include "setup.h"
#include "serial.h"


char V24puffer[v24bufsize];    /* Sendepuffer */
volatile int v24sin;           /* Index im Sendepuffer */
volatile int v24sout;          /* Index im Sendepuffer */


struct FracEntry
{ byte div;    /* Faktor*16 - 256        */
  byte FDR;    /* (MULVAL<<4) | DIVADVAL */
};

/* Hilfstafel: (K * 16) - 256, Code für UxFDR,   Teilerverhältnis */
const struct FracEntry FracTabelle[72] =
{{   0, 0x10 },  /*  0.  1.000 */
 {  17, 0xF1 },  /*  1.  1.067 */
 {  18, 0xE1 },  /*  2.  1.071 */
 {  20, 0xD1 },  /*  3.  1.077 */
 {  21, 0xC1 },  /*  4.  1.083 */
 {  23, 0xB1 },  /*  5.  1.091 */
 {  26, 0xA1 },  /*  6.  1.100 */
 {  28, 0x91 },  /*  7.  1.111 */
 {  32, 0x81 },  /*  8.  1.125 */
 {  34, 0xF2 },  /*  9.  1.133 */
 {  37, 0x71 },  /* 10.  1.143 */
 {  39, 0xD2 },  /* 11.  1.154 */
 {  43, 0x61 },  /* 12.  1.167 */
 {  47, 0xB2 },  /* 13.  1.182 */
 {  51, 0x51 },  /* 14.  1.200 */
 {  55, 0xE3 },  /* 15.  1.214 */
 {  57, 0x92 },  /* 16.  1.222 */
 {  59, 0xD3 },  /* 17.  1.231 */
 {  64, 0x41 },  /* 18.  1.250 */
 {  68, 0xF4 },  /* 19.  1.267 */
 {  70, 0xB3 },  /* 20.  1.273 */
 {  73, 0x72 },  /* 21.  1.286 */
 {  77, 0xA3 },  /* 22.  1.300 */
 {  79, 0xD4 },  /* 23.  1.308 */
 {  85, 0x31 },  /* 24.  1.333 */
 {  91, 0xE5 },  /* 25.  1.357 */
 {  93, 0xB4 },  /* 26.  1.364 */
 {  96, 0x83 },  /* 27.  1.375 */
 {  98, 0xD5 },  /* 28.  1.385 */
 { 102, 0x52 },  /* 29.  1.400 */
 { 107, 0xC5 },  /* 30.  1.417 */
 { 110, 0x73 },  /* 31.  1.429 */
 { 114, 0x94 },  /* 32.  1.444 */
 { 116, 0xB5 },  /* 33.  1.455 */
 { 118, 0xD6 },  /* 34.  1.462 */
 { 119, 0xF7 },  /* 35.  1.467 */
 { 128, 0x21 },  /* 36.  1.500 */
 { 137, 0xF8 },  /* 37.  1.533 */
 { 138, 0xD7 },  /* 38.  1.538 */
 { 140, 0xB6 },  /* 39.  1.545 */
 { 142, 0x95 },  /* 40.  1.556 */
 { 146, 0x74 },  /* 41.  1.571 */
 { 149, 0xC7 },  /* 42.  1.583 */
 { 154, 0x53 },  /* 43.  1.600 */
 { 158, 0xD8 },  /* 44.  1.615 */
 { 160, 0x85 },  /* 45.  1.625 */
 { 163, 0xB7 },  /* 46.  1.636 */
 { 165, 0xE9 },  /* 47.  1.643 */
 { 171, 0x32 },  /* 48.  1.667 */
 { 177, 0xD9 },  /* 49.  1.692 */
 { 179, 0xA7 },  /* 50.  1.700 */
 { 183, 0x75 },  /* 51.  1.714 */
 { 186, 0xB8 },  /* 52.  1.727 */
 { 188, 0xFB },  /* 53.  1.733 */
 { 192, 0x43 },  /* 54.  1.750 */
 { 197, 0xDA },  /* 55.  1.769 */
 { 199, 0x97 },  /* 56.  1.778 */
 { 201, 0xEB },  /* 57.  1.786 */
 { 205, 0x54 },  /* 58.  1.800 */
 { 209, 0xB9 },  /* 59.  1.818 */
 { 213, 0x65 },  /* 60.  1.833 */
 { 217, 0xDB },  /* 61.  1.846 */
 { 219, 0x76 },  /* 62.  1.857 */
 { 222, 0xFD },  /* 63.  1.867 */
 { 224, 0x87 },  /* 64.  1.875 */
 { 228, 0x98 },  /* 65.  1.889 */
 { 230, 0xA9 },  /* 66.  1.900 */
 { 233, 0xBA },  /* 67.  1.909 */
 { 235, 0xCB },  /* 68.  1.917 */
 { 236, 0xDC },  /* 69.  1.923 */
 { 238, 0xED },  /* 70.  1.929 */
 { 239, 0xFE },  /* 71   1.933 */
};


/* UART0: Interrupt.
 1. Der Int wird ausgelöst, wenn THR leer wird, also
 durch die Flanke voll-->leer.
 wenn THR erstmal leer ist, dann gibt's keinen
 weiteren Int
 ABER: Das Int-Signal zum VIC muß gelöscht werden.
 Entweder durch Beschrteiben von U0THR oder durch
 Lesen von U0IIR
 2. der Int kann auch per Software-Int aktiviert werden
 und muß dann in der Int-Routine dediziert rückgesetzt
 werden.
 Die Interrupt-Bedienung ist ausgelagert, siehe Interrupts.c
 */



/* UART0 initialisieren */
/* ==================== */
/*
 Baudraten-Rechengang für alle LPC2xxx:
     Divisor = PCLK / (Baudrate * K);

 ohne fraktionalen Vorteiler ist K = 16
 mit ihm kann K sein: 16.00 bis 30.03 in 72 Stufen
 Es gibt DIVADVAL (0..15) und MULVAL (1..15), wobei gilt:
     K = 16 * (1 + DIVADVAL/MULVAL));
 */

dword V24_Init (long baudrate)
{ long divisor;
  long rest;
  long ksoll;
  long kist;
  long brcal;
  byte FDRval;
  int  i;

  PINSEL0 = (PINSEL0 & 0xFFFFFFF0) | 1 | 4;  /* Portpins als RxD und TxD von UART0 setzen */
  U0LCR = 0x83;                              /* 8 bits, no Parity, 1 Stop bit     */

  divisor = PCLK / baudrate;                 /* erstmal das 16 fache */
  rest = divisor & 15;                       /* Rest merken */
  divisor = divisor >> 4;
  FDRval = (1<<4);                           /* Fraktionalteiler aus       */

  if (rest > 13)                             /* sind nahe genug an integer */
  { ++divisor;                               /* divisor aufrunden */
    brcal = PCLK / (16 * divisor);           /* Returnwert */
    goto _setzebaud;
  }

  if (rest < 2)                              /* sind nahe genug an integer */
  { brcal = PCLK / (16 * divisor);           /* Returnwert */
    goto _setzebaud;
  }


  /* ok, wir sind in der Prärie und suchen jetzt divisor, MULVAL und DIVADVAL,
     merke: teiler = PCLK / (baudrate * K);  K von 16 bis ca. 31,
     wir nehmen die Mitte, also 75% vom eigentlichen teiler
   */
  divisor = divisor - (divisor>>2);          /* Divisor = 0.75 * divisor;  */

  /* der divisor steht erstmal, jetzt zurückrechnen, um K zu bestimmen */
  ksoll = PCLK * 16;
  ksoll = ksoll / (baudrate * divisor);      /* ksoll ist jetzt (16 .. ca. 31) * 16 */
  ksoll = ksoll - 255;                       /* um nicht einseitig zu suchen   */

  /* passenden Wert in der Tabelle suchen */
  kist = 2048;
  i = 72;
  while (kist > ksoll)
  { --i;
    kist = FracTabelle[i].div;
    FDRval = FracTabelle[i].FDR;
  }

  /* Returnwert berechnen (in long!!) */
  kist = kist + 256;
  brcal = PCLK * 16;
  brcal = brcal / (divisor * kist);

  _setzebaud:
  U0FDR = FDRval;                         /* Fraktionalteiler            */
  U0DLL = divisor & 0xFF;                 /* lo Teil des Divisors        */
  U0DLM = (divisor >> 8) & 0xFF;          /* hi Teil des Divisors        */
  U0FCR = 7;                              /* Enable FIFO und Rücksetzen  */
  U0LCR = 0x03;                           /* DLAB = 0                    */
  v24sin = v24sout = 0;                   /* TX Puffer löschen           */
  U0FCR = 1;                              /* Enable Fifo                 */
  VICVectAddr10 = (dword)Uart0TxService;  /* wir benutzen hier Slot 10   */
  VICIntSelect  &=  ~(1<<6);              /* kein FIQ                    */
  VICVectCntl10 = (1<<5) | 6;             /* UART0 hat VIC-Nummer 6      */
  VICIntEnable  = (1<<6);                 /* enable UART0 im VIC         */
  U0IER = 2;                              /* enable TX-Interrupt         */
  return brcal;
}


/* Zeichen zur V24 ausgeben.
   =========================
Die Zeichen werden immer nur in den Sendepuffer geschrieben,
nie direkt in die Hardware - das macht der Interrupt.
Zum eventuellen Starten wird ein Int per Software ausgelöst
Danach wird gewartet, bis wenigstens 1 Platz frei ist im
Sendepuffer (mit Timeout, damit er nicht hängen bleibt).

Rückgabe: c oder 0 bei Timeout
 */

char V24_CharOut (char c)
{ int  h;
  long L;

  /* wir lassen immer einen Platz im Puffer frei! */
  h = v24sin;
  V24puffer[h] = c;
  v24sin = h = (h + 1) & (v24bufsize-1);  /* Zeiger weiterstellen           */
  h = (h + 1) & (v24bufsize-1);           /* h = nächster Ringpufferplatz   */
  VICSoftInt = (1<<6);                    /* Int auslösen zum evtl. Starten */
  L = 2000000;                            /* wir können nicht ewig warten!  */
  while (h==v24sout)                      /* warten bis Platz im Puffer ist */
  { --L;
    if (!L)  return 0;                    /* Timeout */
  }
  return c;
}

/* String auf USART0 ausgeben
   ==========================
 */
void V24_StrOut (char* P)
{ while (*P) V24_CharOut(*P++); }

  /* CR und LF auf USART0 ausgeben  */
void V24_CRLF (void)
{ V24_CharOut(13);
  V24_CharOut(10);
}



/* Anzahl freier Plätze im Sendepuffer ermitteln
   =============================================
 */
int V24_Txfree (void)
{ int i;
  i = v24sin - v24sout;    /* i = belegte Plätze */
  if (i<0) i = i + v24bufsize;
  return v24bufsize - i;
}


/* Test, ob Zeichen an USART0 da ist
   =================================
 */
bool V24_CharAvail (void)
{ if (U0LSR & 1)
   return 1;
  else
   return 0;
}

/* Zeichen auf USART0 empfangen (ohne Echo)
   ========================================
 */
char V24_CharIn (void)
{ if (!(U0LSR & 1)) return 0;
  return (U0RBR);
}


/*  Ende  */
