PIC and shift reg: peculiar delay - Page 1
Forum section: electronics / general

author: PerranOak
author_rank: Frequent Contributor
timestamp: November 24, 2021, 05:04:34 pm
content: I have setup a PIC12F683 with the shift reg 74HC595 (circuit attached – I’ve not shown ground and Vcc connects but they’re there) and a simple prog to display a number on LEDs:Code:#pragma config FOSC = INTOSCCLK // Internal oscillator with FOSC/4 output on OSC2 and I/O on OSC1/CLKIN.#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)#pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)#pragma config BOREN = ON // Brown Out Detect (BOR enabled)#pragma config IESO = ON // Internal External Switchover bit (Internal External Switchover mode is enabled)#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)#include <xc.h>#define _XTAL_FREQ 250000 //slowed clock: doesn't overwhelm shift reg#define data GPIObits.GP0#define pulse_clock GPIObits.GP1 = 1, GPIObits.GP1 = 0;void main(void){OSCCON = (OSCCON & 0xF) | 0b00100000; //Blanks upper bits then ORs freq. Currently: 250kHzTRISIO = 0; //TRISIO to outputCMCON0 = 0b00000111; //Sets GPIO pins to I/O and turns off comparatorANSEL = 0; //Sets pins to digital, as usual.int display = 0b11110000;while(1){for (int count = 0; count <8; count++){data = (display >> count) & 1; //right shifts by 0,1,2,3.. then blanks all but bit0pulse_clock}pulse_clock //Final pulse RCLK expects: it is one pulse behind SRCLK__delay_ms(1000); //needed or shift reg refresh blanks all LEDs}}I’ve made it like this because I want to use the absolute minimum number of pins on the uC as it has very few. The purpose is to investigate its registers “in-the-wild” as it were.It seems to work well except for one peculiar thing: I noticed that when I increase the delay it increases the time before the number is displayed. This means that the number is not displayed until after the delay!I cannot understand why this is so. Why does the number not display BEFORE the delay?Any guidance gratefully received, thank you.

author: Ian.M
author_rank: Super Contributor
timestamp: November 24, 2021, 05:43:22 pm
content: Seehttp://picforum.ric323.com/viewtopic.php?f=38&t=12My first suspicion when fast bit-banged I/O fails on a 'classic' midrange, or baseline PIC is always the RMW effect.

author: NorthGuy
author_rank: Super Contributor
timestamp: November 24, 2021, 06:40:20 pm
content: Don't run it in a loop. It only make things difficult to debug. Do the required pulses and stop.GP1 must be initialized to '0'. If it is '1' at the start, there will be no first pulse.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 24, 2021, 07:10:47 pm
content: Yeah. I suspect you're interpreting what you see wrongly. If the first iteration of the 'while(1)' doesn't cause anything to display, then you'll see what you are seeing. And a likely cause is an initialization issue, such as what NorthGuy just suggested.Just comment out the 'while(1)' statement, so there is only one iteration. If nothing displays, you'll have confirmation.

