When a variable exceeds its maximum value, it rolls over to zero and increments the next higher-order variable.Hi Kittu,
Why are you testing for rollovers in following IF statements, if the current IF is less than the maximum value of the current test?
E
Thank you for your patience. Here's the complete code with detailed comments to ensure clarity. Please take a look at the code below and let me know if you have any further questions or if there's anything specific you'd like to point out."hi K,
As you say IF it rolls over it increments the next higher order, but you are testing for all the following order roll-overs orders, even when a proceeding order test, has not reached the rollover value.
Simply put, your program is wasting time, testing for states that cannot have reached rollover.
#include <xc.h>
#include "config.h"
volatile int timeFlag = 0;
volatile unsigned char seconds = 0;
volatile unsigned char minutes = 0;
volatile unsigned char hours = 0;
volatile unsigned char days = 1;
volatile unsigned char months = 1;
volatile unsigned int years = 2023;
void initializeTimer1() {
// Configure Timer1 in 8-bit mode with a prescaler of 8
T1CON = 0b00110000;
// Calculate Reload_Value for 1ms with a 20 MHz oscillator and prescaler 8
uint16_t Reload_Value = 625; // (20,000,000 / 4 / 8) = 625
// Configure CCP module in Special Event Trigger mode
CCP1CON = 0b00001011; // 0x0B
// Load the high and low bytes of Reload_Value into CCPR1
CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value
// Clear Timer1 registers
TMR1H = 0;
TMR1L = 0;
// Clear Timer1 interrupt flag
TMR1IF = 0;
// Enable Timer1 and CCP module
TMR1ON = 1;
CCP1IE = 1; // Enable CCP1 interrupt
}
void main() {
// Initialize Timer1
initializeTimer1();
// Initialize INTCON register
INTCON = 0; // Clear INTCON register
// Enable global and peripheral interrupts
INTCONbits.GIE = 1; // Global Interrupt Enable
INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
while (1) {
// Check if the timeFlag is set (1ms interval)
if (timeFlag) {
// Increment seconds
seconds++;
// Handle rollover for seconds
if (seconds >= 60) {
seconds = 0; // Reset seconds to 0 when it reaches 60
minutes++; // Increment the minutes part of the time
// Handle rollover for minutes
if (minutes >= 60) {
minutes = 0; // Reset minutes to 0 when it reaches 60
hours++; // Increment the hours part of the time
// Handle rollover for hours
if (hours >= 24) {
hours = 0; // Reset hours to 0 when it reaches 24
days++; // Increment the days part of the time
// Check for month and year changes (simplified, not accounting for leap years)
if (days > 30) { // If 30 days have passed (1 month, simplified)
days = 1; // Reset days to 1 to start a new month
months++; // Increment the months part of the time
if (months > 12) { // If 12 months have passed (1 year, simplified)
months = 1; // Reset months to 1 to start a new year
years++; // Increment the years part of the time
}
}
}
}
}
// Reset the timeFlag to indicate that the 1ms interval has been processed
timeFlag = 0;
}
}
}
void __interrupt() ISR() {
if (CCP1IF) {
// CCP1 interrupt occurred (1ms interval)
timeFlag = 1; // Set the timeFlag
// Clear the CCP1 interrupt flag
CCP1IF = 0;
}
}
if (seconds >= 60) {
seconds = 0;
minutes++;
}
Hi K,If seconds is 50, and the code executes with the condition if (seconds >= 60), it will not trigger the rollover because 50 is not greater than or equal to 60. Therefore, in this case, seconds will remain at 50, and minutes will not be incremented.
Each if statement is responsible for checking and handling rollovers for a specific time unit (e.g., seconds, minutes, hours, days, months).Hi K,
I think you are missing my point, relook at what I stated.
while (1) {
// Check if the timeFlag is set (1ms interval)
if (timeFlag) {
// Increment seconds
seconds++;
// Handle rollover for seconds
if (seconds >= 60) {
seconds = 0; // Reset seconds to 0 when it reaches 60
minutes++; // Increment the minutes part of the time
}
// Handle rollover for minutes
if (minutes >= 60) {
minutes = 0; // Reset minutes to 0 when it reaches 60
hours++; // Increment the hours part of the time
}
// Handle rollover for hours
if (hours >= 24) {
hours = 0; // Reset hours to 0 when it reaches 24
days++; // Increment the days part of the time
}
// Check for month and year changes (simplified, not accounting for leap years)
if (days > 30) { // If 30 days have passed (1 month, simplified)
days = 1; // Reset days to 1 to start a new month
months++; // Increment the months part of the time
}
if (months > 12) { // If 12 months have passed (1 year, simplified)
months = 1; // Reset months to 1 to start a new year
years++; // Increment the years part of the time
}
// Reset the timeFlag to indicate that the 1ms interval has been processed
timeFlag = 0;
}
}
Here's the updated code:Hi K,
Please post your complete Code listing, make sure I am not missing a point.
E
#include <xc.h>
#include "config.h"
volatile int timeFlag = 0;
volatile unsigned char seconds = 0;
volatile unsigned char minutes = 0;
volatile unsigned char hours = 0;
volatile unsigned char days = 1;
volatile unsigned char months = 1;
volatile unsigned int years = 2023;
void initializeTimer1() {
// Configure Timer1 in 8-bit mode with a prescaler of 8
T1CON = 0b00110000;
// Calculate Reload_Value for 1ms with a 20 MHz oscillator and prescaler 8
uint16_t Reload_Value = 625; // (20,000,000 / 4 / 8) = 625
// Configure CCP module in Special Event Trigger mode
CCP1CON = 0b00001011; // 0x0B
// Load the high and low bytes of Reload_Value into CCPR1
CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value
// Clear Timer1 registers
TMR1H = 0;
TMR1L = 0;
// Clear Timer1 interrupt flag
TMR1IF = 0;
// Enable Timer1 and CCP module
TMR1ON = 1;
CCP1IE = 1; // Enable CCP1 interrupt
}
void main() {
// Initialize Timer1
initializeTimer1();
// Initialize INTCON register
INTCON = 0; // Clear INTCON register
// Enable global and peripheral interrupts
INTCONbits.GIE = 1; // Global Interrupt Enable
INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
while (1) {
// Check if the timeFlag is set (1ms interval)
if (timeFlag) {
// Increment seconds
seconds++;
// Handle rollover for seconds
if (seconds >= 60) {
seconds = 0; // Reset seconds to 0 when it reaches 60
minutes++; // Increment the minutes part of the time
}
// Handle rollover for minutes
if (minutes >= 60) {
minutes = 0; // Reset minutes to 0 when it reaches 60
hours++; // Increment the hours part of the time
}
// Handle rollover for hours
if (hours >= 24) {
hours = 0; // Reset hours to 0 when it reaches 24
days++; // Increment the days part of the time
}
// Check for month and year changes (simplified, not accounting for leap years)
if (days > 30) { // If 30 days have passed (1 month, simplified)
days = 1; // Reset days to 1 to start a new month
months++; // Increment the months part of the time
}
if (months > 12) { // If 12 months have passed (1 year, simplified)
months = 1; // Reset months to 1 to start a new year
years++; // Increment the years part of the time
}
// Reset the timeFlag to indicate that the 1ms interval has been processed
timeFlag = 0;
}
}
}
void __interrupt() ISR() {
if (CCP1IF) {
// CCP1 interrupt occurred (1ms interval)
timeFlag = 1; // Set the timeFlag
// Clear the CCP1 interrupt flag
CCP1IF = 0;
}
}
Here's the updated code with the changes you've suggested:A couple of things:
If you use CCP mode 0xB (special event trigger), the Reload_Value should be 624 to account for how the CCP special event trigger works. The average cycle count shown in #19 is one TMR1 count too long.
I am surprised that the cycle count varies at all. I don't know if that is an artifact of the simulator or if it is accurately showing some 'feature' of the chip. It simulates the same here on MPLABX 5.40 with your code.
I DID change the code to use @MrChips ' method of CCP mode 0x08 and manually advancing the CCP compare register. It simulated with the exact counts expected so in the absence of testing on actual hardware, I'd use that method instead of the special event trigger. In the interrupt code just add the line:
CCPR1 += Reload_Value; // advance CCP count by 1ms
You can also use the same construct for initializing instead of calculating the individual bytes.
Nits:
timeFlag does not need to be an integer, a __bit will do and the generated code is smaller.
You can use #define Reload_Value 625 instead of storing it. That makes smaller code too.
In init:
Clear CCP1IF instead of TMR1IF. That's the one you're using.
Make GIE = 1 the LAST thing you do after all other initialization is done.
Hope you're having fun.
#include <xc.h>
#include "config.h"
// Calculate Reload_Value for 1ms with a 20 MHz oscillator and prescaler 8
#define Reload_Value 625
volatile __bit timeFlag = 0;
volatile unsigned char seconds = 0;
volatile unsigned char minutes = 0;
volatile unsigned char hours = 0;
volatile unsigned char days = 1;
volatile unsigned char months = 1;
volatile unsigned int years = 2023;
void initializeTimer1() {
// Configure Timer1 in 8-bit mode with a prescaler of 8
T1CON = 0b00110000;
// Configure CCP module in Special Event Trigger mode
CCP1CON = 0b00001011; // 0x0B
// Load the high and low bytes of Reload_Value into CCPR1
CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value
// Clear Timer1 registers
TMR1H = 0;
TMR1L = 0;
// Clear Timer1 interrupt flag
TMR1IF = 0;
// Enable Timer1 and CCP module
TMR1ON = 1;
CCP1IE = 1; // Enable CCP1 interrupt
}
void main() {
// Initialize Timer1
initializeTimer1();
// Initialize INTCON register
INTCON = 0; // Clear INTCON register
// Enable global and peripheral interrupts
INTCONbits.GIE = 1; // Global Interrupt Enable
INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
while (1) {
// Check if the timeFlag is set (1ms interval)
if (timeFlag) {
// Increment seconds
seconds++;
// Handle rollover for seconds
if (seconds >= 60) {
seconds = 0; // Reset seconds to 0 when it reaches 60
minutes++; // Increment the minutes part of the time
}
// Handle rollover for minutes
if (minutes >= 60) {
minutes = 0; // Reset minutes to 0 when it reaches 60
hours++; // Increment the hours part of the time
}
// Handle rollover for hours
if (hours >= 24) {
hours = 0; // Reset hours to 0 when it reaches 24
days++; // Increment the days part of the time
}
// Check for month and year changes (simplified, not accounting for leap years)
if (days > 30) { // If 30 days have passed (1 month, simplified)
days = 1; // Reset days to 1 to start a new month
months++; // Increment the months part of the time
}
if (months > 12) { // If 12 months have passed (1 year, simplified)
months = 1; // Reset months to 1 to start a new year
years++; // Increment the years part of the time
}
// Reset the timeFlag to indicate that the 1ms interval has been processed
timeFlag = 0;
}
}
}
void __interrupt() ISR() {
if (CCP1IF) {
// CCP1 interrupt occurred (1ms interval)
timeFlag = 1; // Set the timeFlag
// Advance CCP1 compare register by Reload_Value
CCPR1 += Reload_Value;
// Clear the CCP1 interrupt flag
CCP1IF = 0;
}
}
Definitely, we will be discussing data structures for handling the varying number of days per month. One approach I've been considering is using enum to represent the months and associate the days with each monthhi K,
For the different number of days per Month you could consider an Array and Index the pointer.
E
https://www.simplilearn.com/tutorials/cpp-tutorial/cpp-array
// Calendar 09/09/23
int Mntharray[13] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int Indx = 0;
int Year = 2023;
int Days = 0;
int Leap = 0;
void setup() {
Serial.begin(9600);
Serial.print("January");
Serial.print(",");
Serial.println(Year);
}
void loop() {
Days = (Mntharray[Indx]);
Leap= (Year % 4);
if (Leap == 0) {
(Mntharray[1]) = 29;
} else {
(Mntharray[1]) = 28;
}
Serial.print("Days=");
Serial.println(Days);
//Serial.print(",");
Indx++;
if (Indx >= 12) {
Indx = 0;
Serial.println(" ");
Serial.print("January");
Serial.print(",");
Year++;
Serial.println(Year);
}
delay(1000);
}
Sure. The only things I messed with are the TIMER1 and COMPARE stuff. Use in your ongoing code as desired..Could you please share code you've tested on a simulator, along with an stopwatch output screenshot?
// THESE REPLACE config.h in Kittu's code - for simulation only
// PIC18F45K80 Configuration Bit Settings
// CONFIG1L
#pragma config RETEN = OFF // VREG Sleep Enable bit (Ultra low-power regulator is Disabled (Controlled by REGSLP bit))
#pragma config INTOSCSEL = HIGH // LF-INTOSC Low-power Enable bit (LF-INTOSC in High-power mode during Sleep)
#pragma config SOSCSEL = HIGH // SOSC Power Selection and mode Configuration bits (High Power SOSC circuit selected)
#pragma config XINST = OFF // Extended Instruction Set (Disabled)
// CONFIG1H
#pragma config FOSC = INTIO2 // Oscillator (Internal RC oscillator)
#pragma config PLLCFG = OFF // PLL x4 Enable bit (Disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF // Internal External Oscillator Switch Over Mode (Disabled)
// CONFIG2L
#pragma config PWRTEN = OFF // Power Up Timer (Disabled)
#pragma config BOREN = SBORDIS // Brown Out Detect (Enabled in hardware, SBOREN disabled)
#pragma config BORV = 3 // Brown-out Reset Voltage bits (1.8V)
#pragma config BORPWR = ZPBORMV // BORMV Power level (ZPBORMV instead of BORMV is selected)
// CONFIG2H
#pragma config WDTEN = OFF // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config WDTPS = 1048576 // Watchdog Postscaler (1:1048576)
// CONFIG3H
#pragma config CANMX = PORTB // ECAN Mux bit (ECAN TX and RX pins are located on RB2 and RB3, respectively)
#pragma config MSSPMSK = MSK7 // MSSP address masking (7 Bit address masking mode)
#pragma config MCLRE = ON // Master Clear Enable (MCLR Enabled, RE3 Disabled)
// CONFIG4L
#pragma config STVREN = ON // Stack Overflow Reset (Enabled)
#pragma config BBSIZ = BB2K // Boot Block Size (2K word Boot Block size)
// CONFIG5L
#pragma config CP0 = OFF // Code Protect 00800-01FFF (Disabled)
#pragma config CP1 = OFF // Code Protect 02000-03FFF (Disabled)
#pragma config CP2 = OFF // Code Protect 04000-05FFF (Disabled)
#pragma config CP3 = OFF // Code Protect 06000-07FFF (Disabled)
// CONFIG5H
#pragma config CPB = OFF // Code Protect Boot (Disabled)
#pragma config CPD = OFF // Data EE Read Protect (Disabled)
// CONFIG6L
#pragma config WRT0 = OFF // Table Write Protect 00800-01FFF (Disabled)
#pragma config WRT1 = OFF // Table Write Protect 02000-03FFF (Disabled)
#pragma config WRT2 = OFF // Table Write Protect 04000-05FFF (Disabled)
#pragma config WRT3 = OFF // Table Write Protect 06000-07FFF (Disabled)
// CONFIG6H
#pragma config WRTC = OFF // Config. Write Protect (Disabled)
#pragma config WRTB = OFF // Table Write Protect Boot (Disabled)
#pragma config WRTD = OFF // Data EE Write Protect (Disabled)
// CONFIG7L
#pragma config EBTR0 = OFF // Table Read Protect 00800-01FFF (Disabled)
#pragma config EBTR1 = OFF // Table Read Protect 02000-03FFF (Disabled)
#pragma config EBTR2 = OFF // Table Read Protect 04000-05FFF (Disabled)
#pragma config EBTR3 = OFF // Table Read Protect 06000-07FFF (Disabled)
// CONFIG7H
#pragma config EBTRB = OFF // Table Read Protect Boot (Disabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
__bit timeFlag = 0;
//volatile int timeFlag = 0;
volatile unsigned char seconds = 0;
volatile unsigned char minutes = 0;
volatile unsigned char hours = 0;
volatile unsigned char days = 1;
volatile unsigned char months = 1;
volatile unsigned int years = 2023;
// Calculate Reload_Value for 1ms with a 20 MHz oscillator and prescaler 8
#define Reload_Value 625 // (20,000,000 XTAL / 4 / 8 prescale) = 625
void initializeTimer1() {
// Configure Timer1 in 8-bit mode with a prescaler of 8
T1CON = 0b00110000; // mode 0x08 interrupt, CCP1 pin hi on ==
// Configure CCP module
// CCP1CON = 0b00001011; // 0x0B
CCP1CON = 0b00001000; // 0x08
// Load the high and low bytes of Reload_Value into CCPR1
CCPR1 = Reload_Value;
// CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
// CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value
// Clear Timer1 registers
TMR1H = 0;
TMR1L = 0;
// Clear Timer1 interrupt flag
// TMR1IF = 0;
// Clear CCP1 interrupt flag
CCP1IF = 0;
// Start Timer1 and CCP module
TMR1ON = 1;
CCP1IE = 1; // Enable CCP1 interrupt
}
void main() {
// Initialize Timer1
initializeTimer1();
// Initialize INTCON register
INTCON = 0; // Clear INTCON register
// Enable global and peripheral interrupts
INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
INTCONbits.GIE = 1; // Global Interrupt Enable
while (1) {
// Check if the timeFlag is set (1ms interval)
if (timeFlag) {
// Increment seconds
seconds++;
// Handle rollover for seconds
if (seconds >= 60) {
seconds = 0; // Reset seconds to 0 when it reaches 60
minutes++; // Increment the minutes part of the time
}
// Handle rollover for minutes
if (minutes >= 60) {
minutes = 0; // Reset minutes to 0 when it reaches 60
hours++; // Increment the hours part of the time
}
// Handle rollover for hours
if (hours >= 24) {
hours = 0; // Reset hours to 0 when it reaches 24
days++; // Increment the days part of the time
}
// Check for month and year changes (simplified, not accounting for leap years)
if (days > 30) { // If 30 days have passed (1 month, simplified)
days = 1; // Reset days to 1 to start a new month
months++; // Increment the months part of the time
}
if (months > 12) { // If 12 months have passed (1 year, simplified)
months = 1; // Reset months to 1 to start a new year
years++; // Increment the years part of the time
}
// Reset the timeFlag to indicate that the 1ms interval has been processed
timeFlag = 0;
}
}
}
void __interrupt() ISR() {
if (CCP1IF){
// Clear the CCP1 interrupt flag
CCP1IF = 0;
// advance COMPARE value
CCPR1 += Reload_Value;
timeFlag = 1; // Notify main routine
}
}

short IsLeapYear(short thisyear)
// is thisyear a leap year?
{
short result;
result = FALSE;
if ( (thisyear % 1000) == 0 )
{ // millenium
result = ((thisyear / 1000) % 2) == 0;
} // millenium
else
{ // not millenium
if ( (thisyear % 400) == 0)
{ // every 400 years
result = TRUE;
} // every 400 years
else
{ // not 400 years
if ( (thisyear % 100) == 0)
{ // every 100 years
result = FALSE;
} // every 100 years
else
{ // not 100 years
if ( (thisyear % 4) == 0)
{ // every 4 years
result = TRUE;
} // every 4 years
} // not 100 years
} // not 400 years
} // not millenium
return result;
}
Thank you very much for posting code along with an output screenshot for clarification.Sure. The only things I messed with are the TIMER1 and COMPARE stuff. Use in your ongoing code as desired..
My plan is to display real-time information such as seconds, hours, and days on the serial terminal of my PC.For experimenting without regard for long term accuracy, any oscillator will do.