Pretty good weekend effort

nsaspook

Joined Aug 27, 2009
16,330
I know what you are saying, but you kinda missed my point.

I don't need dynamic structures (or even static ones!). I don't need the overhead of dynamic memory allocation. The hardware is fixed.

I just need an assembler pre-processor that can do more than just add and subtract integers and basic conditional assembly. Then I can write macros to build the structures for me at assembly time, abstracted away from the actual code.
In other words you need a HLL front-end to write asm code.

BTW I'm not using dynamic memory allocation for this (the only usage is for special uncached memory for DMA physical transfers in the main program because of the pic32mz L-1 cache) because there is no need for it. The pointers, arrays and structures are used to index directly to cpu hardware register space with offsets for the required device parameter. Most of the end results of the pointer calculations result in a single atomic instruction to set/clear register bits in the cpu hardware.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
So you're saying you've built yourself your own personal "middle-level" language based on a low-level library?
No. I use macros often...they make things easier. Macro assemblers have been around for decades, and their use is common place. It's got nothing to do with middle/high level language. You are still producing .asm code -- just packaged a bit into something that's easier to manage.

I would like to be able to extend the preprocessor, though, so that I can do more fancy things than what typical assembler directives let you do.
 

cmartinez

Joined Jan 17, 2007
8,768
No. I just need an extensible preprocessor.

I am not sure the things I have in mind would even be available in a typical C compiler preprocessor.
A (possibly) dumb question: did you write your own personal floating point math routines? And I mean complex ones, such as square root (not too hard) and trigonometric functions (considerably harder).
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
did you write your own personal floating point math routines?
Yup. Stack-based postfix 32 and 56 bit math. Currently, it supports:

Compare, multiply, divide, add, subtract, log, exponential, negate, absolute value, floor, min, max, polynomials, matrices, and linear and polynomial regression, and (for display purposes) conversion to scientific notation.

No trig functions. I haven't needed any yet, but if I ever do they'd be easy to implement.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
@cmartinez, I needed a simple RC digital filter for my code, to be used in a few different places as part of my signal processing chain.

So, I added a macro to my float library:
Code:
;**************************************
;** RCFILT -- Simple RC Filter       **
;**   sample on stack                **
;**   stack = (1-a)regfs + (a)sample **
;**   sample removed from stack         **
;**************************************

rcfilt    macro    regfs,falpha

    pushfpc    falpha        ;get alpha
    call    mulf        ;multiply by sample
    call    push1        ;get 1
    pushfpc    falpha        ;get alpha
    call    subf        ;compute 1-a
    pushfl    regfs        ;get regfs
    call    mulf        ;comput (1-a)*regfs
    call    addf        ;compute (1-a)*regfs + (a)*sample

    endm
Where ever I need to RC filter a value with a new sample, I just write something like:

Code:
    rcfilt    fvbat,fvfilt    ;filter new result
Who ever said .asm has to be hard???
 

cmartinez

Joined Jan 17, 2007
8,768
@cmartinez, I needed a simple RC digital filter for my code, to be used in a few different places as part of my signal processing chain.

So, I added a macro to my float library:
Code:
;**************************************
;** RCFILT -- Simple RC Filter       **
;**   sample on stack                **
;**   stack = (1-a)regfs + (a)sample **
;**   sample removed from stack         **
;**************************************

rcfilt    macro    regfs,falpha

    pushfpc    falpha        ;get alpha
    call    mulf        ;multiply by sample
    call    push1        ;get 1
    pushfpc    falpha        ;get alpha
    call    subf        ;compute 1-a
    pushfl    regfs        ;get regfs
    call    mulf        ;comput (1-a)*regfs
    call    addf        ;compute (1-a)*regfs + (a)*sample

    endm
Where ever I need to RC filter a value with a new sample, I just write something like:

Code:
    rcfilt    fvbat,fvfilt    ;filter new result
