Danny Higgins 0 Posted June 26, 2008 Report Share Posted June 26, 2008 What is the recommended way of setting/clearing a single output line? Is there any difference between: PORTC.1 = 1; LATC.1 = 1; set_bit(PORTC, 1); set_bit(LATC, 1); Should individual output bits be declared as volatile? What is meant by a Read - Modify - Write operation in the PIC datasheets? Thanks, Danny, G3XVR Quote Link to post Share on other sites
mchristisen 0 Posted June 27, 2008 Report Share Posted June 27, 2008 (edited) check out Mid-Range Referance Manual page 155 for description of Read-Modify-Write. and this manual page 4 for LAT description. Edited June 27, 2008 by mchristisen Quote Link to post Share on other sites
Reynard 0 Posted June 27, 2008 Report Share Posted June 27, 2008 Writing a bit on a port sometime depends upon the hardware attached to the port. Sometime you can write a '1' to a port bit, but when you read the port that bit can read as a '0' because the output pin voltage is less than the '1' threshold due to what is connected to the port pin. i.e. base of a transistor. To avoid Read-Modify-Write problems, I sometime use a shadow register in RAM. I do all my changes to the shadow register then do a write only to the port. If I need to know the state of an output pin I can refer to the shadow register which should reflect the port output. Updating the port can be done immediately after changing the shadow register or sometime I wait and write the shadow register to the port during a timer tick interrupt. This means there is only a single place for all the port writing. All depends on your application and hardware design really. set_bit(PORTC, 1); is basically a macro for PORTC.1 = 1; Cheers Reynard Quote Link to post Share on other sites
Danny Higgins 0 Posted June 27, 2008 Author Report Share Posted June 27, 2008 Thanks mchristisen and Reynard. I am writing to some port bits and reading from others, not mixing read and write on the same pin. Writing to one pin was OK, but when I wrote to another pin it reset the first pin, so I'm not quite sure what is happening. I will investigare further. Danny, G3XVR Quote Link to post Share on other sites
Ian Harris 0 Posted July 1, 2008 Report Share Posted July 1, 2008 (edited) Thanks mchristisen and Reynard. I am writing to some port bits and reading from others, not mixing read and write on the same pin. Writing to one pin was OK, but when I wrote to another pin it reset the first pin, so I'm not quite sure what is happening. I will investigare further. Danny, G3XVR Hi Danny, What you're seeing is the classic read-before-write problem. The only way reliably around it is to cache the writes before actually changing the port - and only the changing the port with a byte-wide write (no rhyming intended) Here are the routines that come from the pic_utils.h in the PicPack library (see Embedded Adventures) #define set_pin(port, pin) \ set_bit(port_shadow[port - PORTA], pin); \ port_array[port - PORTA] = port_shadow[port - PORTA]; #define clear_pin(port, pin) \ clear_bit(port_shadow[port - PORTA], pin); \ port_array[port - PORTA] = port_shadow[port - PORTA]; #define toggle_pin(port, pin) \ port_shadow[port - PORTA] ^= (1 << (pin)); \ port_array[port - PORTA] = port_shadow[port - PORTA]; #define test_pin(port, pin) \ ((port_array[port - PORTA] & (1 << pin)) != 0) #define change_pin(port, pin, value) \ if (value) { \ set_pin(port, pin); \ } else { \ clear_pin(port, pin); \ } Fundamentally, the way these devices work is that they can only write to a port a complete byte at a time. So what happens if you only want to change one bit (a single pin)? Well, the device reads the value on all the pins in the port, then changes the pin (bit) you care about, then writes it all back to the port. Fine, except when as previous posters have mentioned, the current value on the port doesn't reflect the value that you last wrote to it! The only way around this is to "shadow" all your writes, so that your code remembers what was previously written to the port. 18f devices have a LAT (latch) register for each port that does this in hardware, however, the shadow register method is safe on 18f devices as well, although it uses slightly more code than just writing to the LAT registers directly. The routines above allow you to do things like: set_pint(PORTA, 1); (note the PORTA is in captials due to the way the array is set up). This results in the fewest lines of assembler in order to use a shadow: 05A4 884A BSF gbl_port_shadow+D'1',4 05A6 504A MOVF gbl_port_shadow+D'1', W 05A8 6E81 MOVWF gbl_port_array+D'1' This is on a 18f device, but in either case, it's three instructions and saves you from the craziness of the chip seeming to do, well, crazy things. I've seen read-before-write issues on just led+resistor on the output, let along when the pins are tied to some other device. I use this method on everything and cringe when I see code that's simply porta.1 = 0; since who knows what other pins you've just changed at that point? lata.1 = 0; is however, perfectly safe on 18f devices. cheers Ian. Edited July 1, 2008 by Ian Harris Quote Link to post Share on other sites
Danny Higgins 0 Posted July 1, 2008 Author Report Share Posted July 1, 2008 Many thanks Ian. You have explained it extremely well. You can stop cringing now - I'm about to change all my "portx.y = 1" lines of code. Danny Quote Link to post Share on other sites
davidb 0 Posted July 2, 2008 Report Share Posted July 2, 2008 Hi all, I do exactly the same as Reynard. Since starting with embedded 16F PICs many moons ago (using assembler) I found the safest way was to always use port shadow registers. Using them means direct writes to a port must not be done if unexpected operation is to be avoided. All writes and port output status reads should be carried out via the shadow register. Many of my projects require multiple port pin changes that need to be synchronised with an event of some sort, either a timer, input edge or interrupt etc., so the shadow registers are typically changed in a number of places but are only copied to the port when the required event occurs. This is in addition to any instantaneous, single pin, updates that may also be required. Ians PickPack library is a neat way of implementing port shadow registers as the user doesn't have to worry about the shadow register itself. Unfortunately, it is not very code efficient if you need to change more than two pins at a time. More importantly, for me at least, it does not allow delayed or synchronised writes to the actual port. The way round this is to either do what I do and simply use shadow registers directly in the code or use the PickPack library and create some additional macros to do the job. I think the later is preferable, certainly for newbies, since it reduces the chance of errors. Just remember to use upper case for the port name! Typically a port may need initialising to a value. Rather than using a series of the clear_port(PORT,x) or set_port(PORT,x) instructions from the library simply do the whole lot in one go using the following macros: // Set a port to a value via the shadow register. e.g. set_port(PORTB, 0b00111100) #define set_port(port, value) \ port_shadow[port - PORTA] = value; port_array[port - PORTA] = port_shadow[port - PORTA]; // Clear a port via the shadow register. e.g. clear_port(PORTB) #define clear_port(port) \ port_shadow[port - PORTA] = 0; port_array[port - PORTA] = port_shadow[port - PORTA]; Rather than change the whole port in one go it is often required to change the state of a number of pins so we could use the following macro: // Change multiple port pins via the shadow register using a literal and port pin mask to select the pins to be changed // e.g. change_port_pins(PORTD, 0b00010011, 0b00011111) // Note that this is very useful but not so user friendly since the individual pins are not named #define change_port_pins(port, literal, mask) \ port_shadow[port - PORTA] ^= ((mask & 0xff) & (port_shadow[port - PORTA] ^ (literal & 0xff))); \ port_array[port - PORTA] = port_shadow[port - PORTA]; The rest of the macros allow a shadow register to be changed without writing the value directly to the port. The shadow register is copied to the port when required using another macro (below). First individual pins: // Set port shadow register pin. e.g. set_shadow_pin(PORTB,6); #define set_shadow_pin(port, pin) \ set_bit(port_shadow[port - PORTA], pin); // Clear port shadow register pin. e.g. clear_shadow_pin(PORTB,5); #define clear_shadow_pin(port, pin) \ clear_bit(port_shadow[port - PORTA], pin); // Toggle port shadow register pin. e.g. toggle_shadow_pin(PORTB,4); #define toggle_shadow_pin(port, pin) \ toggle_bit(port_shadow[port - PORTA], pin); Now for multiple pins: // Set port shadow register to a literal. e.g. set_shadow_port(PORTB, 0b00111100); #define set_shadow_port(port, value) \ port_shadow[port - PORTA] = value; // Clear port shadow register. e.g. clear_shadow_port(PORTB); #define clear_shadow_port(port) \ port_shadow[port - PORTA] = 0; // Change multiple pins in a port shadow register using a literal and a port pin mask to select bits to change // e.g. change_shadow_pins(PORTC, 0b00001100, 0b00001111) #define change_shadow_pins(port, literal, mask) \ port_shadow[port - PORTA] ^= ((mask & 0xff) & (port_shadow[port - PORTA] ^ (literal & 0xff))); The following macro simply copies the shadow register to the port output register as and when required: // Update port from shadow register. e.g. update_port(PORTC); #define update_port(port) \ port_array[port - PORTA] = port_shadow[port - PORTA]; The last macro is used to read a port pin value from the shadow register: // Test a port shadow register pin. e.g. test_shadow_pin(PORTB,3); #define test_shadow_pin(port, pin) \ ((port_shadow[port - PORTA] & (1 << pin)) != 0) Please note that I have not fully tested these macros and there may be errors, they are just offered as ideas at the moment. The advantage is that they work in a consistent way with Ians PickPack library. The disadvantage is that they now expose the user to the shadow register. Can they be improved or are there better ways? Perhaps they can be given more imaginative names? Perhaps Ian will consider adding them or similar macros to his library? Thoughts anyone? davidb Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.