Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTC Problem #33

Open
wojciechz9 opened this issue Jun 11, 2016 · 10 comments
Open

RTC Problem #33

wojciechz9 opened this issue Jun 11, 2016 · 10 comments

Comments

@wojciechz9
Copy link

Hi man,
at first i really want to thank you for this sdk, it's fucking perfect :D

Okay, so there is my problem. I wanted to add a clock to my program, i used your code from example, edited it and i saw it's count a seconds too fast.
When simple timer have a 120 seconds an RTC have something like 122-123 seconds.
Your clean example works good, but when i add a new PutText it's too fast.

My edited example so you can test it by yourself:

/*
 * This file is part of eVic SDK.
 *
 * eVic SDK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * eVic SDK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with eVic SDK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2016 ReservedField
 */

#include <stdio.h>
#include <M451Series.h>
#include <Display.h>
#include <Font.h>
#include <RTCUtils.h>
#include <TimerUtils.h>

volatile uint32_t time;

// Names of weekdays
const char const *weekDays[] = {
    "Sun", "Mon", "Tue", "Wed",
    "Thu", "Fri", "Sat"
};

// Names of months
const char const *months[] = {
    "Jan", "Feb", "Mar", "Apr",
    "May", "Jun", "Jul", "Aug",
    "Sep", "Nov", "Dec"
};

void timerCallback(uint32_t counterIndex) {
    // We use the optional parameter as an index
    // counterIndex is the index in timerCounter
    time++;
}

int main() {
    char buf[100];
    RTCUtils_DateTime_t dateTime;

    // Initialize RTC date/time
    // Wednesday, 4th May 2016, 23:59:00
    dateTime.year      = 16;
    dateTime.month     = 5;
    dateTime.day       = 4;
    dateTime.dayOfWeek = 3;
    dateTime.hour      = 23;
    dateTime.minute    = 59;
    dateTime.second    = 0;
    RTCUtils_SetDateTime(&dateTime);

    time = 0;
    Timer_CreateTimer(1, 1, timerCallback, 1);

    while(1) {
        // Get RTC date/time
        RTCUtils_GetDateTime(&dateTime);

        // Display date/time
        siprintf(buf, "%s %02d\n%s %d\n\n%02d:%02d:%02d",
            weekDays[dateTime.dayOfWeek], dateTime.day,
            months[dateTime.month - 1], 2000 + dateTime.year,
            dateTime.hour, dateTime.minute, dateTime.second);
        Display_Clear();
        Display_PutText(0, 0, buf, FONT_DEJAVU_8PT);
        siprintf(buf, "Time:%d", time);
        Display_PutText(0, 80, buf, FONT_DEJAVU_8PT);
        Display_Update();
    }
}

Am I doing something wrong?

@ClockSelect
Copy link

Hello

Problem is that on the VTC mini, circuitry is so badly isolated than anything will come and tamper with the 32kHz signal, even is set at max gain (wich is the case by default anyway).

For example, when you fire the PWM, which is a 150kHz signal theorically completly independant from the LTX, the RTC speed will skyrocket to 4.5 seconds per seconds, just like if the PWM output was directly connected in place of the 32kHz crystal.

In fact, any activity will perturbate the RTC speed due to the so bad PCB design.

The only way to get a consistant RTC speed is to drive it with the internal 10kHz LIRC oscillator, which is correctly isolated from external pertuebations, but there I face another problem: the RTC just gains 1 second every 3.2768 real seconds... The chip allows us to drive the RTC with the LIRC, but I couldn't find how to tell the RTC that it's the case and adapt its speed to the LIRC.

Maybe someone could help?

Regards.

@ReservedField
Copy link
Owner

Ouch. I had totally forgotten about this issue.
Ok so this is a bit embarassing, but I think the RTC code was the product of crappy late-night coding.
This is what I use in src/rtc/RTCUtils.c:

CLK_EnableXtalRC(CLK_PWRCTL_LXTEN_Msk);
CLK_WaitClockReady(CLK_STATUS_LXTSTB_Msk);
CLK_EnableModuleClock(RTC_MODULE);

