Learning to program the PIC16LF1823

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Dang it, NSA ... I knew about the shadow registers, but I had somehow forgotten that PCLATH is among them and that it's also saved automatically when an interrupt is triggered. Anyway, after re-reading that tiny detail in the datasheet, I realized I didn't have to back up and restore the value of PCLATH. All I had to do is clear it as soon as the interrupt routine was entered. And that kept the problem as solved and also made the code simpler and faster. Thanks for your previous post.

Anyway, here goes a very deliberate rant from my part:

Do not ever, never ever ever freaking NEVER! use the "bsf PORTC, RCx" instruction!!!! (or its equivalent "bcf" counterpart) ... I somehow thought that it would be easier if I were to just use PORTC instead of LATC for changing pin outputs ... Big mistake. A very strange bug that took me hours to trace was caused because of that.


From the datasheet:

Reading the PORTC register reads the status of the pins, whereas writing to it will write to the PORT latch. All write operations are read-modify-write operations. Therefore, a write to a port implies that the port pins are read, this value is modified and then written to the PORT data latch (LATC)


What was happening when I used said instruction (instead of the plain "bsf LATC, RCx") is that all of the pins in the port were somehow affected. It's like they all "blinked" when that instruction was executed, and it was wreaking havoc with my circuit and the rest of the logic.

I thought it was easier and simpler to always use PORTC for reading and writing to the pins so that things would be kept uniform and easier to read ... I was wrong...
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Something very strange is going on ... here's the preamble:

  • I'm using an external (32.768 kHz) oscillator to run my pic16lf1825 mcu
  • Said external oscillator is configured to work as Timer1 which in turn is configured to be the system's clock
  • I've enabled the interrupt for when Timer1 overflows, and set up a counter that increments when said interrupt is serviced. That results in a reliable two second "tick", which is used for timekeeping purposes.
  • As long as I don't mess around with the values of TMR1H and TMR1L, timekeeping stays extremely accurate and reliable. In fact, not a single line in my code writes or even reads said registers.

Considering what I've mentioned above, I've written several time delay routines of the blocking type that are necessary for the tasks I'm trying to control. So far, so good. The blocking delay routines work just fine and are dependable and repeatable to a tolerable extent. I mention this because they're not 100% perfectly accurate, since they get interrupted by the timekeeping routine I mentioned above. But that is not a serious problem because they perfectly comply with my tolerance requirements.

The real problem started when I ran out of space in page 0 of my chip, and had to start using page 1 to continue programming.

Then something very strange started happening. Any delay routine located in page 0 that I called from page 1 (using the pagesel directive) had very unreliable and inaccurate results. Say I wanted to block execution for 1/2 a second, the result could vary between 1/16 to a whole second before the routine was completed.

So what I did, was write the very same delay routine, but this time in page 1, and called it from code also lying in page 1. But the results were equally unstable and unrepeatable.

Here's the mystery: If I call a blocking delay routine in page 0 from code also lying in page 0 it works just fine. But if I call it from a different page, or even if I place the same routine at a page that is not page 0 the timing result will be unstable and non-repeatable. .... what gives?
 

MrSalts

Joined Apr 2, 2020
2,767
Dang it, NSA ... I knew about the shadow registers, but I had somehow forgotten that PCLATH is among them and that it's also saved automatically when an interrupt is triggered. Anyway, after re-reading that tiny detail in the datasheet, I realized I didn't have to back up and restore the value of PCLATH. All I had to do is clear it as soon as the interrupt routine was entered. And that kept the problem as solved and also made the code simpler and faster. Thanks for your previous post.

Anyway, here goes a very deliberate rant from my part:

Do not ever, never ever ever freaking NEVER! use the "bsf PORTC, RCx" instruction!!!! (or its equivalent "bcf" counterpart) ... I somehow thought that it would be easier if I were to just use PORTC instead of LATC for changing pin outputs ... Big mistake. A very strange bug that took me hours to trace was caused because of that.


From the datasheet:

Reading the PORTC register reads the status of the pins, whereas writing to it will write to the PORT latch. All write operations are read-modify-write operations. Therefore, a write to a port implies that the port pins are read, this value is modified and then written to the PORT data latch (LATC)


What was happening when I used said instruction (instead of the plain "bsf LATC, RCx") is that all of the pins in the port were somehow affected. It's like they all "blinked" when that instruction was executed, and it was wreaking havoc with my circuit and the rest of the logic.

