Trying to set up UART dividers on an LPC1817, most likely doing something wrong.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Trying to set up UART dividers on an LPC1817, most likely doing something wrong.

Jump to solution
1,276 Views
Martin42
Contributor II

I'm porting code from an LPC1768 to an LPC1817 and have a problem that the UART is running at the wrong speed. All the old code looks sane and from what I can read in the documentation the registers in the 18xx should be pretty much directly compatible with the 17xx.

So to figure out what's going on I wrote some raw C testcode that access the registers directly just to verify that the MCU and I are on the same page, but apparently we're not. This is the raw testcode:

    *((volatile uint32_t *) 0x40086100) = 0xf1; // Set up P2_0
    *((volatile uint32_t *) 0x40086104) = 0xf1; // Set up P2_1
    *((volatile uint32_t *) 0x40051508) = 0x07; // Enable UART0 clock (should already be enabled)
    *((volatile uint32_t *) 0x4008100C) = 0x83; // Set DLAB in LCR
    *((volatile uint32_t *) 0x40081000) = 6; // Set low byte divider
    *((volatile uint32_t *) 0x40081004) = 7; // Set high byte divider
    *((volatile uint32_t *) 0x40081028) = 0x10; // Set fraction divider
    volatile uint32_t uart0_lcr = *((volatile uint32_t *) 0x4008100C);
    volatile uint32_t uart0_dll = *((volatile uint32_t *) 0x40081000);
    volatile uint32_t uart0_dlm = *((volatile uint32_t *) 0x40081004);
    volatile uint32_t ccu1_pm = *((volatile uint32_t *) 0x40051000);
    volatile uint32_t ccu1_base_stat = *((volatile uint32_t *) 0x40051004);
    volatile uint32_t ccu1_usart0_cfg = *((volatile uint32_t *) 0x40051508);
    volatile uint32_t ccu1_usart0_stat = *((volatile uint32_t *) 0x4005150c);
    volatile uint32_t ccu2_pm = *((volatile uint32_t *) 0x40052000);
    volatile uint32_t ccu2_base_stat = *((volatile uint32_t *) 0x40052004);
    volatile uint32_t ccu2_usart0_cfg = *((volatile uint32_t *) 0x40052500);
    volatile uint32_t ccu2_usart0_stat = *((volatile uint32_t *) 0x40052504);

Aaaand it doesn't work. When I check the values they all look sane, except for dll and dlm that still have their reset values of 1 versus 0. Clearly I'm missing something that probably is very obvious when you see it, but I've been trying to figure out what for a few days and I'm pretty much stuck. And the UART still run at the wrong speed and changing the dividers make no difference (verified with oscilloscope).

So if anyone could throw a quick look at this and tell me what I'm missing I'd be very grateful.

Regards

/M
Labels (2)
0 Kudos
1 Solution
1,251 Views
Martin42
Contributor II

Solved it!

Writing down the explanation just in case someone else googles for the same problem and ends up here.

The problem was that the boot mode for the board (early prototype, there's some fun quirks here and there that we're ironing out) was wrong so it went into the built in boot loader in the MCU. Since we're so early in the bring up I still run the board entirely on JTAG so it's not something that I even noticed, but when I looked closer at the registers I noted that the fraction divider was set to 0x85 even though the reset value according to the documentation should be 0x10. However 0x85 is what's expected after "Reset value after UART0/3 boot"!

So I patched the board with a little wire that stops the MCU from going into boot loader mode and now it works! Or rather, right now it runs at 111.111 BAUD rather than 115.200 since 12 MHz can't be cleanly divided by an integer, but it's close enough for me to be very, very happy since I've been chasing this problem for days.

I need a beer. Possibly several.

View solution in original post

0 Kudos
6 Replies
1,252 Views
Martin42
Contributor II

Solved it!

Writing down the explanation just in case someone else googles for the same problem and ends up here.

The problem was that the boot mode for the board (early prototype, there's some fun quirks here and there that we're ironing out) was wrong so it went into the built in boot loader in the MCU. Since we're so early in the bring up I still run the board entirely on JTAG so it's not something that I even noticed, but when I looked closer at the registers I noted that the fraction divider was set to 0x85 even though the reset value according to the documentation should be 0x10. However 0x85 is what's expected after "Reset value after UART0/3 boot"!

