Embedded System Design and Implement (Nuvoton 140)
Professional embedded C on the Nuvoton NUC140, a 32-bit Arm Cortex-M0: clocks, registers, GPIO, timers and interrupts. Press play on any sketch to watch it happen.
1 Meet the NUC140
The NUC140 is a 32-bit Arm Cortex-M0 microcontroller, running at up to 50 MHz with 128 KB of flash and 16 KB of SRAM, and with USB and CAN built in. Unlike Arduino, nothing is hidden: you set up the clocks and peripherals yourself.
Tools: commonly Keil MDK to compile, and a Nu-Link adapter to flash and debug.
Board support package: Nuvoton's BSP gives you startup code and drivers.
Clock tree: choose a clock source, then enable the clock of each peripheral you use.
Protected registers: key system registers must be unlocked before writing them.
int main(void) {
SYS_UnlockReg();
// set up clocks and pins here
SYS_LockReg();
while (1) {
// your application
}
}
Boot sequence
1/6 Power on. The Cortex-M0 reads its first instructions from flash.
Rule of thumb: If a peripheral seems dead, check its clock first. A peripheral with no clock does nothing at all.
Hardware is controlled through registers: special addresses where writing a bit changes something physical. A driver call such as GPIO_SetMode sets those bits for you, and PC12 is a macro that reads or writes one pin.
Memory-mapped I/O: peripherals appear as addresses, so C pointers can reach them.
Mode: each pin must be set as input, output and so on before use.
Bit masks: BIT12 selects one bit, so the other pins stay untouched.
Active low: on many boards an LED lights when the pin is 0, so check your schematic.
#include "NUC100Series.h"
int main(void) {
GPIO_SetMode(PC, BIT12, GPIO_PMD_OUTPUT);
while (1) {
PC12 = 0; // LED on, if active low
CLK_SysTickDelay(200000); // 200 ms
PC12 = 1;
CLK_SysTickDelay(200000);
}
}
Register diagram
1/4 GPIO_SetMode first makes the pin an output.
Rule of thumb: Keep the reference manual open. Every register bit is explained there.
3 Timers and interrupts
Delay loops waste the processor. A hardware timer counts on its own and raises an interrupt when it reaches its target, so timing stays exact while the main loop does other work. The NVIC decides which interrupts run.
Timer clock: give the timer a clock source and enable it first.
Periodic mode: the timer fires again and again at a set rate.
NVIC: the Cortex-M interrupt controller, which enables interrupts and sets priorities.
Clear the flag: if you do not, the interrupt will fire again as soon as you return.
void TMR0_IRQHandler(void) {
TIMER_ClearIntFlag(TIMER0);
PC12 ^= 1; // toggle the LED
}
// after setting up clocks
CLK_EnableModuleClock(TMR0_MODULE);
CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0_S_HXT, 0);
TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, 2); // 2 Hz
TIMER_EnableInt(TIMER0);
NVIC_EnableIRQ(TMR0_IRQn);
TIMER_Start(TIMER0);
Interrupt flow
1/5 main() does its work while the timer counts by itself in hardware.
Rule of thumb: Every interrupt needs three things: the peripheral enabled, the NVIC enabled, and the flag cleared.