Real time clock , MSP430 - c

I need to set an alarm every 15 minutes (00:15, 00:30, 00:45, 01:00, ...)
using a Real time clock do some process and then set new alarm value.
Well I have a written the code, it does well to run the clock. But no period alarms occur.
It would be great to have feedback on the code
void rtc_init(void)
{
RTCCTL01 = RTCMODE + RTCTEVIE + RTCTEV_0;
RTCCTL01 |= RTCHOLD;
RTCSEC = 0x00;
RTCMIN = 0x28;
RTCHOUR = 0x12;
RTCDOW = 0x05;
RTCDAY = 0x1A;
RTCMON = 0x08;
RTCYEAR = 0x07DB;
RTCAMIN = timer;
RTCCTL01 &= ~RTCHOLD;
__enable_interrupt();
}
#pragma vector=RTC_VECTOR
__interrupt void handle_rtc_interrupt(void)
{
switch(__even_in_range(RTCIV,8))
{
case 6:
get_fix();
timer += timer;
if (timer == 60) timer = 1;
RTCAMIN = timer;
RTCCTL1 &= ~RTCHOLD;
break;
}//switch
}//ISR

At the very least you need to set the AE bit in the RTCAMIN register for the alarm to go off when the minutes match:
RTCAMIN = AE | (timer & 0x7F);
It also looks like you have the event interrupt selected to occur on every change of a minute ("RTCCTL01 = RTCMODE + RTCTEVIE + RTCTEV_0;") which is not the same as the user programmable alarm that you seem to want to use. You need to set the alarm interrupt bits:
RTCCTL01 = RTCMODE + RTCTAIE;
Your method of incrementing the timer value is incorrect as it will double each time, not go up by 15 minutes. Your should use this in your ISR:
timer += 15;
If this period needs to change then you will need two variables, one to store the new timer value and one to store the period of the alarms. You could use the register to store the timer value, so it would be something like this (assuming "timer" is the period of the alarm that the user wants):
next_timer = RTCAMIN & 0x7F;
next_timer += timer;
if (next_timer) >= 60
{
next_timer -= 60;
}
RTCAMIN = AE | (next_timer & 0x7F);
You should re-set the timer to 0, not 1, when it reaches 60 otherwise your alarms will go off at xx:00:xx xx:15:xx xx:30:xx xx:45:xx xx:01:xx xx:16:xx etc.
You should not compare exactly for 60 minutes in your timer variable. It does not matter so much, but with the other two bugs above you would never have gotten exactly 60 on the second iteration. Also if 60 is not exactly divisible by your alarm period then you will go past 60 and need to reduce it rather than set it to a specific value to maintain the correct timing. You should do this to be safer:
if (timer >= 60) timer -= 60;
Finally, the datasheet says you should disable the alarm interrupt flags while modifying the alarm values. Remember to do that in your ISR.
Other things to check are:
ensure you are not going into a low power mode that stops the RTC from updating
you've used the correct definitions for the combined register RTCCTL01 and not mixed them with definitions that are meant for the individual registers (RTCCTL0)
I cannot tell whether the interrupt is the correct one (it looks like it should be) as you've still not told us what the part number is.

Related

STM32F0 - Multiple interrupts from one Timer?