It might look good, until you realize I'm never setting the RTC module to actually use the LXT clock as source (i.e. CLK_SetModuleClock). But there are defaults, so maybe it's not that bad...
The RTC clock source is selected by the CLK_CLKSEL3 register, bit 8 (RTCSEL): 0 = LXT, 1 = LIRC. CLK_CLKSEL3 is 0x00000003 at reset, which means RTCSEL defaults to 0, i.e. LXT.
Ok, we're indeed using LXT (to my excuse I have to say Nuvoton examples don't set it explicitly, too).

There's more though. I checked hi-res pics I have for two eVic boards, followed the X32 traces and found a Y2 unpopulated spot for a 32kHz crystal. Y1 is the main 12MHz of course. So it can't be pulling the clock from LXT, because there is no 32kHz crystal on board.
How on earth can that ever appear to work when there's no 32kHz crystal attached? No shit Sherlock it's messed up :D

About LIRC, as far as I understand you can only use it for spare registers write/read but not for actual RTC operation (see page 519 of M451 TRM). Using RTC_FREQADJ is also out of the question as it won't cover a 22kHz gap.

So it seems to me we can only use RTC on devices that support it, e.g. the VTwo?

@ClockSelect
Copy link

Hello and thanks for your answer.

(I'm fairly interessed by your HI-res pics - it may help sometimes)

The unpopulated Y2 may explain things^^
In fact, it appers to work, but at a fairly low rate; my clock is running at about 80% of the real time speed - loosing 10 secs per minute, and with a great variability depending on the board activity. So it would confirm the the X32_IN and OUT pins slots are in fact only receiving parasitics signals from miscellaneous places, enough to fake an inaccurate clock.

I tried to drive the RTC by the LIRC and the signal seems to be steady and consistent - so maybe we could think about a way to fake a clock with the LIRC, converting date & time from a given starting point and just multiplying elapsed time by 3.2768. We could even use the spare registers to store the reference date^^ (but maybe an u32 in the datafash will be more convenient). I'll have a look.

@ClockSelect
Copy link

Back after a few hours. Well... it works... almost :)

I configure the RTC on the LIRC clock, and I configure the date & time to now (I wrote a config screen for this).
I save this date & time in an epoch form in a register as the reference time (I use the spare registers, but I could have used dataflash). I also store a clock ratio constant, that I originaly set up to 32768. I also enable RTC tick interrupt for screen refresh when I display time on the main screen (I added this option to the amp/puff/time line).

When the clock ticks, I read the time, convert it to an epoch form, substract the result from the reference time, multiply the delta by the ratio constant, add the result to the reference time, and I get (almost) the actual real time to display.

The rest is adjusting the ratio constant; I made a config screen for this, too. It shows the clock and the number, and you adjust it by + or - until the clock is in synch with the real time. It gives me a constant around 23~24000; it means that the LIRC is really running around 14kHz, which is surprising. Also, clock speed seems to vary a little depending on box activity, but far less than with the (non-existing) X32. I'm curious to know if it varies from box to box.

Eventually, I got a very usable clock for every day use when high accuracy is not a concern. Also, I saw that you have a few seconds to change battery before the RTC whipes out the time; not very long, but far enough if you have the other battery by hand. No need to reconfigure each time.

It works... almost. :)

@ReservedField
Copy link
Owner

ReservedField commented Jul 14, 2016

Nice job!

First off, don't store the reference in dataflash. The best way is to use RTC spare regs as you're doing. It'll be kept by the backup power domain (there isn't one in devices without official RTC support like the eVic, but still it's better for other devices).

About the tick interrupt: if the frequency is low enough that can be acceptable. What I'm more worried about is how this is going to affect sleep (it might cause periodic wakeups, have to handle going back to sleep, etc).
Wouldn't it be better to just let the RTC run and perform the calculation in GetDateTime()? That should handle it without requiring interrupts. Of course if you're using the interrupt just to know when to update display that's a different story.

