UART baud rate test for LPC11U35

Dependencies:   USBDevice mbed

Fork of UranusTest by Siwei Xu

main.cpp

Committer:
swxu
Date:
2017-05-19
Revision:
3:98e614fc55c5
Parent:
2:626c243adc03

File content as of revision 3:98e614fc55c5:

#include "mbed.h"
#include "USBSerial.h"

#include <time.h>

/**
* Python script for high baud rate test on Host PC
*
* @code
* #!/usr/bin/env python
*
* import os
* import sys
* import serial
*
* PORT = '/dev/ttyUSB0'
* BAUD = 9600
*
* def main(args):
*     port = len(args) > 1 and args[1] or PORT
*     baud = len(args) > 2 and args[2] or BAUD
*     count = 0
*     with serial.Serial(port, baud) as s:
*         while True:
*             b = s.read()
*             count += 1
*             sys.stdout.write('%x ' % b)
*             sys.stdout.flush()
* if __name__ == '__main__':
*     main(sys.argv)
* @endcode
*/

DigitalOut led0(P0_20);
DigitalOut led1(P0_21);
DigitalOut led2(P0_11);

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 < 20; i++) {
        uart.printf("%s: %d %d\r\n", __FUNCTION__, clock(), i);
        wait(0.1);
    }
}

void try_baudrate(int baudrate)
{
    vcom.printf("try UART baudrate: %d...\r\n", baudrate);

    serial_baud(baudrate);

    vcom.printf("press button to start\r\n");
    DigitalIn btn(P0_1, PullUp);
    while (btn.read()) wait(0.010);
    
    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.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);
    }
    
    for (int i = 1; i < 9; i++) {
        try_baudrate(115200 * i);
    }
    
    while(1) {
//        led2 = button.read();
        clock_t ts = clock();
        
        uart.printf("Hello UART! %d\r\n", ts);
        vcom.printf("I am a virtual vcom port %d\r\n", ts);
        
        led0 = !led0; wait(1);
        led1 = !led1; wait(1);
        // 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);
}