I'm trying to figure out if there is a way of using any of the timers to generate interrupts at more than one delay time.
E.G. I want to be able to start a timer, then have it cause interrupts at say 20us, 50us, 100us, 300us.
I can see that its perfectly possibly (and easy) to have timers cause an interrupt when the end of the count has elapsed (using HAL) but having trouble working out if I can do what I want using only one timer.
I notice some timers are 4 channel but not sure if they can be set as required.
I guess my fall back is to use one timer for each (but ideally I would like to keep other timers in case they are needed for other tasks).
I've read the docs but having trouble understanding if the device can be configured as I want it to.
The easiest way - is to change the timer period (write a different value to the ARR register, use __HAL_TIM_SET_AUTORELOAD macro if you prefer HAL) in each interrupt. This way every other period could be different.
Just keep in mind ARR buffering, or Auto-reload preload, see ARPE bit decription in TIMx_CR1. if it is enabled - the new value of ARR will be taken in account only after the next update event.
You can have more than one interrupt generated during one cycle. You need to use output compare registers and enable their interrupts.
In the timer interrupt handler routine you will need to check what has triggered the interrupt.
There are multiple ways you can achieve it. One a little blunt and simple, another one slightly more complicated, but once set up, very effective and quick to process.
Approach number 1, a little limited, but also a little simpler: use capture/compare (in this case, compare specifically) to trigger an interrupt. Since timers have up to 4 capture/compare values, you can have up to 4 interrupts. You will have to check which exactly interrupt fired tho in the handler. Which specifically compare value, I mean.
Approach number 2, more flexible, more precise, but a bit more hassle to set up: master-slave timers. You can have one timer (including basic) be the clock source for the other timers ("tick" on the rising edge of master slave setting). For example, master timer ticks at 10kHz, and its slave timer uses its output as a clock source and ticks to, say, 50. Or 100. You can have multiple slaves. Depending on what timer's interrupt fires, you know right away how much time exactly passed, no need to check values in compare register or anything.
Mixed method: slave timers can have capture/compare too by the way, if you're into that. You can create huge timer chains if you want to.
You have a dozen timers in your MCU, you can probably spare 1 or 2 more for this purpose. Once you get it working, should make your life very easy.
This is how I set up a timer that is a slave, but also a master to another timer. TMR is TIM3 with TIM1 as a master:
/*
* CR1 reset value = 0x00000000
* ARR is not buffered
* upcounter (default)
* update request source: only overflow/underflow
*
* */
TMR->CR1 = 0x00; //reset
TMR->CR1 |= TIM_CR1_URS;
/*
* 108MHz Clock, 64000 prescaler -> 2kHz Timer Clock
* Reload value 6, Period = 6/2000s = 3ms
* */
TMR->PSC = (108000U / 2U) - 1U; //APB1 is 54MHz, timer is 2x APB1 Freq, so now the timer is at 2kHz; 16-bit value!!! 65535 max!
TMR->ARR = 6U - 1U; //6 ticks at 2kHz is 3ms
TMR->CNT = 0x00; //start value
TMR->EGR |= TIM_EGR_UG; //force update event and load all values into registers
TMR->SR &= ~TIM_SR_UIF; //force clear update event
/*
* SMCR Slave Mode Control Register reset value = 0x00000000
* Trigger Selection - ITR0 (TIM1 as Master for TIM3)
* Slave Mode Selection - Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not
* reset). Only the start of the counter is controlled (0b0110)
*/
TMR->SMCR = 0x00; //reset
TMR->SMCR |= (0x00 << TIM_SMCR_TS_Pos) | (0x00 << 16U) | (0x06 << TIM_SMCR_SMS_Pos);
/*
* CR2 reset value = 0x00000000
* Master Mode Selection 1: OC1REF Triggers TRGO to start another timer
* Master Mode Selection 2: reset
* Compare value: 4 (output LOW: CNT = 0, 1, 2, 3; output HIGH CNT = 4, 5)
* Duty cycle: 33.33%
* Output compare 1 mode - PWM mode 2 (0b0111)
*
* */
TMR->CR2 = 0x00; //reset
TMR->CR2 |= (0x04 << TIM_CR2_MMS_Pos); //OC1REF as TRGO
TMR->CCR1 = 4U;
TMR->CCMR1 = 0x00;
TMR->CCMR1 |= (0x07 << TIM_CCMR1_OC1M_Pos);
/*
* Capture Compare Enable Register
* Polarity: default - active high
* Capture Compare Output Enable
* */
TMR->CCER = 0x00; //reset
TMR->CCER |= TIM_CCER_CC1E;
The rest of my example/training project, including screenshots of waveform, are here: Github Chained Timers Demo. There you will find TIM1 to be the master, TIM3 its slave and the master of TIM5, and so on. Timers are connected in different master-slave modes.