I thought it was easier and simpler to always use PORTC for reading and writing to the pins so that things would be kept uniform and easier to read ... I was wrong...
That "read-modify-write" glitch has been getting PIC users since the beginning of time. That is the reason microchip added the LATC - to avoid the pain.

Here... you can look at some old BBS posts if you want older than this.
https://www.microchip.com/forums/m/tm.aspx?m=147383&p=1
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Something very strange is going on ... here's the preamble:

  • I'm using an external (32.768 kHz) oscillator to run my pic16lf1825 mcu
  • Said external oscillator is configured to work as Timer1 which in turn is configured to be the system's clock
  • I've enabled the interrupt for when Timer1 overflows, and set up a counter that increments when said interrupt is serviced. That results in a reliable two second "tick", which is used for timekeeping purposes.
  • As long as I don't mess around with the values of TMR1H and TMR1L, timekeeping stays extremely accurate and reliable. In fact, not a single line in my code writes or even reads said registers.

Considering what I've mentioned above, I've written several time delay routines of the blocking type that are necessary for the tasks I'm trying to control. So far, so good. The blocking delay routines work just fine and are dependable and repeatable to a tolerable extent. I mention this because they're not 100% perfectly accurate, since they get interrupted by the timekeeping routine I mentioned above. But that is not a serious problem because they perfectly comply with my tolerance requirements.

The real problem started when I ran out of space in page 0 of my chip, and had to start using page 1 to continue programming.

Then something very strange started happening. Any delay routine located in page 0 that I called from page 1 (using the pagesel directive) had very unreliable and inaccurate results. Say I wanted to block execution for 1/2 a second, the result could vary between 1/16 to a whole second before the routine was completed.

So what I did, was write the very same delay routine, but this time in page 1, and called it from code also lying in page 1. But the results were equally unstable and unrepeatable.

Here's the mystery: If I call a blocking delay routine in page 0 from code also lying in page 0 it works just fine. But if I call it from a different page, or even if I place the same routine at a page that is not page 0 the timing result will be unstable and non-repeatable. .... what gives?
My last question still stands ... anyone here has an idea as to why blocking delay routines behave so unreliably when they're executed from a program memory page other than zero? ... this is proving to be a real headache for me
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Perhaps I should summarize my aforementioned situation as briefly and succinctly as possible, so as not to waste anyone's time with hollow perfunctory small-talk and introductory chit-chat stuffed with banal formalities:


HELP!!!!
 

MrChips

Joined Oct 2, 2009
34,819
I know that this is a useless post to you.

I stopped using Microchip PICs many years ago because the architecture is insane IMO (as this thread demonstrates).

I know there are many ardent PIC users here and hopefully someone will provide you with some support.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
I know that this is a useless post to you.

I stopped using Microchip PICs many years ago because the architecture is insane IMO (as this thread demonstrates).

I know there are many ardent PIC users here and hopefully someone will provide you with some support.
Thank you, Chips. A negative answer is better than no answer at all. And yes, it's architecture is insane. But the main reason I'm using it is because of its superb power efficiency. There are better architectures out there that I could've used, I'm sure, but that ship has sailed. I have to make this work any which way I can.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
What I meant was using a timer module in the MCU itself.
I think I know what you mean ... so far, I'm only using Timer1, but my chip has three Timer modules. My objection is that the interrupt routine is already saturated and I don't want to add more of a load to it.

What I don't understand is that even though the interrupt routine is quite big for my taste, interruptions happen only once every two seconds. I would understand my blocking delays instabilities if interruptions happened much more often, but that is not the case. And as I said, such instabilities only present themselves when the routines are invoked from a page memory other than 0
 

nsaspook

Joined Aug 27, 2009
16,329
I think I know what you mean ... so far, I'm only using Timer1, but my chip has three Timer modules. My objection is that the interrupt routine is already saturated and I don't want to add more of a load to it.

What I don't understand is that even though the interrupt routine is quite big for my taste, interruptions happen only once every two seconds. I would understand my blocking delays instabilities if interruptions happened much more often, but that is not the case. And as I said, such instabilities only present themselves when the routines are invoked from a page memory other than 0
I see problems like this (I don't know if this is your problem, but it's something to think about) when 16-bit or greater values are updated in a non-atomic (multi-instructions to read-modify write values) fashion on 8-bit controllers. The set of instruction can get interrupted in a way that changes the outcome and value of some global variable/register like an integer which is used inside the interrupt, but is updated outside the interrupt These are hard problems to fix after the fact and usually require preventive measures when writing the code to have interrupt guards (disable interrupt, re-enable interrupts) about sensitive sets of instructions.

