News:

Let's find out together what makes a PIC Tick!

Main Menu

UART Calculator

Started by Amateurtje, Jan 15, 2022, 11:41 AM

Previous topic - Next topic

Amateurtje

Hello All,

In my Proton IDE I used to have a UART calculator which I used a lot. Now, it seems I do not have it installed anymore (probably after the last re-install) but I cna not find it anywhere. Not in my computers nor on the internet / this website.

Does anybody know where I can download it or can anybody place it here?
It detected the PIC (I thought) , gave in the crystal value, and it gave all the UART or UART2 settings...

It would be great if I cna have it again...

Giuseppe

Yes, it could be useful for me too

Amateurtje

#2
After  some searching I found this:
https://protoncompiler.com/old-files/archive.protonbasic.co.uk/Files/Executables/1035-USART%20%2b%20EUSART%20Calculator.zip

And, I have my calculator back... Yeeehh.. Here are a lot more plugins, like the fuseconfigurator, etc....

Looking back, it is logical, but .....

keytapper

To whom can manage with python I have wrote a little script.

#!/usr/bin/env python
import sys

def best(lst):
    maxdiff = 64e6
    res = None
    for idx, i in enumerate(lst):
        diff = abs(i)
        if diff < maxdiff:
            maxdiff = diff
            res = idx
    return res

def calcbaudrate(freq, baudrate, divisor):
    return int(freq / divisor / baudrate) -1

def percent(value, mybr):
    return (value - mybr)/mybr

def main(args):
    xtal = int(args[0])
    wantedBR = int(args[1])
    test = -2, -1, 0, 1, 2
    myList = []
    foundBR = {}
    i = 0
    xtalhz = xtal * 1e6
    print('Low baudrate list')
    lowbr = calcbaudrate(xtalhz, wantedBR, 64)
    hibr = calcbaudrate(xtalhz, wantedBR, 16)
    for n in test:
        value = percent((xtalhz / (64 * (lowbr +n +1))), wantedBR)
        myList.append(value)
        print('{0:.2%}\t{1}\t{2}'.format(value, lowbr +n, i))
        foundBR[i] = lowbr + n
        i += 1
    print('High baudrate list')
    for n in test:
        value = percent((xtalhz / (16 * (hibr +n +1))), wantedBR)
        myList.append(value)
        print('{0:.2%}\t{1}\t{2}'.format(value, hibr +n, i))
        foundBR[i] = hibr + n
        i += 1
    index = best(myList)
    print('Preferred %s' % foundBR[index])
if __name__ == '__main__':
    args = (4,)
    if len(sys.argv) > 1:
        args = (sys.argv[1:])
    main(args)
There are two option to add after the command which are the Frequency in MHz and the wanted baud rate. This will work for most all single byte SPBRG on the midrange family of MCU.
If interested for the other MCU I'm glad to provide some additional feature.
Ignorance comes with a cost

John Lawton

#4
Quote from: Amateurtje on Jan 15, 2022, 01:43 PMAfter  some searching I found this:
https://protoncompiler.com/old-files/archive.protonbasic.co.uk/Files/Executables/1035-USART%20%2b%20EUSART%20Calculator.zip

And, I have my calculator back... Yeeehh.. Here are a lot more plugins, like the fuseconfigurator, etc....

Looking back, it is logical, but .....

Fascinating, there's a lot of the old stuff here, it looks like Crownhill are being friendly now: http://archive.protonbasic.co.uk/

shantanu@india

This is good coding keytapper.
Python is making our lives simpler day by day
Regards
Shantanu

top204

#6
A nice piece of coding keytapper. Reading it is like stepping back to the good old days of the 1980s with BBC computer BASIC, which was the better of the BASICs back them. :-)

Remember, the compiler now automatically calculates and sets the SFR bits for the Baud given in the Declare HSerialx_Baud directive, based upon the Xtal value and the device type. It will also automatically manipulate the bit for the 16-bit or 8-bit Baud ganerator, and set the appropriate SFRs based upon that as well.

It calls the function below to calculate the Baud rate, for different Baud generator settings:

//---------------------------------------------------------------------------
// Calculate the values for a given Baud rate and divisor
// Returns the achievable Buad rate.
// Loads 2 global variables
// Global_SPBRGx: The value to place into SPBRGx
// Global_fBaudError: The error difference between the desired Baud and the calculated Baud
//
double GetBaudValues(unsigned long pRequiredBaudrate, unsigned long pBaudDivisor)
{
    double fOsc = (Global_fActual_Xtal_Frequency * 1000000);
    double fAchievableBaudRate;
    unsigned long iCalculatedBaud;
    unsigned long SPBRGx;
try                                                                                                 // Trap a division by 0
{
//
// Calculate the value to place into SPBRGx for a given Baud rate
// Based upon the calculation:
//      SPBRGx =  ((fOsc / Desired Baud Rate) / Divisor) – 1
//
    if(pBaudDivisor == 4)
        {
            SPBRGx = (double)((((fOsc / pRequiredBaudrate) + 4) / 4) - 1);
            iCalculatedBaud = (unsigned long)(fOsc / (4 * (SPBRGx + 1)));
        }
    else if(pBaudDivisor == 16)
        {
            SPBRGx = (double)((((fOsc / pRequiredBaudrate) + 8) / 16) - 1);
            iCalculatedBaud = (unsigned long)(fOsc / (16 * (SPBRGx + 1)));
        }
    else if(pBaudDivisor == 64)
        {
            SPBRGx = (double)((((fOsc / pRequiredBaudrate) + 32) / 64) - 1);
            iCalculatedBaud = (unsigned long)(fOsc / (64 * (SPBRGx + 1)));
        }
    Global_SPBRGx = SPBRGx;
    Global_fBaudError = fabs(((double)iCalculatedBaud - (double)pRequiredBaudrate) / (double)pRequiredBaudrate);
    Global_fBaudError = Global_fBaudError * 100;
    fAchievableBaudRate = (fOsc / (pBaudDivisor * (Global_SPBRGx + 1)));                            // Calculate the actual Baud rate achievable
    return fAchievableBaudRate;                                                                     // Return the achievable Baud rate
}
catch(...)
{
    Global_SPBRGx = 0x24;
    Global_fBaudError = 0;
    return 0;
}

Then uses the Global_fBaudError variable to find the better Baud value from them using different pBaudDivisor parameters passed to it.

Amateurtje

Do I understand correctly that we do not have to define the below anymore:
;Calculated Baudrate = 9597 @ Xtal 40MHz, Error = -0,03%
Declare Hserial2_RCSTA  = 144 ; Enable continuous receive
Declare Hserial2_TXSTA  = 36  ; Enable transmit, BRGH = 1
Declare Hserial2_Clear  = On  ; Clear overflow automatically
Declare Hserial2_SPBRG  = 17  ; Baud Rate Generator Low Byte Value
SPBRGH2                = 4  ; Baud Rate Generator High Byte Value
BAUDCON2.3              = 1  ; Enable the 16 bit Baud Rate Generator
;Calculated Baudrate = 9597 @ Xtal 40MHz, Error = -0,03%
RCSTA2    = 144 ; Enable continuous receive
TXSTA2    = 36  ; Enable transmit, BRGH = 1
SPBRG2    = 17  ; Baud Rate Generator Low Byte Value
SPBRGH2    = 4  ; Baud Rate Generator High Byte Value
BAUDCON2.3 = 1  ; Enable the 16 bit Baud Rate Generator

But we can replace this just with:

Declare Hserial2_Baud  = 9600


top204

#8
Yep.... They are no longer needed for the USARTs.

For example, on a PIC18F25K20 device, using USART1:

    Device = 18F25K20
    Declare Xtal = 64
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600
    Declare Hserial1_Clear = 1                   ' Enable Error clearing on received characters
    Declare HRSOut1_Pin = PORTC.6
    Declare HRSIn1_Pin = PORTC.7
'
' Create a variable
'   
    Dim MyByte As Byte
         
'-------------------------------------------------------------
' The main program starts here
'
Main:                    
    Do
        MyByte = HRSIn
        HRSOut MyByte
    Loop
   
'-------------------------------------------------------------
' Setup the fuses for the 4xPLL
'
Config_Start
    FOSC = HSPLL        ' HS oscillator, PLL enabled and under software control
    Debug = Off         ' Background debugger disabled' RB6 and RB7 configured as general purpose I/O pins
    XINST = Off         ' Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
    STVREN = Off        ' Reset on stack overflow/underflow disabled
    WDTEN = Off         ' WDT disabled (control is placed on SWDTEN bit)
    FCMEN = Off         ' Fail-Safe Clock Monitor disabled
    IESO = Off          ' Two-Speed Start-up disabled
    WDTPS = 128         ' Watchdog is 1:128
    BOREN = Off         ' Brown-out Reset disabled in hardware and software
    BORV = 18           ' VBOR set to 1.8 V nominal
    MCLRE = On          ' MCLR pin enabled, RE3 input pin disabled
    HFOFST = Off        ' The system clock is held Off until the HF-INTOSC is stable.
    LPT1OSC = Off       ' T1 operates in standard power mode
    PBADEN = Off        ' PORTB<4:0> pins are configured as digital I/O on Reset
    CCP2MX = PORTC      ' CCP2 input/output is multiplexed with RC1
    LVP = Off           ' Single-Supply ICSP disabled
    Cp0 = Off           ' Block 0 (000800-001FFFh) not code-protected
    CP1 = Off           ' Block 1 (002000-003FFFh) not code-protected
    CPB = Off           ' Boot block (000000-0007FFh) not code-protected
    CPD = Off           ' Data eeprom not code-protected
    WRT0 = Off          ' Block 0 (000800-001FFFh) not write-protected
    WRT1 = Off          ' Block 1 (002000-003FFFh) not write-protected
    WRTB = Off          ' Boot block (000000-0007FFh) not write-protected
    WRTC = Off          ' Configuration registers (300000-3000FFh) not write-protected
    WRTD = Off          ' Data eeprom not write-protected
    EBTR0 = Off         ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = Off         ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = Off         ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

The compiler will create the assembler code, before the user's program starts:

; UART1_ACTUALBAUD = 9592.33
; UART1_BAUDERROR = 0.0833333
    bsf BAUDCON,PP_BRG16
    movlw 160
    movwf SPBRG
    movlw 1
    movwf SPBRGH
    movlw 32
    movwf TXSTA
    movlw 144
    movwf RCSTA
    movlb 0

The original Declares have been left in place, and the auto Baud generation will be disabled if they are used in a program, for backward compatability.

I have recently found my calculations can be tweeked a little to give a better Baud calculation, but they work well and I have used them ever since I added them to the compilers many years ago.

See the compiler manual's section on Compiler Directives\USARTx declares:

"Declare Hserial_Baud = Constant value
Sets the Baud rate that will be used to transmit and receive a value serially. The Baud rate is
calculated using the Xtal frequency declared in the program. The compiler will assign the best
values to the SFRs for the Baud rate required. Within the asm file listing are the Baud rate
achieved and the error percentage. Once compiled, press the F2 button and view the asm listing."

Amateurtje

Thats great. Did not know that...

I see in your example that you als define the ports:
Declare HRSOut1_Pin = PORTC.6
Declare HRSIn1_Pin = PORTC.7

Why is that? Are they not (always) fixed? Aren't the uart ports not hardware defined? I use mostly the 18F46k22, although want to try the 18F47k84 in a couple of weeks.. can they be defined to other pins? (I never read anything about that in the 18F46k22 datasheet, as far as I can remember....)

top204

#10
With the newer devices, they have PPS (Peripheral Pin Select), which means the USARTx pins, as well as some other peripheral pins, can be moved around. With these types of 8-bit devices, the compiler will automatically assign the pins chosen in the USART and PWM declares to the SFRs for the PPS. It has become a habit now to add the pins used, in a code listing. It helps when scanning a program to see what pin is used for what. :-)

