Understanding How DelayMS Function Works With PIC18F Microcontroller - c

I am trying to use a PIC18F45K22 to flash an LED on for a second, then off for a second, repeating indefinitely. The code incorporates a delay function that keeps the PIC busy for one second but I am not sure how it works (it was done through trial-and-error.) To achieve a 1 second delay, tWait should equal 125,000. I am using a 8MHz MCU clock frequency with PLL disabled. I know that 8MHz is 125nSec but after that, I am stuck. Can anyone please explain to me how it works? I've been at it for a few hours and it's driving me bonkers.
#define SYSTEM_FREQUENCY (8000000) // 8MHz
// Prototype Function
void delayS(unsigned int sec);
void main()
{
// Sets RA0 to output...
TRISA.RA0 = 0;
do
{
// Turns RA0 off...
LATA.RA0 = 0;
delayS(1);
// Turns RA0 on...
LATA.RA0 = 1;
delayS(1);
} while(1); // Repeat...
}
// My attempt at writing the function...
void delayS(unsigned int sec)
{
unsigned long tWait, tStart;
/*
To achieve a 1 Second On 1 Second Off cycle, tWait should be 125,000.
How? Why? 64 is arbitrary to achieve 125,000.
*/
tWait = Sec*(SYSTEM_FREQUENCY/64);
for(tStart = 0; tStart < tWait; tStart++);
}

The clock frequency is 8MHz, so there are 2.10^6 cycles per seconds since a cycle takes 4 clock ticks. Any assembly operation of the microcontroller takes at least one cycle. There is still a factor 16 to explain.
The factor 16 must correspond to one pass of the loop. Since unsigned long are used in this loop, it is not surprising that it takes a few operations. You will need to look at the assembly code to understand what is happening and get a precise count, like here.
If you are working with MPLAB and the xc8 compiler, there are some functions you can use, such as Delay10KTCYx(unsigned char);.
The microcontroller spends 99.99% of its time counting, wich is not critical if you flash leds. But, to use efficiently a pic, you will need to avoid these functions by using timers and interruptions. Have fun !

Related

Why is my C program continuously run forever without the usage of loop?

I am testing my microcontroller PIC18f4550 with the following circuit on Proteus simulation software.
My objective is to turn the led on for one second and turn it off.
Following is my code:
#include <xc.h>
#define _XTAL_FREQ 8000000
void delay(int t);
void main(void)
{
TRISB = 0x00; // set Port B as output
LATB = 0x00; // set all 8 outputs on Port B to 0
delay(20);
LATB = 1b00000001; // set port B0 to 1
delay(20);
LATB = 1b00000000; // set port B0 to 0
}
void delay(int t)
{
for (int i=0; i<t ; i++)
{
__delay_ms(50); //using xc8 compiler internal delay function - it doesn't like it if delay is too big so it is used inside for loop
}
}
When the simulation is run, the LED keep blinking forever. I did not use any loop in my program and I only intend to turn the LED on for once. What is causing the LED to blink continuously?
[EDIT 1]
I found a solution to my problem. I simply added while(1){} at the end of my code to stop the PIC from going into infinite loop.
You are programming for a microcontrollers. They don't have an extensive OS. Most of the cases, your IDE hides the fact that your main is running in a loop (like Arduino IDE does), or exiting your program makes the microcontroller reset, and will just start the program again.
You labeled it "mplab" , if you check the documentation of it it states:
"The compiler inserts special code at the end of main() which is executed if this function ends, i.e., a return statement inside main() is executed, or code execution
reaches the main()’s terminating right brace. This special code causes execution to
jump to address 0, the Reset vector for all 8-bit PIC devices. This essentially performs
a software Reset. Note that the state of registers after a software Reset can be different
to that after a hardware Reset.
It is recommended that the main() function does not end."
The C runtime environment may call main() in an endless loop. This can happen deliberately like this:
/* some other stuff... */
for (;;) {
main();
}
Or it can happen because after the return of main() the next bytes in the program memory make the processor restart, for example because of an exception, or other code following the call.
You might like to use a simulator that shows the assembly level of your program to follow the flow of control.
Another option is to use your tools to disassemble the executable.
I suggest you to use the debugger (inside simulator) tool provided in MPLAB X IDE and look how single instructions are being executed. Check if microprocessor is not getting reset by any peripherals.

I can't understand how to set period of PWM signal

