Friday, November 27, 2015

Phase 2: UART the sunshine of my life ...

I took thanksgiving week off (as I do every year) and decided to put an end to a problem that has plagued me off and on for almost a month. I would love to say I was focused on it in all that time but alas I wasnt ... but this week I was.

So I modified the test program to toggle a GPIO pin that I am to use as external trigger for the scope. I needed a clear picture of what the output looked like. Then I simple configured the uart channel 0 and wrote '0' or 0x30 (ascii code) to the data register

Originally my configuration settings for the LPC1788 uart is based on the peripheral clock which is the cpu clock/4 (for my test case). Thats 40Mhz.

So if you did the math based on the data sheet you can generate both a Uart clock divider and a fractional divider (divaddval and mulval).

This is nicely summarized in the comment section of the function setup_uart in the file IE_egwu_setup.c

     ************************************************************************
     *governing formula is as follows:
     *Uart(baudrate) =  PCLK (40Mhz)/16 x (256 x DLM) + DLL x (1 + DivVal/MulVal)
     *constraints
     *                  1 <= MulVal     <= 15
     *                  0 <= DivAddVal  <= 14
     *                  DivAddVal       <  MulVal
     *[for 115200]
     ************************************************************************
     *115200 = 40Mhz/16 x [(256 x DLM) + DLL x (1 + D/M)
     *115200 = 40Mhz/16 x DLest
     *DLest  = 21.7
     *FRest  = 1.5
     ************************************************************************
     *DLest  = INT(40Mhz/(16 x 115200 x FRest)
     *FRest  = 40Mhz/(16 x 115200 x DLest)
     ************************************************************************
     *DLest  = INT(14.46) = 14
     *FRest  = 1.55
     ************************************************************************
     *this satisfies the condition 1.1 < Frest < 1.9
     *DL     = INT(40Mhz/(16 x 115200 x 1.55) = 14
     *DL*FR  = 14 x 1.55 =  21.7
     ************************************************************************
     *from the table
     *DivAdd = 5
     *MulVal = 9
     ************************************************************************
 
The problem however is that this setting the the fractional dividers above and below the chosen value above ... produce gibberish under minicom on my PC. Transmits and Receives have FRAMING errors - alignment issues.

A close look at the scope showed that value 0x30 was there, but compressed in time.



It occupied 3.6*20e-6 or 72uS. That means there are 13,888 bytes transmitted in one second. Thats verses the ideal of 115200/10 (1 start bit, 8 bits of data and 1 stop bit) = 11,520

The MulVal and DivAddVal fractional clock divider  values can be experimented with. The result is an improvement in uart baud clock and a reduction of bit error rates. But there are 14 such values for MulVal and 15 for DivAddVal.

My first thought was to modify my test program to cycle through the 210 possible combinations while writing 0x30 or '0'. Then I can look at the minicom logs searching for when '0' occurs.

That worked but was tedious in terms of finding out which combination generated that.

A further modification of my program involved
1. setting fractional divider values
2. writing '0'
3. in a loop - emptying receiver fifo
4. looking for '0'
5. displaying the value of MulVal and DivAddVal


On the cable end I just put a paper clip between pins 2 and 3 (RX and TX) creating a loop back. Then just set a break point for when the "if" statement becomes true.

#include "main.h"

#define FRAC_TEST       1
#define TEST_VALUE      '0'



int
main(void)
{
volatile unsigned int wdTemp, wdCount, wdLast;
volatile unsigned int wdMulVal, wdDivAddVal;
const    int          wdGpio=18;
unsigned char sbString[10];


asm ("ldr sp, =0x10010000");

setup_pll();
setup_nvic();
setup_gpio();
gpio_set_lpc_biled(0x3);

wdTemp = 0;

setup_gpdma();
setup_uart();
        
gpio_set_lpc_biled(0x0);

while(1)
        {
        //wdTemp++;
        //uartio_printf("%d\r", wdTemp);
#ifdef  FRAC_TEST
        for(wdMulVal = 1; wdMulVal <= 15; wdMulVal++)
                {
                for(wdDivAddVal = 0; wdDivAddVal <= 15; wdDivAddVal++)
                        {
                        wdTemp  = (wdMulVal << 4);
                        wdTemp |=  wdDivAddVal;
                        EGWU_ONBOARD_UART->FDR = wdTemp;

#endif

                        (LPC_GPIO1->SET) = (1 << wdGpio); 
                        uartio_putc(TEST_VALUE);
                        (LPC_GPIO1->CLR) = (1 << wdGpio);  
                        wdLast = 0;
                        while((wdTemp = uartio_getch()) != 0)
                                wdLast = wdTemp;

                        if(wdLast == TEST_VALUE)
                                {
                                sbString[0] = 'D';
                                sbString[1] = '=';
                                IE_tclib_itoa(wdDivAddVal,sbString+2);

                                uartio_putc(0xd);
                                for(wdCount = 0; wdCount <10; wdCount++)
                                        {
                                        if(sbString[wdCount] == 0)
                                                break;
                                        else
                                                uartio_putc(sbString[wdCount]);
                                        }

                                sbString[0] = 'M';
                                sbString[1] = '=';
                                IE_tclib_itoa(wdMulVal,sbString+2);

                                uartio_putc(0xd);
                                for(wdCount = 0; wdCount <10; wdCount++)
                                        {
                                        if(sbString[wdCount] == 0)
                                                break;
                                        else
                                                uartio_putc(sbString[wdCount]);
                                        }

                                        
                                while(1)
                                        ;
                                }

#ifdef  FRAC_TEST
                        timer_delay_mS(1);
                        }
                }
#endif
        }
}



That worked marvelously.
When the breakpoint hit, I examined wdMulVal and wdDivAddVal (both equal to 1). Fearing a mistake, I then proceeded to write a read a bunch of values to the uart data register (both RX and TX fifos are 16 bytes wide).


(gdb) load
Loading section .data, size 0x160 lma 0x10000000
Loading section .text, size 0x3470 lma 0x10000160
Start address 0x10001a54, load size 13776
Transfer rate: 20 KB/sec, 2755 bytes/write.
(gdb) c
Continuing.

Breakpoint 1, main () at app/main.c:34
34 const    int          wdGpio=18;
(gdb) c
Continuing.

Breakpoint 2, main () at app/main.c:76
76     sbString[0] = 'D';
(gdb) p wdDivAddVal 
$1 = 15
(gdb) p wdMulVal 
$2 = 1
(gdb) 



I was satisfied with the results.


Breakpoint 2, main () at app/main.c:76
76     sbString[0] = 'D';
(gdb) p wdMulVal 
$5 = 1
(gdb) p wdDivAddVal 
$6 = 1
(gdb) monitor mdw 0x4000c000 
0x4000c000: 00000030 
(gdb) 
0x4000c000: 00000000 
(gdb) monitor mww 0x4000c000 0x34
(gdb) monitor mdw 0x4000c000 
0x4000c000: 00000034 
(gdb) monitor mww 0x4000c000 0x32
(gdb) monitor mdw 0x4000c000 
0x4000c000: 00000032 
(gdb) monitor mww 0x4000c000 'a'
Invalid command argument
value option value (''a'') is not valid
in procedure 'mww'
(gdb) monitor mww 0x4000c000 0xd
(gdb) monitor mdw 0x4000c000 
0x4000c000: 0000000d 
(gdb) set wdTemp = 'c'
(gdb) print /x wdTemp
$7 = 0x63
(gdb) monitor mww 0x4000c000 0x61
(gdb) monitor mww 0x4000c000 0x62
(gdb) monitor mww 0x4000c000 0x63
(gdb) monitor mww 0x4000c000 0x30
(gdb) monitor mww 0x4000c000 0xd
(gdb) monitor mww 0x4000c000 0x0
(gdb) monitor mdw 0x4000c000 
0x4000c000: 00000061 
(gdb) 
0x4000c000: 00000062 
(gdb) 
0x4000c000: 00000063 
(gdb) 
0x4000c000: 00000030 
(gdb) 
0x4000c000: 0000000d 
(gdb) 
0x4000c000: 00000000 
(gdb) 
0x4000c000: 00000000 
(gdb) 
0x4000c000: 00000000 
(gdb) print wdDivAddVal 
$8 = 1
(gdb) print wdMulVal 
$9 = 1
(gdb) monitor mdw 0x4000c000 10
0x4000c000: 00000000 00000001 000000c1 00000003 00000000 00000060 00000000 00000000 
0x4000c020: 00000000 00000000 
(gdb) monitor mdw 0x4000c000 20
0x4000c000: 00000000 00000001 000000c1 00000003 00000000 00000060 00000000 00000000 
0x4000c020: 00000000 00000000 00000011 00000000 00000080 00000000 00000000 00000000 
0x4000c040: 00000000 00000000 00000000 00000000 
 

I looked at the scope output to verify what I expected to see ~4.2 * 20e-6 = 84uS. Which means a new character is generated roughly 11,904 times a second. I can live with that and so can the UART ;)










Things should move more swiftly after this. I can proceed with the monitor program (already written in my notebook) after I test DMA controlled UART transmission later this weekend ... Phase 3 cant come soon enough for me ....

No comments:

Post a Comment