I assume your constant is multiplied by 10^4 (that's what gives 14KHz). 14KHz seems a bit off even for an RC oscillator. IMO a better way to determine it is to measure against HXT (not HCLK/PLL, those can be jitterish). HXT should be a pretty stable 12MHz, as stable as you'll get from a crystal. This calibration could be done at system init to minimize impact on performance (we're dealing with a pretty slow clock, to accurately measure you'll probably have to disable IRQs for a relatively long time, which is not very good if user code is running). The calibrated ratio should be stored into the RTC regs (again, power domains) just like you're doing.

I'm fixing up the first iteration of dispatcher optimizations right now (no unnecessary context push/pop, next up will be FPU lazy stacking) but I'll be sure to play around with this later.

Also, I'm surprised it can keep settings for a few seconds. Seems like RTC has a lower BOD maybe?

@ClockSelect
Copy link

The reference is in spare regs, but the ratio has to be in dataflash not to be wiped out by a clock reset, since it's a finely user configured value. That's the way I do.

For the 14kHz: The RTC is supposed, when driven by a clock, to increase time by 1 second every 32768 clock cycles; by fine-tuning the ratio against a real-world clock, I see that the RTC gains one second every ~2.35 real seconds (that's also the frequency of RTC IRQ, btw). It means that the RTC is driven by a clock that ticks 32768 cycles in ~2.35 seconds, and that's ~14kHz. Way of what the LIRC is supposed to do; I know, that's weird...

I use the RTC IRQ to reset a clock correction counter driven by the existing 1kHz TMR2, so that I can display a good-looking clock that properly ticks every second on screen. If I did not do this, I'd see a clock that stays still during 2 seconds, sometimes 3, then jumps by 2 or three seconds (due to the 1/2.35Hz IRQ)... Too weird-looking (and I want my seconds;). I also disable RTC IRQ when entering power-down mode, etc. (In fact things are still not perfect, and I may get rid of this IRQ alltogether, eventually, I've other ideas).

You're right, I'm gonna try to use the HXT to measure things. Btw, did you notice that when the OFW calls TIMER_Open to create the 5kHz buck-boost regulator timer, the return value of this call (supposed to be the real frequency attributed by the system, the value is ignored by the FW) is not 5000, but 625? I don't know what to think about this... I see that you're actively talking about B/B frequency in another issue, this may be a point.

@ReservedField
Copy link
Owner

The reference is in spare regs, but the ratio has to be in dataflash not to be wiped out by a clock reset, since it's a finely user configured value.

I agree, but if we can get a nice, reliable calibration going on the the user doesn't have to fine tune anything (and at 1/12 of a millionth of a second resolution we're probably going to be more precise than a human) and as such there's no need to store it permanently. I'm thinking of ways to do it.

If I did not do this, I'd see a clock that stays still during 2 seconds, sometimes 3, then jumps by 2 or three seconds (due to the 1/2.35Hz IRQ)...

You may want to look into the RTC_TICK registers to make the tick more frequent. You're going to get ~2.35*1/TICK seconds of jitter.

Btw, did you notice that when the OFW calls TIMER_Open to create the 5kHz buck-boost regulator timer, the return value of this call (supposed to be the real frequency attributed by the system, the value is ignored by the FW) is not 5000, but 625?

Are you taking this from inside TimerUtils? I'll check this too, later, but it looks strange as the Nuvoton code seems good.

@ClockSelect
Copy link

The TIMER_Open return value is from the official 3.03 firmware (I made a full clean disassembly of it). It seems that sometimes the Nuvoton may attribute a frequency way off the one you asked for.
I'll continue to think about this issue of clock accuracy, too.

@ReservedField
Copy link
Owner

How can you know a runtime return value from static analysis?
Btw, join us at ##evic-modding on Freenode, if you want I can link you to the IDA loader/plugins I coded for the eVic.

@ClockSelect
Copy link

I modify the code, take the return value, store it in a place where I can download it via the download dataflash USB command, and I see the values. The dataflash download command sends you 0x800 bytes of code the last 0x600 of them beeing pure free and unused RAM. This is my playfield.
I don't know Freenode. Gonna have a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants