Siwei Xu
/
uart_baudrate_test
UART baud rate test for LPC11U35
Fork of UranusTest by
Diff: main.cpp
- 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); +} + +