Who ever said .asm has to be hard???
Nice! ... I think that you already know, but my comfort zone is the 8051 architecture, specifically the AT89LP family... But one thing I probably haven't mentioned is that I never use tabs when I code. I always use spaces, it makes the code easier to view and transport to different editors.

Another thing is that I'm in love with is the Programmer's Notepad editor. It makes working with code in different languages a breeze.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Nice! ... I think that you already know, but my comfort zone is the 8051 architecture...
What's that got to do with anything?

Except for call, I didn't (expressly) use a single PIC instruction in that code.

And [a or l]call works on 8051, in exactly the same way -- with acall corresponding with rcall, and lcall corresponding with call.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Well, my new sensor is still not ready to test, so I am just playing with some code.

I came up with a nice battery life monitor. 2 'AA' cells discharged at constant power down to 1.8V (0%).

IMG_20180525_192418.jpg
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
BTW, it takes about 10ms for my float library to compute a 6th order polynomial with 32 bit coefficients and input variable (Vbat).

This is a lifetime to me.

So, I rewrote my poly code to compute one term each main program loop. I use the following iterative process (both in the original and "distributed" version):

y=((((((C6*x)+C5)*x+C4)*x+C3)*x+C2)*x)+C1)*x+C0

Note that the actual order of the polynomial is arbitrary, and built into the "structure" that contains the coefficients, like this:

Code:
;battery life in percent polynomial constants

pbpct    db      6                 ;sixth order polynomial
         dtfl    0x44B63BFA        ;1457.8742129360    X^0
         dtfl    0xC568B3AA        ;-3723.2289133948   X^1
         dtfl    0x45756B22        ;3926.6958393141    X^2
         dtfl    0xC508C42F        ;-2188.2613966824   X^3
         dtfl    0x4429D653        ;679.3488289131     X^4
         dtfl    0xC2DEB831        ;-111.3597478158    X^5
         dtfl    0x40F0EE95        ;7.5291236609       X^6
 

cmartinez

Joined Jan 17, 2007
8,768
So what you do is you generate a reference voltage, and feed the battery's output into an ADC (8 bit should suffice, right?) and then run whatever value it read through said polynomial, thus obtaining a charge percentage value. Is that it?

Also, is that curve derived from commercial rechargeable lithium batteries?
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Also, is that curve derived from commercial rechargeable lithium batteries?
The curve is derived from Energizer 'AA' alkaline cells.

So what you do is you generate a reference voltage, and feed the battery's output into an ADC (8 bit should suffice, right?) and then run whatever value it read through said polynomial, thus obtaining a charge percentage value. Is that it?
Actually, I run both the battery voltage and the reference voltage into the A/D, using Vdd (nominally 3.3V) as the reference. This way, I do not need to scale the battery voltage with a current-consuming voltage divider.

Here's how it works:

Battery is on A/D channel 0, reference (2.500V) is on A/D channel 1.

I alternately sample the channels and accumulate 256 readings for each channel (oversampling to reduce noise). By configuring the A/D with left-justified output, this fills the upper two bytes of each accumulator with a 16 bit fixed point fraction from 0x0000 (for 0 volts) to ~0xFFFF (for full scale -- 3.3V).

Then, I cast the fractions into floats. From there, the math is easy:

Vdd = 2.5/(Fractional representation of Vref)
Vbat = Vdd*(Fractional representation of Vbat)

Vbat is then passed through a digital RC filter (to remove about 0.001 volt of remaining jitter) before converting to %life via the polynomial computation.

There is more: the batteries tend to recover after being powered-off for a while, but it is a "fake" recovery. The terminal voltage rapidly drops to near its last value.

I save last value of battery life % prior to powering off, and only show percentages equal to or less than the saved value.

The value reverts automatically to 100% when the batteries are replaced.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Here's the code for computing Vdd and Vbat:

Code:
;*******************************************************************
;** COMPVDD -- Compute Vdd = Vref (2.5V) / Vref Fractional Counts **
;**    and VBat = Vdd * Vbat Fractional Counts                    **
;*******************************************************************

compvdd
    pushfpc    fvref        ;get reference voltage (2.5)