Is it possible to get an hour timer interrupt from Atmega328p?

Coming from this question and I just wonder How to calculate maximum time that an Atmega328 timer can give us before trigger an interrupt? I want it to trigger every hour or so in my project but due to the fact that C integer and OCR1A register has some limitation in size it seems far fetch to get an hour from it.
Is it possible to modify my last question code to get some hour delay?
It should be, depending on the frequency of your microcontroller and the prescaler you're going to use.
Atmega 328P is 20 MHz. So if you take 20,000,000/1024 = 19531. Thats the number of cycles in 1 second.
You can find this in the data sheet for a 16 Bit Timer:
volatile uint8_t count = 0;
void set_up_timer() {
TCCR1B = (1 << WGM12); // from the datasheet
OCR1A = 19531; // number of ticks in a second
TIMSK1 = (1 << OCIE1A); // from the data sheet
TCCR1B |= (1 << CS12) | (1 << CS10);
You can set a global variable, and increment it in the ISR Routine until the desired value is achieved. Something along the lines of:
ISR(TIMER1_COMP1_VECT) {
counter++;
if(counter >= 3600) {
// do whatever needs to be done
}
The comment by Jabberwocky translates to this code (based on the other question to which your have posted the link)
... includes
/* In milliseconds */
const unsigned int ISR_QUANTUM = 1000; // once in a second
/* How much time should pass (1 hours = 3600 seconds = 3600 * 1000 ms */
const unsigned long LONG_TIME_INTERVAL = 1000 * 3600;
volatile unsigned long time_counter;
void once_an_hour() {
... do what needs to be done
}
int main(void) {
... setup your timer interrupt (high-precision, "short-range")
// reset your 'seconds' time counter
time_counter = 0;
while (1)
{
// busy-wait for time counter passing
if (time_counter > LONG_TIME_INTERVAL) {
// reset global timer
time_counter = 0;
once_an_hour();
}
// do other things
...
}
}
ISR (TIMER1_COMPA_vect)
{
// action to be done every Xms - just increment the time_counter
time_counter += ISR_QUANTUM;
}
This way you just increment a "global" time counter in a "local" interrupt handler.

Inconsistent IRQ frequency with SP804 Dual-Timer Module on QEMU ARM versatilepb platform

I wrote a bare-metal timer program on an ARM platform with QEMU. The platform is versatilepb. The timer is SP804 ARM Dual-Timer Module.
The SP804 provides 2 timer modules. Each module provides 2 timers. So there are totally 4 timers, namely Timer 0, 1, 2, 3.
Timer 0/1 share the IRQ 4.
Timer 2/3 share the IRQ 5.
I noticed that if I start a single timer, or 2 timers from different timer modules, say, 0/2 or 1/3. The interrupt frequency is quite normal.
But If I start the 2 timers from the same timer module, say 0/1 or 2/3. The interrupt frequency is much higher.
And when I start the 2 timers from the same timer module, the first timer I started got much more IRQ frequency than the second one, while the latter one seems to have a normal IRQ frequency.
Below is some of my IRQ handler code:
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
// VIC status BITs: timer0,1=4, timer2,3=5
if (vicstatus & (1<<4))
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TVALUE) == 0) // timer 0
timer_handler(0);
if (*(timer[1].base + TVALUE) == 0) // timer 1
timer_handler(1);
}
if (vicstatus & (1<<5))
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TVALUE) == 0) // timer 2
timer_handler(2);
if (*(timer[3].base + TVALUE) == 0) // timer 3
timer_handler(3);
}
}
void timer_handler(u32 n)
{
TIMER *t = &timer[n];
t->tick++; // Assume 20 ticks per second. Need to calculate it for more accuracy.
if (t->tick == 20)
{
t->tick = 0;
t->ss++;
if (t->ss == 60)
{
t->ss = 0;
t->mm++;
if (t->mm == 60)
{
t->mm = 0;
t->hh++; // no 24 hour roll around
}
}
t->clock[7] = '0' + (t->ss % 10);
t->clock[6] = '0' + (t->ss / 10);
t->clock[4] = '0' + (t->mm % 10);
t->clock[3] = '0' + (t->mm / 10);
t->clock[1] = '0' + (t->hh % 10);
t->clock[0] = '0' + (t->hh / 10);
kprintf("Timer [%d]: %s\n", n, (u8 *)&t->clock[0]);
}
timer_clearInterrupt(n); // clear timer interrupt
}
Screenshot:
Single timer: (the timer has normal frequency)
2 timers 1/3 from different module: (both timers have normal frequency)
2 timers 2/3 from same module: (timer 2 started first and it has much higher frequency than 3. And the overall frequency of both 2/3 are much higher)
ADD 1 - 6:23 PM 5/4/2020
Thanks to jcmvbkb's comment. I change to use the Masked Interrupt Status register (offset = TMIS) to detect which timer is issuing the interrupt.
According to the spec:
This value is the logical AND of the raw interrupt status with the
Timer Interrupt Enable bit from the control register, and is the same
value which is passed to the interrupt output pin, TIMINTX.
The interrupt frequency of the 2 timers from the same timer module appears normal now.
I am still thinking about why the previous approach with the Current Value Register cannot work. It seems so natural though.
void IRQ_handler()
{
u32 vicstatus = VIC_STATUS;
//UART 0
if (vicstatus & UART0_IRQ_VIC_BIT)
{
uart_handler(&uart[0]);
}
//UART 1
if (vicstatus & UART1_IRQ_VIC_BIT)
{
uart_handler(&uart[1]);
}
// VIC status BITs: timer0,1=4, uart0=13, uart1=14
if (vicstatus & TIMER01_IRQ_VIC_BIT)
{// bit4=1:timer0,1, handle timer 0 and 1 one by one
if (*(timer[0].base + TMIS) == 1) // timer 0 <===== HERE changed to use TMIS
timer_handler(0);
if (*(timer[1].base + TMIS) == 1) // timer 1 <===== HERE changed to use TMIS
timer_handler(1);
}
if (vicstatus & TIMER23_IRQ_VIC_BIT)
{// bit5=1:timer2,3, handle timer 2 and 3 one by one
if (*(timer[2].base + TMIS) == 1) // timer 2 <===== HERE changed to use TMIS
timer_handler(2);
if (*(timer[3].base + TMIS) == 1) // timer 3 <===== HERE changed to use TMIS
timer_handler(3);
}
}
Below is screenshot for starting timer 2/3 from the same timer module:
IRQ_handler uses Current Value Register to detect which timer caused an interrupt, it doesn't look right. There's Masked Interrupt Status Register that is apparently meant for that, I'd suggest to use it instead. In addition checking for the IRQ source, recording the result of that check and clearing IRQ by writing to Interrupt Clear Register must be protected from reentrancy, otherwise single timer IRQ may still be counted multiple times.
My guess is that the original issue is observed because timer IRQ is triggered by something else or IRQ_handler is called for some other IRQ when timer counter is zero.

