Siwei Xu
/
uart_baudrate_test
UART baud rate test for LPC11U35
Fork of UranusTest by
main.cpp@2:626c243adc03, 2017-05-19 (annotated)
- Committer:
- swxu
- Date:
- Fri May 19 17:37:27 2017 +0000
- Revision:
- 2:626c243adc03
- Parent:
- 1:7d1fb66613d9
- Child:
- 3:98e614fc55c5
UART baud rate test
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
swxu | 0:ae1f7d2c4fbc | 1 | #include "mbed.h" |
swxu | 0:ae1f7d2c4fbc | 2 | #include "USBSerial.h" |
swxu | 0:ae1f7d2c4fbc | 3 | |
swxu | 1:7d1fb66613d9 | 4 | #include <time.h> |
swxu | 1:7d1fb66613d9 | 5 | |
swxu | 1:7d1fb66613d9 | 6 | DigitalOut led0(P0_20); |
swxu | 1:7d1fb66613d9 | 7 | DigitalOut led1(P0_21); |
swxu | 1:7d1fb66613d9 | 8 | DigitalOut led2(P0_11); |
swxu | 1:7d1fb66613d9 | 9 | |
swxu | 2:626c243adc03 | 10 | USBSerial vcom; // Virtual serial port over USB |
swxu | 2:626c243adc03 | 11 | Serial uart(P0_19, P0_18); |
swxu | 2:626c243adc03 | 12 | |
swxu | 2:626c243adc03 | 13 | void serial_baud(int baudrate); |
swxu | 2:626c243adc03 | 14 | |
swxu | 2:626c243adc03 | 15 | void uart_send_test() |
swxu | 1:7d1fb66613d9 | 16 | { |
swxu | 2:626c243adc03 | 17 | for (int i = 0; i < 20; i++) { |
swxu | 2:626c243adc03 | 18 | uart.printf("%s: %d %d\r\n", __FUNCTION__, clock(), i); |
swxu | 2:626c243adc03 | 19 | wait(0.1); |
swxu | 1:7d1fb66613d9 | 20 | } |
swxu | 1:7d1fb66613d9 | 21 | } |
swxu | 1:7d1fb66613d9 | 22 | |
swxu | 2:626c243adc03 | 23 | void try_baudrate(int baudrate) |
swxu | 2:626c243adc03 | 24 | { |
swxu | 2:626c243adc03 | 25 | vcom.printf("try UART baudrate: %d...\r\n", baudrate); |
swxu | 1:7d1fb66613d9 | 26 | |
swxu | 2:626c243adc03 | 27 | serial_baud(baudrate); |
swxu | 1:7d1fb66613d9 | 28 | |
swxu | 2:626c243adc03 | 29 | vcom.printf("press button to start\r\n"); |
swxu | 1:7d1fb66613d9 | 30 | DigitalIn btn(P0_1, PullUp); |
swxu | 2:626c243adc03 | 31 | while (btn.read()) wait(0.010); |
swxu | 1:7d1fb66613d9 | 32 | |
swxu | 2:626c243adc03 | 33 | uart.printf("UART under baudrate %d is OK!\r\n", baudrate); |
swxu | 2:626c243adc03 | 34 | uart_send_test(); |
swxu | 2:626c243adc03 | 35 | |
swxu | 2:626c243adc03 | 36 | vcom.printf("done try UART baudrate: %d\r\n", baudrate); |
swxu | 1:7d1fb66613d9 | 37 | } |
swxu | 0:ae1f7d2c4fbc | 38 | |
swxu | 0:ae1f7d2c4fbc | 39 | int main(void) { |
swxu | 0:ae1f7d2c4fbc | 40 | uart.format(); |
swxu | 0:ae1f7d2c4fbc | 41 | |
swxu | 1:7d1fb66613d9 | 42 | uart.printf("BUILD: %s %s\r\n", __DATE__, __TIME__); |
swxu | 1:7d1fb66613d9 | 43 | uart.printf("System core clock: %d\r\n", SystemCoreClock); |
swxu | 2:626c243adc03 | 44 | |
swxu | 2:626c243adc03 | 45 | int timeout = 10; |
swxu | 2:626c243adc03 | 46 | while (timeout--) { |
swxu | 2:626c243adc03 | 47 | led2 = !led2; |
swxu | 2:626c243adc03 | 48 | wait(1); |
swxu | 2:626c243adc03 | 49 | } |
swxu | 1:7d1fb66613d9 | 50 | |
swxu | 2:626c243adc03 | 51 | for (int i = 1; i < 9; i++) { |
swxu | 2:626c243adc03 | 52 | try_baudrate(115200 * i); |
swxu | 2:626c243adc03 | 53 | } |
swxu | 1:7d1fb66613d9 | 54 | |
swxu | 0:ae1f7d2c4fbc | 55 | while(1) { |
swxu | 1:7d1fb66613d9 | 56 | // led2 = button.read(); |
swxu | 0:ae1f7d2c4fbc | 57 | clock_t ts = clock(); |
swxu | 0:ae1f7d2c4fbc | 58 | |
swxu | 0:ae1f7d2c4fbc | 59 | uart.printf("Hello UART! %d\r\n", ts); |
swxu | 1:7d1fb66613d9 | 60 | vcom.printf("I am a virtual vcom port %d\r\n", ts); |
swxu | 1:7d1fb66613d9 | 61 | |
swxu | 1:7d1fb66613d9 | 62 | led0 = !led0; wait(1); |
swxu | 1:7d1fb66613d9 | 63 | led1 = !led1; wait(1); |
swxu | 1:7d1fb66613d9 | 64 | // led2 = !led2; wait(1); |
swxu | 0:ae1f7d2c4fbc | 65 | } |
swxu | 1:7d1fb66613d9 | 66 | } |
swxu | 2:626c243adc03 | 67 | |
swxu | 2:626c243adc03 | 68 | |
swxu | 2:626c243adc03 | 69 | |
swxu | 2:626c243adc03 | 70 | void serial_baud(int baudrate) { |
swxu | 2:626c243adc03 | 71 | LPC_SYSCON->UARTCLKDIV = 0x1; |
swxu | 2:626c243adc03 | 72 | uint32_t PCLK = SystemCoreClock; |
swxu | 2:626c243adc03 | 73 | // First we check to see if the basic divide with no DivAddVal/MulVal |
swxu | 2:626c243adc03 | 74 | // ratio gives us an integer result. If it does, we set DivAddVal = 0, |
swxu | 2:626c243adc03 | 75 | // MulVal = 1. Otherwise, we search the valid ratio value range to find |
swxu | 2:626c243adc03 | 76 | // the closest match. This could be more elegant, using search methods |
swxu | 2:626c243adc03 | 77 | // and/or lookup tables, but the brute force method is not that much |
swxu | 2:626c243adc03 | 78 | // slower, and is more maintainable. |
swxu | 2:626c243adc03 | 79 | uint16_t DL = PCLK / (16 * baudrate); |
swxu | 2:626c243adc03 | 80 | |
swxu | 2:626c243adc03 | 81 | uint8_t DivAddVal = 0; |
swxu | 2:626c243adc03 | 82 | uint8_t MulVal = 1; |
swxu | 2:626c243adc03 | 83 | int hit = 0; |
swxu | 2:626c243adc03 | 84 | uint16_t dlv; |
swxu | 2:626c243adc03 | 85 | uint8_t mv, dav; |
swxu | 2:626c243adc03 | 86 | if ((PCLK % (16 * baudrate)) != 0) { // Checking for zero remainder |
swxu | 2:626c243adc03 | 87 | int err_best = baudrate, b, a; |
swxu | 2:626c243adc03 | 88 | for (mv = 1; mv < 16 && !hit; mv++) |
swxu | 2:626c243adc03 | 89 | { |
swxu | 2:626c243adc03 | 90 | for (dav = 0; dav < mv; dav++) |
swxu | 2:626c243adc03 | 91 | { |
swxu | 2:626c243adc03 | 92 | // baudrate = PCLK / (16 * dlv * (1 + (DivAdd / Mul)) |
swxu | 2:626c243adc03 | 93 | // solving for dlv, we get dlv = mul * PCLK / (16 * baudrate * (divadd + mul)) |
swxu | 2:626c243adc03 | 94 | // mul has 4 bits, PCLK has 27 so we have 1 bit headroom which can be used for rounding |
swxu | 2:626c243adc03 | 95 | // for many values of mul and PCLK we have 2 or more bits of headroom which can be used to improve precision |
swxu | 2:626c243adc03 | 96 | // note: X / 32 doesn't round correctly. Instead, we use ((X / 16) + 1) / 2 for correct rounding |
swxu | 2:626c243adc03 | 97 | |
swxu | 2:626c243adc03 | 98 | if ((mv * PCLK * 2) & 0x80000000) // 1 bit headroom |
swxu | 2:626c243adc03 | 99 | dlv = ((((2 * mv * PCLK) / (baudrate * (dav + mv))) / 16) + 1) / 2; |
swxu | 2:626c243adc03 | 100 | else // 2 bits headroom, use more precision |
swxu | 2:626c243adc03 | 101 | dlv = ((((4 * mv * PCLK) / (baudrate * (dav + mv))) / 32) + 1) / 2; |
swxu | 2:626c243adc03 | 102 | |
swxu | 2:626c243adc03 | 103 | // datasheet says if DLL==DLM==0, then 1 is used instead since divide by zero is ungood |
swxu | 2:626c243adc03 | 104 | if (dlv == 0) |
swxu | 2:626c243adc03 | 105 | dlv = 1; |
swxu | 2:626c243adc03 | 106 | |
swxu | 2:626c243adc03 | 107 | // datasheet says if dav > 0 then DL must be >= 2 |
swxu | 2:626c243adc03 | 108 | if ((dav > 0) && (dlv < 2)) |
swxu | 2:626c243adc03 | 109 | dlv = 2; |
swxu | 2:626c243adc03 | 110 | |
swxu | 2:626c243adc03 | 111 | // integer rearrangement of the baudrate equation (with rounding) |
swxu | 2:626c243adc03 | 112 | a = b = ((PCLK * mv / (dlv * (dav + mv) * 8)) + 1) / 2; |
swxu | 2:626c243adc03 | 113 | |
swxu | 2:626c243adc03 | 114 | // check to see how we went |
swxu | 2:626c243adc03 | 115 | b = abs(b - baudrate); |
swxu | 2:626c243adc03 | 116 | if (b < err_best) |
swxu | 2:626c243adc03 | 117 | { |
swxu | 2:626c243adc03 | 118 | err_best = b; |
swxu | 2:626c243adc03 | 119 | float er = b * 100.0 / baudrate; |
swxu | 2:626c243adc03 | 120 | vcom.printf("b: %d, er: %f, err_best: %d\r\n", a, er, err_best); |
swxu | 2:626c243adc03 | 121 | |
swxu | 2:626c243adc03 | 122 | DL = dlv; |
swxu | 2:626c243adc03 | 123 | MulVal = mv; |
swxu | 2:626c243adc03 | 124 | DivAddVal = dav; |
swxu | 2:626c243adc03 | 125 | |
swxu | 2:626c243adc03 | 126 | if (b == baudrate) |
swxu | 2:626c243adc03 | 127 | { |
swxu | 2:626c243adc03 | 128 | hit = 1; |
swxu | 2:626c243adc03 | 129 | break; |
swxu | 2:626c243adc03 | 130 | } |
swxu | 2:626c243adc03 | 131 | } |
swxu | 2:626c243adc03 | 132 | } |
swxu | 2:626c243adc03 | 133 | } |
swxu | 2:626c243adc03 | 134 | } |
swxu | 2:626c243adc03 | 135 | |
swxu | 2:626c243adc03 | 136 | // set LCR[DLAB] to enable writing to divider registers |
swxu | 2:626c243adc03 | 137 | LPC_USART->LCR |= (1 << 7); |
swxu | 2:626c243adc03 | 138 | |
swxu | 2:626c243adc03 | 139 | // set divider values |
swxu | 2:626c243adc03 | 140 | LPC_USART->DLM = (DL >> 8) & 0xFF; |
swxu | 2:626c243adc03 | 141 | LPC_USART->DLL = (DL >> 0) & 0xFF; |
swxu | 2:626c243adc03 | 142 | LPC_USART->FDR = (uint32_t) DivAddVal << 0 |
swxu | 2:626c243adc03 | 143 | | (uint32_t) MulVal << 4; |
swxu | 2:626c243adc03 | 144 | |
swxu | 2:626c243adc03 | 145 | // clear LCR[DLAB] |
swxu | 2:626c243adc03 | 146 | LPC_USART->LCR &= ~(1 << 7); |
swxu | 2:626c243adc03 | 147 | |
swxu | 2:626c243adc03 | 148 | vcom.printf("PCLK: %d\r\n", SystemCoreClock); |
swxu | 2:626c243adc03 | 149 | vcom.printf("DL: %d\r\n", DL); |
swxu | 2:626c243adc03 | 150 | vcom.printf("DivAddVal: %d\r\n", DivAddVal); |
swxu | 2:626c243adc03 | 151 | vcom.printf("MulVal: %d\r\n", MulVal); |
swxu | 2:626c243adc03 | 152 | vcom.printf("FDR: %d\r\n", LPC_USART->FDR); |
swxu | 2:626c243adc03 | 153 | } |
swxu | 2:626c243adc03 | 154 | |
swxu | 2:626c243adc03 | 155 |