With acknowledged thanks to Angel Mier and Ccin E Crout for their ReadVdd code suggestions in the thread "18LF18313 - LPBOREN Not working as expected ! Well it is but not quite !", I have attached a working version of my solution and I was amazed by the accuracy of measurement.
In it there are a couple of issues that I'd like clarified / explained :
I couldn't get Read10(an31) to return a meaningful value other than 1023 so I came up with my own read routine.
When reading volts from the ADRES register (a word) , GCBasic treated it as a byte. Then I tried reading the byte values and found that there was an error according to whether the low byte or high byte was read first. So my solution was to redefine ADRES, in an alias, to be ADRESW as a word or just write an assembler patch.
Anyway there's some food for thought and explanation and a ReadVDD routine that does work for me using a 16LF18323.
Now, just when I thought that I understood everything - PIC programming proved my foolishness :
I wanted to set up the ADC stuff using an interrupt and built a standalone test version that worked :
On Interrupt ADCReady Call ADCReady_Handler
sub ADCReady_Handler
ADCON0.ADON = 0 ; Turn ADC off ... save power !
ADIF = 0 ; Clear ADC interrupt flag.
ADCEvent = 1 ; Tell the world that something happened.
volts = ADRESW ; Grab a copy of the ADC result.
volts = 1048576 / volts ; Calculate the actual voltage relative to 1.024v.
end sub ;(ADCReady_Handler)
But when I added it to my existing project nothing worked :o(
It looks like the addition of the above breaks something in the cascade of interrupt handlers that I use but I can't tell that for sure. The problem is isolated to the above code addition without actually having added any code to initialize the associated registers. If I remove all the lines from the subroutine my project runs as before - I tried adding just an LED on / off test into the handler but that too killed everything.
I checked further as I also need to trigger 1 second update interrupts using Timer2 and came across the same dilemma :
On Interrupt Timer2Match Call Timer2Match_Handler
sub Timer2Match_Handler
TMR2 = 0
PIR1.TMR2IF = 0 ; Clear Timer2 interrupt flag.
Tim2Event = 1 ; Tell the world that something happened.
Tim2Count = Tim2Count + 1 ;
end sub ;(Timer2Match_Handler)
Once more this addition caused the same problem and again removing the sub lines "fixed it" !
Any suggestions and comments gratefully received ... going for a beer now !
Thanks Andrew
For the sake of completion w.r.t. the ReadVdd interrupt code, this is my ReadVdd trigger :
if (ADCEvent = 0) then
ADCON0.ADON = 1 ; Don't turn ADC on until we need it - 40uA hit !
NOP ; Need 8µS for the ADC to stabilize - so do 2 x NOPs !
NOP ; (1 cycle for a NOP is 4 µS.)
ADCON0.GO = 1 ; Start a conversion.
end if
(I'm using a 1MHz clock - NOPs make an efficient and accurate delay.)
I couldn't get Read10(an31) to return a meaningful value other than 1023 so I came up with my own read routine.
Anyway there's some food for thought and explanation and a ReadVDD routine that does work for me using a 16LF18323.
I would need to see the code. This could be that AN31 does not exist in the library ( total guess ). We you reading to a word? Did you use READA10 with any parameters. A simple example code please.
Now, just when I thought that I understood everything - PIC programming proved my foolishness :
I wanted to set up the ADC stuff using an interrupt and built a standalone test version that worked :
On Interrupt ADCReady Call ADCReady_Handler
sub ADCReady_Handler
ADCON0.ADON = 0 ; Turn ADC off ... save power !
ADIF = 0 ; Clear ADC interrupt flag.
ADCEvent = 1 ; Tell the world that something happened.
volts = ADRESW ; Grab a copy of the ADC result.
volts = 1048576 / volts ; Calculate the actual voltage relative to 1.024v.
end sub ;(ADCReady_Handler)
But when I added it to my existing project nothing worked :o(
It looks like the addition of the above breaks something in the cascade of interrupt handlers that I use but I can't tell that for sure. The problem is isolated to the above code addition without actually having added any code to initialize the associated registers. If I remove all the lines from the subroutine my project runs as before - I tried adding just an LED on / off test into the handler but that too killed everything.
Very unlikely that some cascade failed. As there is no cascade. :-) Each interrupt is explicit.
Example code that I can compile please to see what is happening.
I checked further as I also need to trigger 1 second update interrupts using Timer2 and came across the same dilemma :
On Interrupt Timer2Match Call Timer2Match_Handler
sub Timer2Match_Handler
TMR2 = 0
PIR1.TMR2IF = 0 ; Clear Timer2 interrupt flag.
Tim2Event = 1 ; Tell the world that something happened.
Tim2Count = Tim2Count + 1 ;
end sub ;(Timer2Match_Handler)
Example code that I can compile please to see what is happening.
Clearing TMR2IF is not required as this is specific to the interrupt. The DAT has the data to control this.....
Once more this addition caused the same problem and again removing the sub lines "fixed it" !
Not sure I follow. Which addition ?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
And, volts = 1048576 / volts in the ISR. This is a long running calculation.
Speed is your friend. No long run code is always best.
sub ADCReady_Handler
ADCON0.ADON = 0 ; Turn ADC off ... save power !
// NOT NEEDED THIS IS DONE AUTOMATICALLY ADIF = 0 ; Clear ADC interrupt flag.
ADCEvent = 1 ; Tell the world that something happened.
volts = ADRESW ; Grab a copy of the ADC result.
// move to main loop and use ADCEvent to indicate that the main looop has some work to do
// volts = 1048576 / volts ; Calculate the actual voltage relative to 1.024v.
end sub
timer ISR
sub Timer2Match_Handler
TMR2 = 0
// not needed automatically handled PIR1.TMR2IF = 0 ; Clear Timer2 interrupt flag.
Tim2Event = 1 ; Tell the world that something happened.
Tim2Count = Tim2Count + 1 ;
end sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Tried commenting out the ADCON0.ADON and current consumption jumped from a fairly static 170uA to a variable 530 - 590uA. (I made a typo in the code - the ADC takes about 400uA not 40uA.)
Thanks for the quick feedback. I'll try to create a test program that exhibits the problem - essentially if I add an "empty" interrupt handler with a sub with minimal code then it looks like all my other handlers fail. And w.r.t. the an31 in a READA10 ... yes, I didn't understand the an31 or the additional parameters - it was reading to a word and always returned 1023.
I'll take onboard the fact the an interrupt should be short but I need to figure out why adding another handler is having such a catastrophic effect.
Cheers Andrew
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
about reading vdd: it's important that you enable internal vref (it is off by default and you can turn it off after reading to save power) and set it to 1.2v, also make sure that the ADC references are gnd and vdd. AN31 reads internal vref on your part.
test if the voltage reference library turns on the vref, if not, turn it manually with the registers, I think that I have problems turning it on with the library but I say that from memory.
also I take a few reads and get the mean value of them (good practice with ADC); don't know if the division will work as desired, I used a lookup table.
Angel
Last edit: Angel Mier 2024-08-19
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It's not the interrupt handler - it's any reference to reading the ADRES register, indeed any register. The ADC interrupt works fine and sets my event variable but any access to ADRES from anywhere in my code causes a complete failure / corruption of the main program; as does similar attempts to read any timer registers just as a test. The result is that the core watchdog timer keeps resetting and nothing else happens.
I created a standalone ADC interrupt program and it works without any problem but I'll try to get a small test example that has the same problem.
Dim CCPR1W Alias CCPR1H, CCPR1L As Word
Dim TMR1W Alias TMR1H, TMR1L As Word
Dim ADRESW Alias ADRESH, ADRESL As Word
I'm still trying to understand how you read registers like the above into a word variable. My alias example seems to work quite well and you can also create alias variables :
Dim Volts Alias VoltsH, VoltsL As Words
Then you can read Volts in two assignments - VoltsH = ADRESH & VoltsL = ADRESL but Volts = ADRESW looks better.
Yes, I'd not really considered the hit by 1048576 / volts which is better as (65536 / volts) * 16. When I get a minute, I intend to investigate interpolated results using a lookup table or maybe two tables ... PIC coding forces you to really think !
Years ago, we developed a TV studio control suite using VAXes and opted to write it in DEC Pascal. Two of us wrote our own sections and we routinely compiled and linked the pieces until one day nothing worked - we blamed one another (nearly came to blows !) but found that variables were mysteriously acquiring random values ... assign x := 10 but later read x as 19 - stack corruption caused by the compiler. A lot of wasted effort and hassle trying to get language purchase and licence fess refunded by DEC - seem to recollect that the language cost around £20,000 and yearly fees of £15,000 - so not cheap ! Ended up writing it all in Basic and VMS batch code.
Digression but feeling the same sense that maybe it's not my coding !
Cheers Andrew
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This VoltsH = ADRESH & VoltsL = ADRESH but Volts = ADRESW ...
This will is:
VoltsH = ( ADRESH & VoltsL) = ADRESH
where this is ADRESH AND VoltsL - this will be a bitwise AND.
where that result = ADRESH which is a binary result. True or False.
Is this a typo?
(65536 / volts) * 16
This would be better as = FnLSL( (65536 / volts), 4 ) it would he a lot faster.
VAX.. did a lot of real processing back in the day on VAXes. I coded our solution - I think I still have the listings!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Another day gone trying to figure out why my project crashes whilst trying to read ADRES. Still trying to reproduce it in a small test project, again without success. It's not the interrupt as I wrote it all inline and as soon as I include any reference to ADRES the result is a crash - nothing runs; the registers must be getting initialized as the WD times out at my setting of 8 seconds. Even adding a LED on/off blink at the very start does nothing - the only way I know that the WD is working is because 2 LEDs momentarily flash at the moment the port settings are initialized
Googling has revealed many similar situations where the ADC module can cause inexplicable crashes - best advice was to look at the errata datasheet but not covered by the 16LF18323.
Think that I'll move on - it was a "nice to have feature" but not vital and maybe an inclusion if I ever migrate to another chip !
Time to unscramble my brain - a glass of Pinot Giorgio is calling !
Yes, I too have kept "old code" - just came across my PDP11 based 6502 cross assembler and its associated project - an industrialized BBC micro used by IRN to monitor / measure their networks - going back to 1985 !
Last edit: Andrew Jameson 2024-08-22
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thought that I'd move away from the ADC problem for a bit and carry on with a sample trigger based around Timer2 set to generate an interrupt every second.
Despite having written a test program that uses both ADC and Timer2 code that works, putting them into my main program adds to the continuance of "something's wrong".
The problem all along is that it stubbornly refuses to happen in small test programs.
Today, whilst trying to add a Timer2 interrupt handler, I discovered that just the addition of a variable declaration and its initialization is enough to create the "crash". The presence of these two lines stop the PIC dead and the only functional response that remains is to the 8 second WDT.
Dim Tim2Event As Byte
Tim2Event = 0
I noted with the ADC code, the crash was caused by the presence of the code and not its actual execution. It's looking like memory corruption (the chip, not mine !) or some sort of address problem.
The compiler reports :
Program Memory: 1091/2048 words (53.27%)
RAM: 97/256 bytes (37.89%)
So not pushing memory limits.
Just curious, would the addition of a dormant / never called interrupt handler cause a problem ? I triggered the same crash scenario by adding a Timer2Match and an empty subroutine and no change to any Timer2 related registers.
Not giving in yet - just got a sore head and no wine left !
Cheers Andrew
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
With the greatest thanks to Anobium, the issue was pinpointed and resolved. In my boundless optimism, I thought I could create my own assembler code that would be faster and better tailored to my needs. Well, on both counts, I was wrong. My enthusiasm led me down a journey following the white rabbit to where logic made no sense. It turns out my mistake was due to page errors, with my code joyfully using invalid addresses and wreaking havoc on my program. Lesson learned !
Problem now is that the solution opened new doors to create new functionality leading to memory exhaustion - both chip and myself !
Solution ? ... a gin & tonic sounds like a good idea and enjoying a good film with Jane, my wife !
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
With acknowledged thanks to Angel Mier and Ccin E Crout for their ReadVdd code suggestions in the thread "18LF18313 - LPBOREN Not working as expected ! Well it is but not quite !", I have attached a working version of my solution and I was amazed by the accuracy of measurement.
In it there are a couple of issues that I'd like clarified / explained :
I couldn't get Read10(an31) to return a meaningful value other than 1023 so I came up with my own read routine.
When reading volts from the ADRES register (a word) , GCBasic treated it as a byte. Then I tried reading the byte values and found that there was an error according to whether the low byte or high byte was read first. So my solution was to redefine ADRES, in an alias, to be ADRESW as a word or just write an assembler patch.
Anyway there's some food for thought and explanation and a ReadVDD routine that does work for me using a 16LF18323.
Now, just when I thought that I understood everything - PIC programming proved my foolishness :
I wanted to set up the ADC stuff using an interrupt and built a standalone test version that worked :
On Interrupt ADCReady Call ADCReady_Handler
sub ADCReady_Handler
ADCON0.ADON = 0 ; Turn ADC off ... save power !
ADIF = 0 ; Clear ADC interrupt flag.
ADCEvent = 1 ; Tell the world that something happened.
volts = ADRESW ; Grab a copy of the ADC result.
volts = 1048576 / volts ; Calculate the actual voltage relative to 1.024v.
end sub ;(ADCReady_Handler)
But when I added it to my existing project nothing worked :o(
It looks like the addition of the above breaks something in the cascade of interrupt handlers that I use but I can't tell that for sure. The problem is isolated to the above code addition without actually having added any code to initialize the associated registers. If I remove all the lines from the subroutine my project runs as before - I tried adding just an LED on / off test into the handler but that too killed everything.
I checked further as I also need to trigger 1 second update interrupts using Timer2 and came across the same dilemma :
On Interrupt Timer2Match Call Timer2Match_Handler
sub Timer2Match_Handler
TMR2 = 0
PIR1.TMR2IF = 0 ; Clear Timer2 interrupt flag.
Tim2Event = 1 ; Tell the world that something happened.
Tim2Count = Tim2Count + 1 ;
end sub ;(Timer2Match_Handler)
Once more this addition caused the same problem and again removing the sub lines "fixed it" !
Any suggestions and comments gratefully received ... going for a beer now !
Thanks Andrew
For the sake of completion w.r.t. the ReadVdd interrupt code, this is my ReadVdd trigger :
if (ADCEvent = 0) then
ADCON0.ADON = 1 ; Don't turn ADC on until we need it - 40uA hit !
NOP ; Need 8µS for the ADC to stabilize - so do 2 x NOPs !
NOP ; (1 cycle for a NOP is 4 µS.)
ADCON0.GO = 1 ; Start a conversion.
end if
(I'm using a 1MHz clock - NOPs make an efficient and accurate delay.)
Last edit: Andrew Jameson 2024-08-19
I would need to see the code. This could be that AN31 does not exist in the library ( total guess ). We you reading to a word? Did you use READA10 with any parameters. A simple example code please.
Very unlikely that some cascade failed. As there is no cascade. :-) Each interrupt is explicit.
Example code that I can compile please to see what is happening.
Example code that I can compile please to see what is happening.
Clearing TMR2IF is not required as this is specific to the interrupt. The DAT has the data to control this.....
Not sure I follow. Which addition ?
about AN31, I can confirm it exist in the library and it is mapped to read internal vref on most pics
And,
volts = 1048576 / volts
in the ISR. This is a long running calculation.Speed is your friend. No long run code is always best.
timer ISR
Tried commenting out the ADCON0.ADON and current consumption jumped from a fairly static 170uA to a variable 530 - 590uA. (I made a typo in the code - the ADC takes about 400uA not 40uA.)
Thanks for the quick feedback. I'll try to create a test program that exhibits the problem - essentially if I add an "empty" interrupt handler with a sub with minimal code then it looks like all my other handlers fail. And w.r.t. the an31 in a READA10 ... yes, I didn't understand the an31 or the additional parameters - it was reading to a word and always returned 1023.
I'll take onboard the fact the an interrupt should be short but I need to figure out why adding another handler is having such a catastrophic effect.
Cheers Andrew
Look forward to some code. :-)
about reading vdd: it's important that you enable internal vref (it is off by default and you can turn it off after reading to save power) and set it to 1.2v, also make sure that the ADC references are gnd and vdd. AN31 reads internal vref on your part.
test if the voltage reference library turns on the vref, if not, turn it manually with the registers, I think that I have problems turning it on with the library but I say that from memory.
also I take a few reads and get the mean value of them (good practice with ADC); don't know if the division will work as desired, I used a lookup table.
Angel
Last edit: Angel Mier 2024-08-19
With the code that I came up with, the measured result, without averaging, is extremely accurate and returns the same as a Fluke 70 meter.
Last edit: Andrew Jameson 2024-08-19
Good work.
I will try to figure what was not right using your example code. :-)
Other people may have the same challenge in the future. All best to fix the root cause.
It's not the interrupt handler - it's any reference to reading the ADRES register, indeed any register. The ADC interrupt works fine and sets my event variable but any access to ADRES from anywhere in my code causes a complete failure / corruption of the main program; as does similar attempts to read any timer registers just as a test. The result is that the core watchdog timer keeps resetting and nothing else happens.
I created a standalone ADC interrupt program and it works without any problem but I'll try to get a small test example that has the same problem.
Dim CCPR1W Alias CCPR1H, CCPR1L As Word
Dim TMR1W Alias TMR1H, TMR1L As Word
Dim ADRESW Alias ADRESH, ADRESL As Word
I'm still trying to understand how you read registers like the above into a word variable. My alias example seems to work quite well and you can also create alias variables :
Dim Volts Alias VoltsH, VoltsL As Words
Then you can read Volts in two assignments - VoltsH = ADRESH & VoltsL = ADRESL but Volts = ADRESW looks better.
Yes, I'd not really considered the hit by 1048576 / volts which is better as (65536 / volts) * 16. When I get a minute, I intend to investigate interpolated results using a lookup table or maybe two tables ... PIC coding forces you to really think !
Years ago, we developed a TV studio control suite using VAXes and opted to write it in DEC Pascal. Two of us wrote our own sections and we routinely compiled and linked the pieces until one day nothing worked - we blamed one another (nearly came to blows !) but found that variables were mysteriously acquiring random values ... assign x := 10 but later read x as 19 - stack corruption caused by the compiler. A lot of wasted effort and hassle trying to get language purchase and licence fess refunded by DEC - seem to recollect that the language cost around £20,000 and yearly fees of £15,000 - so not cheap ! Ended up writing it all in Basic and VMS batch code.
Digression but feeling the same sense that maybe it's not my coding !
Cheers Andrew
Read/write the ALIAS variables.
This
VoltsH = ADRESH & VoltsL = ADRESH
butVolts = ADRESW
...This will is:
VoltsH = ( ADRESH & VoltsL) = ADRESH
where this is
ADRESH AND VoltsL
- this will be a bitwise AND.where that result =
ADRESH
which is a binary result. True or False.Is this a typo?
(65536 / volts) * 16
This would be better as = FnLSL( (65536 / volts), 4 ) it would he a lot faster.
VAX.. did a lot of real processing back in the day on VAXes. I coded our solution - I think I still have the listings!
Another day gone trying to figure out why my project crashes whilst trying to read ADRES. Still trying to reproduce it in a small test project, again without success. It's not the interrupt as I wrote it all inline and as soon as I include any reference to ADRES the result is a crash - nothing runs; the registers must be getting initialized as the WD times out at my setting of 8 seconds. Even adding a LED on/off blink at the very start does nothing - the only way I know that the WD is working is because 2 LEDs momentarily flash at the moment the port settings are initialized
Googling has revealed many similar situations where the ADC module can cause inexplicable crashes - best advice was to look at the errata datasheet but not covered by the 16LF18323.
Think that I'll move on - it was a "nice to have feature" but not vital and maybe an inclusion if I ever migrate to another chip !
Time to unscramble my brain - a glass of Pinot Giorgio is calling !
Yes, I too have kept "old code" - just came across my PDP11 based 6502 cross assembler and its associated project - an industrialized BBC micro used by IRN to monitor / measure their networks - going back to 1985 !
Last edit: Andrew Jameson 2024-08-22
I have many programs with ADC on that chip so I am puzzled.
Post something that is not working.
Thought that I'd move away from the ADC problem for a bit and carry on with a sample trigger based around Timer2 set to generate an interrupt every second.
Despite having written a test program that uses both ADC and Timer2 code that works, putting them into my main program adds to the continuance of "something's wrong".
The problem all along is that it stubbornly refuses to happen in small test programs.
Today, whilst trying to add a Timer2 interrupt handler, I discovered that just the addition of a variable declaration and its initialization is enough to create the "crash". The presence of these two lines stop the PIC dead and the only functional response that remains is to the 8 second WDT.
Dim Tim2Event As Byte
Tim2Event = 0
I noted with the ADC code, the crash was caused by the presence of the code and not its actual execution. It's looking like memory corruption (the chip, not mine !) or some sort of address problem.
The compiler reports :
Program Memory: 1091/2048 words (53.27%)
RAM: 97/256 bytes (37.89%)
So not pushing memory limits.
Just curious, would the addition of a dormant / never called interrupt handler cause a problem ? I triggered the same crash scenario by adding a Timer2Match and an empty subroutine and no change to any Timer2 related registers.
Not giving in yet - just got a sore head and no wine left !
Cheers Andrew
This could be a page error. I would need to see the errant program.
With the greatest thanks to Anobium, the issue was pinpointed and resolved. In my boundless optimism, I thought I could create my own assembler code that would be faster and better tailored to my needs. Well, on both counts, I was wrong. My enthusiasm led me down a journey following the white rabbit to where logic made no sense. It turns out my mistake was due to page errors, with my code joyfully using invalid addresses and wreaking havoc on my program. Lesson learned !
Problem now is that the solution opened new doors to create new functionality leading to memory exhaustion - both chip and myself !
Solution ? ... a gin & tonic sounds like a good idea and enjoying a good film with Jane, my wife !