author: PerranOak
author_rank: Frequent Contributor
timestamp: November 25, 2021, 03:43:25 pm
content: Thanks all.I initialised GP1 and removed the while(1) however, the PIC just looped and it became a blur. I then read Ian.M's link and, though I didn't quite get from that what the solution was, I guessed that a delay somewhere might do the trick.I put the delay between "data" and "pulse" and it worked! Odd thing was that only a delay of at least 250ms (a huge amount of time in uC world!) would do, otherwise some bits were missed.Then I experimented and came up with this (all config, etc. remain the same as before):Code:void main(void){OSCCON = (OSCCON & 0xF) | 0b00100000; //Blanks upper bits then ORs freq. Currently: 250kHzTRISIO = 0; //TRISIO to outputCMCON0 = 0b00000111; //Sets GPIO pins to I/O and turns off comparatorANSEL = 0; //Sets pins to digital, as usual.GPIObits.GP1 = 0; //initialise GP1int display = 0b10000001;__delay_ms(250); //this has to be here! Why? Dunno!for (int count = 0; count <8; count++){data = (display >> count) & 1; //right shifts by 0,1,2,3.. then blanks all but bit0pulse_clock}pulse_clock //Final pulse RCLK expects: it is one pulse behind SRCLKwhile(1);}It worked too!I'd thought that I needed a delay between the changing of GP0 and GP1 in re RMW but what's needed is a delay beforeanychanges to bits!Again, a minimum of 250ms is required.How can I understand why this works?Cheers.

author: NorthGuy
author_rank: Super Contributor
timestamp: November 25, 2021, 05:37:17 pm
content: There might be a need for a small delay if PIC becomes operational before the shift register, but 250 ms is absurdly long. You may have discrepancy between real instruction speed and _XTAL_FREQ setting, so the delay you produce is not really 250 ms, or you may have really slow voltage ramp at startup.

author: MarkF
author_rank: Super Contributor
timestamp: November 25, 2021, 05:57:15 pm
content: You might want to try changing the PWRTE config bit to ON."The Power-up Timer provides a fixed 64 ms (nominal)time-out on power-up.The chip is kept in Reset as long as PWRT is active.The PWRT delay allows the VDD to rise to an acceptable level."#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)Also, I don't understand why you need to run the PIC at such a slow clock (250 KHz).The 74HC595 is capable of clock speeds over 20 MHz according to its datasheet.While the instruction speed is a max of 2 MHz (Fosc/4).I would like see the serial clock and data line on a scope....We are really just guessing here without it.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: November 25, 2021, 09:27:59 pm
content: Problems like this are a perfect application for an EUR 10 logic analyzer.Sometimes ISR's are interfering with your software.There are also "synchronizers" on input pins that may lead to confusing results.I also like to sprinkle some output pin toggles through my code to to correlate the software with the rest of the outputs that the Logic Analyzer logs.Also:From memory, I think the 74xx595 also needs a latch signal to transfer data from the shift registers to the outputs.

author: nigelwright7557
author_rank: Frequent Contributor
timestamp: November 25, 2021, 09:36:49 pm
content: I might be missing something but why no series resistors on LED's ?Might be killing your power supply.

author: newbrain
author_rank: Super Contributor
timestamp: November 26, 2021, 08:54:15 am
content: Quote from: Doctorandus_P on November 25, 2021, 09:27:59 pmNot the case here, as shift clock and latch clock are tied together: the extra pulse at the end of the loop will latch the current internal state to the output (while and shifting it internally - but we don't care).I'll be obnoxious and comment on style, so not really related to OP's problem (I don't do PIC, though IthinkIan.M got this right):Code:#define pulse_clock GPIObits.GP1 = 1, GPIObits.GP1 = 0;This is ugly for a number of reasons, and make the code look like Pascal (no semicolon, and no argument list in soimething that behaves like a function).The use of comma operator plus semicolon in the #define can also cause some problems whit conditionals or loops, if care is not taken.The usual way of declaring a function-like macro that acts (as much as possible...) as a real function and needs more than one statement is:Code:#define pulse_clock() do { GPIObits.GP1 = 1; GPIObits.GP1 = 0; } while (0)Then in the body of the code it can be used "normally":Code:pulse_clock();

author: Ian.M
author_rank: Super Contributor
timestamp: November 26, 2021, 01:45:36 pm
content: Try:Code:#pragma config FOSC = INTOSCCLK // Internal oscillator with FOSC/4 output on OSC2 and I/O on OSC1/CLKIN.#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)#pragma config MCLRE = OFF // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)#pragma config BOREN = ON // Brown Out Detect (BOR enabled)#pragma config IESO = ON // Internal External Switchover bit (Internal External Switchover mode is enabled)#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)#include <xc.h>#define _XTAL_FREQ 250000 //slowed clock: doesn't overwhelm shift reg#define DATA GP0 // pins on GPIO port#define CLK GP1/******************** GPIO port shadowing ********************/#define sGPIObits (*(GPIObits_t * volatile __bank(0))&sGPIO)#define UpdateGPIO() ((void)(GPIO=sGPIO))volatile __bank(0) uint8_t sGPIO; // software shadow for port/*************************************************************//*********** macro to pulse clock and update port ************/#define pulseCLK() do{ \UpdateGPIO(); \sGPIObits.CLK=1; \UpdateGPIO(); \sGPIObits.CLK=0; \UpdateGPIO(); \}while(0)/*************************************************************/void main(void){uint8_t display = 0b11110000;OSCCON=0x20; //Set 250kHz FoscTRISIO=0; //TRISIO to outputCMCON0=0b00000111; //Sets GPIO pins to I/O and turns off comparatorANSEL=0; //Sets pins to digital, as usual.while(1){for (uint8_t mask=1; mask; mask<<=1){sGPIObits.DATA=!!(display&mask);pulseCLK();}pulseCLK(); //Final pulse RCLK expects: it is one pulse behind SRCLK__delay_ms(1000); //needed or shift reg refresh blanks all LEDs}}which should totally avoid the RMW issue by doing software shadowing for the GPIO port.Caution: I don't currently have the XC8 toolchain installed, so the above has not been compiled and tested so may have some syntax errors. Also its quite a while since I did anything in XC8 PIC C . . .'Air Code' is the programmer's equivalent of 'Air Guitar' but is a *LOT* more embarrassing ;-)

author: NorthGuy
author_rank: Super Contributor
timestamp: November 26, 2021, 02:58:14 pm
content: Quote from: newbrain on November 26, 2021, 08:54:15 amPascal does use semicolons.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 26, 2021, 04:29:26 pm
content: Quote from: NorthGuy on November 26, 2021, 02:58:14 pmOf course, but semicolons are not required for the last statement of a block. Maybe seeing this 'pulse_clock' wtihout semicolon just before the closing } made newbrain think of Pascal, but this was just a particular use case here.For macros that have a side-effect, I much prefer defining them with parentheses, even if they take no parameter. That makes them look like functions and the intent is clearer. And yes, avoid semicolons in macros too. I also prefer the convention of all-uppercase macros, so that you can immediately spot that this is a macro. So I'd define it like so:Code:#define PULSE_CLOCK() .....And invoking it just looks like so:Code:PULSE_CLOCK();