For example, the above program listing operating on a PIC18F26K40 at 16MHz:

    Device = 18F26K40
    Declare Xtal = 16
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600
    Declare Hserial1_Clear = 1                   ' Enable Error clearing on received characters
    Declare HRSOut1_Pin = PORTC.6
    Declare HRSIn1_Pin = PORTC.7
'
' Create a variable
'   
    Dim MyByte As Byte
         
'-------------------------------------------------------------
' The main program starts here
'
Main:                    
    Do
        MyByte = HRSIn
        HRSOut MyByte
    Loop

Will produce the assembler code:

; UART1_ACTUALBAUD = 9615.38
; UART1_BAUDERROR = 0.15625
    bcf BAUD1CON,PP_BRG16
    movlw 103
    movwf SP1BRG
    clrf SP1BRGH
    movlw 36
    movwf TX1STA
    movlw 144
    movwf RC1STA
; CONFIGURE HRSOUT1 PPS
    movlw 0X16
    movff WREG,TX1PPS
    movlw _PPS_FN_TX1
    movff WREG,RC6PPS
; CONFIGURE HRSIN1 PPS
    movlw 0X17
    movff WREG,RX1PPS
    movlw _PPS_FN_DT1
    movff WREG,RC7PPS
    movlb 0

Microchip have not quite made their mind up how the PPS works, so they have changed its mechanism on quite a few devices. And I had to add a new directive in the PPI files to tell the compiler what mechanism Microchip have chosen for the device being used, so it could produce the correct assembler code and load the correct SFRs with a particular value in a particular sequence. I have to search the datasheets for new devices to find the differences, which is not a good read, because they hide so much and say so little in them now. :-)

PPS_TYPE=1      ; The type of PPS to use for the device

Amateurtje

PPS, Now you mention it, I read it once... I hope my new ordered pic also has this while it can solve a possible problem.... ( i want to use it on an existing board...)

Sorry for my ignorance :) Tonight it is going to be a datasheet for bed time reading... :)  :)