Hi Guys,
I have pic program I need to code count, and aim for total 25 cycles so that the output of the ports are
100 kHz roughly 50% duty cycle driven by 10 MHz clock.. The duty cycle and desynchronisation between the two pins are not important because the decade counter outputs are still 50% duty cycle even if their input from the microcontroller isn't.

Code (Text):

cycle: ' main routine

'

@ nop

@ nop

@ nop

@ nop

@ nop

@ nop

@ nop

@ nop

@ clrwdt ; clear watchdog timer manually (1)

portb.4 = 1 ' (2)

portb.5 = 1' (2)

@ nop

@ nop

@ nop

@ nop

@ nop ; (1)

@ nop

portb.4 = 0

portb.5 = 0

'

goto cycle ' do it again (2)

It turns out that my scope will count between 99.7xx kHz and 100.20 kHz or so when the pic clocked
with a rubidium frequency standard, and the scope is only referencing it's own ovenised crystal oscillator.
There are a few nops I can add or remove from this routine, and still not be sure which version outputs 100kHz.
There is no evaluation or conditional branching, so the instruction time should be static.
The compiler does the conversion of the writes to portb bits efficiently, as you would in asm.
Any help appreciated.

You didn't say which processor you're using, but most (all?) PICs have an instruction cycle time that's 1/4 the oscillator frequency. So if "10MHz clock" means 10MHz oscillator, you have a 2.5MHz instruction rate, and that's a problem--you need 25 instructions to get one cycle of a 100KHz wave, and 25 is an odd number. The best you can do is 12 cycles high and 13 low, but the frequency should be OK.

Every PIC instructon that doesn't change the program counter takes 1 cycle time, and if the counter is changed, then it's 2 cycles. The only one you've used that takes 2 cycles is the GOTO. So it should be easy enough to figure out a distribution of the program lines to get your 100KHz.

Ok, thanks, if that is the case it would be running fast because I have counted turning port bit on/off as 2 cycles.
I did take into account to divide the 10 MHz freq by 4, and try to execute 25 cycles of time ready to run the loop again,
but looks like I'll get it fixed

Just curious why you would not use one of the PWMs (unless your PIC doesn't have one of course ) As you've seen, counting clocks can be a hassle and if you ever want to do something else, it can be a bear.

Hi John,
It's a pic 16F628A, and I believe they do have HWPWM. To be honest I've just never used one, or looked into it.
I suppose I should have a go...
Were I to use a button with interrupt to do something, then the HWPM shouldn't be paused while the interrupt executes.
There is a possibility this chip could also send serial back to the rubidium standard to change it's frequency,
but I was going to use a separate pic with an Xtal clock for that otherwise the main pic would have it's freq changed from 10 MHz,
and it's serial would have to be recalculated for 9600 baud when the chip is running at it's new frequency.
Not a real problem until the new frequency doesn't make it through the filter to drive the pic's clock, and the main pic can no longer run to change it back.

Just curious why you would not use one of the PWMs (unless your PIC doesn't have one of course ) As you've seen, counting clocks can be a hassle and if you ever want to do something else, it can be a bear.

Were I to use a button with interrupt to do something, then the HWPM shouldn't be paused while the interrupt executes.

Click to expand...

No worries. Once set up the PWM will run without further intervention. In fact, you'll run into trouble with the software PWM servicing interrupts or even doing other processing.

There is a possibility this chip could also send serial back to the rubidium standard to change it's frequency,
but I was going to use a separate pic with an Xtal clock for that otherwise the main pic would have it's freq changed from 10 MHz,
and it's serial would have to be recalculated for 9600 baud when the chip is running at it's new frequency.

Click to expand...

Completely doable with the '628A at 10MHz (BRGH=1, SBRG=15 decimal). Use an interrupt-driven receiver (unrelated to any PWM use, just a good idea) to receive the messages. I personally would use an interrupt-driven transmitter as well. When messages are decoded to change the PWM values, store the settings then enable the TIMER 2 interrupt to know when its just rolled over then while its running the next cycle, load the duty / freq registers for smooth changes. Disable TMR2IE until the next change. All of this assumes that you can get the desired freqs/duty cycle out of the normal PWM settings of course.

If you stay with the brute force cycle count PWM, consider replacing some of the NOPs with code to shadow the output port. Consecutive bcf/bsf on a midrange port can lead to r-m-w problems.

Well I can wrap my head around all of that except the last part. Would you mind explaining that?
I will likely continue with the project because it's working and play with this toward the end simply
because it's more fun to do some other parts of it for now

If you stay with the brute force cycle count PWM, consider replacing some of the NOPs with code to shadow the output port. Consecutive bcf/bsf on a midrange port can lead to r-m-w problems.

This is a weird construction feature of PIC processors, which I think few people understand entirely, certainly not me. What happens (maybe someone can correct me if I'm getting it wrong) is that single-bit operations to the port pins (BSF, set a bit, or BCF, clear a bit) are actually executed by a read-modify-write process. What this means is that the first action when you call for a port output bit to change is the entire 8-bit port is read into a hardware register; then the appropriate bit of that register is set or cleared; then the register is written back to the port. What can happen is that the initial read operation may involve reading pins that are set as outputs, and if any of those pins have been pulled high or low by external loads, they are read as having the wrong status, and that status gets written back to the port pin when the read-modify-write completes. So you try to change the output of one port pin, and other pins that you thought you hadn't touched get changed too. It leads to mysterious errors that don't involve anything you can find in your code.

The easiest way to deal with this PICfall (like a pitfall, you know...) is to have a "shadow" register which you make changes to, and then write the entire register out to the port, which isn't a read-modify-write process. Like this (using C code, but it should be obvious how it would look in assembly):

Code (Text):

bit_set(porta, 5); // This is legal, but it may get you in trouble

bit_set(porta_shadow, 5);

porta = porta_shadow; // Takes longer, but it's guaranteed to do what you think it does

The price of safety, unfortunately, is that the dangerous procedure only takes 1 instruction, whereas being safe takes 3, and it requires a memory location. Engineering often involves compomises.

Here's a bit more progress... The decade counter is supposed to take frequency input once it's got 3 more faster 4017 chips in front of it,
but for now it just takes input from one of the faster frequencies available on the gate timer board.

Hello again Guys,
Since the main chip in the project (16F877A) has HPWM, I could ditch the first chip and just divide that for the gate timer.
Should I assume the resolution is only as accurate as the input osc where the hpwm frequency is evenly divisible by fosc/4 ?
ie.. a 1 kHz PWM from a 10 MHz osc is absolutely accurate at 50% duty cycle?

I want to generate the lowest frequency divisible by 10 that is absolutely accurate so that I can further divide it down to 1 Hz with as few decade counters as possible.

How does that help connect decade bargraphs to decade counters with less soldering?
The idea is to run the cascaded decade counters for a gate time, and alternately disable the counters so the display can be read,
then reset and start again.
The real time clock display section is multiplexed.