author: NorthGuy
author_rank: Super Contributor
timestamp: November 26, 2021, 05:02:36 pm
content: Quote from: SiliconWizard on November 26, 2021, 04:29:26 pmI just use inline functions.

author: newbrain
author_rank: Super Contributor
timestamp: November 26, 2021, 05:57:17 pm
content: Quote from: SiliconWizard on November 26, 2021, 04:29:26 pmYes, exactly what I was referring to. I loathed that thing - even if Pascal was my first high level language.I also use UPPER_CASE_SNAKE for macros.Quote from: NorthGuy on November 26, 2021, 05:02:36 pmSo do I, most of the time - and TBH, modern compilers do a decent job of inlining short static functions even without the hint.That, plus a liberal use of enums rather than #defines limits my use of the preprocessor to little more than includes and conditionals.As this was an aside wrt the main topic, I did not want dig go too deep...

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 26, 2021, 06:13:56 pm
content: Quote from: newbrain on November 26, 2021, 05:57:17 pmI found it looked inconsistent. But the reason for this was that Pascal (and all its successors AFAIK) used the semicolon as a statement separator, rather than as a statement terminator.I personally prefer statement terminators. And I have even more of a problem with languages having neither separators nor terminators (such as Python, Go...) But anyway.

author: NorthGuy
author_rank: Super Contributor
timestamp: November 26, 2021, 08:10:59 pm
content: Quote from: SiliconWizard on November 26, 2021, 06:13:56 pmTechnically yes, but you can use extra semicolons - you just create empty statements this way. So, anyone in the right mind would use a semicolon after the last statement in the block anyway. Otherwise, every time you add or delete a statement, you would have need to edit the previous line by adding or deleting the semicolon.There's a similar thing in C with commas in enums. You don't need the last comma, but if you write enumerators one per line, it is very handy to have it.

author: PerranOak
author_rank: Frequent Contributor
timestamp: November 30, 2021, 01:47:31 pm
content: Thanks NorthGuy - neither was the case but cheers.MarkF - tried the PWRTE but no difference. You're right about the clock: it was a hang-over from a previous investigation. I let the chip default to 4MHz and everything was the same.Doctorandus_P - I don't have a logic analyser, worse luck!nigelwright7557 - no I'm the one missing something: I forgot to show the resistors in the diagram, sorry, they are there though.newbrain - oh. I though it was both clever and elegant, sorry. I would rather do things according to convention though so thank you.Ian.M - thank you once more for your help (ref. many, many of my posts in the past). I tried it out and it does work (of course) thank you. See below.Thank you very much to everyone. I used your points, and indeed code, to investigate. I was still getting weird errors that varied with all kinds of things: delays, whether MCLRE = ON or =OFF, when I cycled the power and so on.Then came the final breakthrough. No matter what these things (delay, etc.) were set to or what I did it worked every time. What did I change? I unplugged the pickit3! When I unplugged it from the chip and powered on or reset it worked perfectly.What a pain in the @rse.