i would like to create a PWM signal. And i want the frequency to be close to 38 khz. My theoretical calculation for period is 26.3 microseconds. So i choose 26 microseconds. And i can observe my signal.
But i don't understand how my code works properly :)
(My clock frequency is 1MHz so my clock signal is 1 microseconds )
if((P1IN & BIT3)!=BIT3) { // if button is pressed
for(i=0;i<692;i++){ // pwm signal's duration is 9ms
P2OUT^=0x01; // switch from 1 to 0 or vice versa
__delay_cycles(4);
}
P2OUT=0x00;
}
my calculation is:
i <692,i++,P2OUT^=0x01; // total 3 cycles
__delay_cycles(4); //total 4 cycles
so 4+3=7. but i'm confused because i think it should be 13 not 7
(here is my signal)
https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/166/f0fd36b0_2D00_bebd_2D00_4a31_2D00_b564_2D00_98962cf4749e-_2800_1_2900_.jpg
You can not calculate cycles based on C or C++ code. you need to check the assembly file(s) generated during the compilation of the program. Based on your compiler (which you did not mention) you can pass some some compiler parameters/switches to ask the compiler to leave the generated assembly file(s) in place for you to check the generated assembly instructions. but basically the for loop would have a jump instruction which may take 2/3 cycles and you did not calculate that.
I recommend that you later check the number of cycles of each instruction from the micro controller datasheet.
The posted code (per your calculations) switches the output every ~7cycles. and does this 692 times. For a total of 346 total cycles, however the total pulse ON time is only ~7cycles. Suggest:
if((P1IN & BIT3)!=BIT3)
{ // if button is pressed
// start pwm signal
P2OUT = 0x01;
for(int i=0; i< (9*1000);i++) // may need to be adjusted
{ // so pwm signal's duration is 9ms
_delay( 1 );
}
// stop pwm signal
P2OUT=0x00;
// wait for button to be released
while( P1IN & BIT3)!=BIT3 ){;}
}
I'm not familiar with your microcontroller's PWM details, However, most have an initialization to set how fast the PWM timer counts and its start/termination count and if it repeats and if the output is a square wave or a step up or a step down signal and the percentage of ON .vs.OFF time.
However, the posted code indicates the PWM is only a regular GPIO bit.
The posted code indicates the PWM on percentage is to be 50percent. Is this what you want?

XC8 compiler: program returning to beginning of main()

