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