PIC16F628A output toggle...yes I know its ancient.

Thread Starter

odm4286

Joined Sep 20, 2009
243
Hello all,

Working with what I had left over in a bin. It looks like the rest of my code works fine, but I'm having trouble toggling one output pin. This is what I'm trying to do. Using MPLAB 5.05 and XC8 1.35

Code:
#define HEARTBEAT PORTAbits.RA0             // heartbeat I/O 
if(mS_count >= ONE_SECOND)
        {
            // Heartbeat
            HEARTBEAT = ~HEARTBEAT;
            mS_count = 0;
        }
Now here is my timer ISR, I verified I'm entering my ISR.

Code:
void interrupt master_isr(void)                           // only one interrupt vector on this chip
{
    // timer0 interrupt routine
    if(INTCONbits.T0IF)
    {
        INTCONbits.T0IF = 0;                                    // clear the flag
        ++mS_count;                                                 // increment mS_count       
        ++motor_start_time;                                      // increment motor start time
    } 
}
My problem is my output, HEARTBEAT, is constantly high. It never toggles. I know this chip does not have the latch register and I think this may be the source of my problem. Any tips? Thank you.
 

JohnInTX

Joined Jun 26, 2012
4,063
A few things:

Be sure the the comparators are off using
CMCON = 0x07;

You can avoid r-m-w issues by keeping a copy of what you want on the port in a RAM byte, modifying that then copying the whole thing to PORTA as a byte:

C:
unsigned char PORTAimage;

PORTAimage = 0;// init image and port
PORTA = PORTAimage; // write to port
PORTAimage ^= 0x01;  // toggle bit 0
PORTA = PORTAimage; // write updated value to port
If you do 'shadow' port A, keep in mind that if you write the shadowed port both in interrupt AND in main, you have to disable interrupts when writing from a main routine.

Just shutting down so didn't look too much at your code logic but hope that helps!
 

AlbertHall

Joined Jun 4, 2014
9,826
If RA0 is connected to an LED with a low value resistor or no resistor then the read-modify-write may be your problem.
Of course you set the TRIS bit for RA0.
 

Picbuster

Joined Dec 2, 2013
998
make life simple
set tris porta

#define Alive_led PORTAbits.RA0 '// example to flip Ra0

in interrupt routine

  1. if(INTCONbits.T0IF)
  2. {
  3. INTCONbits.T0IF = 0; // clear the flag
  4. ++mS_count; // increment mS_count
  5. if (mS_count> second) // second value needed to achieve one second.
  6. {
  7. Alive_led = ! Alive_led ;
  8. }
  9. ++motor_start_time; // increment motor start time
  10. }
  11. make sure that all interrupts are enabled.
This works for me. ( apart for motor_ start_time LOL)

Picbuster
 

geekoftheweek

Joined Oct 6, 2013
347
Nevermind... I see what HEARTBEAT = ~HEARTBEAT; does now. Just had to think a second.

I do know in asm you cannot read and then write to the ports in the next instruction. You have to have a nop between the read and write or write and read. It does strange things if you do. Is it possible the compiler is not doing this?
 

Thread Starter

odm4286

Joined Sep 20, 2009
243
Nevermind... I see what HEARTBEAT = ~HEARTBEAT; does now. Just had to think a second.

I do know in asm you cannot read and then write to the ports in the next instruction. You have to have a nop between the read and write or write and read. It does strange things if you do. Is it possible the compiler is not doing this?
Yeah, it looks like that's exactly what is happening. I decided not to waste my time with an older chip and grabbed a 16f1619. Thanks everyone!
 

jpanhalt

Joined Jan 18, 2008
9,385
I do know in asm you cannot read and then write to the ports in the next instruction. You have to have a nop between the read and write or write and read. It does strange things if you do. Is it possible the compiler is not doing this?
I have not been part of this thread as it dealt with a C-programming problem, but that comment caught my eye. Now that the TS's question has been resolved, I wanted to follow-up. Can you point to some documentation for that requirement, particularly the read then write issue? Albeit, the occasion to do that may be rare. I could not find that requirement on Google or PICList.