Reset timer on msp430

Using CCS, embededded programming, MSP430F63736A
In my app I´m usingTimer A to switching LED(ON/OFF) for various time.
For example:
Led is ON - 0.5 sec
Led is OFF- 3 sec
I need to reset timer when it reach the value in register TA1CCR0. These times(0.5 s and 3 s) are in the register TA1CCR0. The problem is, that if I change the value of register TA1CCR0 from 0.5 sec to 3 sec, it will count from 0.5 to 3 sec. And I need a full 3 sec so I need a reset timer.
Thats the interrupt routine where I´m switching LED
if (P4OUT == 0x00)
{
P4OUT ^= BIT6; // LED ON
TA1CCR0 = (sekunda*t1); //t1- 3 s.... pc 2s
}
else
{
P4OUT = 0x00;
TA1CCR0 = (sekunda*t2);
}
}
Which counter mode are you using? You need to set the mode by writing to the TA1CTL register. The modes are:
#define MC_0 (0x0000) /* Timer A mode control: 0 - Stop */
#define MC_1 (0x0010) /* Timer A mode control: 1 - Up to CCR0 */
#define MC_2 (0x0020) /* Timer A mode control: 2 - Continuous up */
#define MC_3 (0x0030) /* Timer A mode control: 3 - Up/Down */
"Continuous" mode is typically the most used one. It counts until the overflow of the timer register, then restarts from zero. If you have it enabled, add to CCR continuously rather than setting it to the interval value:
TA1CCR0 += (sekunda*t1);
You can also use the "up" mode. In that case the timer counter should automatically reset to zero after reaching CCR. If you're modifying CCR within the ISR and then apparently this reset does not happen, you can modify the timer counter register (TAR or TA1R) by subtracting the value of the shortest period. Do not just set the timer register to zero, as it's usually not a good coding practice: it opens doors for timing inaccuracy accumulation in case the ISR calls are occasionally delayed (e.g. because some other interrupt is execution for multiple ticks).
In your ISR add an offset to TA1CCR0 and not reset it:
Say you want a 1 sec delay after the first interrupt, and let's say to generate 1 sec delay the timer must count up to 50000. So what you need to do in you ISR is TA1CCR0 +=50000.
In your case:
if (P4OUT == 0x00)
{
P4OUT ^= BIT6; // LED ON
TA1CCR0 += (sekunda*t1); //t1- 3 s.... pc 2s
}
else
{
P4OUT = 0x00;
TA1CCR0 += (sekunda*t2);
}
}
For an a complete example please see