So I patched the board with a little wire that stops the MCU from going into boot loader mode and now it works! Or rather, right now it runs at 111.111 BAUD rather than 115.200 since 12 MHz can't be cleanly divided by an integer, but it's close enough for me to be very, very happy since I've been chasing this problem for days.

I need a beer. Possibly several.

0 Kudos
1,271 Views
frank_m
Senior Contributor III

If you haven't seen bad code before, now you know ...

The posted lines exclusively use what is called "magic numbers", i.e. the code itself documents nothing.

I suspect the issue is in the clock divider / peripheral clock ratios, guessing the MCUs ar not running on the same clock speeds.

I would pick up both user manuals, decode what the code wants to achieve, and cross-check it against the manual of the new MCU, and a peripheral register view in the debugger.

0 Kudos
1,269 Views
Martin42
Contributor II

Yep, as I said, it's raw test code and I accessed the raw memory addresses on purpose to avoid any problems in the HAL etc to be able to rule out any issues in HAL or similar.

The problem is that no matter what I set DLL and DLM to I get the same bitrate on the LPC18xx and I always read back 1 versus 0 when I read the registers after trying to set them. So the problem isn't that I don't get the same speed as on the LPC17xx, it's that I can't change it. At all.

0 Kudos
1,263 Views
frank_m
Senior Contributor III

>Yep, as I said, it's raw test code and I accessed the raw memory addresses on purpose to avoid any problems in the HAL etc to be able to rule out any issues in HAL or similar.

I would have used the constants defined in the device header instead of magic numbers. Most often they are identical to the register/bit names in the MCU manual.

> The problem is that no matter what I set DLL and DLM to I get the same bitrate on the LPC18xx and I always read back 1 versus 0 when I read the registers after trying to set them.

I don't have LPC18xx to try at all.

But check the hardware manual again. Different peripherals are on different (peripheral) busses, with separate clock generation settings. I know of MCU models (of other vendors) who have serial/UART units on different busses in one MCU. 

In short, you might change settings in the wrong place.

0 Kudos
1,260 Views
Martin42
Contributor II

> I would have used the constants defined in the device header instead of magic numbers. Most often they are identical to the register/bit names in the MCU manual.

Yeah, well. Back in the days when this code was originally written someone suffered from a great deal of Not Invented Here (mind you, before my time) and that's why I don't trust the C code. As soon as I'm able to actually modify DLL and DLM this magic testcode will die a very quick death.

> In short, you might change settings in the wrong place.

Yeah, I have double and triple checked. When DLAB is 1 I can read the reset values for DLL and DLM, when DLAB is 0 writing to THR makes the UART send out data (caught by oscilloscope) and I can change the values in IER, as per documentation. So the addresses are right, despite being magic numbers.

What I CAN'T do is change the values in DLL and DLM despite that those registers are marked as R/W and I can't find anything in the documentation about a lock bit or a condition that has to be fulfilled (more than the DLAB bit) for writing to DLL and DLM.

So that's why I wonder if there might be other bits or conditions that hinders me from updating the DLL and DLM-registers. Or if I'm missing something bloody obvious.

Martin42_0-1655725713956.png

 

 

0 Kudos
1,254 Views
frank_m
Senior Contributor III

> Yeah, well. Back in the days when this code was originally written someone suffered from a great deal of Not Invented Here (mind you, before my time) and that's why I don't trust the C code.

While your code might compile into the same instruction sequence like a "proper" written version, I guess you see what the problem is. I suspect every developer fell into this trap at least once ...

> So that's why I wonder if there might be other bits or conditions that hinders me from updating the DLL and DLM-registers. Or if I'm missing something bloody obvious.

Did you enable the LPC1817 UART peripheral correctly ?

I think it is the SYSCON register in most LPC controllers.

Other MCUs (of other vendors) give you hardfaults if you try to access registers of a non-enabled peripheral.

Errors in GPIO initialisation and assignment (pinmux) usually results in "missing" signals.

0 Kudos