Forum: Mikrocontroller und Digitale Elektronik LPC176x uart-baudrate


von Jürgen (jliegner)


Lesenswert?

Ich habe festgestellt, dass selbst bei den ganzen uart-Examples zum 
LPCXpresso die Einstellung der Baudrate nur über die Register DLM/DLL 
erfolgt und die Möglichkeiten der fractalen Baudratengeneration nicht 
benutzt werden. Damit wird u.U. ein nicht nötiger Baudratenfehler in 
Kauf genommen. Im Folgenden stelle ich mal einen kleinen Codeschnipsel 
vor der diese Werte zur Laufzeit berechnet. Das mache ich mit der 
Holzhammermethode in dem ich alle 54 Möglichkeiten der Register für die 
fraktalen Werte mal 4 Möglichkeiten des Taktvorteilers durchrechne und 
die Parameter mit der kleinsten Abweichung verwende. Wenn gleiche 
Abweichungen zustande kommen, wird die mit dem größten Vorteiler 
verwendet. Eventuell kann das ja jemand gebrauchen bzw. noch Fehler oder 
Verbesserungen finden.
1
 
2
3
#include <stdlib.h>
4
#include "LPC17xx.h" 
5
#include "gpio.h"
6
7
struct uartBaudRateParam
8
{
9
  int nSysClk;
10
  int nPclkArg;   // zum setzen des Vorteilers
11
  int nMulVal;  
12
  int nDivAddVal;  
13
  int nDlm;  
14
  int nDll;  
15
  int nBaudRate;
16
  int nBaudRateHas;
17
  int nDiff;    
18
  int nClkDiv;
19
};
20
  
21
void uartCalcBaudRate(struct uartBaudRateParam *pP)
22
{
23
  int mindiff=pP->nBaudRate; 
24
  
25
  for (int n=0; n<4; n++)
26
    {
27
    int nClkDiv;  // uart clock vorteiler
28
    int nPclkArg; // for PCLKSELx Registers
29
    switch (n)
30
      {
31
      case 0: nClkDiv=8; nPclkArg=0x03; break;
32
      case 1: nClkDiv=4; nPclkArg=0x00; break;
33
      case 2: nClkDiv=2; nPclkArg=0x02; break;
34
      case 4: nClkDiv=1; nPclkArg=0x01; break;
35
      } 
36
    
37
    // Zwischenergebnis (mit 10 multipl.)     
38
    int dzw=((uint32_t)(pP->nSysClk)*10)/(16*pP->nBaudRate*nClkDiv);  
39
40
    for (int nMulVal=1; nMulVal<=15; nMulVal++)
41
      {
42
      for (int nDivAddVal=0; nDivAddVal<nMulVal; nDivAddVal++)
43
        {
44
        // errechnen was in dlm/dll stehen wird incl Rundung
45
        int dl=((dzw*nMulVal)/(nMulVal+nDivAddVal)+5)/10; 
46
        
47
        // resultierende Baudrate
48
        // die Gleichung aus der Dokumentation:
49
        //
50
        //    UARTnbaud=PCLK/(16*(DLM*256+DLL)*(1+DivAddVal/MulVal))
51
        //
52
        // wurde mit MulVal multipliziert und ergibt damit:
53
        //
54
        //    UARTnbaud=(PCLK*MulVal)/(16*(DLM*256+DLL)*(MulVal+DivAddVal))
55
        //
56
        // damit wird der Divident schoen gross und es braucht kein float oder double
57
        // verwendet werden        
58
        int br=((uint32_t)pP->nSysClk*nMulVal)/(16*dl*(nMulVal+nDivAddVal)*nClkDiv);
59
60
        // differenz zur gewünschten Baudrate
61
        int diff=abs(br-pP->nBaudRate);
62
63
        if (diff<mindiff)
64
          {
65
          pP->nBaudRateHas=br;
66
          pP->nDiff=diff;
67
          pP->nPclkArg=nPclkArg;
68
          pP->nMulVal=nMulVal;
69
          pP->nDivAddVal=nDivAddVal;;
70
          pP->nDlm=dl/256;
71
          pP->nDll=dl%256;
72
          pP->nClkDiv=nClkDiv;
73
          mindiff=diff;
74
          }
75
        }          
76
      }
77
    }
78
}