;put Vref counts on stack

    clrf    temp0        ;16 bit counts
    movdff    vref,temp1
    pushfrac temp0        ;get reference voltage counts

;compute Vdd  

    call    divf        ;compute instantaneous Vdd (on stack)

    call    pushcp        ;save a copy for vbat computation

    bbc    fbfirst,cpvd1st
  
    rcfilt    fvdd,fvfilt    ;filter new result, alpha=fvfilt
  
cpvd1st    popfl    fvdd        ;save Vdd

;put Vbat counts on stack

    clrf    temp0        ;16 bit counts
    movdff    vbat,temp1
    pushfrac temp0        ;get battery voltage counts

;compute Vbat

    call    mulf        ;multiply for Vbat

    bbc    fbfirst,cpvb1st

    rcfilt    fvbat,fvfilt    ;filter new result

cpvb1st    popfl    fvbat        ;save VBat

    return
And for generating the % life remaining:

Code:
;*****************************************************
;** COMPLBAT -- Compute remaining battery life in % **
;*****************************************************

complbat

    bbc    fadrdy,clbcont    ;continue computation of poly computation for battery

;adready set --  start a new sequence

clbinit    movdf    tblptr,pbpct    ;set up table pointer
    tblrd    *+        ;get polynomial order
    incf    tablat,w    ;get order
    movwf    batpcnt        ;and save order + 1

    zerofl    fvpcti        ;clear intermediate accumulator

    return            ;and get out

;adrdy not set -- continue an old sequence

clbcont    movf    batpcnt,f    ;more terms to compute?
    retz            ;nope, get out fast

    pushfl    fvpcti        ;put accumulator on stack
    pushfl    fvbat        ;get battery voltage
    call    mulf        ;and multiply

    decf    batpcnt,w    ;index into coeff table
    pushfpci pbpct+2     ;load up next coefficient
    call    addf        ;and add it in

    decf    batpcnt,f    ;decrement coeff coutner
    bz    clbdone        ;poly done if zero  

;not done, save intermediate

    popfl    fvpcti        ;save intermediate
    return            ;and get out

;done, finish computation

clbdone    pushfpc f100        ;in percent
    call    mulf

    call    push0        ;restrain 0 to 100%
    call    flmax
    pushfpc    f100  
    call    flmin

    movff    batpct,temp0    ;get last battery reading
    clrn    temp1,2

    pushuint temp0
    call    flmin        ;get smaller of the two
    popuint    temp0        ;convert to byte

    movfw    temp0        ;get new battery percentage
    xorwf    batpct,w    ;equal?
    retz            ;no change, quit.

    movff    temp0,batpct    ;copy new value

#ifdef BLUETOOTH
    btsend    cbtbat        ;send to bluetooth
#endif

    return
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Completely unimportant but interesting:

For fun, I decided to run a set of Duracell batteries against my Energizer battery life curve:

Selection_008.png

First, note that I had already run the battery down to about 88% life remaining before I started the test.

The interesting thing is what happened from about 27 hours to 36 hours: The terminal voltage did not change at all for 9 hours! Like, it hit some kind of resistance point or something.

After the "resistance" period, the terminal voltage rapidly got back on track to a more realistic value and then rode the curve down normally.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
Here's the final chart for the Duracell batteries. The orange line is "ideal".

Selection_009.png

Edit: it's a reasonably good track. I just don't know if it's worth the CPU Tinsts required for solving the 6th order polynomial. I can probably get just as good accuracy with a 3rd order poly.
 
Last edited:

wayneh

Joined Sep 9, 2010
18,108
Here's the final chart for the Duracell batteries. The orange line is "ideal".

View attachment 153299

Edit: it's a reasonably good track. I just don't know if it's worth the CPU Tinsts required for solving the 6th order polynomial. I can probably get just as good accuracy with a 3rd order poly.
If the theoretical is a straight line, it may be tough to justify the significance of any additional terms. They might help fit existing data but not really have much predictive value.
 
Top