I've also used timer High Low byte latches (a one byte write latches the other byte in a 16-bit buffer) to sequence 16-bit value updates to eliminate the need to disable interrupts.
 
Last edited:

MrChips

Joined Oct 2, 2009
34,819
Along the same argument, there is a problem when using multi-byte and multi-word variables.
As an example, suppose the timer module uses an 8-bit counter/register. Now 8 bits is a bit too short for most applications. Hence one attempts to capture counter overflows in another 8-bit register.

Thus we have a 16-bit value, [high byte] [low byte].
Suppose we get a value such as [24] [00].
How do we know that the answer is correct or that the high-byte is yet to be incremented?
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
If one interrupt every two seconds is saturating your available instruction cycles, you're doing it wrong. Bigly.
Thanks for chiming in, Joey ... But no, my argument wasn't that the two second interrupt was saturating the available instruction cycles, but rather that I found no reason for the interrupt itself to be the problem of instability precisely because its two second cycle made it unlikely.
As I've already mentioned, the instability only presents itself when I call a blocking delay routine from any page other than page 0.

But you've given me an idea. I'm going to turn off all interrupts, and then attempt the blocking delay routines again and see how they behave.
 

joeyd999

Joined Jun 6, 2011
6,305
Thanks for chiming in, Joey ... But no, my argument wasn't that the two second interrupt was saturating the available instruction cycles, but rather that I found no reason for the interrupt itself to be the problem of instability precisely because its two second cycle made it unlikely.
As I've already mentioned, the instability only presents itself when I call a blocking delay routine from any page other than page 0.

But you've given me an idea. I'm going to turn off all interrupts, and then attempt the blocking delay routines again and see how they behave.
Just FYI: aside from single NOPs for uS timing, in 40 years of programming I've found very few cases where blocking routines were required -- or even desired. All they do is uselessly consume instruction cycles that can be productively used elsewhere.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
Quick question. I have the following code, and would like to assign the value of W depending of the the match between two sets of constants such as this one:

Code:
;Index location of each configuration string that will be stored in EEPROM:
idx_DevName            EQU   d'0'
idx_FTP_Server         EQU  d'24'
idx_FTP_Un             EQU  d'88'
idx_FTP_Pwd            EQU d'112'
idx_FTP_Dir            EQU d'136'
idx_FTP_ConfFile       EQU d'200'
idx_FTP_RepPrefix      EQU d'224'

;configuration string prefix:
cmd_Rcv_DevName        EQU 'a'
cmd_Rcv_FTP_Server     EQU 'b'
cmd_Rcv_FTP_Un         EQU 'c'
cmd_Rcv_FTP_Pwd        EQU 'd'
cmd_Rcv_FTP_Dir        EQU 'e'
cmd_Rcv_FTP_ConfFile   EQU 'f'
cmd_Rcv_FTP_RepPrefix  EQU 'g'
For example, if the current value of w is 'd', I'd like a function that would return w loaded with d'112'
in pseudo code, that would look something like: If W = cmd_Rcv_FTP_Pwd then W = idx_FTP_Pwd

I know a lookup table is in order for this kind of application, but I've only used them before without needing to match two values such as this.

What would be the most efficient technique to accomplish this?
 

boostbuck

Joined Oct 5, 2017
1,043
I'd use an interleaved table where the entries are 'a', '112', 'b', '136', 'c', '200', 'd', '224',......
Searching uses a double increment, and on a hit on a found key a single increment points to the required return value.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
I'd use an interleaved table where the entries are 'a', '112', 'b', '136', 'c', '200', 'd', '224',......
Searching uses a double increment, and on a hit on a found key a single increment points to the required return value.
Yeah, now I remember seeing something similar somewhere in the docs I have... gonna see if I can find it. Many thanks!
 

Thread Starter

cmartinez

Joined Jan 17, 2007
8,767
I'm currently working on a small program for the PIC10LF320. My intention is to use its internal oscillator at the lowest frequency possible, that is 31 kHz

The OSCCON register is responsible for this operation, and its internal bits are configured as follows:

1667222490105.png

My question is, can I just write a 0x00 value to it in order to clear it in its entirety? Or better yet, simply use a "clrf OSCCON" instruction. I ask because bits 0, 1 and 3 are read-only. And I fear attempting to write to them could cause a glitch/crash of sorts.
 
Top