Discretizing continuous controllers, implementing interrupt-driven control loops, designing finite state machines, and integrating all course concepts into a complete mechatronic system.
A continuous PID must be converted to a discrete-time difference equation for implementation on a microcontroller. The choice of method affects stability and frequency response of the digital implementation.
Euler Forward: s ≈ (z-1)/T_s [simple but unstable for large T_s]
Euler Backward: s ≈ (z-1)/(T_s·z) [always stable but distorts frequency]
Tustin (Bilinear): s ≈ (2/T_s)(z-1)/(z+1) [best frequency preservation]
Discrete PID (Tustin, velocity form):
u[k] = u[k-1] + K_p(e[k]-e[k-1])
+ K_i(T_s/2)(e[k]+e[k-1])
+ K_d(2/T_s)(e[k]-2e[k-1]+e[k-2]) [with filter]
The control loop must execute at a precise, fixed rate. Using delay() in Arduino is not acceptable for real control because it blocks all other code and the rate varies with computation time. Timer interrupts enforce precise execution.
// Configure Timer1 for 100 Hz interrupt (10 ms period)
void setup() {
noInterrupts();
TCCR1A = 0; TCCR1B = 0;
OCR1A = 156; // 16 MHz / 1024 / 100 Hz - 1
TCCR1B |= (1<
A finite state machine (FSM) models a system with a discrete set of states, transitions triggered by events, and actions associated with states or transitions.
Output depends only on current state. Simpler to analyze. State transition diagram shows outputs labeled on states.
Output depends on current state AND input. More compact (fewer states). Outputs labeled on transitions.
Use an enum for states, a switch-case for transitions. Update state at fixed intervals or on event. Document all transitions explicitly.
Control mode switching (idle → startup → run → fault), sequence control, communication protocols, user interface logic.
| Protocol | Topology | Speed | Wires | Best Use |
|---|---|---|---|---|
| UART | Point-to-point | Up to 1 Mbps | 2 (TX, RX) | Debugging, GPS, PC communication |
| I2C | Multi-drop bus | 100 kHz / 400 kHz / 3.4 MHz | 2 (SDA, SCL) | Short-range sensors, displays (IMUs, EEPROMs) |
| SPI | Master-slave, full-duplex | Up to 50 MHz | 4 (MOSI, MISO, SCK, CS) | High-speed DACs, ADCs, SD cards |
| CAN bus | Multi-node bus, differential | Up to 1 Mbps | 2 (CAN-H, CAN-L) | Automotive, industrial, noisy environments |
Tustin discretization of integrator 1/s → T_s(z+1)/(2(z-1)):
Integral output: I[k] = I[k-1] + (K_i · T_s/2)(e[k] + e[k-1])
= I[k-1] + (0.5 × 0.1/2)(e[k] + e[k-1]) = I[k-1] + 0.025(e[k] + e[k-1])
Total: u[k] = K_p·e[k] + I[k] = 3.0·e[k] + I[k-1] + 0.025(e[k] + e[k-1])
I[k] = I[k-1] + 0.025(e[k] + e[k-1]); u[k] = 3.0·e[k] + I[k]
IMU to microcontroller: SPI is preferred at 1 kHz because it has higher speed than I2C and full-duplex operation. Most modern IMU ICs (e.g., ICM-42688) support SPI at speeds up to 24 MHz, easily handling 1 kHz reads of 12 bytes.
Microcontroller to laptop: UART (USB-UART bridge). Standard for debug and logging. At 115200 baud this transmits ~11,000 bytes/s, which comfortably handles 12 bytes per read × 1000 Hz = 12,000 bytes/s — marginal. Use 921600 baud or pack multiple reads per transmission.
IMU to MCU: SPI (high speed, low latency). MCU to laptop: UART at high baud rate (921600) or USB CDC.