3
\$\begingroup\$

I am looking to make very basic timings on an STM32. For example, I would like to program my STM32 to output bytes on the UART for 1 minute. What clock/timer should I use?

Looking through the reference manual, a lot of clocks are available. It seems that some of the most appropriate clocks would be the Real Time Clock (RTC), a General-Purpose Timer or an Advanced-Control Timer. What clock would be the easiest to use for making basic fixed timings?

\$\endgroup\$
  • \$\begingroup\$ A General-Purpose Timer sounds the least complex for your task \$\endgroup\$ – m.Alin Mar 28 '12 at 13:32
  • \$\begingroup\$ github.com/dwelch67 I have a number of cortex-m examples. Note that the cortex-m4 does not have the systick timer the cortex-m3 has, dont get used to that being there. (or maybe it was the cortex-m0 that doesnt have it). the short answer though is just pick one of the timers, the general purpose ones are usually easy, need one that can count to a second without too much trouble. no need to use interrupts, too complicated until later, start without interrupts. \$\endgroup\$ – old_timer May 5 '12 at 2:23
6
\$\begingroup\$

For periodic tasks, I would recommend using a SysTick interrupt. Here's an example application:

void SysTick_Handler(void) {
    static uint8_t tick   = 0;
    static uint8_t second = 0;
    static uint8_t minute = 0;

    switch (tick++) {
        case 0:
            // task 0 here
            break;
        case 1:
            // task 1 here
            break;
        // and so on
        case 99:
            tick = 0;
            second = (second < 59) ? (second + 1) : 0;
            minute = (second == 0) ? ((minute < 59) ? (minute + 1) : 0) : minute;
            break;  
    }
}

int main(void)
{
    // interrupt at 100Hz
    SysTick_Config(SystemCoreClock/100);

    while (1);
}
\$\endgroup\$
  • \$\begingroup\$ This is a basic example, yes, but you shouldn't use an 8-bit integer for your tick variable. Depending on the frequency of the systick interrupt it should be at least a 16 bit variable. Otherwise you may never be able to count high enough to reach one second - especially if your systick interrupt is run by a typical 32KHz watch crystal. \$\endgroup\$ – AngryEE Mar 28 '12 at 13:48
  • \$\begingroup\$ The tick counter only goes to 99 and is then reset, so I'm not sure what you mean. \$\endgroup\$ – Armandas Mar 28 '12 at 13:54
  • \$\begingroup\$ I don't know about this platform, but; if SysTick_Handler() is an interrupt routine, then will it have stack overflow if you put too much code in it, or is it a good practice to do so? \$\endgroup\$ – abdullah kahraman Mar 28 '12 at 14:45
  • \$\begingroup\$ @abdullahkahraman Those variables in the isr are static, they're not on the stack \$\endgroup\$ – Toby Jaffey Mar 28 '12 at 16:06
  • \$\begingroup\$ My point is that for most circumstances I've needed better timing precision than a 10ms tick - usually 1ms works best for me. I think it's bad practice to have an 8 bit value store the ticks as it's very likely you might want to change the interrupt period from something like 10ms to 1ms. An 8 bit variable can't count to 1000, so you'll never hit your case statement that increments the seconds. It's not a vague fear - I've DONE this before, hence why I call it best practices. \$\endgroup\$ – AngryEE Mar 28 '12 at 16:12
5
\$\begingroup\$

One limitation of SysTick is that its speed is tied to the main clock. If you slow down the main clock to reduce power consumption, SysTick will slow down correspondingly. Many (all?) STM32 parts have some sort of "real-time clock" function which can run independently from the main clock; in some cases, it can even run independently from the main power supply. The real-time clock functions can be programmed to wake up the CPU at specified times, though their precision is rather limited (e.g. some restrict wake-ups to one-second increments unless one uses some rather icky tricks). Note also that some chips, for some reason that completely baffles me, rather annoyingly store the time as (0-9) seconds + (0-5) tens of seconds + (0-9) minutes + (0-5) tens of minutes + (0-9) hours up to 23, plus (0-2) tens of hours up to 24, etc. If you won't need to go more than an hour without polling the clock on the STM32LF151, I would suggest that you ignore the upper portions of the real-time clock and simply do something like:

uint32_t present_time;
uint32_t last_clock_reading;
uint32_t last_raw_reading;

uint32_t get_time(void)
{
 // Capture seconds and minutes 0-9, in BCD format
  uint32_t this_clock_reading = (RTC->TR) & 0x0FFF;
  // Early-exit if no change
  if (this_clock_reading == last_raw_reading)
    return present_time;
  last_raw_reading = this_clock_reading;
 // Each ten minutes should be 600 seconds, not 4096
  this_clock_reading -= (4096-600)*(this_clock_reading & 0x0F00)
 // Each minute should be 60 seconds, not 256
  this_clock_reading -= (256-60)*(this_clock_reading & 0x0F00)
 // Each ten seconds should be 10 seconds, not 16
  this_clock_reading -= (16-10)*(this_clock_reading & 0x00F0);
  // Update present time
  if (last_clock_reading > this_clock_reading)
    present_time += 3600 + this_clock_reading - last_clock_reading;
  else
    present_time += this_clock_reading - last_clock_reading;
  last_clock_reading = this_clock_reading;

  return present_time;
}
int set_alarm(uint32_t alarm_time) // Returns -1 if alarm time already passed
{
  int32_t temp;
  // How far in future (if at all) is alarm time?
  temp = (alarm_time - get_time());
  if (temp  3000)  // Set alarm a max of 50 minutes in future
    temp = 3000;
 // Compute clock reading when we should have our alarm
  temp += last_clock_reading;
  if (temp > 3600)
    temp -= 3600;
 // Convert to BCD by undoing corrections which would be done on reading
  temp += (16-10)*(temp / 10) + (256-60)*(temp / 60) + (4096-60)*(temp / 600);
 // Now store the alarm value (first write-enable the registers)
  RTC->WPR = 0xCA;
  RTC->WPR = 0x53;
  RTC->ALARMAR = temp | 0x80800000; // Set alarm (only match minutes and seconds)
}

Using routines like the above, the time used by the application will be kept in a nice uniformly-incrementing 32-bit value. One can set an alarm up to 50 minutes in advance, and then have the chip go to sleep; the chip will wake up when the alarm time arrives. Setting an alarm more than 50 minutes in advance will cause it to be set 50 minutes in advance; when the CPU wakes up, it can go "back to bed" if the alarm time hasn't arrived yet.

On the STM32L151, one may if desired set the clock to run at 2x, 4x, 16x, or various other multiples of normal speed. The above code would not change when this is done; the only effects would be (1) the values in present_time and the parameter to set_alarm would represent a faster time unit than seconds, and (2) the maximum time before the next alarm would be scaled accordingly. It's somewhat ugly working with BCD units that represent e.g. 37.5 seconds, 3.75 seconds, 0.625 seconds, and 0.0625 seconds (which is what the "minutes" and "seconds" would represent at 16x speed) but such is life.

\$\endgroup\$
0
\$\begingroup\$

If you want to send data on a UART for a period of time, you could user a system timing method as others have suggested to decide when to stop sending, but this would be slightly asynchronous to the operation of sending, which itself consumes time.

I would personally be tempted to use the amount of time required to send the characters as the source of timing. Provided that you never fail to write a new character into the "ready to send" register before the existing character is fully clocked out, you can keep the serial transmit line in constant use, and transmit (baud rate) / (character length) characters per second.

In many cases, either method will be sufficiently accurate.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.