Und hier ein erweiterbares Beispiel der Verwendung:
1
LPC_UART_TypeDef *uart_init(int uartnr, int baudrate)
2
{
3
  struct uartBaudRateParam aP;
4
5
  // die gewünschte Baudrate setzen 
6
  aP.nBaudRate=baudrate;
7
8
  // und die Systemfrequenz
9
  aP.nSysClk=SystemFrequency;
10
11
  // jetzt rechnen lassen 
12
  uartCalcBaudRate(&aP);
13
14
  LPC_UART_TypeDef *LPC_UART;
15
  switch (uartnr)
16
    {
17
    case 0:
18
      LPC_SC->PCONP |= (1<<3);
19
      LPC_UART=LPC_UART0;
20
      LPC_SC->PCLKSEL0 &= ~(0x03<<6);  
21
      LPC_SC->PCLKSEL0 |= (aP.nPclkArg<<6);
22
      GPIO_PINSEL(0,02,0x01);
23
      GPIO_PINSEL(0,03,0x01);              // rx0
24
      GPIO_PINMODE_IN(0,01,GPIO_IN_FLOAT); // rx0 float
25
      break;
26
    
27
    case 1:
28
      LPC_SC->PCONP |= (1<<4);
29
      // UART1 ist anders als die anderen, aber nicht
30
      // an den hier relevanten Stellen. s.u.
31
      LPC_UART=(LPC_UART_TypeDef *)LPC_UART1;
32
      LPC_SC->PCLKSEL0 &= ~(0x03<<8);  
33
      LPC_SC->PCLKSEL0 |= (aP.nPclkArg<<8);
34
      GPIO_PINSEL(0,15,0x01);
35
      GPIO_PINSEL(0,16,0x01);              // rx1
36
      GPIO_PINMODE_IN(0,16,GPIO_IN_FLOAT); // rx1 float
37
      break;
38
39
    case 2:
40
      LPC_SC->PCONP |= (1<<24);
41
      LPC_UART=LPC_UART2;
42
      LPC_SC->PCLKSEL1 &= ~(0x03<<16);  
43
      LPC_SC->PCLKSEL1 |= (aP.nPclkArg<<16);
44
      GPIO_PINSEL(0,10,0x01);
45
      GPIO_PINSEL(0,11,0x01);  // rx2
46
      GPIO_PINMODE_IN(0,11,GPIO_IN_FLOAT); // rx2 float
47
      break;
48
    
49
    case 3:
50
      LPC_SC->PCONP |= (1<<25);
51
      LPC_UART=LPC_UART3;
52
      LPC_SC->PCLKSEL1 &= ~(0x03<<18);  
53
      LPC_SC->PCLKSEL1 |= (aP.nPclkArg<<18);
54
      GPIO_PINSEL(0,0,0x02);
55
      GPIO_PINSEL(0,1,0x02);  // rx3
56
      GPIO_PINMODE_IN(0,1,GPIO_IN_FLOAT); // rx3 float
57
      break;
58
    }
59
60
  // Zugrif auf das DLL und DLM Register
61
  LPC_UART->LCR|=(1<<7);        
62
  LPC_UART->DLM = aP.nDlm;
63
  LPC_UART->DLL = aP.nDll;
64
  // Zugrif auf das DLL und DLM Register ausschalten
65
  LPC_UART->LCR&=~(1<<7);       
66
  LPC_UART->FDR=aP.nMulVal << 4 | aP.nDivAddVal;
67
68
  LPC_UART->LCR=0x03;   // 8bit  1Stopbit keine Parität 
69
70
  // rx tx-buffer reseten
71
  LPC_UART->FCR=0x07;
72
  return LPC_UART; 
73
}

Der Code ist an einer Stelle unsauber. Die Strukturen für UART0,2,3 sind 
etwas anders als bei UART1. Da aber alle Register, die ich verwende, an 
der selben stelle in den Structuren liegen kann ich den Pointer 
LPC_UART1 auf (LPC_UART_TypeDef *) casten;
Die GPIO Macros sind von mir, wenn die noch einer benötigen sollte bitte 
melden.
Ist es richtiger den Beitrag in dei Codesammlung zu stellen oder hier 
her?

von N. Müller (Gast)


Lesenswert?

Jürgen Liegner schrieb:
> Die GPIO Macros sind von mir, wenn die noch einer benötigen sollte bitte
> melden.

Ja, wäre cool wenn Du die auch noch mit reinstellst...dann ist es 
vollständig.

Grüße und Danke.
N. Müller