FWIW: I did find an example (https://www.microchip.com/forums/m478014.aspx) of using a delay after a bit-read to ensure the previous action was completed, e.g.,
Code:
bcf     PORTA,0
btfsc   PORTA,0   
bra     $-1
<rest of code>
But that does not deal with potential corruption of other bits in the port caused by the initial bcf instruction or unpredicatble behavior cause by reading then writing. (I am assuming the read only instructions applicable to ports in the mid-range are movf,W and btfsx .)

Regards, John
 
I have not been part of this thread as it dealt with a C-programming problem, but that comment caught my eye. Now that the TS's question has been resolved, I wanted to follow-up. Can you point to some documentation for that requirement, particularly the read then write issue? Albeit, the occasion to do that may be rare. I could not find that requirement on Google or PICList.

FWIW: I did find an example (https://www.microchip.com/forums/m478014.aspx) of using a delay after a bit-read to ensure the previous action was completed, e.g.,
Code:
bcf     PORTA,0
btfsc   PORTA,0   
bra     $-1
<rest of code>
But that does not deal with potential corruption of other bits in the port caused by the initial bcf instruction or unpredicatble behavior cause by reading then writing. (I am assuming the read only instructions applicable to ports in the mid-range are movf,W and btfsx .)

Regards, John
Once I thought about it some more I figured it wasn't the problem with this particular issue since there would be a compare and change bits in between the read and write. It's a pic 16 issue as far as I know... and I'm not even too sure about the higher level pic 16.

I would have to look at the datasheets to see where I've seen that exactly. If I remember after work tonight I'll take a look.
 

jpanhalt

Joined Jan 18, 2008
9,385
In part, I agree. Writing to a single or pair of LED's is not the typical scenario for R-M-W as the usual "problem" is when an unknown state appears on another pin/port-bit from the previous read cycle. That is, a bsf or bcf alone should set the identified pin unambiguously. Now, if one uses an XOR function to alternate on-off states, in theory there could be a problem, but I have done that many times with LED's <5 mA with no problem. (Caveat: Be careful if I2C in particular, other serial hardware coms (?), or other things (?) are on the same port. The the I2C problem seems to be Microchip's poster child for the problem.) This thread was not any of those.

My concern was that the "add a nop" advice might be in the same category as the problem with clearing GIE in a few early chips (e.g., 16C71, See: https://forum.allaboutcircuits.com/threads/follow-up-to-gie-problem.128159/#post-1048715). Once recognized and fixed, it was cured, but the legend continued to be spread long after the problem had disappeared.

If you come across the example or reference, I would be delighted to add it to my collection of the arcane. Otherwise, it is a combination of instructions I am unlikely to use.

Regards, John
 

JohnInTX

Joined Jun 26, 2012
4,063
In part, I agree. Writing to a single or pair of LED's is not the typical scenario for R-M-W as the usual "problem" is when an unknown state appears on another pin/port-bit from the previous read cycle. That is, a bsf or bcf alone should set the identified pin unambiguously. Now, if one uses an XOR function to alternate on-off states, in theory there could be a problem, but I have done that many times with LED's <5 mA with no problem. (Caveat: Be careful if I2C in particular, other serial hardware coms (?), or other things (?) are on the same port. The the I2C problem seems to be Microchip's poster child for the problem.) This thread was not any of those.

My concern was that the "add a nop" advice might be in the same category as the problem with clearing GIE in a few early chips (e.g., 16C71, See: https://forum.allaboutcircuits.com/threads/follow-up-to-gie-problem.128159/#post-1048715). Once recognized and fixed, it was cured, but the legend continued to be spread long after the problem had disappeared.

If you come across the example or reference, I would be delighted to add it to my collection of the arcane. Otherwise, it is a combination of instructions I am unlikely to use.

Regards, John
John, the 'add a nop' advice is as bogus as it ever was - the underlying problem is a feature of the port design i.e. a r-m-w instruction to PORT, reads the logic value determined by the voltage on the port's PIN (not an internal register like LATx and consequently not necessarily the last value written) to get the current value 1 or 0, modifies the value read, and then writes the result to the destination. If some external condition adversely affects the pin voltage at the instant it is read for the r-m-w, the instruction will act upon bad data and write an invalid value.

The old 'nop' advice is from when they increased the clock speeds on early PICs. On successive r-m-w instructions to the same port, the second r-m-w instruction would sample the pin voltages before they settled from the first r-m-w and thus the initial value for the second r-m-w operation would be invalid. Someone added a NOP to let the pins settle and that 'fixed' the problem. Pin capacitance was the stated culprit.

All true enough but total nonsense too. A NOP won't help in the case where the pin is driving a load with more capacitance and it takes longer to settle. More NOPs maybe or a delay but that doesn't remove the root problem either. A real problem is transients and noise. Contact noise from switches and relays have caused problems - a spike of noise hits the port pins just as they are sampled. Same effect, the r-m-w operation fails because it starts by reading bad data. Overloaded pins from driving LEDs and whatnot have the same effect.

So, it's just bsf/bcf right? Nope. Take a look at the instruction set. EVERY instruction that processes stored data in RAM (or the RAM-mapped ports). is a r-m-w instruction. Going down the list the only ones that don't are those that use literal data from ROM. So things like ADDWF, ANDWF, COMF, DECF,INCF, INCFSZ, IORWF, RLF,RRF, SUBWF,SWAPF,XORWF,BCF,BSF are all r-m-w instructions. Arguably MOVF is suspect since it too reads a register. We usually don't have problems with them since they're used with internal RAM which always reads correctly. The problem comes when the 'read' part is on a PORT because the value read is not necessarily the value you last wrote due to the influence external factors (loads, noise etc) can have on the value read from the pin at the instant the Q1 part of the 'read' is performed. Any of these instructions performed on a PORT can have issues.

BSF/BCF are especially notable since they are frequently used on PORTs. All of the problems noted above apply plus one more. Even though these are 'single-bit' instructions, they work by reading the entire 8 bits of the PORT, flipping one bit then writing back the result. This is why you see complaints along the lines of 'I do BSF PORTB,0 and RB4 changes'. Well, during Q1 of the BSF instruction, RB4 got mis-read and its new, incorrect value is written back on Q4 along with the other 7 bits. Making things worse, BSF/BCF is usually generated by C compilers for constructs such as:

C:
PORTC |= 0x80;

//will usually generate
BSF PORTC,7
and there you go.

But keep in mind that those other instructions aren't safe either, not even MOVF. Consider:
Code:
MOVF PORTC,W
IORLW 0x80
MOVWF PORTC
No r-m-w problem there right? Wrong! The pin voltages on 8 bits of PORTC got read during Q1 of the MOVF, just like they would with BSF/BCF and the initial value read is subject to the same ugly real-world problems described above. Same holds true with any of those others I listed and for the same reason.

Look at it another way. If you were reading some device in a noisy environment, you probably would want to sample the input a few times to be sure that you were reading the device and not transient noise, yes? Maybe several times until the input value read was stable. I sure would (and I sure do). IO is ugly stuff. I've been bit. You have too. So if you would do that, why would you accept reading once in the same environment with a 50ns sample time (Q1 at 20MHz)? You wouldn't, right? But that's exactly what you are doing with BSF PORTx,x or any of those instructions listed. Bada-Bing! (kind of like the sound of the traverse hitting the end when your BCF PORTx,MotorBit failed because you turned an LED off on the same port. Scary).

So you see why I called the 'add nops' approach bogus. It's, well, bogus. A band-Editaid on a symptom of the problem without addressing the root of the problem itself i.e. the PORT can't be relied upon to return a valid initial value because it's connected to the big, ugly world.* Operations on RAM can be relied upon because reading RAM always returns a valid initial value. SFRs and peripherals... weeeelll, depends. That's for later. Maybe. I2C and GIE.. hmmm..

What do you do?
Easy rule: read inputs from PORT, everything else to LAT. Simple as that.

What? Don't have LAT?
Use a better PIC. Almost every PIC socket has a later part that will drop right in, has LATx registers and is cheaper to boot.

Still?
OK. The only way to be sure your IO works on PICs (and any other micro that takes initial values from instantaneous pin voltages) is to maintain a copy of what you want on the port, modify that then write that copy as a byte to the port. That's it. In effect, you are making your own LATx register.

In assembler:
Code:
 cblock 20h
PORTBimg:1  ; current value of PORTB
endc

clrf PORTBimg ; init PORTB
movf PORTBimg,W
movwf PORB

bsf PORTBimg,7   ; bsf PORTB,7
movf PORTBimg,W
movwf PORTB
In C:
C:
 unsigned char PORTBimg;

PORTBimg = PORTB = 0x00; // image and port initialized to 0

PORTBimg |= 0x80; // set bit 7 of PORTB
PORTB=PORTBimg; // write as byte.
That's a lot of motion but that's what it takes. It's a solid solution but not without problems of its own:
  • It makes bigger code. Adding code to the typical banked ROM midrange PICs can be .. expensive!
  • While you can mitigate the problem with macros and #defines, it's still clumsy.
  • Special attention is required to use the shadowed outputs in foreground and background in a system with interrupts. I'll leave the 'why' as an exercise for the student - Don't forget the GIE problem :cool:
  • It can be hard to retrofit to existing code especially when the IO is inline (can't skip over those one-instruction BSFs anymore).
  • It can be impossible to retrofit existing code when the bigger IO runs you out of a bank or out of program memory altogether.
  • It can be really expensive to recall products to fix 'intermittent' IO problems - I actually like that one. A big part of my business awhile back was fixing those problems as a consultant.

That's it in more than a nutshell. I think I'll archive this and add to it for future issues. In the meantime, I hope that gives you something to chew on. Later, I'll why it's bad to r-m-w the TRIS registers as well. Microchip is finally saying in later datasheets not to bit-diddle the TRIS. I know why.

* There are those who assert that thinking this way is a result of the effects of 'bad engineering', that good practices make the port issue moot. I've had some famous fights over on the Microchip forum on this very subject.
Sure, I agree that some problems can be self-inflicted but the sum total of my experience (and research - and in-person conversations with the guys who design the *!#%* things) says to shadow the stinkin' ports or use a better PIC.

'nite
 
Last edited:
Thanks JohnInTX for the clarification. I looked over a 16f887 datasheet which is what I worked with in the past. I believe at the time I misunderstood the r-m-w note in the instruction set section of the datasheet. I started playing with the pic18 early on also and found they were easier to learn so I stuck with them. I've come a little ways since then, but for everything I've learned there's a thousand things I still don't know.
 

jpanhalt

Joined Jan 18, 2008
9,385
So, it's just bsf/bcf right? Nope. Take a look at the instruction set. EVERY instruction that processes stored data in RAM (or the RAM-mapped ports). is a r-m-w instruction. Going down the list the only ones that don't are those that use literal data from ROM. So things like ADDWF, ANDWF, COMF, DECF,INCF, INCFSZ, IORWF, RLF,RRF, SUBWF,SWAPF,XORWF,BCF,BSF are all r-m-w instructions. Arguably MOVF is suspect since it too reads a register.
I agree that even MOVF is subject to R-M-W, which is easy to miss when used as MOVF PORTx,W. Whether it creates a problem depends on what you do with W. Of course, MOVF PORTx,F is no better than any of the other R-M-W instructions.

Part of the problem dealing with R-M-W seems to lie with Microchip's somewhat muddy explanations:
http://ww1.microchip.com/downloads/en/DeviceDoc/31029a.pdf
29.3.3 Bit Manipulation
All bit manipulation instructions will first read the entire register, operate on the selected bit and
then write the result back (read-modify-write (R-M-W)) the specified register. The user should
keep this in mind when operating on some special function registers, such as ports.
Note: Status bits that are manipulated by the device (including the interrupt flag bits) are
set or cleared in the Q1 cycle
[emphasis added]. So there is no issue with executing R-M-W instructions
on registers which contain these bits. <snip>

This [ed: R-M-W instructions] includes ADDWF, SUBWF, BCF, BSF, INCF, XORWF, etc... Instructions that do not depend on
the current register value, like MOVWF, CLRF, and so on are not R-M-W instructions.
It's the "and so on" that muddies the water. So far as I know, for mid-range devices MOVWF and CLRF are it for operations that modify ports and registers other than W. Of course, literal, control, inherent, and C-optimized operations are not included in that statement (e.g., CLRWDT, OPTION).
I would add BTFS(s,c) to the safe list, as it does not inherently include a write but may return an inaccurate result for slowly changing bits.

Later, I'll why it's bad to r-m-w the TRIS registers as well. Microchip is finally saying in later datasheets not to bit-diddle the TRIS. I know why.
Anxious to see that. Here is one take from Microchip:
https://www.microchip.com/forums/m1064930.aspx
My program running with SSP module enable for I2C memory read/write but reading datasheet of PIC16F88, i encounter this:
"To ensure proper communication of the I2C Slave mode,
the TRIS bits (TRISx [SDA, SCL]) corresponding to the
I2C pins must be set to ‘1’. If any TRIS bits (TRISx<7:0>)
of the port containing the I2C pins (PORTx [SDA, SCL])
are changed in software during I2C communication
using a Read-Modify-Write instruction (BSF, BCF), then
the I2C mode may stop functioning properly and I2C
communication may suspend. Do not change any of the
TRISx bits (TRIS bits of the port containing the I2C pins)
using the instruction BSF or BCF during I2C communication.
If it is absolutely necessary to change the TRISx
bits during communication, the following method can be
used:"
upload_2018-9-11_7-32-50.png
EDIT: Added complete Example. There is a little gotcha that may apply only to Assembly programmers. If you happen to be in the wrong Bank, e.g., Bank1 where TRIS is instead of Bank 2 where LATx is, doing a R-M-W instruction such as BSF LATA,0 will actually affect TRISA with the same caveats as if you intentionally did that on TRIS. But that is the writer's error. I know the 16F88 does not have port latches, but I elected not to change that specific example given.

John
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,063
Thanks for posting that, @jpanhalt . Interesting to see that uCHIP is getting more detailed info into the datasheets. Your forum link shows that there is still a lot of misunderstanding and bad advice going around regarding r-m-w to TRIS and PORT. As you correctly point out, MOVF is just as subject to r-m-w issues as BSF/BCF.

The fix shown in EXAMPLE 10-1 that you posted is the smoking gun. If reading TRISC is valid, you shouldn't have to manually set TRIS bits <4:3> (SCL and SDA), right? But you do. The reason is in the I2C logic and how it's wired internally.

Consider the case of the SSP I2C running as a slave. It receives a byte from the master by reading the SDA input on each SCL clock. Then at bit 9, the slave needs to generate an ACK i.e. the slave actively pulls down SDA for the 9th SCL. To do that, the slave changes SDA from an input to an output for that 9th SCL. Unfortunately, it does that by using the TRIS logic and for that one SCL clock, the SDA TRIS bit will be a '0' for output temporarily replacing the normal '1' for SDA input. Well.. if you happen to read TRISC during that ACK sequence, the TRIS bit for SDA will read as '0'. Then when it gets written back, that '0' becomes the permanent value for TRIS SDA, the I2C SDA line is locked to '0' and you're done.

Other peripherals on PORTC are variously affected as well. Back in the day, databooks were silent on the issue and we had to find out the hard way. In my case we added a little software PWM to drive a shared LED on PORTC. The LED logic was HiZ=OFF, '0' = ON using BSF/BCF TRISC.. to do it. It worked flawlessly until we went to stress testing the code. Once in a couple of million messages an I2C slave would lock up, but only when the LED was active. That took some sleuthing and an in-house document from uCHIP to confirm the issue was r-m-w. We fixed it by shadowing the TRIS register but as I noted above, its ugly to redo your IO and add code to a tight project late in the game.

As the forum post indicates, the problem can happen in C or assembler. It's the hardware, not the code.

Thanks again, John.
 

MrChips

Joined Oct 2, 2009
21,077
I identified a couple of bugs in the Microchip PICs. They never responded to my alerts and never fixed the problems.
Just one of the reasons I stopped using PICs a long time ago.

Too bad Atmel is now owned by Microchip. Don't know how that is going to pan out.
 

jpanhalt

Joined Jan 18, 2008
9,385
I identified a couple of bugs in the Microchip PICs. They never responded to my alerts and never fixed the problems.
You mean features, right? Like no warning when you read/write to an SPF register that is not in the current bank? It is obvious why what I described above happens (parallel offsets), but why no warnings from the Assembler? (MPLAB 8.92) I have only been hit by that once, but it was hard to find.

Of course, anyone using PIC's knows to keep the silicon errata handy too. Some of them can affect a slew of devices.
 

JohnInTX

Joined Jun 26, 2012
4,063
Thanks JohnInTX for the clarification. I looked over a 16f887 datasheet which is what I worked with in the past. I believe at the time I misunderstood the r-m-w note in the instruction set section of the datasheet. I started playing with the pic18 early on also and found they were easier to learn so I stuck with them. I've come a little ways since then, but for everything I've learned there's a thousand things I still don't know.
You're welcome. I probably don't have to remind you that if you don't use LATx for all of those instructions, you'll be right back in r-m-w problems i.e. PORTx on the 18F has the same r-m-w issues as on midrange.

Carry on!
 
Top