Timer logic on AVR

I am trying to understand this piece of code, What I am not able to understand is how the interrupt routine works as the OCR1A is getting updated. I am using AVR series of controller to run this code.
void TimerInit(void)
{
DISABLE_TIMER_INT; // Disable timer interrupt
m_nGlobalTime = 0; // Reset system time
OCR1A += TICKS_PER_MSECOND; // Set first clock period
TCCR1A = 0;// Set TimerMode to Normal
TCCR1B |= (1 << CS10);// ClckIo, no pre-scaler; set TimerMode
ENABLE_INTERRUPTS;
ENABLE_TIMER_INT;// Enable send timer interrupt (1 ms)
}
ISR( TIMER1_COMPA_vect) {
uint16_t nTemp;
nTemp = TCNT1; // Get current time
nTemp -= OCR1A; // Subtract interrupt time
if (nTemp < (TICKS_PER_MSECOND / 2))// If more than half period left
{
OCR1A += (TICKS_PER_MSECOND);// Add Offset to OCR1A relative
}
else
{
OCR1A = TCNT1 + (TICKS_PER_MSECOND);// Set OCR1A to 1 ms absolute
}
m_nGlobalTime++;
}
The usual way to get an output compare interrupt to fire at a regular interval is to add a constant amount to the OCR1A. This is happening at
OCR1A += (TICKS_PER_MSECOND);
For some reason, the writer added some extra logic to handle bad luck. Perhaps, the period is too short, or maybe the OC interrupt may be delayed due to some other interrupt running.
If these cases were to occur, then the next OC interrupt would not occur TICKS_PER_MSECOND later than the last, but rather TICKS_PER_MSECOND plus an entire cycle of the counter. That is, the correct time would be missed, as the OC register would be set to a number after the number has been passed.
This code is an attempt to correct for this situation. That being said, I'm not sure it works correctly. The potential problem is that nTemp is unsigned, so the < comparison might not do what the writer expects.

Resources