von Jürgen (jliegner)


Lesenswert?

na dann bitte:
1
#ifndef GPIO_H
2
#define GPIO_H
3
4
#define  PINSEL     ((volatile uint32_t*)&LPC_PINCON->PINSEL0)
5
#define  PINMODE     ((volatile uint32_t*)&LPC_PINCON->PINMODE0)
6
#define  PINMODE_OD ((volatile uint32_t*)&LPC_PINCON->PINMODE_OD0)
7
8
#define GPIO_PINSEL(p,b,v)      PINSEL[(p) * 2 + (b) / 16] = (PINSEL[(p) * 2 + (b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
9
#define GPIO_PINMODE_IN(p,b,v)  PINMODE[(p) * 2 + (b) / 16] = (PINMODE[(p) * 2 + (b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
10
#define GPIO_PINMODE_OUT(p,b,v) PINMODE_OD[(p)] = ((PINMODE_OD[(p)] & ~(1 << b))) | (v << (b))
11
12
#define GPIO_IN_PULLUP     0x00
13
#define GPIO_IN_REPEATER   0x01
14
#define GPIO_IN_FLOAT      0x02
15
#define GPIO_IN_PULLDOWN   0x03
16
17
#define GPIO_OUT_NORMAL    0x00
18
#define GPIO_OUT_OPENDRAIN 0x01
19
20
#define LPC_GPIO(a)       (LPC_GPIO0 + a*(LPC_GPIO1-LPC_GPIO0))
21
#define GPIO_DIR_OUT(a,b) (LPC_GPIO(a)->FIODIR |=  (1<<b))
22
#define GPIO_DIR_IN(a,b)  (LPC_GPIO(a)->FIODIR &= ~(1<<b))
23
#define GPIO_SET(a,b)     (LPC_GPIO(a)->FIOSET  =  (1<<b)) 
24
#define GPIO_CLR(a,b)     (LPC_GPIO(a)->FIOCLR  =  (1<<b)) 
25
#define GPIO_IS_SET(a,b)  ((LPC_GPIO(a)->FIOPIN & (1<<b)) !=0)
26
27
#endif

von Jim M. (turboj)


Lesenswert?

@OP: Hast Du das Errata-sheet gelesen?
PCLKSELx kann man bei aktiver PLL0 nicht mehr verstellen.

Übrigens gibt es so eine Funktion in der "lpc17xx CMSIS driver" library:
Link von:
http://support.code-red-tech.com/CodeRedWiki/NXPDriverLibraries
zeigt auf:
http://ics.nxp.com/support/documents/microcontrollers/zip/lpc17xx.cmsis.driver.library.zip

Auf einer der Seiten für den LPC1768 bei NXP.com ist das ebenfalls 
verlinkt.

von Jürgen (jliegner)


Lesenswert?

Hallo Jim Meba,

danke für deine Infos. Das wusste ich bisher noch nicht. Das Setzten von 
PCLKSELx findet man in fast jedem Beispielcode. Ich denke in der Regel 
immer nachdem die PLL0 gesetzt ist. Ich muss nochmal debuggen, ob bei 
meinen Tests immer 0x00 für das Bitpaar (CLK/4) rauskam und es deshalb 
nicht zum Fehler kam oder ob ich ICs habe bei denen das trotzdem geht.
Die "lpc17xx CMSIS driver" habe ich mir zwar schon mal angesehen aber 
nicht den uart-Teil. Ich habe gesehen, daß das Setzen eines Bits bei den 
Gpio Funktionen ganz schönen Overhead produziert (2 Function-Calls). 
Deshalb wollte ich auf diese Lib ganz verzichten. Ausserdem will ich die 
Register des MC verstehen und nicht nur wissen wie man entsprechende 
Funktionen aufruft. Wenns um die Fehlersuche geht sind ja dann trotzdem 
wieder die Register gefragt und ich muss eigentlich alles doppelt lernen 
wenn ich die Lib für alles benutze.
Vor diesem gesamten Hintergrund hat meine Funktion der 
Baudratenberechnung noch den Vorteil, dass man die Werte berechnen kann 
bevor die PLL0 gesetzt und connectetd wird. Dann die PCLKSELx-Register 
mit den berechnetetn Werten setzten, die PLL0 starten (oder den normalen 
sysinit) und dann die UARTs mit den restlichen Parametern 
initaialisieren.
Danke nochmal.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.