I'm new to programming PIC microcontrollers, and I've been having some difficulty with a piece of code. In the main() function, it sets some registers, then enters an infinite while loop. In this while loop, a second function is called, which creates a delay (without calling any other functions, including main() then returns. However, it seems that when the second function ends, the program goes back to the top of main(), rather than continuing in the while loop.
Edit 1: I've done some more tests, and I still get the problem without the function, so it's almost certain that the microcontroller is resetting every second or so, for some unknown reason. There's supposed to be a register that tells you the reason for the reset, so I'll have a look at that.
Edit 2: Here is a very basic version of the code, in which the RC0 LED flashes briefly every 2 seconds (it should stay off).
void main()
{
TRISC = 0x00;
PORTCbits.RC0 = 1;
OSCCON = 0b00000010;
TRISA = 0x00;
PORTA = 0x00;
while(1)
{
PORTCbits.RC0=0;
}
}
Edit 3: It turns out it was the watchdog timer causing the resets, fixed now by putting a CLRWDT() in the loop.
Could be quite a few things, checking the RCON or equivalent register should help discern the cause, also provide the part number and part voltage. Some of the possibles are:
- Watchdog timer tripping,
- Brown-out on protection tripping,
- Stack over/underflow,
- Your code is jumping off into space (bad pointer?),
- Your code is performing a software reset,
- Your dividing by 0,
- others...

CCS PICC CCP settings

(I'll gladly post code if someone can point out how to paste it in here without using the 4 space indentation system that doesn't work)
Hello folks
After ~9Hours racking by brains, I can't find an answer or find where my calculations are going wrong... but they are.
I have a circuit built using a microchip 18F2550 microcontroller.
I am using this circuit to measure the delay between 2 signals and am using the 2 CCP registers in capture mode.
This all works and the result is sent to the PC (over USB serial) all dandy, but the results are wrong.
I have to apply a gain of ~16000 to any results to get somewhere near the delay presented to the pins.
I have the delay set in the line
Timer1 is set as an internal
Timer3 is disabled
relevant interrupts are enabled
and the main routine runs continuously.
When I get a rising edge detection on the CCP1 pin, the interrupt is configured to reset timer1 to zero as well as the overflow counter
#INT_CCP1
void ccp1_isr() // Captures the rising edge of CCP1 pin.
{
if(timing==FALSE){ // only do this on the edge, any bouncing will reset timers etc.
set_timer1(0);
T1_Overflow = 0;
Pulse_Time = 0;
timing = 1; // Set flag to indicate timing.
output_high(BLUE_LED);
}
}
the timing flag ensures the times cannot be reset by another pulse on the CCP1 pin.
Timer1 should then be reset and start counting as normal. Every time it rolls around by 65535 (16bit device) another interrupt is fired after which the amount of overflows are incremented.
#INT_TIMER1
void isr()
{
T1_Overflow++;
}
Finally, when the input pin on CCP2 goes high, the CCP_2 interrupt is triggered. This captures the value of the CCP register (which is the value of Timer0 at the time the interrupt was fired) and the overflow register.
#INT_CCP2
void ccp2_isr()
{
if(timing == TRUE){ // only output this when preceded by CCP1
if(Count_Done == FALSE) // do this once only
{
Count_Done = TRUE; // and also flag to the main routine to output data to the terminal.
Pulse_time = CCP_2;
Pulse_Overflow = T1_Overflow;
measureCount++; // increment the number of measures.
}
output_low(BLUE_LED);
timing = FALSE;
}
}
CCP1 can now start responding to the inputs again.
The idea of this is that every time I get a pulse of one input at CCP1 followed by CCP2, a string is sent to the terminal with a counter, the number of overflows and the time left in the timer.
while(TRUE) // do forever while connected
{
usb_task(); // keep usb alive
if(Count_Done == TRUE)
{
printf(usb_cdc_putc, "%lu , %lu , %lu \r\n",measureCount, pulse_time, pulse_overflow);
Count_Done = FALSE;
}
so, I should get an output to the terminal of something like "1,61553,35" for a ~12ms delay between CCP1 and CCP2.
The problem is that these are the results I am getting for a 200ms pulse provided to the circuit. (Verified twice)
so where am I going wrong.
I have a 48MHZ Clock with no prescaler which implies a cycle every 20ns.
Divide by for 4 instructions per cycle for the clock which implies 5.2ns every cycle
16 bit timer which implies rollover every 65535*5.2ns = 341us per rollover.
when you do the calculations (0.000341*pulse_overflow)+pulse_time*(5.2*(10^-9))
then the above data gives 0012.27ms and not the 200ms provided.
Can anyone point out where I am going wrong with these calculations???
Your error is in "Divide by for 4 instructions per cycle for the clock which implies 5.2ns every cycle"
The counter ticks once every 4 cycles, not 4 times per cycle. So, the correct calculations are:
2.08333E-08 s/cycle of osc
8.33333E-08 s/tick of timer
0.005461333 s/rollover
You are off by a factor of 16.

Accuracy of Timer1 as real time clock with PIC Interrupts on 16F*

I'm using C with the BoostC compiler. I'm worried how accurate my code is. The configuration below ticks at more or less 1Hz, (tested with an LED to the naked eye). (It uses an external watch crystal 32kHz for Timer1 on the 16f74).
I'm hoping someone can tell me...
Does my code below need any adjustments? What would be the easiest way of measuring the accuracy to the nearest CPU clock period? Will I need to get dirty with assembly to reliably ensure accuracy of the 1Hz signal?
I'm hoping the time taken to execute the timer handler (and others) doesn't even come into the picture, since the timer is always counting. So long as the handlers never take longer to execute than 1/32kHz seconds, will the 1Hz signal have essentially the accuracy of the 32kHz Crystal?
Thanks
#define T1H_DEFAULT 0x80
#define T1L_DEFAULT 0
volatile char T1H = T1H_DEFAULT;
volatile char T1L = T1L_DEFAULT;
void main(void){
// setup
tmr1h = T1H;
tmr1l = T1L;
t1con = 0b00001111; // — — T1CKPS1 T1CKPS0 T1OSCEN NOT_T1SYNC TMR1CS TMR1ON
// ...
// do nothing repeatedly while no interrupt
while(1){}
}
interrupt(void) {
// Handle Timer1
if (test_bit(pir1, TMR1IF) & test_bit(pie1, TMR1IE)){
// reset timer's 2x8 bit value
tmr1h = T1H;
tmr1l = T1L;
// do things triggered by this time tick
//reset T1 interrupt flag
clear_bit(pir1, TMR1IF);
} else
... handle other interrupts
}
I can see some improvements...
Your timer initiation inside interrupt isn't accurate.
When you set the timer counter in interrupt...
tmr1h = T1H;
tmr1l = T1L;
... then you override the current value what isn't good for accuracy.
... just use:
tmr1h = T1H; //tmr1h must be still 0!
Or even better, just set the 7th bit of tmr1h register.
The compiler must compile this order to single asm instruction like...
bsf tmr1h, 7
...to avoid losing data in tmr1 register. Because if this is made with more than one instructions the hardware can increment the counter value between execution of: read-modify-write.

Resources