Most ME32F030 projects start with a blinking LED driven by a basic timer — and stop there. The chip’s advanced timers, TIM6 and TIM7, can do far more: four match channels, four capture channels, PWM output, and eight independent interrupt sources. This article walks through the three patterns we use most often in customer projects, then closes with the pitfalls that cost us time so they don’t cost you any.
TIM6/TIM7 vs the basic timers (TIM0–TIM3)
| Feature | TIM0–3 (basic) | TIM6/7 (advanced) |
|---|---|---|
| Resolution | 16-bit | 16-bit |
| Match channels | 1 | 4 (Match0–3) |
| Capture channels | none | 4 (Capture0–3) |
| PWM output | none | 3 channels (from Match0–2) |
| Counting modes | timer only | timer / counter / quadrature / gated / triggered |
| Interrupt sources | 1 (Match0) | 8 (4 match + 4 capture) |
One thing that confuses newcomers: TIM6 and TIM7 share the same API. Every
TIM6_xxx() function works on TIM7 too — just pass the TIM7 pointer instead.
Pattern 1: Periodic interrupt (the workhorse)
A 1-second tick used for scheduled tasks. The two-step idea: TIM6_Init() sets how fast
the counter counts; TIM6_ConfigMatch0() sets how far it counts before interrupting.
#include "CMSDK_CM0.h"
#include "sys.h"
#include "timer.h"
#include "ioconfig.h"
volatile uint32_t tick_count = 0;
int main(void)
{
SYS_SystemInitial();
/* Count at 1000 Hz: one tick per millisecond.
PR = SystemCoreClock / tickpersecond - 1 = 20000000/1000 - 1 */
TIM6_Init(TIM6, 1000);
TIM6_SetTimerType(TIM6, 0); /* 0 = plain timer mode */
/* Fire an interrupt and auto-reset the counter every 1000 ticks = 1 s */
TIM6_ConfigMatch0(TIM6, 1000,
TIM_MATCH_TRIGGER_INT | TIM_MATCH_RESET_COUNTER,
TIM_MATCH_OUT_DO_NOTHING);
NVIC_EnableIRQ(TIM6_IRQn); /* the NVIC enable is on YOU */
TIM6_START;
while (1) { __WFI(); } /* sleep until interrupt */
}
void TIM6_IRQHandler(void)
{
uint8_t int_status = TIM6_GetIntStatus(TIM6);
if (int_status & TIM_MATCH_0_INT) { /* 0x01 */
tick_count++;
/* periodic work here */
}
TIM6_ClearIntFlag(TIM6); /* MANDATORY — see pitfall 2 */
}
The timing math (default 20 MHz system clock):
count frequency = SystemCoreClock / (PR + 1)
interrupt period = match value / count frequency
TIM6_Init(TIM6, 1000) -> 1 kHz tick
TIM6_ConfigMatch0(...,1000) -> 1000 ticks / 1 kHz = 1 s per interrupt
TIM6_Init(TIM6, 1000000) -> 1 MHz tick (1 µs resolution)
TIM6_ConfigMatch0(...,1000) -> 1 ms per interrupt
Pattern 2: Input capture (measure pulse width or frequency)
Catch rising edges on PB4 and measure the time between them. At a 2 MHz count rate you get 0.5 µs resolution — plenty for IR and most protocol work.
volatile uint16_t capture_value = 0;
volatile uint8_t capture_flag = 0;
int main(void)
{
SYS_SystemInitial();
PB_4_INIT(PB_4_TIM6_CAP0); /* PB4 -> capture channel 0 */
TIM6_Init(TIM6, 2000000); /* 2 MHz: 0.5 µs per tick */
TIM6_SetTimerType(TIM6, 0);
TIM6_ConfigCapture0(TIM6, RISE_EDGE, TIM_CAPTURE_TRIGGER_INT);
NVIC_EnableIRQ(TIM6_IRQn);
TIM6_START;
while (1) {
if (capture_flag) {
capture_flag = 0;
/* period = capture_value / 2000000 seconds
freq = 2000000 / capture_value Hz */
}
}
}
void TIM6_IRQHandler(void)
{
if (TIM6_GetIntStatus(TIM6) & TIM_CAPTURE_0_INT) /* 0x10 */
capture_value = TIM6_GetCapture0Value(TIM6),
capture_flag = 1;
TIM6_ClearIntFlag(TIM6);
}
Pattern 3: Match + Capture together (IrDA-style decoding)
The SDK’s Demo-Timer - Irda example is the best reference for combining both
mechanisms: Capture measures the gap between edges, Match0 acts as a timeout
detector — if no edge arrives for ~6.5 s the counter overflows and the decoder
resynchronizes to the start bit.
TIM6_Init(TIM6, 10000); /* 100 µs per tick */
TIM6_SelectTimerClearSignal(TIM6, 0, RISE_EDGE); /* CAP0 edge clears counter */
TIM6_ConfigMatch0(TIM6, 0xFFFF, /* overflow ≈ 6.5 s of silence */
TIM_MATCH_TRIGGER_INT,
TIM_MATCH_OUT_DO_NOTHING);
TIM6_ConfigCapture0(TIM6, FALL_EDGE, TIM_CAPTURE_TRIGGER_INT);
In the ISR: a Match0 flag means “signal lost, wait for start bit”; anything else is a capture event whose value tells you whether the bit was a 0 or a 1.
API cheat sheet
| Task | API | Notes |
|---|---|---|
| Init | TIM6_Init(TIM6, freq) | freq ≤ SystemCoreClock |
| Mode | TIM6_SetTimerType(TIM6, m) | 0=timer, 1=edge count, 4=quadrature, 5=trigger, 7=gated |
| Match0–3 | TIM6_ConfigMatch0..3(TIM6, ticks, int_act, out_act) | SDK subtracts 1 internally |
| Capture0–3 | TIM6_ConfigCapture0..3(TIM6, edge, int_req) | RISE / FALL / BOTH_EDGE |
| Read capture | TIM6_GetCapture0..3Value(TIM6) | 16-bit register value |
| Interrupt status | TIM6_GetIntStatus(TIM6) | bit0–3 = Match0–3, bit4–7 = Capture0–3 |
| Clear flags | TIM6_ClearIntFlag(TIM6) | writes 0xFF — clears all eight |
| Start / stop | TIM6_START / TIM6_STOP | macros on the TCR register |
| PWM | TIM6_PWMInit(TIM6, freq) + TIM6_SetPWM0..2Duty(TIM6, d) | Match3 is the period; duty 0–100 |
Interrupt status byte:
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
CAP3 CAP2 CAP1 CAP0 MAT3 MAT2 MAT1 MAT0
0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01
Match actions (combine with |): TIM_MATCH_TRIGGER_INT (0x1),
TIM_MATCH_RESET_COUNTER (0x2), TIM_MATCH_STOP_COUNTER (0x4).
Pin output actions: do-nothing / reset low / set high / toggle.
Seven pitfalls from real projects
- Match values are +1. The SDK computes
MR = ticks - 1. To interrupt after 1000 counts, pass 1000 — not 999. - You must clear the interrupt flag.
TIM6_ClearIntFlag()writes 0xFF and clears all eight flags at once. Forget it and the ISR re-enters forever. - TIM6 and TIM7 share everything — including this article’s entire API. The IRQ
handler names differ though:
TIM6_IRQHandlervsTIM7_IRQHandler. - Handler names are fixed. They’re exported
[WEAK]from the startup file; define your own with exactly that name to override. - The SDK’s
buzz.chijacksTIM0_IRQHandler. If you use the buzzer demo and your own TIM0 interrupt, they collide. TIM6/TIM7 have no such conflict — one more reason to prefer them. - Configuring match interrupts is not enough.
TIM6_ConfigMatch0()only sets the timer-local MCR bits. You still needNVIC_EnableIRQ(TIM6_IRQn)for the Cortex-M0 core to see the interrupt. - The system clock changes your math. All the numbers above assume the default
20 MHz
SystemCoreClock. Bump the clock to 40 or 48 MHz and your tick rates scale with it.
Using the ME32F030 in a new design, or migrating onto it from another part? Ask us for a quote or free samples — and if your question is about timer configuration specifically, you now know exactly which registers to mention.
Need help applying this to your design?
Ask our FAE team — free migration review, sample support and honest advice.
Contact Our Engineers