UART baud rate test for LPC11U35

Dependencies:   USBDevice mbed

Fork of UranusTest by Siwei Xu

Revision:
2:626c243adc03
Parent:
1:7d1fb66613d9
Child:
3:98e614fc55c5
diff -r 7d1fb66613d9 -r 626c243adc03 main.cpp
--- a/main.cpp	Fri May 19 07:41:18 2017 +0000
+++ b/main.cpp	Fri May 19 17:37:27 2017 +0000
@@ -3,97 +3,54 @@
 
 #include <time.h>
 
-USBSerial vcom; // Virtual serial port over USB
-Serial uart(P0_19, P0_18);
-
 DigitalOut led0(P0_20);
 DigitalOut led1(P0_21);
 DigitalOut led2(P0_11);
 
-void led_test()
+USBSerial vcom; // Virtual serial port over USB
+Serial uart(P0_19, P0_18);
+
+void serial_baud(int baudrate);
+
+void uart_send_test()
 {
-    for (int i = 0; i < 10; i++) {
-        led0 = !led0; wait(0.33);
-        led1 = !led1; wait(0.33);
-        led2 = !led2; wait(0.33);
+    for (int i = 0; i < 20; i++) {
+        uart.printf("%s: %d %d\r\n", __FUNCTION__, clock(), i);
+        wait(0.1);
     }
-    led0 = led1 = led2 = 1;
-    uart.printf("led_test done!\r\n");
 }
 
-DigitalIn button(P0_1, PullUp);
-struct InterruptTest {
-    InterruptTest(PinName pin) : _ini(pin) {
-        _ini.mode(PullNone);
-        _ini.rise(callback(this, &InterruptTest::on_rise));
-        _ini.fall(callback(this, &InterruptTest::on_fall));
-        _rises = _falls = 0;
-    }
-
-    int rises() { return _rises; }
-    int falls() { return _falls; }
+void try_baudrate(int baudrate)
+{
+    vcom.printf("try UART baudrate: %d...\r\n", baudrate);
 
-private:
-    void on_rise() {
-        led1 = !led1;
-        _rises++;
-    }
-        
-    void on_fall() {
-        led2 = !led2;
-        _falls++;
-    }
-    
-private:
-    InterruptIn _ini;
-    int _rises, _falls;
-};
+    serial_baud(baudrate);
 
-void button_test()
-{
+    vcom.printf("press button to start\r\n");
     DigitalIn btn(P0_1, PullUp);
-    
-    uart.printf("button input test start(10s) %d:\r\n", clock());
+    while (btn.read()) wait(0.010);
     
-    int count = 0;
-    clock_t start = clock();
-    while (clock() - start <= 10*CLOCKS_PER_SEC) {
-        led2 = btn.read();
-        if (!led2) {
-            count++;
-        }
-    }
-    uart.printf("\r\n");
-    uart.printf("button input test done! count: %d, %d\r\n", count, clock());
-}
-
-void interrupt_test()
-{
-    uart.printf("button interrupt test start(10s) %d:\r\n", clock());
-    InterruptTest it(P0_1);
-    wait(10);
-    uart.printf("button interrupt test done, %d\r\n", clock());
-    uart.printf("rises: %d, falls: %d\r\n", it.rises(), it.falls());
-}
-
-void check_button()
-{
-    led2 = button.read();
+    uart.printf("UART under baudrate %d is OK!\r\n", baudrate);
+    uart_send_test();
+    
+    vcom.printf("done try UART baudrate: %d\r\n", baudrate);
 }
 
 int main(void) {
-    uart.baud(115200);
     uart.format();
     
     uart.printf("BUILD: %s %s\r\n", __DATE__, __TIME__);
     uart.printf("System core clock: %d\r\n", SystemCoreClock);
+
+    int timeout = 10;
+    while (timeout--) {
+        led2 = !led2;
+        wait(1);
+    }
     
-//    led_test();
-//    button_test();
-//    interrupt_test();
-    
-    Ticker ticker;
-    ticker.attach_us(check_button, 10000);
+    for (int i = 1; i < 9; i++) {
+        try_baudrate(115200 * i);
+    }
     
     while(1) {
 //        led2 = button.read();
@@ -107,3 +64,92 @@
         // led2 = !led2; wait(1);
     }
 }
+
+
+
+void serial_baud(int baudrate) {
+    LPC_SYSCON->UARTCLKDIV = 0x1;
+    uint32_t PCLK = SystemCoreClock;
+    // First we check to see if the basic divide with no DivAddVal/MulVal
+    // ratio gives us an integer result. If it does, we set DivAddVal = 0,
+    // MulVal = 1. Otherwise, we search the valid ratio value range to find
+    // the closest match. This could be more elegant, using search methods
+    // and/or lookup tables, but the brute force method is not that much
+    // slower, and is more maintainable.
+    uint16_t DL = PCLK / (16 * baudrate);
+    
+    uint8_t DivAddVal = 0;
+    uint8_t MulVal = 1;
+    int hit = 0;
+    uint16_t dlv;
+    uint8_t mv, dav;
+    if ((PCLK % (16 * baudrate)) != 0) {     // Checking for zero remainder
+        int err_best = baudrate, b, a;
+        for (mv = 1; mv < 16 && !hit; mv++)
+        {
+            for (dav = 0; dav < mv; dav++)
+            {
+                // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul))
+                // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul))
+                // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding
+                // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision
+                // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding
+
+                if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom
+                    dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2;
+                else // 2 bits headroom, use more precision
+                    dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2;
+
+                // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood
+                if (dlv == 0)
+                    dlv = 1;
+
+                // datasheet says if dav > 0 then DL must be >= 2
+                if ((dav > 0) && (dlv < 2))
+                    dlv = 2;
+
+                // integer rearrangement of the baudrate equation (with rounding)
+                a = b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2;
+
+                // check to see how we went
+                b = abs(b - baudrate);
+                if (b < err_best)
+                {
+                    err_best  = b;
+                    float er =  b * 100.0 / baudrate;
+                    vcom.printf("b: %d, er: %f, err_best: %d\r\n", a, er, err_best);
+
+                    DL        = dlv;
+                    MulVal    = mv;
+                    DivAddVal = dav;
+
+                    if (b == baudrate)
+                    {
+                        hit = 1;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    
+    // set LCR[DLAB] to enable writing to divider registers
+    LPC_USART->LCR |= (1 << 7);
+    
+    // set divider values
+    LPC_USART->DLM = (DL >> 8) & 0xFF;
+    LPC_USART->DLL = (DL >> 0) & 0xFF;
+    LPC_USART->FDR = (uint32_t) DivAddVal << 0
+                   | (uint32_t) MulVal    << 4;
+    
+    // clear LCR[DLAB]
+    LPC_USART->LCR &= ~(1 << 7);
+
+    vcom.printf("PCLK: %d\r\n", SystemCoreClock);
+    vcom.printf("DL: %d\r\n", DL);
+    vcom.printf("DivAddVal: %d\r\n", DivAddVal);
+    vcom.printf("MulVal: %d\r\n", MulVal);
+    vcom.printf("FDR: %d\r\n", LPC_USART->FDR);
+}
+
+