author: Ian.M
author_rank: Super Contributor
timestamp: November 30, 2021, 02:18:20 pm
content: Yes. Microchip ICSP programmer/debuggers have pulldown resistors on PGC (ICSPCLK) and PGD (ICSPDAT) for ICD (in-circuit debug) support to prevent glitches when changing the link direction between the programmer/debugger and the debug executive running on the PIC. IIRC they are typically 4K7. The additional loading can cause problems for some application circuits.Exception: The original black button PICkit 2, didn't have the pulldowns - you had to provide them on the target or in your debug cable. They added them internally for the revised red button PICkit 2.However your circuit should *NOT* be loading sensitive - the fact it is indicates you aren't handling the RMW effect properly, which may bite you in the ass if it is ever used in a high EMI environmentSee John Oldenkamp's comments athttps://www.microchip.com/forums/FindPost/313101For anything more critical than blinking a LED to debug your code, either use the shadow register mitigation (e.g. the code I provided or one of the alternative implementations), or only write to the *WHOLE* port (and don't use the port register to store internal state for the code), or preferably switch to a PIC with LAT registers (e.g. PIC12/16F1xxx Enhanced Midrange, or PIC18) and use the LAT registers for *ALL* output, especially bit-banging.

author: Dabbot
author_rank: Regular Contributor
timestamp: November 30, 2021, 11:46:45 pm
content: Quote from: PerranOak on November 30, 2021, 01:47:31 pmUsing the ICSPCLK / ICSPDAT pins as digital outputs with the PICKit3 still attached should not cause problems. I've done this before, using these same pins for data / clock on a chain of 595, with programmer still attached.Are you decoupling your micro and 595? I don't see caps in your schematic.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 01, 2021, 05:35:56 am
content: Quote from: PerranOak on November 30, 2021, 01:47:31 pmNo kidding. The pins you're using to control your shift register are the same pins used in ICSP. PICkit3 is probably still driving them while the PIC already started running, so you have to wait for PICkit3 to get out. Use different pins.

author: Ian.M
author_rank: Super Contributor
timestamp: December 01, 2021, 06:59:05 am
content: Yes, As you (PerranOak) are using:Quotethe PICkit 3 cant hold the PIC in Reset to exit programming mode cleanly, so there may be a brief conflict on the GPIO pins used for ICSP.I would always advise leaving /MCLR enabled, with an external 10K pullup. Disabling it to use it as an ordinary input pin should be your absolutely last resort, only to be considered if its impractical to use a higher pin count PIC, and you cant figure out any other ways to save pins.

author: MarkF
author_rank: Super Contributor
timestamp: December 01, 2021, 04:38:11 pm
content: Quote from: Ian.M on December 01, 2021, 06:59:05 amI usually disable the MCLR- to save the pullup resistor. As rarely do I need the reset function.However, I avoid using any of the programming pins; MCLR-, ICSPDAT, ICSPCLK. I just bring them straight to the programming header. Thereby avoiding the messy multiplexing between the programming function and the application usage.On these 8 pin PICs, it just might not be an option to avoid using the programming pins.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: December 05, 2021, 10:42:01 am
content: If you don't have a Logic Analyser yet, then go buy one. (or two so you have a spare)Sigrok / Pulseview has lots of supported hardware:https://sigrok.org/wiki/Supported_hardwareFor beginners I recommend an EUR 8 box with an Cypress CY7C68013A such as:https://sigrok.org/wiki/VKTECH_saleae_cloneAll those Saleeaeaea clones are pretty much equivalent.Some time ago I also saw a pretty nice schematic to control a 74xx595 from a single uC pin.It had two extra resistors and capacitors, and combined with some clever timing from the microcontroller it could control the clock, data and the output latch / clock independent from each other.

author: Ian.M
author_rank: Super Contributor
timestamp: December 06, 2021, 08:46:09 am
content: Quote from: Doctorandus_P on December 05, 2021, 10:42:01 amYou are probably thinking of some variant of Roman Black's'Shift1'concept.With a few more components it can be extended for 8 bit output. Seehttps://www.eevblog.com/forum/projects/latched-shift-register-74hc595-control-via-single-mcu-io-pin/