Professor Giriraja C. V.'s Exam Pattern β’ Full Unit I & II + Lab Peripherals (50 Marks Midterm)
Based on the official September 2026 examination paper, here is the exact question structure and marking formula:
| Question Type & Marks | Topics Tested | Evaluation Criteria (How to Get 50/50) |
|---|---|---|
| Part 1: Theory & Advantages [5M] (Q1: BTL1) |
Cortex-M Processor Family Advantages, Architecture overview. | List 5 crisp points from 1 23ECE313_ES_Ch4 Text 1.pdf (Low power/WIC, Thumb-2, NVIC 12-cycle latency, OS support with MSP/PSP, CoreSight debug). |
| Part 2: Assembly & Stack Trace [5M] (Q2: BTL2) |
Instruction execution (LDR, MOVS flags, IT blocks, STMDB/LDMIA, Full Descending Stack, ICI bits). | Calculate exact register hex values, draw 4-byte memory map addresses, explain EPSR ICI bits preventing multi-cycle restart. |
| Part 3: Hardware Interfacing [5M] (Q3: BTL2) |
JTAG vs SWD, CoreSight host connection, SWJ-DP, DAP, AHB-AP. | Draw host-to-core DAP interconnect diagram from 5 23ECE313_ES_Ch15_Txt 1a.pdf. Explain non-intrusive memory access while CPU runs. |
| Part 4: Timing Waveforms [15M] (Q4, Q5, Q6: 5M each, BTL2) |
1. SysTick task switching timeline. 2. Interrupt pending & activation state machine. 3. Context switch IRQ blocking & PendSV solution. |
"With the help of a neat timing diagram" is mandatory. Must draw the exact waveforms from Ch12 and Ch_Txt1 slides with timeline states. |
| Part 5: Low Power / Core Feature [5M] (Q7: BTL2) |
Wake-Up Interrupt Controller (WIC) in Deep Sleep, Sleep-on-Exit. | Draw WIC always-on power domain block diagram and list the 7-step sequence of events restoring CPU clocks. |
| Part 6: Applied Coding & Circuit [15M] (Q8: 5M + Q9: 10M, BTL3) |
β’ Q8: PWM Duty Cycle control (MOSFET, 3V from 5V DC). β’ Q9: Analog sensor (PA0), Relay Fan (PA5), UART PuTTY display. |
Must draw both Circuit Diagram AND write complete C code with exact registers (MODER, AFR, ARR, CCR1, ADC1->SQR3, CR2, BRR). |
SHPR3 register).PENDSVSET bit in the Interrupt Control and State Register (ICSR) and exits immediately.SVC #imm8 instruction causes Exception 11. It provides an architected gate for unprivileged user tasks to switch to privileged Handler mode to request operating system kernel services.
SVC instruction executes, hardware automatically pushes 8 registers onto the current stack: R0, R1, R2, R3, R12, LR, PC (return address), and xPSR.SVC.SVC.svc_number = ((uint8_t*)stacked_pc)[-2];
| Fault Name | Exc # | Programmable Priority? | Exact Root Causes | Associated Status Registers |
|---|---|---|---|---|
| Memory Management Fault (MemManage) | 4 | Yes (via SHPR1) |
β’ MPU permission violation (unprivileged access to privileged memory). β’ Instruction fetch from Execute-Never ( XN) memory.β’ Stack access crossing MPU region boundaries. |
MMFSR (Memory Management Fault Status Register)MMFAR (Holds faulting address if MMARVALID is set) |
| Bus Fault | 5 | Yes (via SHPR1) |
β’ Bus error response on read/write transfers. β’ Precise Bus Fault: Processor knows exact faulting instruction and address. β’ Imprecise Bus Fault: Asynchronous error resulting from write buffers. β’ Stacking/unstacking memory bus errors. |
BFSR (Bus Fault Status Register)BFAR (Holds faulting address if BFARVALID is set) |
| Usage Fault | 6 | Yes (via SHPR1) |
β’ Undefined instruction execution. β’ Attempting to clear Thumb bit ( T=0 in EPSR) to switch to ARM state.β’ Unaligned memory access (if UNALIGN_TRP is set). β’ Divide by zero (if DIV_0_TRP is enabled). β’ Invalid EXC_RETURN value during exception return. |
UFSR (Usage Fault Status Register) |
| HardFault | 3 | No (Fixed at -1) | β’ Fault Escalation: Configurable fault occurs while its handler is disabled in SHCSR.β’ Fault occurring inside another fault handler with equal/higher priority. β’ Bus fault during vector table fetch. |
HFSR (Hard Fault Status Register) |
SYSRESETREQ bit in the AIRCR register. Common in deeply embedded microcontrollers where state recovery is impossible.1 to the respective bits (Write-1-to-Clear / R/W1C).
printf) through the Serial Wire Output (SWO) pin without stopping the core.| Feature | Halt Mode | Debug Monitor Mode |
|---|---|---|
| CPU Execution | Completely stopped (CPU halted). | CPU continues running; enters Exception 12 (Debug Monitor ISR). |
| SysTick Timer | Stopped while CPU is halted. | SysTick counter continues running normally. |
| Interrupt Handling | Interrupts are pended and masked during stepping. | Higher-priority interrupts can still preempt the debug monitor. |
| Typical Use Case | Initial firmware development, single-stepping bare-metal code. | Time-critical applications (motor control, power electronics) where stopping the clock causes hardware damage. |
SYST_RVR, counts down to 0, sets COUNTFLAG (bit 16 in SYST_CSR), and generates Exception 15 (SysTick).
| Register Name | Address Offset | Bit Fields & Functionality |
|---|---|---|
SYST_CSR |
0xE000E010 | Control & Status Register: β’ Bit 0 ( ENABLE): 1 = Counter running, 0 = Disabled.β’ Bit 1 ( TICKINT): 1 = Generates SysTick exception when counter reaches 0.β’ Bit 2 ( CLKSOURCE): 0 = External reference clock, 1 = Core clock.β’ Bit 16 ( COUNTFLAG): Set to 1 when counter counts from 1 to 0; cleared by reading CSR. |
SYST_RVR |
0xE000E014 | Reload Value Register: Bits [23:0] hold the 24-bit value loaded into CVR upon reaching 0. Example: Reload = 999 produces a tick every 1000 clock cycles. |
SYST_CVR |
0xE000E018 | Current Value Register: Bits [23:0] hold the live counter value. Writing any value to CVR clears it to 0 and clears COUNTFLAG. |
SYST_CALIB |
0xE000E01C | Calibration Value Register: Factory calibration for 10ms tick rate. |
SLEEPONEXIT bit in the System Control Register (SCR).
SLEEPDEEP bit set in SCR). The main oscillator, processor core clock, and NVIC clocks are completely shut down. The NVIC cannot detect interrupts!
SEV (Send Event): Instruction executed by a core to pulse its TXEV output pin to notify peer processors or peripherals.WFE (Wait For Event): Instruction that places the core into low-power idle until an event signal arrives at its RXEV input or an interrupt occurs.The Cortex-M3 provides two self-reset control features via the Application Interrupt and Reset Control Register (AIRCR) located at address 0xE000ED0C. To write to AIRCR, a security key (0x05FA) must be written to bits [31:16]:
SYSRESETREQ (Bit 2): Requests a system-level reset. Pulls the microcontroller\'s reset line, resetting the core, memories, and all external peripherals (GPIO, Timers, UART).VECTRESET (Bit 0): Resets only the Cortex-M processor core. It does not reset peripherals outside the core. Targeted strictly for debugger use.
| Classification | State / Mode / Level | Description & Rules |
|---|---|---|
| Operating States | Thumb State | Cortex-M3 runs exclusively in Thumb state (Thumb-2 instruction set mixing 16-bit and 32-bit instructions). It does not support the classic 32-bit ARM state. Bit 0 of instruction addresses must always be 1. |
| Operating States | Debug State | Entered when processor is halted by a debugger. |
| Operating Modes | Thread Mode | The default mode for user applications and background software. Can be either Privileged or Unprivileged. |
| Operating Modes | Handler Mode | Entered automatically when executing an exception handler or ISR. Always Privileged. |
| Privilege Levels | Privileged Level | Full access to all memory regions, system control space, and instructions (e.g. MSR, CPSID). |
| Privilege Levels | Unprivileged Level | Restricted access. Limited access to MPU-protected regions; cannot access NVIC or System Control Space. Cannot switch back to Privileged mode directly (must execute SVC). |
EXC_RETURN value.
1. Clearing it triggers a Usage Fault!LDM/STM) so interrupted instructions can resume where they left off; If-Then (IT) bits hold conditional execution states for the IT block.
nPRIV): Defines thread mode privilege level:
0 = Privileged thread mode.1 = Unprivileged thread mode.SPSEL): Selects active stack pointer in Thread mode:
0 = Main Stack Pointer (MSP) active.1 = Process Stack Pointer (PSP) active.CPSID i / CPSIE i.CPSID f / CPSIE f.
0x00000000 - 0x1FFFFFFF).0x20000000 - 0xDFFFFFFF).0xE0000000 - 0xE00FFFFF.
0x00000000 - 0x1FFFFFFF): Contains Vector Table, Flash ROM, and boot code. Executed via I-Code bus.0x20000000 - 0x3FFFFFFF): Internal RAM for data, stack, and heap. First 1 MB contains SRAM Bit-band region.0x40000000 - 0x5FFFFFFF): Internal on-chip peripherals (GPIO, Timers, ADC, UART). First 1 MB contains Peripheral Bit-band region. Execute-Never (XN).0x60000000 - 0x9FFFFFFF): Off-chip SRAM, SDRAM, or NOR Flash.0xA0000000 - 0xDFFFFFFF): Off-chip memory-mapped I/O devices. Non-bufferable, non-cacheable, XN.0xE0000000 - 0xFFFFFFFF): PPB memory space containing NVIC, SysTick, MPU, and CoreSight debug registers.
0x20000000 - 0x200FFFFF (1 MB) $\longrightarrow$ Maps to Alias: 0x22000000 - 0x23FFFFFF (32 MB).0x40000000 - 0x400FFFFF (1 MB) $\longrightarrow$ Maps to Alias: 0x42000000 - 0x43FFFFFF (32 MB).
STR instruction instead of LDR $\rightarrow$ ORR/AND $\rightarrow$ STR (reduces 3 instructions to 1).0x20 (decimal 32) and bit number by 0x4.
0x00000000 and loads it directly into the Main Stack Pointer (MSP). This ensures a valid stack exists before executing any code!0x00000004 and loads it directly into the Program Counter (PC). This word contains the address of the Reset_Handler function. Bit 0 of this address must be 1 to specify Thumb state.Reset_Handler in Privileged Thread Mode.
PRIGROUP field in the Application Interrupt and Reset Control Register (AIRCR) splits the priority bits into two fields:
NVIC_ISPR) and Interrupt Clear-Pending Registers (NVIC_ICPR).Every program here is written directly for the STM32F446RE using bare-metal register manipulation (CMSIS syntax). Each code section includes a step-by-step logic explanation, a complete register breakdown table, and an execution flow diagram.
1 when released, 0 when pressed (Active-LOW).
| Register Name | Full Name & Purpose | Exact Bits Used & Meaning |
|---|---|---|
RCC->AHB1ENR |
AHB1 Peripheral Clock Enable Register | β’ Bit 0 (GPIOAEN): Set to 1 to supply clock to Port A.β’ Bit 2 (GPIOCEN): Set to 1 to supply clock to Port C. Without this, GPIO registers do not respond! |
GPIOA->MODER |
GPIO Port Mode Register (Port A) | Each pin uses 2 bits. For PA5: Bits [11:10]. β’ 00 = Inputβ’ 01 = General Purpose Output (Written as 1U << (5*2))β’ 10 = Alternate Functionβ’ 11 = Analog |
GPIOC->MODER |
GPIO Port Mode Register (Port C) | For PC13: Bits [27:26]. Cleared to 00 for Input mode. |
GPIOC->IDR |
GPIO Port Input Data Register | Read-only register. Bit 13 reflects the digital voltage level at PC13 pin. (GPIOC->IDR & (1U << 13)) isolates pin 13. |
GPIOA->ODR |
GPIO Port Output Data Register | Read/Write register. Bit 5 controls PA5 voltage level. GPIOA->ODR ^= (1U << 5); flips bit 5 between 0 and 1, toggling the LED. |
#include "stm32f4xx.h"
// Software delay function for contact debouncing
void delay_ms(uint32_t ms) {
uint32_t count = (SystemCoreClock / 10000) * ms;
for (volatile uint32_t i = 0; i < count; i++) {
__NOP();
}
}
int main(void) {
// 1. Enable AHB1 bus clocks for GPIO Port A and Port C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN;
// 2. Configure PA5 (LED) as General Purpose Output (Mode '01')
GPIOA->MODER &= ~(3U << (5 * 2)); // Clear bits [11:10]
GPIOA->MODER |= (1U << (5 * 2)); // Set to 01 (Output)
// 3. Configure PC13 (Button) as Input Mode (Mode '00')
GPIOC->MODER &= ~(3U << (13 * 2)); // Clear bits [27:26] to 00
uint8_t last_button_state = 1; // Default state: released (Active-LOW)
while (1) {
// Read current state of Pin 13 from IDR
uint8_t current_state = (GPIOC->IDR & (1U << 13)) ? 1 : 0;
// Detect Falling Edge: transition from Released (1) to Pressed (0)
if (last_button_state == 1 && current_state == 0) {
GPIOA->ODR ^= (1U << 5); // Toggle LED via ODR bit 5
delay_ms(50); // 50ms software debounce
}
last_button_state = current_state;
}
}
| Register Name | Full Name & Purpose | Exact Bits Used & Meaning |
|---|---|---|
RCC->APB1ENR |
APB1 Peripheral Clock Enable Register | β’ Bit 3 (TIM5EN): Enables clock for Timer 5 on the APB1 peripheral bus. |
TIM5->PSC |
Timer 5 Prescaler Register (16-bit) | Divides the incoming bus clock. Value loaded is 15999 ($16000 - 1$). Frequency becomes $16\text{ MHz}/16000 = 1\text{ kHz}$. |
TIM5->ARR |
Timer 5 Auto-Reload Register (32-bit) | Defines the ceiling of the counter in up-counting mode. When CNT reaches ARR, the counter overflows to 0 and asserts the Update Interrupt Flag (UIF). |
TIM5->CNT |
Timer 5 Counter Register (32-bit) | Holds the current count value. Cleared to 0 prior to starting. |
TIM5->CR1 |
Timer 5 Control Register 1 | β’ Bit 0 (CEN - Counter Enable): 1 starts counting; 0 stops the counter. |
TIM5->SR |
Timer 5 Status Register | β’ Bit 0 (UIF - Update Interrupt Flag): Set to 1 by hardware upon counter overflow/reload. Cleared by writing 0 by software. |
#include "stm32f4xx.h"
void delay_seconds(uint32_t seconds) {
// 1. Enable TIM5 peripheral clock on APB1 bus (Bit 3)
RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
// 2. Configure Timer Prescaler (PSC) for 1 ms tick resolution
// Counter Clock = 16 MHz / (15999 + 1) = 1000 Hz = 1 ms per count
TIM5->PSC = 15999;
// 3. Set Auto-Reload Register (ARR) for requested seconds
TIM5->ARR = (seconds * 1000) - 1;
// 4. Reset counter register to 0
TIM5->CNT = 0;
// 5. Start the timer by setting Counter Enable (CEN) bit in CR1
TIM5->CR1 |= TIM_CR1_CEN;
// 6. Wait until Update Interrupt Flag (UIF) in Status Register is set
while (!(TIM5->SR & TIM_SR_UIF)) {
__NOP(); // Wait for counter overflow
}
// 7. Clean up: clear UIF flag and stop timer counter
TIM5->SR &= ~TIM_SR_UIF;
TIM5->CR1 &= ~TIM_CR1_CEN;
}
AF1 (Timer 2 Channel 1).
| Register Name | Full Name & Purpose | Exact Bits Used & Meaning |
|---|---|---|
GPIOA->AFR[0] |
GPIO Alternate Function Low Register | Controls alternate functions for pins PA0 to PA7 (4 bits per pin). Bits [23:20] control PA5. Value 0001 (0x1) selects AF1, connecting PA5 to TIM2_CH1. |
TIM2->CCR1 |
Capture/Compare Register 1 | Holds the compare threshold (6400). When counter CNT < CCR1, the PWM output is HIGH. When CNT >= CCR1, the output goes LOW. |
TIM2->CCMR1 |
Capture/Compare Mode Register 1 | β’ Bits [6:4] (OC1M): Output Compare 1 Mode. Set to 110 for PWM Mode 1.β’ Bit 3 (OC1PE): Output Compare 1 Preload Enable. Latches CCR1 value at update events to prevent glitching. |
TIM2->CCER |
Capture/Compare Enable Register | β’ Bit 0 (CC1E): Output Enable for Channel 1. Connects the channel waveform to the physical pin. |
TIM2->CR1 |
Control Register 1 | β’ Bit 7 (ARPE): Auto-reload preload enable. β’ Bit 0 (CEN): Counter enable (starts timer counting). |
#include "stm32f4xx.h"
void TIM2_PWM_Init(void) {
// 1. Enable AHB1 clock for GPIOA and APB1 clock for TIM2
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. Configure PA5 as Alternate Function Mode ('10')
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (2U << (5 * 2));
// 3. Connect PA5 to AF1 (TIM2_CH1) via Alternate Function Low Register AFR[0]
GPIOA->AFR[0] &= ~(0xFU << (5 * 4));
GPIOA->AFR[0] |= (1U << (5 * 4)); // 0x1 selects AF1
// 4. Configure Timer Period for 1 kHz PWM
TIM2->PSC = 0; // Counter runs at full 16 MHz
TIM2->ARR = 15999; // Period = 16 MHz / 16000 = 1 kHz
// 5. Configure Duty Cycle (40% of 16000 = 6400)
TIM2->CCR1 = 6400;
// 6. Configure Channel 1 for PWM Mode 1 (OC1M = '110') and enable preload
TIM2->CCMR1 |= (6U << 4) | TIM_CCMR1_OC1PE;
// 7. Enable Channel 1 Output
TIM2->CCER |= TIM_CCER_CC1E;
// 8. Enable Auto-Reload Preload and start counter
TIM2->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN;
}
| Register Name | Full Name & Purpose | Exact Bits Used & Meaning |
|---|---|---|
RCC->APB2ENR |
APB2 Peripheral Clock Enable Register | β’ Bit 8 (ADC1EN): Enables clock for ADC1 on high-speed APB2 bus. |
GPIOA->MODER |
GPIO Mode Register (Port A) | Bits [1:0] configure PA0. Set to 11 (Analog Mode). Disables the Schmitt trigger, saving power and eliminating digital noise on the analog line. |
ADC1->SQR1 |
ADC Regular Sequence Register 1 | β’ Bits [23:20] (L): Regular channel sequence length. 0000 defines 1 conversion. |
ADC1->SQR3 |
ADC Regular Sequence Register 3 | β’ Bits [4:0] (SQ1): 1st conversion in regular sequence. Loaded with 0 for Channel 0 (PA0). |
ADC1->CR2 |
ADC Control Register 2 | β’ Bit 0 (ADON): ADC ON / Power Enable. β’ Bit 30 (SWSTART): Start conversion of regular channels by software. |
ADC1->SR |
ADC Status Register | β’ Bit 1 (EOC - End of Conversion): Set by hardware when conversion data is ready. Cleared by reading ADC1->DR. |
ADC1->DR |
ADC Regular Data Register (16-bit) | Bits [11:0] store the converted 12-bit digital value. |
#include "stm32f4xx.h"
void ADC1_PA0_Init(void) {
// 1. Enable clocks: GPIOA on AHB1, ADC1 on APB2
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 2. Configure PA0 as Analog Mode (Bits [1:0] = '11')
GPIOA->MODER |= (3U << (0 * 2));
// 3. Configure Regular Sequence: 1 conversion, Channel 0 first
ADC1->SQR1 = 0; // Length = 1 conversion
ADC1->SQR3 = 0; // 1st conversion is Channel 0
// 4. Power up ADC1 module
ADC1->CR2 |= ADC_CR2_ADON;
}
uint32_t ADC1_Read(void) {
// 1. Start conversion by setting SWSTART bit in CR2
ADC1->CR2 |= ADC_CR2_SWSTART;
// 2. Wait until End Of Conversion (EOC) flag in Status Register is set
while (!(ADC1->SR & ADC_SR_EOC));
// 3. Read 12-bit converted value from Data Register (clears EOC flag automatically)
return ADC1->DR;
}
EXTI_IMR).EXTI_PR) recording active requests.#include "stm32f4xx.h"
void EXTI13_Init(void) {
// 1. Enable clocks for GPIOC, GPIOA (LED), and System Configuration (SYSCFG)
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// 2. Set PA5 as Output (LED) and PC13 as Input (Button)
GPIOA->MODER |= (1U << (5 * 2));
GPIOC->MODER &= ~(3U << (13 * 2));
// 3. Select Port C for EXTI13 in SYSCFG_EXTICR4 (bits [7:4] = 0010)
SYSCFG->EXTICR[3] &= ~(0xFU << 4);
SYSCFG->EXTICR[3] |= (0x2U << 4); // 0x2 maps Port C
// 4. Configure Falling-edge trigger (button press pulls signal Low)
EXTI->FTSR |= (1U << 13);
EXTI->RTSR &= ~(1U << 13); // Disable rising edge
// 5. Unmask interrupt line 13 in IMR
EXTI->IMR |= (1U << 13);
// 6. Enable interrupt line in NVIC
NVIC_SetPriority(EXTI15_10_IRQn, 2);
NVIC_EnableIRQ(EXTI15_10_IRQn);
}
// Interrupt Service Routine for lines 10 to 15
void EXTI15_10_IRQHandler(void) {
// Check if line 13 triggered the interrupt
if (EXTI->PR & (1U << 13)) {
GPIOA->ODR ^= (1U << 5); // Toggle Green LED
EXTI->PR = (1U << 13); // Clear pending bit (WRITE 1 TO CLEAR!)
}
}
Click on any question below to expand the complete 50/50 model answer, exact circuit schematics, and slide diagram citations:
1 23ECE313_ES_Ch4 Text 1.pdf (Slide 16)LDR R0, =0x40000000
LDR SP, =0x800000F0
LDR R1, =0x12349876
LDR R2, =0xABCDEF12
MOVS R3, 0x00
IT EQ
STMDB SP!, {R0-R2}
STOP B STOP
LDR R0, =0x40000000 -> R0 = 0x40000000.LDR SP, =0x800000F0 -> SP = 0x800000F0.LDR R1, =0x12349876 -> R1 = 0x12349876.LDR R2, =0xABCDEF12 -> R2 = 0xABCDEF12.MOVS R3, 0x00 -> Moves 0 into R3. Because the instruction has the 'S' suffix, it updates the condition flags in APSR. Since result is 0, the Z (Zero) flag is set to 1.IT EQ -> If-Then block with condition EQ (Equal / Z == 1). Since Z == 1, the condition evaluates to TRUE. The following instruction executes!STMDB SP!, {R0-R2} -> Store Multiple Decrement Before with write-back (!).
0x0C).| Register | Final Value | Explanation |
|---|---|---|
| R0 | 0x40000000 | Unchanged by STMDB |
| R1 | 0x12349876 | Unchanged by STMDB |
| R2 | 0xABCDEF12 | Unchanged by STMDB |
| SP | 0x800000E4 | Decremented by 12 bytes (3 words) |
| Address | Stored 32-bit Value | Stored Register |
|---|---|---|
0x800000F0 | Unmodified Data | Original SP Base |
0x800000EC | 0xABCDEF12 | R2 (Highest register at highest address) |
0x800000E8 | 0x12349876 | R1 |
0x800000E4 | 0x40000000 | R0 (Lowest register at lowest address) ← New SP |
0x800000E0 | Unmodified Data | Below current SP |
(Directly from Slide Deck 1: Ch4 Text 1, Slide 42)
STMDB is a multi-cycle store instruction. If an interrupt occurs halfway through executing STMDB (e.g. after pushing R2 and R1, but before R0), the processor saves the transfer progress into the ICI bits inside the Execution Program Status Register (EPSR). Upon return from the interrupt via EXC_RETURN, the processor inspects the saved ICI bits and resumes execution directly from the remaining transfer (storing R0) instead of restarting the entire instruction from the beginning. This reduces interrupt latency and prevents duplicate bus transactions.
SWCLK (clock) and SWDIO (bidirectional data), with an optional SWO (Serial Wire Output) pin for trace data.
PSP).COUNTFLAG, and asserts Exception 15 (SysTick).MSP).BX LR with EXC_RETURN = 0xFFFFFFFD triggers hardware unstacking of R0-R3, R12, LR, PC, xPSR from Task B's stack. The CPU resumes execution of Task B in Thread mode.
NVIC_ISPR). The interrupt is now Pending.NVIC_ISER.BASEPRI / PRIMASK registers.
PENDSVSET bit and exits immediately, allowing the external IRQ to run with zero delay. PendSV executes only after all pending IRQs complete!
SLEEPDEEP in SCR and executes WFI.#include "stm32f4xx.h"
int main(void) {
// 1. Enable AHB1 clock for GPIOA and APB1 clock for TIM2
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. Configure PA5 as Alternate Function Mode ('10')
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (2U << (5 * 2));
// 3. Connect PA5 to AF1 (TIM2_CH1) via AFR[0] (Bits [23:20] = 0001)
GPIOA->AFR[0] &= ~(0xFU << (5 * 4));
GPIOA->AFR[0] |= (1U << (5 * 4)); // 0x1 selects AF1
// 4. Set Timebase: 1 kHz PWM frequency at 16 MHz clock
TIM2->PSC = 0; // Clock = 16 MHz
TIM2->ARR = 15999; // Period = 16 MHz / 16000 = 1 ms (1 kHz)
// 5. Set 60% Duty Cycle for 3V average output from 5V supply: CCR1 = 9600
TIM2->CCR1 = 9600;
// 6. Configure Channel 1 for PWM Mode 1 (OC1M = 110) with Preload Enable
TIM2->CCMR1 |= (6U << 4) | TIM_CCMR1_OC1PE;
// 7. Enable Output on Channel 1
TIM2->CCER |= TIM_CCER_CC1E;
// 8. Enable Auto-Reload Preload and start counter
TIM2->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN;
while (1) {
// Continuous 60% PWM waveform generated autonomously by hardware
}
}
#include "stm32f4xx.h"
#include
void USART2_Init(void);
void USART2_SendChar(char c);
void USART2_SendString(char *str);
void ADC1_PA0_Init(void);
uint32_t ADC1_Read(void);
void delay_ms(uint32_t ms) {
uint32_t count = (SystemCoreClock / 10000) * ms;
for (volatile uint32_t i = 0; i < count; i++) { __NOP(); }
}
int main(void) {
char buffer[80];
uint32_t adc_val = 0;
float voltage = 0.0f;
// 1. Initialize Peripherals
USART2_Init();
ADC1_PA0_Init();
// 2. Configure PA5 as General Output for Relay Control
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (1U << (5 * 2)); // Output mode '01'
USART2_SendString("--- Sensor & Relay Monitoring System Online ---
");
while (1) {
// Read 12-bit ADC value
adc_val = ADC1_Read();
// Convert code to voltage: V = (ADC / 4095) * 3.3
voltage = ((float)adc_val / 4095.0f) * 3.3f;
// Relay Control Logic: Threshold 2.2V corresponds to code 2730
if (adc_val >= 2730) {
GPIOA->ODR |= (1U << 5); // Relay ON (Fan runs)
} else {
GPIOA->ODR &= ~(1U << 5); // Relay OFF (Fan stopped)
}
// Transmit readings over UART to PuTTY Serial Monitor
snprintf(buffer, sizeof(buffer), "ADC: %lu | Volt: %.2f V | Fan: %s
",
adc_val, voltage, (adc_val >= 2730) ? "ON" : "OFF");
USART2_SendString(buffer);
delay_ms(500); // 500 ms sampling period
}
}
// USART2 Configuration: PA2 as TX (AF7), 9600 Baud at 16 MHz
void USART2_Init(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
GPIOA->MODER &= ~(3U << (2 * 2));
GPIOA->MODER |= (2U << (2 * 2)); // Alternate Function '10'
GPIOA->AFR[0] &= ~(0xFU << (2 * 4));
GPIOA->AFR[0] |= (7U << (2 * 4)); // AF7 for USART2
USART2->BRR = 0x0683; // 9600 Baud at 16 MHz
USART2->CR1 = USART_CR1_TE | USART_CR1_UE; // Transmitter Enable & Module Enable
}
void USART2_SendChar(char c) {
while (!(USART2->SR & USART_SR_TXE)); // Wait for TX data register empty
USART2->DR = c;
}
void USART2_SendString(char *str) {
while (*str) { USART2_SendChar(*str++); }
}
// ADC1 Configuration on PA0 (Channel 0)
void ADC1_PA0_Init(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
GPIOA->MODER |= (3U << (0 * 2)); // PA0 Analog Mode '11'
ADC1->SQR1 = 0; // 1 conversion
ADC1->SQR3 = 0; // Channel 0 first
// Sample time: 84 cycles for Channel 0 (SMPR2 bits [2:0] = 100)
ADC1->SMPR2 |= (4U << 0);
ADC1->CR2 |= ADC_CR2_ADON; // Power ON ADC
}
uint32_t ADC1_Read(void) {
ADC1->CR2 |= ADC_CR2_SWSTART; // Trigger conversion
while (!(ADC1->SR & ADC_SR_EOC)); // Wait for End Of Conversion
return ADC1->DR; // Read 12-bit value (clears EOC)
}
Mirroring the exact difficulty, BTL taxonomy, and question pattern of Professor Giriraja C. V. Click to reveal complete solutions and slide citations:
2 23ECE313_ES_Ch_Txt1.pdf (Slide 11)LDR R0, =0x20000000
LDR SP, =0x20000200
LDR R1, =0x11223344
LDR R2, =0x55667788
SUBS R3, R1, R2
IT MI
STMDB SP!, {R0, R2}
STOP B STOP
LDR R0, =0x20000000 -> R0 = 0x20000000.LDR SP, =0x20000200 -> SP = 0x20000200.LDR R1, =0x11223344 -> R1 = 0x11223344.LDR R2, =0x55667788 -> R2 = 0x55667788.SUBS R3, R1, R2 -> Subtracts R2 from R1. Since R1 < R2, the result is negative. The N (Negative) flag in APSR is set to 1.IT MI -> If-Then block with condition MI (Minus / Negative: N == 1). Since N = 1, condition evaluates to TRUE.STMDB SP!, {R0, R2} -> Pushes 2 registers (8 bytes = 0x08).
0x200001FC, R0 is placed at 0x200001F8.| Register / Address | Value |
|---|---|
| R0 | 0x20000000 |
| R1 | 0x11223344 |
| R2 | 0x55667788 |
| SP | 0x200001F8 |
0x200001FC | 0x55667788 (R2) |
0x200001F8 | 0x20000000 (R0) |
| Feature | Halt Mode | Debug Monitor Mode |
|---|---|---|
| CPU State | Completely stopped (halted) | Executes Exception 12 handler |
| SysTick Timer | Stopped | Continues running |
| Interrupts | Masked/pended | Higher-priority interrupts can still preempt |
| Application | Bare-metal firmware debugging | Safety-critical motor control / RTOS |
0x00000000 and loads it directly into the Main Stack Pointer (MSP). This ensures a valid stack exists before executing any code!0x00000004 and loads it directly into the Program Counter (PC). This word contains the address of the Reset_Handler function. Bit 0 of this address must be 1 to specify Thumb state.Reset_Handler in Privileged Thread Mode.
PRIGROUP field in the Application Interrupt and Reset Control Register (AIRCR) splits the priority bits into two fields:
SHCSR.
Configured by setting SLEEPONEXIT in System Control Register (SCR). After ISR completes, the processor skips Thread Mode and returns directly to sleep, eliminating stack restore overhead in interrupt-only systems.
SEV instruction pulses TXEV pin to wake other cores. WFE (Wait For Event) sleeps until RXEV is asserted.
#include "stm32f4xx.h"
void delay_seconds(uint32_t seconds) {
RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; // Enable TIM5 clock
TIM5->PSC = 15999; // 1 ms tick (16 MHz / 16000)
TIM5->ARR = (seconds * 1000) - 1; // Reload value
TIM5->CNT = 0; // Reset count
TIM5->CR1 |= TIM_CR1_CEN; // Start timer
while (!(TIM5->SR & TIM_SR_UIF)); // Wait for overflow UIF flag
TIM5->SR &= ~TIM_SR_UIF; // Clear flag
TIM5->CR1 &= ~TIM_CR1_CEN; // Stop timer
}
int main(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (1U << (5 * 2)); // Output mode '01'
while (1) {
GPIOA->ODR ^= (1U << 5); // Toggle LED
delay_seconds(2); // 2-second delay
}
}
#include "stm32f4xx.h"
void EXTI13_Init(void) {
// 1. Enable Clocks for GPIOA (LED), GPIOC (Button), and SYSCFG
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
// 2. PA5 as Output (LED), PC13 as Input (Button)
GPIOA->MODER |= (1U << (5 * 2));
GPIOC->MODER &= ~(3U << (13 * 2));
// 3. Connect EXTI13 to Port C in SYSCFG_EXTICR4 (bits [7:4] = 0010)
SYSCFG->EXTICR[3] &= ~(0xFU << 4);
SYSCFG->EXTICR[3] |= (0x2U << 4); // Port C
// 4. Configure Falling-edge Trigger in FTSR (Button press pulls to 0)
EXTI->FTSR |= (1U << 13);
EXTI->RTSR &= ~(1U << 13);
// 5. Unmask interrupt line 13 in IMR
EXTI->IMR |= (1U << 13);
// 6. Enable in NVIC
NVIC_SetPriority(EXTI15_10_IRQn, 2);
NVIC_EnableIRQ(EXTI15_10_IRQn);
}
void EXTI15_10_IRQHandler(void) {
if (EXTI->PR & (1U << 13)) {
GPIOA->ODR ^= (1U << 5); // Toggle LED
EXTI->PR = (1U << 13); // Clear pending bit (WRITE 1 TO CLEAR!)
}
}
int main(void) {
EXTI13_Init();
while (1) {
__WFI(); // Sleep until button press interrupt
}
}
Comprehensive coverage of Unit 1, Unit 2, and Peripheral Interfacing. Click any question to reveal complete step-by-step solutions, slide citations, register breakdowns, and schematics:
1 23ECE313_ES_Ch4 Text 1.pdf (Slides 19, 23, 27)
EXC_RETURN.0xE0000000 - 0xE00FFFFF), and restricts MPU regions marked privileged-only. Prevents user bugs from crashing the operating system. Cannot directly switch back to privileged mode; must execute an SVC (Supervisory Call).
0 = Privileged thread execution.1 = Unprivileged thread execution.0 = Use Main Stack Pointer (MSP) in Thread mode.1 = Use Process Stack Pointer (PSP) in Thread mode.2 23ECE313_ES_Ch_Txt1.pdf (Slide 12)
Cortex-M3 provides two 1 MB bit-band regions mapped to 32 MB alias regions:
0x20000000 - 0x200FFFFF βββΊ Alias: 0x22000000 - 0x23FFFFFF0x40000000 - 0x400FFFFF βββΊ Alias: 0x42000000 - 0x43FFFFFFIn the alias region, every single bit in the bit-band region is mapped to an entire 32-bit (4-byte) word. Therefore:
8 Γ 4 = 32 bytes in the alias region.n Γ 4 bytes.Bit_Word_Addr = Alias_Base + (Byte_Offset Γ 32) + (Bit_Number Γ 4)Byte_Offset = Target_Address - Bit_Band_Base
0x40020014 (Peripheral Region)50x400000000x420000000x40020014 - 0x40000000 = 0x00020014 (in decimal: 131,092 bytes)0x00020014 Γ 0x20 = 0x00400280 (decimal: 131,092 Γ 32 = 4,194,944 = 0x400280)5 Γ 4 = 20 = 0x000000140x42000000 + 0x00400280 + 0x00000014 = 0x424002940x42400294. Writing 1 to this address sets PA5; writing 0 clears PA5.
ODR |= (1<<5)) corrupt adjacent bits. Bit-banding prevents this completely!LDR, ORR, STR) with a single atomic STR instruction.3 23ECE313_ES_Ch12_Txt1.pdf (Slides 10β14, 23)| Feature | Precise Bus Fault | Imprecise Bus Fault |
|---|---|---|
| Cause | Synchronous bus error (Instruction fetch, data read, or non-buffered write). | Asynchronous bus error caused by the Write Buffer during buffered writes. |
| Stacked PC Value | Exact: Stacked PC points directly to the instruction that triggered the bus error. | Inaccurate: The processor moved on while the write buffer was writing. Stacked PC points to an arbitrary later instruction! |
| Fault Address Register (BFAR) | Valid: BFSR->BFARVALID = 1. BFAR register contains the exact faulting memory address. |
Invalid: BFSR->BFARVALID = 0. The memory address that triggered the fault is lost. |
| Status Flag in BFSR | PRECISERR (Bit 1) = 1 |
IMPRECISERR (Bit 2) = 1 |
To maximize execution throughput, Cortex-M incorporates an internal Write Buffer on the system bus. When a program writes to memory, the write data is queued in the buffer, and the CPU immediately proceeds to execute subsequent instructions without waiting for bus acknowledgement. If the bus later rejects the transaction (e.g. invalid peripheral address, slave timeout), the error arrives cycles later. By then, the CPU is already executing unrelated code!
Because imprecise bus faults make debugging nearly impossible (unknown PC and unknown address), ARM provides a hardware override:
// Set DISDEFWBUF bit (Bit 1) in ACTLR (Address: 0xE000E008)
SCB->ACTLR |= SCB_ACTLR_DISDEFWBUF_Msk; // Disables Write BufferEffect: Disabling the write buffer forces all write operations to complete synchronously before the next instruction executes. When the faulty write occurs, it immediately triggers a Precise Bus Fault, allowing the developer to inspect the exact stacked PC and read the faulty address from BFAR!
1 23ECE313_ES_Ch4 Text 1.pdf (Slide 17)
Clock Cycle: | T1 | T2 | T3 | T4 | T5 |
Instr 1 (ADD): | Fe | De | Ex | | |
Instr 2 (SUB): | | Fe | De | Ex | |
Instr 3 (MOV): | | | Fe | De | Ex |
When a branch instruction (e.g., B label, BL, BX) reaches the Execute stage and the branch is taken, the instructions already fetched and decoded in stages 1 and 2 are invalid and must be flushed (discarded).
Clock Cycle: | T1 | T2 | T3 | T4 | T5 |
Instr 1 (B target):| Fe | De | Ex | | | <-- Branch target computed in Ex
Instr 2 (Next): | | Fe | De | FLUSH | | <-- Discarded!
Instr 3 (Next+1): | | | Fe | FLUSH | | <-- Discarded!
Target Instr 1: | | | | Fe | De | Ex <-- 2 cycle branch penalty!
3 23ECE313_ES_Ch12_Txt1.pdf (Slides 28β34)
The SVC (Supervisory Call) instruction generates a synchronous software exception used by unprivileged user application tasks to request privileged operating system services (file system, hardware drivers, memory allocation).
SVC #number (e.g. SVC #3).[SP+0] = R0, [SP+4] = R1, [SP+8] = R2, [SP+12] = R3, [SP+16] = R12, [SP+20] = LR, [SP+24] = Return PC, [SP+28] = xPSR.EXC_RETURN into LR.SVC_Handler using the vector fetched from vector table entry 11 (offset 0x0000002C).__asm void SVC_Handler(void) {
TST LR, #4 // Test Bit 2 of EXC_RETURN (0 = MSP, 1 = PSP)
ITE EQ
MRSEQ R0, MSP // Stack pointer was MSP, pass in R0
MRSNE R0, PSP // Stack pointer was PSP, pass in R0
B SVC_Handler_C // Branch to C handler with stack frame pointer in R0
}
void SVC_Handler_C(uint32_t *svc_args) {
// svc_args[6] is the stacked Return PC!
// Since Thumb-2 SVC is a 2-byte opcode [0xDF, SVC_#],
// subtract 2 bytes from Return PC to point directly to the SVC instruction!
uint8_t svc_number = ((uint8_t *)svc_args[6])[-2];
switch (svc_number) {
case 0: // Read Service
svc_args[0] = OS_Read(svc_args[1], svc_args[2]); // Return in R0
break;
case 1: // Write Service
svc_args[0] = OS_Write(svc_args[1], svc_args[2]);
break;
default:
break;
}
}
svc_args[0] (stacked R0), which hardware automatically restores into register R0 upon unstacking!
1 23ECE313_ES_Ch4 Text 1.pdf (Slides 31β33)| Register | Width | Effective Priority Level | Exceptions Blocked | Assembly Instructions |
|---|---|---|---|---|
| PRIMASK | 1 bit | 0 (Highest configurable priority) | Blocks all interrupts with configurable priority. NMI and HardFault still execute! | CPSID i (Disable IRQs)CPSIE i (Enable IRQs) |
| FAULTMASK | 1 bit | -1 (HardFault priority level) | Blocks all interrupts and all configurable faults (MemManage, BusFault, UsageFault) plus HardFault. Only NMI can execute! Cleared automatically on exception exit. | CPSID f (Disable All)CPSIE f (Enable All) |
| BASEPRI | Up to 8 bits | Configurable threshold N | Blocks only interrupts with priority level β₯ N (numerically equal or lower priority). Leaves higher priority interrupts (< N) active. When set to 0, masking is disabled. | MSR BASEPRI, R0MRS R0, BASEPRI |
In hard real-time systems, critical tasks (e.g. emergency motor stop, radar timing) must never experience interrupt latency. By setting BASEPRI to a mid-level threshold (e.g. priority 3), the OS scheduler can protect its internal queues without blocking ultra-high-priority hard real-time interrupts (priorities 0, 1, and 2)!
2 23ECE313_ES_Ch_Txt1.pdf (Slides 25β28)Occurs when an interrupt arrives while the processor is already servicing another interrupt, or during the completion phase of an ISR.
Traditional: [ ISR 1 ] βββΊ [ Unstack (16c) ] βββΊ [ Restack (16c) ] βββΊ [ ISR 2 ] (32+ cycles)
Cortex-M3: [ ISR 1 ] βββΊ [ Tail-Chain (6 cycles) ] ββββββββββββββββΊ [ ISR 2 ] (6 cycles!)
Occurs when a higher-priority interrupt arrives while the processor is in the middle of stacking registers for an earlier, lower-priority interrupt.
STM32F446RE_Timers_PWM_ADC_Mazidi.pdf (Slides 4β9)f_PWM = 10 kHz16 MHzPSC = 0 (Counter frequency = 16 MHz / 1 = 16 MHz)ARR = (16,000,000 / 10,000) - 1 = 1600 - 1 = 1599CCR1 = 0.75 Γ (ARR + 1) = 0.75 Γ 1600 = 1200#include "stm32f4xx.h"
void PWM_TIM2_PA5_Init(void) {
// 1. Enable Clock for GPIOA (AHB1) and TIM2 (APB1)
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 2. Configure PA5 as Alternate Function mode (MODER bits [11:10] = '10')
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (2U << (5 * 2));
// 3. Set Alternate Function to AF1 (TIM2_CH1) in AFR[0] (Bits [23:20] = 0x01)
GPIOA->AFR[0] &= ~(0xFU << (5 * 4));
GPIOA->AFR[0] |= (1U << (5 * 4));
// 4. Configure TIM2 Time Base: 10 kHz
TIM2->PSC = 0; // Prescaler = 0 (16 MHz timer clock)
TIM2->ARR = 1599; // Auto-reload value (Period = 1600 cycles = 100 us)
// 5. Configure 75% Duty Cycle
TIM2->CCR1 = 1200; // Compare match value (75% of 1600)
// 6. Set PWM Mode 1 (OC1M = '110') and enable Preload (OC1PE = 1) in CCMR1
TIM2->CCMR1 &= ~(7U << 4);
TIM2->CCMR1 |= (6U << 4); // PWM Mode 1: High until counter > CCR1
TIM2->CCMR1 |= TIM_CCMR1_OC1PE;
// 7. Enable Channel 1 Output in CCER (CC1E = 1, Active High polarity)
TIM2->CCER |= TIM_CCER_CC1E;
// 8. Enable Main Counter in CR1 (CEN = 1)
TIM2->CR1 |= TIM_CR1_CEN;
}
int main(void) {
PWM_TIM2_PA5_Init();
while (1) {
// Hardware timer generates 10 kHz 75% PWM automatically!
}
}
STM32F446RE_Timers_PWM_ADC_Mazidi.pdf (Slides 11β17)3.3V / 4095 β 0.8058 mV/count.V_th = 2.0V.Threshold = (2.0V / 3.3V) Γ 4095 = 2481.8 β 2482.16 MHz / (16 Γ 115200) = 8.6805.
0x08, Fraction = 0.6805 Γ 16 β 11 = 0x0B.
USART2->BRR = 0x008B.#include "stm32f4xx.h"
void System_Init(void) {
// 1. Enable Peripheral Clocks
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // GPIOA clock
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC1 clock (APB2)
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // USART2 clock (APB1)
// 2. Configure PA0 as Analog Mode for ADC1_IN0 (MODER[1:0] = '11')
GPIOA->MODER |= (3U << (0 * 2));
// 3. Configure PA5 as General Purpose Output for Fan Control (MODER[11:10] = '01')
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (1U << (5 * 2));
// 4. Configure PA2 as Alternate Function AF7 for USART2_TX (MODER[5:4] = '10')
GPIOA->MODER &= ~(3U << (2 * 2));
GPIOA->MODER |= (2U << (2 * 2));
GPIOA->AFR[0] &= ~(0xFU << (2 * 4));
GPIOA->AFR[0] |= (7U << (2 * 4)); // AF7 = USART2
// 5. Configure ADC1
ADC1->CR1 = 0; // 12-bit resolution
ADC1->SMPR2 |= (4U << (0 * 3)); // 84 cycles sample time for Channel 0
ADC1->SQR1 = 0; // 1 conversion in regular sequence
ADC1->SQR3 = 0; // 1st conversion is Channel 0 (PA0)
ADC1->CR2 |= ADC_CR2_ADON; // Enable ADC1 power
// 6. Configure USART2 (115200 Baud @ 16 MHz, 8-N-1)
USART2->BRR = 0x008B; // 115200 baud
USART2->CR1 |= USART_CR1_TE | USART_CR1_UE; // Transmitter enable & USART enable
}
uint16_t ADC1_Read(void) {
ADC1->CR2 |= ADC_CR2_SWSTART; // Start software conversion
while (!(ADC1->SR & ADC_SR_EOC)); // Wait until End Of Conversion flag is set
return ADC1->DR; // Read 12-bit data (clears EOC flag)
}
void UART2_SendString(char *str) {
while (*str) {
while (!(USART2->SR & USART_SR_TXE)); // Wait for Transmit Data Register Empty
USART2->DR = (*str++ & 0xFF); // Transmit byte
}
}
int main(void) {
System_Init();
while (1) {
uint16_t adc_val = ADC1_Read();
// Check if analog voltage > 2.0V (ADC Count > 2482)
if (adc_val > 2482) {
GPIOA->ODR |= (1U << 5); // Turn ON Fan
UART2_SendString("ALERT: HIGH TEMP! FAN ACTIVATED
");
} else {
GPIOA->ODR &= ~(1U << 5); // Turn OFF Fan
}
// Small software delay between readings
for (volatile int i = 0; i < 500000; i++);
}
}
Final master preparation set covering deep architectural traps, vector alignments, MPU sub-regions, and multi-peripheral integration:
2 23ECE313_ES_Ch_Txt1.pdf (Slides 14β18)
The MPU divides the 4 GB memory map into up to 8 programmable regions (numbered 0 to 7). A background region with default privileged access permissions can be enabled via PRIVDEFENA.
MPU->CTRL: Enables MPU (ENABLE bit), enables default memory map in privileged mode (PRIVDEFENA), and enables MPU during HardFault and NMI (HFNMIENA).MPU->RNR: Region Number Register (0 to 7) to select active region.MPU->RBAR: Region Base Address Register (must be aligned to region size).MPU->RASR: Region Attribute and Size Register:
SIZE bits [5:1]: Region size = 2^(SIZE+1) bytes (minimum 32 bytes).AP bits [26:24]: Access Permissions (Privileged RW / User None, Privileged RO / User RO, etc.).XN bit [28]: Execute Never (prevents code execution from data/stack).TEX, C, B bits: Cache and write buffer memory attributes.SRD bits [15:8]: Sub-Region Disable field.SRD field contains 8 bits (one bit for each sub-region).1 to bit k disables sub-region k, allowing that slice to fall through to lower priority region rules or the background region.When an instruction fetch or data access violates MPU permissions (or attempts to execute in an XN region), the processor aborts the transaction and triggers a MemManage Fault. The fault address is stored in MMFAR, and status flags are set in MFSR.
1 23ECE313_ES_Ch4 Text 1.pdf (Slides 25, 48)
ARM Cortex-M processors exclusively execute the Thumb-2 instruction set. They do NOT possess a 32-bit ARM instruction decoder state. Therefore, the T-bit (Bit 24) in the EPSR MUST ALWAYS BE 1 during instruction execution.
BX / BLX):In the ARM architecture, when executing an indirect branch via a register (e.g. BX R0, BLX R3, or loading PC via LDR PC, [R1]), Bit 0 of the target address is used by hardware to update the T-bit:
1 βββΊ Sets EPSR T-bit to 1 (Thumb State). Target instruction executed at Address & ~1.0 βββΊ Attempts to clear EPSR T-bit to 0 (ARM State).// Example: Function resides at Flash address 0x08001000
void (*my_func)(void) = (void (*)(void))0x08001000; // EVEN ADDRESS (Bit 0 = 0)
my_func(); // Executes BX R0 with R0 = 0x08001000
Hardware attempts to switch the CPU into ARM state by clearing the T-bit to 0. Since Cortex-M does not support ARM state, the core detects an illegal state transition and immediately triggers a UsageFault with INVSTATE (Invalid State) bit set in UFSR!
0x08001001 for a function physically located at 0x08001000).
2 23ECE313_ES_Ch_Txt1.pdf (Slides 20, 34β37)
Every Cortex-M vector table begins with 16 system exception vectors (Vectors 0 to 15), followed by N external interrupt vectors (IRQ0 to IRQ N-1). Each vector entry is a 32-bit (4-byte) pointer.
Total Vector Table Size = (16 System Exceptions + N External IRQs) Γ 4 bytes
The base address programmed into VTOR (Address: 0xE000ED08) must be aligned to a power-of-2 boundary that is greater than or equal to the total table size, with an absolute minimum hardware alignment of 128 bytes (32 words).
16 + 32 = 48 words.48 Γ 4 = 192 bytes.0x00 (Bits [7:0] = 0).16 + 75 = 91 words.91 Γ 4 = 364 bytes.0x200 (Bits [8:0] = 0).4 23ECE313_ES_Ch14_Txt 1a.pdf (Slides 5β12); 5 23ECE313_ES_Ch15_Txt 1a.pdf (Slides 4β10)
| Trace Unit | Full Name | Primary Function & Exam Keywords |
|---|---|---|
| DWT | Data Watchpoint and Trace | Contains 4 hardware watchpoint comparators. Monitors data memory accesses (triggering trace on read/write to a specific variable). Measures cycle counts (CYCCNT) for profiling. |
| ITM | Instrumentation Trace Macrocell | Enables high-speed software printf debugging without UART hardware. Application writes directly to ITM stimulus registers (e.g. ITM->PORT[0]), which outputs data over the 1-pin SWO (Serial Wire Output) line. |
| ETM | Embedded Trace Macrocell | Reconstructs the exact instruction execution history in real time. Captures every branch taken/not-taken and exception entry to provide full instruction trace back in time. |
| TPIU | Trace Port Interface Unit | Acts as a hardware multiplexer and serializer. Formats and combines data streams from ITM and ETM, outputting them to external debug hardware (e.g. Keil ULINKpro, Segger J-Trace) via SWO or multi-pin Trace Port. |
1 23ECE313_ES_Ch4 Text 1.pdf (Slides 14β15)
0x00000000 - 0x1FFFFFFF), primarily Flash/ROM.0x00000000 - 0x1FFFFFFF).0x20000000), Peripherals (0x40000000), and External Memories (0x60000000). Can also fetch instructions from SRAM.Cortex-M uses a Harvard architecture enabled by a multi-layer AHB matrix. Because instruction fetches (on I-Code) and data accesses (on System Bus or D-Code) travel over independent physical buses simultaneously, the processor can fetch an instruction and perform a data load/store in the exact same clock cycle without bus contention!
Connects the high-speed AHB system bus to lower-power peripherals (USART, SPI, I2C, Timers, ADC). The APB bridge translates AHB burst transfers into simplified APB read/write cycles and operates at lower clock frequencies to save power.
1 23ECE313_ES_Ch4 Text 1.pdf (Slide 75); 3 23ECE313_ES_Ch12_Txt1.pdf (Slide 44)NRST), independent watchdog timer, or software command (SYSRESETREQ). Resets processor core and all peripherals, but preserves debug logic so active debug sessions are not dropped.VECTRESET.The Application Interrupt and Reset Control Register allows software to trigger a clean system reboot:
To prevent accidental system resets caused by errant software or stack corruption, writes to AIRCR MUST write the secret unlock key 0x05FA into bits [31:16] (VECTKEY field). If any other value is written, the entire write is rejected by hardware!
void System_SelfReset(void) {
__DSB(); // Ensure all outstanding memory transactions finish
SCB->AIRCR = (0x05FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk;
__DSB(); // Wait for reset to assert
while (1); // Trap until hardware reset executes
}
3 23ECE313_ES_Ch12_Txt1.pdf (Slides 38β42)| Mode | Core Clock | Peripherals & Clocks | Voltage Regulator | Wakeup Sources & Latency |
|---|---|---|---|---|
| Sleep Mode | Gated (Off) | Running (HSI, HSE, PLL, Timers active) | Main Regulator ON | Any interrupt. Fast wakeup (< 10 cycles). |
| Deep Sleep (Stop) | Gated (Off) | High-speed clocks stopped. SRAM and register contents retained. | Low-power regulator mode | EXTI, RTC alarm, WIC. Medium wakeup (~few microseconds). |
| Standby Mode | Off | Entire core domain powered down. SRAM lost! Only Backup Domain active. | Regulator Powered Down | Wakeup pin (WKUP), RTC alarm, NRST. Cold boot reset sequence. |
0 = Enter standard Sleep mode on WFI; 1 = Enter Deep Sleep / Stop mode on WFI.WFE.In Deep Sleep mode, the main core power domain and NVIC clocks are shut down to achieve microamp currents. The WIC is a small, ultra-low-power shadow controller that stays powered. When an external line asserts an interrupt, the WIC signals the Power Management Unit to wake the main voltage regulator, restore clocks, and hand off the pending interrupt directly to the NVIC!
3 23ECE313_ES_Ch12_Txt1.pdf (Slides 35β37); lab/systick.c
f_CPU = 16 MHz = 16,000,000 HzT = 1 ms = 0.001 sN = 16,000,000 Γ 0.001 = 16,000 cyclesLOAD down to 0 (a total of LOAD + 1 states):SysTick->LOAD = 16,000 - 1 = 15,999 (0x3E7F)#include "stm32f4xx.h"
void SysTick_Delay_ms(uint32_t ms) {
// 1. Disable SysTick during configuration
SysTick->CTRL = 0;
// 2. Load count value for 1 ms (16,000 cycles at 16 MHz)
SysTick->LOAD = 15999;
// 3. Clear current value register and clear COUNTFLAG
SysTick->VAL = 0;
// 4. Enable SysTick with Processor Clock (Bit 2: CLKSOURCE = 1, Bit 0: ENABLE = 1)
// No interrupt enable (TICKINT = 0)
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
// 5. Loop for the requested number of milliseconds
for (uint32_t i = 0; i < ms; i++) {
// Wait until COUNTFLAG (Bit 16) is set to 1 by hardware
while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
// 6. Disable SysTick after delay finishes
SysTick->CTRL = 0;
}
int main(void) {
// Enable GPIOA clock and configure PA5 as output
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (1U << (5 * 2));
while (1) {
GPIOA->ODR ^= (1U << 5); // Toggle PA5 LED
SysTick_Delay_ms(500); // Accurate 500 ms delay
}
}
7 23ECE313_ES_ Mazidi.pdf & STM32F446RE_Timers_PWM_ADC_Mazidi.pdf__WFI()) while TIM2 continuously outputs PWM on PA5.EXTI15_10_IRQHandler):
SWSTART).EOC flag is set in ADC1->SR.TIM2->CCR1.EXTI->PR = (1U << 13);.#include "stm32f4xx.h"
void System_Peripherals_Init(void) {
// 1. Enable Peripheral Clocks
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN; // GPIOA & GPIOC
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADC1EN; // SYSCFG & ADC1
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // TIM2
// 2. Configure PC13 as Digital Input with Pull-Up (Pushbutton)
GPIOC->MODER &= ~(3U << (13 * 2)); // Input mode ('00')
GPIOC->PUPDR &= ~(3U << (13 * 2));
GPIOC->PUPDR |= (1U << (13 * 2)); // Pull-Up ('01')
// 3. Configure PA0 as Analog Mode (ADC1_IN0)
GPIOA->MODER |= (3U << (0 * 2)); // Analog mode ('11')
// 4. Configure PA5 as Alternate Function AF1 (TIM2_CH1 PWM)
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (2U << (5 * 2)); // Alternate function ('10')
GPIOA->AFR[0] &= ~(0xFU << (5 * 4));
GPIOA->AFR[0] |= (1U << (5 * 4)); // AF1 = TIM2_CH1
// 5. Configure EXTI13 for PC13
SYSCFG->EXTICR[3] &= ~(0xFU << 4);
SYSCFG->EXTICR[3] |= (2U << 4); // Port C = 0x2
EXTI->FTSR |= (1U << 13); // Falling trigger
EXTI->IMR |= (1U << 13); // Unmask interrupt line 13
NVIC_SetPriority(EXTI15_10_IRQn, 2);
NVIC_EnableIRQ(EXTI15_10_IRQn);
// 6. Configure ADC1 (Channel 0, PA0)
ADC1->SMPR2 |= (4U << 0); // 84 cycles sample time
ADC1->SQR1 = 0; // 1 conversion
ADC1->SQR3 = 0; // Channel 0
ADC1->CR2 |= ADC_CR2_ADON; // Turn on ADC
// 7. Configure TIM2 for 1 kHz PWM with 12-bit range (ARR = 4095)
TIM2->PSC = 3; // 16 MHz / 4 = 4 MHz timer clock
TIM2->ARR = 4095; // Matches 12-bit ADC resolution!
TIM2->CCR1 = 2048; // Initial 50% duty cycle
TIM2->CCMR1 |= (6U << 4) | TIM_CCMR1_OC1PE; // PWM Mode 1
TIM2->CCER |= TIM_CCER_CC1E; // Enable channel 1 output
TIM2->CR1 |= TIM_CR1_CEN; // Start counter
}
// EXTI15_10 Interrupt Handler for Button Press
void EXTI15_10_IRQHandler(void) {
if (EXTI->PR & (1U << 13)) {
// Start ADC conversion on PA0
ADC1->CR2 |= ADC_CR2_SWSTART;
while (!(ADC1->SR & ADC_SR_EOC)); // Wait for conversion
uint16_t adc_val = ADC1->DR; // Read 12-bit analog value (0-4095)
// Dynamically update PWM duty cycle (matches ARR = 4095)
TIM2->CCR1 = adc_val;
// Clear interrupt pending bit (WRITE 1 TO CLEAR)
EXTI->PR = (1U << 13);
}
}
int main(void) {
System_Peripherals_Init();
while (1) {
__WFI(); // Sleep until button press interrupt arrives
}
}
Dedicated coverage of TM4C123 (Tiva C Series) Architecture, Register-Level Peripheral Programming, and In-Depth Fault Diagnosis. Click any question to reveal complete step-by-step solutions:
7 23ECE313_ES_ Mazidi.pdf (Chapter 1 & 2) / TM4C123GH6PM DatasheetIn TM4C123, all peripherals are powered down by default to conserve energy. Before accessing any peripheral register, software must explicitly gate the clock using the System Control Block (Base: 0x400FE000):
SYSCTL_RCGCGPIO_R (Offset 0x608): Clock gating for GPIO Ports A through F (Bit 0 = Port A, Bit 1 = Port B, ..., Bit 5 = Port F).SYSCTL_RCGCTIMER_R (Offset 0x604): Clock gating for 16/32-bit General Purpose Timer Modules (Timer 0 to Timer 5).SYSCTL_RCGCADC_R (Offset 0x638): Clock gating for ADC Modules (Bit 0 = ADC0, Bit 1 = ADC1).SYSCTL_RCGCPWM_R (Offset 0x640): Clock gating for PWM Modules 0 and 1.volatile uint32_t delay = SYSCTL_RCGCGPIO_R;) before touching any peripheral register, or the CPU will trigger an immediate BusFault!
7 23ECE313_ES_ Mazidi.pdf (Chapter 2, Section 2.3)Traditional microcontrollers require software read-modify-write sequences (PORT |= (1<<2)) to modify individual I/O pins, which creates severe race conditions when interrupts fire between the read and write. TM4C123 eliminates this by mapping address bus bits [9:2] directly as an 8-bit pin mask!
0x000 to 0x3FC).0x40025000 for Port F. Bits [9:2] of the address offset determine which of the 8 pins (PF7βPF0) can be read or written.(2 + n) is 1, then pin n is affected by the read/write. If bit (2 + n) is 0, pin n is hardware-protected and remains unmodified!0x0E (0b00001110)0x0E << 2 = 0x380x40025000 + 0x38 = 0x40025038GPIO_PORTF_DATA_BITS_R[0x0E] = 0x04; turns on ONLY the Blue LED (Pin 2) without modifying any other pin!
GPIODIR: 0 = Input, 1 = Output.GPIODEN: Digital Enable (1 = Digital I/O enabled; 0 = Disabled for analog mode).GPIOPUR / GPIOPDR: Internal Pull-Up / Pull-Down resistor enable.GPIOLOCK & GPIOCR: Used to unlock special pins (e.g., PF0 and PD7 which default to NMI/JTAG). Write 0x4C4F434B to GPIOLOCK, then set commit register GPIOCR to 0xFF.7 23ECE313_ES_ Mazidi.pdf (Chapter 8, ADC Programming)TM4C123 features two independent 12-bit Successive Approximation ADC modules (ADC0 and ADC1) with up to 12 shared analog input channels (AIN0 to AIN11) and sample rates up to 1 MSPS.
| Sequencer | FIFO Depth | Max Samples per Trigger | Typical Use Case |
|---|---|---|---|
| SS0 | 8 samples | 8 | Complex multi-sensor acquisition / sensor fusion |
| SS1 | 4 samples | 4 | Medium multichannel telemetry |
| SS2 | 4 samples | 4 | Multichannel acquisition with oversampling |
| SS3 | 1 sample | 1 | Single-channel conversion (e.g. potentiometer / temperature sensor) |
ADC0_ACTSS_R: Active Sample Sequencer. Clear bit 3 to disable SS3 during configuration, then set bit 3 to enable it afterwards.ADC0_EMUX_R: Event Multiplexer Select. Writing 0x0000 configures software trigger (Processor trigger) for SS3.ADC0_SSMUX3_R: Sample Sequencer 3 Input Multiplexer. Write channel number (e.g. 0 for AIN0 on PE3).ADC0_SSCTL3_R: Sample Sequencer 3 Control. Set bit 1 (END0) to mark it as the last sample, and bit 2 (IE0) to assert raw interrupt flag when conversion completes (Write 0x0006).ADC0_PSSI_R: Processor Sample Initiate. Write 0x0008 to start SS3 conversion by software.ADC0_RIS_R: Raw Interrupt Status. Poll Bit 3 to wait for conversion completion.ADC0_SSFIFO3_R: Read 12-bit result (0 to 4095).3 23ECE313_ES_Ch12_Txt1.pdf (Slides 16β22)0xE000ED28):The 32-bit CFSR register is physically partitioned into three sub-registers:
Bit: [31 ------------- 16] [15 -------------- 8] [7 ---------------- 0]
| UFSR | BFSR | MMFSR |
| (Usage Fault Stat) | (Bus Fault Status) | (MemManage Status) |
IACCVIOL (Bit 0): Instruction access violation (e.g. attempted execution from an XN region).DACCVIOL (Bit 1): Data access violation (e.g. unprivileged write to privileged region).MMARVALID (Bit 7): MemManage Fault Address Register (MMFAR) holds valid fault address.IBUSERR (Bit 8): Instruction bus error during fetch.PRECISERR (Bit 9): Precise data bus error (stacked PC is exact; BFAR is valid).IMPRECISERR (Bit 10): Imprecise data bus error from Write Buffer (stacked PC is delayed; BFAR invalid!).BFARVALID (Bit 15): Bus Fault Address Register (BFAR) contains valid fault address.UNDEFINSTR (Bit 16): Attempted execution of undefined/illegal opcode.INVSTATE (Bit 17): Illegal state transition (EPSR T-bit cleared to 0 by branch to even address).INVPC (Bit 18): Invalid PC load from illegal EXC_RETURN.UNALIGNED (Bit 24): Unaligned memory access (if trap enabled in CCR).DIVBYZERO (Bit 25): Divide-by-zero executed (if trap enabled in CCR).If a configurable fault occurs while its handler is disabled in SHCSR (System Handler Control and State Register), or if a fault occurs inside an existing fault handler of equal or higher priority, the processor escalates the exception to a HardFault. In HFSR, the FORCED bit (Bit 30) is set to indicate escalation!
3 23ECE313_ES_Ch12_Txt1.pdf (Slides 23β25)__asm void HardFault_Handler(void) {
TST LR, #4 // Test Bit 2 of EXC_RETURN (0 = MSP was used, 1 = PSP was used)
ITE EQ
MRSEQ R0, MSP // Stack pointer was MSP -> pass to C handler in R0
MRSNE R0, PSP // Stack pointer was PSP -> pass to C handler in R0
B HardFault_Handler_C // Branch to C diagnostic function
}
void HardFault_Handler_C(uint32_t *stack_frame) {
// Extract hardware stacked registers from stack pointer
uint32_t stacked_r0 = stack_frame[0];
uint32_t stacked_r1 = stack_frame[1];
uint32_t stacked_r2 = stack_frame[2];
uint32_t stacked_r3 = stack_frame[3];
uint32_t stacked_r12 = stack_frame[4];
uint32_t stacked_lr = stack_frame[5];
uint32_t stacked_pc = stack_frame[6]; // FAULTING INSTRUCTION ADDRESS!
uint32_t stacked_psr = stack_frame[7];
// Read System Control Block Fault Status Registers
uint32_t cfsr = SCB->CFSR; // Configurable Fault Status
uint32_t hfsr = SCB->HFSR; // HardFault Status
uint32_t bfar = SCB->BFAR; // Bus Fault Address (if BFARVALID is set)
uint32_t mmfar = SCB->MMFAR; // MemManage Fault Address (if MMARVALID is set)
// Check if HardFault was forced by an unhandled configurable fault
if (hfsr & (1U << 30)) {
// FORCED bit set: Escalated from BusFault, MemManage, or UsageFault
}
// Hang or initiate safe system reset
__disable_irq();
while (1); // Trap debugger here to inspect registers
}
3 23ECE313_ES_Ch12_Txt1.pdf (Slide 26)The Lockup state is a terminal fault condition in the ARM Cortex-M core that occurs when an exception/fault is triggered while the processor is already executing inside the HardFault Handler or NMI Handler (known as a Double Fault or recursive fault).
HardFault_Handler or NMI_Handler points to invalid memory or has its LSB cleared to 0 (violating Thumb state).0x00000000 or 0x00000004 returns a bus error during boot.LOCKUP.LOCKUP signal can be wired directly to an internal Watchdog Timer or System Reset Controller to force an automatic warm reboot.Lockup cannot be exited by software or regular interrupts. Recovery is only possible via a hardware Power-on Reset (POR), External Reset pin assertion (NRST), or a Watchdog Timer Reset.
7 23ECE313_ES_ Mazidi.pdf (Chapter 6, Interrupts)| Register | Name | Function & Bit Values |
|---|---|---|
GPIOIS |
Interrupt Sense | 0 = Edge-sensitive interrupt.1 = Level-sensitive interrupt. |
GPIOIBE |
Interrupt Both Edges | 0 = Interrupt generation controlled by GPIOIEV.1 = Both rising and falling edges trigger an interrupt. |
GPIOIEV |
Interrupt Event | 0 = Falling edge (or Low level).1 = Rising edge (or High level). |
GPIOIM |
Interrupt Mask | 0 = Interrupt masked (disabled from reaching NVIC).1 = Interrupt unmasked (sent to NVIC). |
GPIOICR |
Interrupt Clear | Write 1 to Clear: Software MUST write a 1 to the corresponding bit in GPIOICR inside the ISR to acknowledge and clear the interrupt flag! Writing 0 has no effect. |
GPIORIS (Raw Interrupt Status): Shows if an interrupt condition occurred on the pin, regardless of mask.GPIOMIS (Masked Interrupt Status): Shows if an active interrupt is currently sent to the NVIC (RIS AND IM).7 23ECE313_ES_ Mazidi.pdf (Chapter 2 & 3)#include
#include "tm4c123gh6pm.h"
void PortF_Init(void) {
// 1. Enable Clock for Port F in RCGCGPIO (Bit 5 = 1)
SYSCTL_RCGCGPIO_R |= 0x20;
// 2. Wait for clock to stabilize (3 cycles)
volatile uint32_t delay = SYSCTL_RCGCGPIO_R;
// 3. Unlock Port F commit register (Required for PF0, good practice)
GPIO_PORTF_LOCK_R = 0x4C4F434B; // Secret unlock key
GPIO_PORTF_CR_R |= 0x14; // Commit PF4 and PF2
// 4. Configure Directions: PF4 is Input (0), PF2 is Output (1)
GPIO_PORTF_DIR_R &= ~0x10; // Clear bit 4 (PF4 Input)
GPIO_PORTF_DIR_R |= 0x04; // Set bit 2 (PF2 Output)
// 5. Disable Analog Mode
GPIO_PORTF_AMSEL_R &= ~0x14;
// 6. Disable Alternate Functions (Regular GPIO)
GPIO_PORTF_AFSEL_R &= ~0x14;
// 7. Enable Internal Pull-Up Resistor on PF4 (Active-Low button)
GPIO_PORTF_PUR_R |= 0x10;
// 8. Enable Digital Functionality on PF4 and PF2
GPIO_PORTF_DEN_R |= 0x14;
}
int main(void) {
PortF_Init();
while (1) {
// Read PF4: If button is pressed (PF4 reads 0)
if ((GPIO_PORTF_DATA_R & 0x10) == 0) {
GPIO_PORTF_DATA_R |= 0x04; // Turn ON Blue LED
} else {
GPIO_PORTF_DATA_R &= ~0x04; // Turn OFF Blue LED
}
}
}
7 23ECE313_ES_ Mazidi.pdf (Chapters 8 & 11)0 to 4095.LOAD = 250 - 1 = 249 (gives 1 kHz PWM).CMPA = (ADC_Value Γ 249) / 4095 (Dynamically varies motor speed from 0% to 100%).#include
#include "tm4c123gh6pm.h"
void ADC0_SS3_Init(void) {
// 1. Enable Clocks for ADC0 and Port E
SYSCTL_RCGCADC_R |= 0x01;
SYSCTL_RCGCGPIO_R |= 0x10; // Port E
volatile uint32_t delay = SYSCTL_RCGCGPIO_R;
// 2. Configure PE3 as Analog Input (AIN0)
GPIO_PORTE_DIR_R &= ~0x08; // Input
GPIO_PORTE_AFSEL_R |= 0x08; // Enable Alternate Function
GPIO_PORTE_DEN_R &= ~0x08; // Disable Digital
GPIO_PORTE_AMSEL_R |= 0x08; // Enable Analog Mode
// 3. Configure Sample Sequencer 3
ADC0_ACTSS_R &= ~0x08; // Disable SS3 during setup
ADC0_EMUX_R &= ~0xF000; // Software trigger for SS3
ADC0_SSMUX3_R = 0; // Select Channel 0 (AIN0)
ADC0_SSCTL3_R = 0x0006; // Set IE0 and END0 (Bit 2 & Bit 1)
ADC0_ACTSS_R |= 0x08; // Enable SS3
}
uint16_t ADC0_Read(void) {
ADC0_PSSI_R = 0x08; // Start SS3 conversion
while ((ADC0_RIS_R & 0x08) == 0); // Wait for conversion completion
uint16_t result = ADC0_SSFIFO3_R; // Read 12-bit value
ADC0_ISC_R = 0x08; // Clear completion flag
return result;
}
void PWM0_PB6_Init(void) {
// 1. Enable Clocks for PWM0 and Port B
SYSCTL_RCGCPWM_R |= 0x01;
SYSCTL_RCGCGPIO_R |= 0x02; // Port B
volatile uint32_t delay = SYSCTL_RCGCGPIO_R;
// 2. Configure PB6 as Alternate Function M0PWM0
GPIO_PORTB_AFSEL_R |= 0x40;
GPIO_PORTB_PCTL_R &= ~0x0F000000;
GPIO_PORTB_PCTL_R |= 0x04000000; // AF4 = PWM
GPIO_PORTB_DEN_R |= 0x40;
// 3. Configure PWM Clock Divider (System Clock / 64)
SYSCTL_RCC_R |= (1 << 20); // Enable PWMDIV
SYSCTL_RCC_R &= ~0x000E0000; // Clear divider bits
SYSCTL_RCC_R |= 0x00060000; // Divide by 64 (250 kHz clock)
// 4. Configure PWM Generator 0
PWM0_0_CTL_R = 0; // Down-count mode
PWM0_0_LOAD_R = 249; // 250 kHz / 250 = 1 kHz PWM
PWM0_0_CMPA_R = 125; // Initial 50% duty cycle
PWM0_0_GENA_R = 0x8C; // High on LOAD, Low on CMPA
PWM0_0_CTL_R |= 0x01; // Enable Generator 0
// 5. Enable PWM Output on PB6
PWM0_ENABLE_R |= 0x01;
}
int main(void) {
ADC0_SS3_Init();
PWM0_PB6_Init();
while (1) {
uint16_t sensor_val = ADC0_Read(); // Read 0 to 4095
// Scale 12-bit ADC value to PWM LOAD (0 to 249)
uint32_t pwm_duty = (sensor_val * 249) / 4095;
if (pwm_duty == 0) pwm_duty = 1; // Prevent underflow
PWM0_0_CMPA_R = pwm_duty; // Dynamically adjust motor speed
}
}
In-Depth Coverage of FreeRTOS Real-Time Kernel, Task Management, Synchronization (Semaphores & Queues), and Advanced Memory Attributes:
lab/Exp 5 and 6.pdf / FreeRTOS Kernel Reference (Chapter 3) βββββββββββββββββ
β SUSPENDED β βββββββββββ vTaskSuspend()
βββββββββ²ββββββββ
β vTaskResume()
βΌ
ββββββββββΊ βββββββββββββββββ βββββββ Event occurs / Timeout
β β READY β
β Yield / βββββββββ¬ββββββββ
Preempt β TimeSlice β Scheduler selects highest priority
β βΌ
β βββββββββββββββββ
ββββββββββ β RUNNING β
βββββββββ¬ββββββββ
β Block for delay (vTaskDelay) or Semaphore/Queue
βΌ
βββββββββββββββββ
β BLOCKED β
βββββββββββββββββ
vTaskDelay(), or synchronization event like awaiting a Semaphore, Mutex, or Queue message). Blocked tasks consume zero CPU cycles.vTaskSuspend(). Remains suspended until explicitly awakened by vTaskResume().lab/Exp 5 and 6.pdf; Course Plan Lecture 20Each task created via xTaskCreate() has a dedicated TCB allocated in RAM containing:
pxTopOfStack: Pointer to the last memory location used on the task's private stack (MUST be the first member of TCB for assembly context switcher!).xStateListItem: Node reference placing the task in Ready, Blocked, or Suspended lists.uxPriority: Numerical priority assigned to the task (0 = Idle task, highest = configMAX_PRIORITIES - 1).pxStack: Pointer to the start of the allocated stack memory.pcTaskName: Text name for debugging.pxCurrentTCB->pxTopOfStack, updates pxCurrentTCB to the next task, restores R4βR11 from the new task's stack, and loads PSP with the new stack pointer!course_plan.txt (Lectures 20β22)| Scheduling Policy | Preemption Allowed? | Mechanism | Disadvantage |
|---|---|---|---|
| Cooperative | No | A task runs until it explicitly relinquishes CPU control via taskYIELD(). |
If a task hangs in an infinite loop, the entire OS crashes; poor responsiveness. |
| Preemptive Fixed-Priority | Yes | The highest priority Ready task ALWAYS runs immediately. A higher priority task preempts a lower priority task instantly upon becoming ready. | Lower priority tasks can suffer starvation if high-priority tasks never block. |
| Round-Robin with Time Slicing | Yes | Tasks of equal priority share CPU time equally, switching at every periodic SysTick quantum tick. | Context switching overhead increases if quantum tick is too short. |
The SysTick timer generates a periodic interrupt (typically configured for 1 ms: configTICK_RATE_HZ = 1000). In the SysTick ISR, FreeRTOS increments the global tick count (xTickCount), unblocks tasks whose delay timers have expired, and triggers a PendSV exception if a higher priority task is ready or if time slicing is needed.
lab/Exp 5 and 6.pdf; Course Plan Lecture 24Task High (Priority 3): ---------[ Wants Mutex: BLOCKED! ]----------------------βΊ [ Runs ]
Task Medium (Priority 2): -----------------[ Preempts Task Low! ]-----------------βΊ
Task Low (Priority 1): --[ Takes Mutex ]-----------------------[ Releases Mutex ]βΊ
Time: T1 T2 T3 T4
When Task High blocks on the mutex, FreeRTOS automatically raises Task Low's effective priority to match Task High's priority! Now, Task Medium cannot preempt Task Low. Task Low runs to completion, releases the mutex, its priority drops back to normal, and Task High immediately acquires the mutex and runs!
lab/Exp 5 and 6.pdf; Course Plan Lecture 25// 1. Create a queue of 10 elements, each holding a uint16_t ADC reading
QueueHandle_t xAdcQueue = xQueueCreate(10, sizeof(uint16_t));
// 2. Task writes to queue (blocks for up to 100 ticks if queue is full)
xQueueSend(xAdcQueue, &adc_val, pdMS_TO_TICKS(100));
// 3. Task reads from queue (blocks indefinitely until data arrives)
uint16_t rx_data;
xQueueReceive(xAdcQueue, &rx_data, portMAX_DELAY);
Regular xQueueSend() can block the caller if the queue is full. In Cortex-M, an ISR CANNOT BLOCK (an ISR has no task context and no private stack to sleep on). The FromISR variant guarantees:
BaseType_t *pxHigherPriorityTaskWoken. If sending the message unblocks a higher priority task, the ISR calls portYIELD_FROM_ISR() to trigger an immediate context switch upon ISR return!2 23ECE313_ES_Ch_Txt1.pdf (Slide 13)| Memory Type | Buffering / Caching | Access Ordering | Typical Mapped Regions |
|---|---|---|---|
| Normal Memory | Bufferable and Cacheable allowed. | Out-of-order execution and speculative reads permitted. | Internal Flash (Code) and Internal SRAM (Data). |
| Device Memory | Non-cacheable. May be bufferable for writes. | Preserves access ordering; no speculative reads. | On-chip Peripherals (0x40000000 - 0x5FFFFFFF). |
| Strongly Ordered | Non-cacheable, Non-bufferable. | Strict hardware serialization: CPU halts until write transaction completes on physical bus! | System Control Space (PPB: 0xE0000000 - 0xE00FFFFF). |
TEX[2:0] (Type Extension): Selects cache policy (Write-Through, Write-Back, Allocate on Write).C (Cacheable): Enables data/instruction caching for the region.B (Bufferable): Permits internal write buffers to absorb writes, allowing the CPU to continue executing without bus stalls.5 23ECE313_ES_Ch15_Txt 1a.pdf (Slides 4β7)The DAP provides the physical hardware bridge between external debug probes (ST-Link, J-Link, Keil ULINK) and the internal microcontroller buses:
[ External Debugger ] βββΊ [ SWJ-DP ] βββ¬βββΊ [ AHB-AP ] βββΊ Memory Bus Matrix (SRAM/Flash/Peripherals)
ββββΊ [ APB-AP ] βββΊ Core Debug Space (DWT, ITM, FPB, ETM)
0xE79E) over the wire to switch the pins into 2-pin SWD mode!A standardized memory table located in PPB space that lists the exact base addresses of all CoreSight components implemented on that specific silicon chip. When a debugger connects, it reads the ROM Table to automatically auto-configure its trace and breakpoint capabilities without requiring chip-specific hardcoded drivers!
lab/Exp 5 and 6.txt (Lab Experiment 5)#include "stm32f4xx.h"
#include "cmsis_os2.h"
// Task Thread Identifiers
osThreadId_t LedTaskHandle;
osThreadId_t UartTaskHandle;
// Hardware Initialization
void Hardware_Init(void) {
// 1. Configure PA5 as Output (LED)
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER &= ~(3U << (5 * 2));
GPIOA->MODER |= (1U << (5 * 2));
// 2. Configure PA2 as Alternate Function AF7 for USART2 TX
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
GPIOA->MODER &= ~(3U << (2 * 2));
GPIOA->MODER |= (2U << (2 * 2));
GPIOA->AFR[0] |= (7U << (2 * 4));
USART2->BRR = 0x008B; // 115200 Baud @ 16 MHz
USART2->CR1 |= USART_CR1_TE | USART_CR1_UE;
}
void UART2_Send(char *str) {
while (*str) {
while (!(USART2->SR & USART_SR_TXE));
USART2->DR = (*str++ & 0xFF);
}
}
// Task 1: Blinks LED every 500 ms
void LedTask(void *argument) {
while (1) {
GPIOA->ODR ^= (1U << 5); // Toggle PA5
osDelay(500); // Non-blocking RTOS delay
}
}
// Task 2: Transmits telemetry string every 1000 ms
void UartTask(void *argument) {
while (1) {
UART2_Send("AMRITA 23ECE313 RTOS RUNNING
");
osDelay(1000); // Non-blocking RTOS delay
}
}
int main(void) {
Hardware_Init();
// Initialize CMSIS-RTOS2 Kernel
osKernelInitialize();
// Create Tasks with Normal Priority
LedTaskHandle = osThreadNew(LedTask, NULL, NULL);
UartTaskHandle = osThreadNew(UartTask, NULL, NULL);
// Start Real-Time Multitasking Scheduler
osKernelStart();
while (1); // Never reached
}
lab/Exp 5 and 6.pdf; Lab Session 6#include "stm32f4xx.h"
#include "cmsis_os2.h"
#include
// Data structure passed through the queue
typedef struct {
uint16_t adc_value;
uint32_t timestamp_ms;
} SensorMsg_t;
// OS Identifiers
osMessageQueueId_t sensorQueueHandle;
osThreadId_t producerTaskHandle;
osThreadId_t consumerTaskHandle;
void Peripherals_Init(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// PA0 Analog (ADC1_IN0)
GPIOA->MODER |= (3U << 0);
ADC1->SMPR2 |= (4U << 0); // 84 cycles sample time
ADC1->SQR3 = 0; // Channel 0
ADC1->CR2 |= ADC_CR2_ADON;
// PA5 Output (Relay / Fan)
GPIOA->MODER &= ~(3U << 10);
GPIOA->MODER |= (1U << 10);
// PA2 Alternate Function AF7 (USART2 TX)
GPIOA->MODER &= ~(3U << 4);
GPIOA->MODER |= (2U << 4);
GPIOA->AFR[0] |= (7U << 8);
USART2->BRR = 0x008B; // 115200 baud
USART2->CR1 |= USART_CR1_TE | USART_CR1_UE;
}
uint16_t ADC_Read_Sample(void) {
ADC1->CR2 |= ADC_CR2_SWSTART;
while (!(ADC1->SR & ADC_SR_EOC));
return ADC1->DR;
}
void UART2_Send_Text(char *buf) {
while (*buf) {
while (!(USART2->SR & USART_SR_TXE));
USART2->DR = (*buf++ & 0xFF);
}
}
// Producer Task: Reads ADC and posts to queue
void ProducerTask(void *arg) {
SensorMsg_t msg;
while (1) {
msg.adc_value = ADC_Read_Sample();
msg.timestamp_ms = osKernelGetTickCount();
// Push to message queue with 10 ms timeout
osMessageQueuePut(sensorQueueHandle, &msg, 0, 10);
osDelay(100); // Sample rate: 10 Hz
}
}
// Consumer Task: Blocks on queue, processes data
void ConsumerTask(void *arg) {
SensorMsg_t rxMsg;
char log_buf[64];
while (1) {
// Block indefinitely until a message arrives
if (osMessageQueueGet(sensorQueueHandle, &rxMsg, NULL, osWaitForever) == osOK) {
// Threshold: 2.0V -> Digital 2482
if (rxMsg.adc_value > 2482) {
GPIOA->ODR |= (1U << 5); // Energize Relay
snprintf(log_buf, sizeof(log_buf), "[%lu ms] ALARM: ADC=%u (OVER 2.0V)
",
rxMsg.timestamp_ms, rxMsg.adc_value);
} else {
GPIOA->ODR &= ~(1U << 5); // De-energize Relay
snprintf(log_buf, sizeof(log_buf), "[%lu ms] NORMAL: ADC=%u
",
rxMsg.timestamp_ms, rxMsg.adc_value);
}
UART2_Send_Text(log_buf);
}
}
}
int main(void) {
Peripherals_Init();
osKernelInitialize();
// Create Message Queue: 8 elements of SensorMsg_t
sensorQueueHandle = osMessageQueueNew(8, sizeof(SensorMsg_t), NULL);
// Create Tasks
producerTaskHandle = osThreadNew(ProducerTask, NULL, NULL);
consumerTaskHandle = osThreadNew(ConsumerTask, NULL, NULL);
osKernelStart();
while (1);
}
Final Comprehensive Mock Midterm Exam synthesizing the entire syllabus: Unit 1 Foundations, Unit 2 OS Architecture, Communication Protocols (SPI/I2C/RTC), and Integrated Multi-Peripheral Hardware:
1 23ECE313_ES_Ch4 Text 1.pdf (Slides 5β10)| Parameter | RISC (e.g. ARM Cortex-M) | CISC (e.g. x86) |
|---|---|---|
| Instruction Set | Small, uniform length (16/32-bit Thumb-2). Single-cycle execution for most instructions. | Large, highly variable instruction length (1 to 15 bytes). Multi-cycle execution. |
| Memory Access | Strict Load/Store architecture (ALU operates ONLY on registers). | Instructions can operate directly on memory operands (e.g. ADD [mem], eax). |
| Hardware Complexity | Simple hardwired control unit, lower silicon area, low power consumption. | Complex microcoded control unit, higher silicon area, higher power consumption. |
| Feature | Von Neumann Architecture | Harvard Architecture (Cortex-M) |
|---|---|---|
| Bus Structure | Unified single bus for both instructions and data. | Physically separate buses for instruction (I-Code) and data (D-Code / System Bus). |
| Throughput / Bottleneck | Suffers from the Von Neumann Bottleneck: CPU cannot fetch code and data simultaneously. | Zero bus contention: CPU can fetch the next instruction while reading/writing data in the same clock cycle! |
2 23ECE313_ES_Ch_Txt1.pdf (Slides 22β24)| Register Group | Array Size | Operational Behavior (Write 1 vs Write 0) |
|---|---|---|
NVIC->ISER[8] |
Interrupt Set-Enable | Writing 1 enables the corresponding IRQ. Writing 0 has NO effect (prevents race conditions!). |
NVIC->ICER[8] |
Interrupt Clear-Enable | Writing 1 disables the corresponding IRQ. Writing 0 has NO effect. |
NVIC->ISPR[8] |
Interrupt Set-Pending | Writing 1 forces the corresponding IRQ into the pending state by software. |
NVIC->ICPR[8] |
Interrupt Clear-Pending | Writing 1 clears the pending status of an unserviced IRQ. |
NVIC->IABR[8] |
Interrupt Active Bit | Read-Only: Bit is 1 while the corresponding ISR is currently executing on the CPU. |
NVIC->IPR[60] |
Interrupt Priority | Byte-accessible registers holding 8-bit priority values for each external IRQ (upper 4 bits used in STM32). |
NVIC->STIR |
Software Trigger Interrupt | Allows software to trigger an external IRQ by writing the IRQ number into bits [8:0]. Can be enabled for unprivileged user code via SCB->CCR (USERSETMPEND). |
STM32F446RE_Timers_PWM_ADC_Mazidi.pdf (Slides 4β7); lab/23ECE387 ES Lab exp 2.txtThe timer counter frequency is derived by dividing the input peripheral clock by (PSC + 1):
f_CNT = f_CLK / (PSC + 1)T_CNT = 1 / f_CNT = (PSC + 1) / f_CLKARR, requiring (ARR + 1) clock counts to generate an update event (overflow):Total Delay (Period) = ((PSC + 1) Γ (ARR + 1)) / f_CLK
f_CLK = 16,000,000 Hz, Target Time = 0.1 s.PSC = 1599 (Prescaler divides clock by 1599 + 1 = 1600).f_CNT = 16,000,000 / 1600 = 10,000 Hz (10 kHz). Each tick = 0.1 ms.(ARR + 1) = Target Time Γ f_CNT = 0.1 s Γ 10,000 = 1000.ARR = 1000 - 1 = 999.((1599 + 1) Γ (999 + 1)) / 16,000,000 = (1600 Γ 1000) / 16,000,000 = 0.1 s = 100 ms. Exactly matches!f_CLK = 84 MHz = 84,000,000 Hz, Target f_PWM = 2,500 Hz.PSC = 0 (Counter runs at full 84 MHz).(ARR + 1) = 84,000,000 / 2,500 = 33,600.ARR = 33,600 - 1 = 33,599.CCR1 = 0.60 Γ (ARR + 1) = 0.60 Γ 33,600 = 20,160.course_plan.txt (Lectures 30β33)| Characteristic | SPI (Serial Peripheral Interface) | I2C (Inter-Integrated Circuit) |
|---|---|---|
| Number of Lines | 4 wires: MOSI, MISO, SCK, CS/SS. | 2 wires: SDA (Serial Data), SCL (Serial Clock). |
| Bus Topology | Point-to-point or daisy-chain (requires 1 CS pin per slave). | Multi-master, multi-drop bus (7-bit or 10-bit software addressing). |
| Duplex & Speed | Full-duplex; very high speed (10 Mbps to 50+ Mbps). | Half-duplex; standard speed (100 kbps, 400 kbps, 1 Mbps). |
| Electrical Wiring | Push-pull outputs. | Open-drain with external pull-up resistors (typically 4.7 kΞ©). |
| Flow Control / ACK | No hardware acknowledgement or flow control. | Every byte followed by mandatory ACK/NACK bit; Clock Stretching supported. |
CPOL = 0: SCK idles LOW; CPOL = 1: SCK idles HIGH.CPHA = 0: Data sampled on the first clock edge; CPHA = 1: Data sampled on the second clock edge.lab/4th exp.txt (Lab Experiment 4)The RTC operates in the Backup Power Domain, powered by V_BAT during main power loss. It uses a dedicated 32.768 kHz Low Speed External (LSE) crystal oscillator.
PREDIV_A = 127 (Divides by 128: 32,768 / 128 = 256 Hz)PREDIV_S = 255 (Divides by 256: 256 / 256 = 1 Hz)1 Hz Clock Output drives seconds counter!
In STM32 RTC, time and date registers store values in BCD format (each decimal digit 0β9 occupies 4 bits). For example, 59 seconds is stored as 0x59 (not 0x3B!).
// Convert standard integer to hardware BCD format
#define BYTE_TO_BCD(val) ((uint8_t)((((val) / 10) << 4) | ((val) % 10)))
// Convert hardware BCD format to standard integer
#define BCD_TO_BYTE(val) (((val) >> 4) * 10 + ((val) & 0x0F))
5 23ECE313_ES_Ch15_Txt 1a.pdf (Slides 15β16)| Feature | Halt Debug Mode | Debug Monitor Mode |
|---|---|---|
| Core Behavior on Breakpoint | The processor core clock is completely stopped. Pipeline and ALU freeze. | The processor remains running and executes an exception handler (DebugMonitor_Handler) at priority 12. |
| Interrupt Servicing | All interrupts are frozen and ignored. Real-time peripherals halt. | Higher-priority interrupts continue executing normally! Only equal or lower priority interrupts are delayed. |
| External Tool Requirement | Requires a hardware debug probe connected via JTAG/SWD pins. | Can operate without a hardware probe by transmitting debug data over a standard serial port (UART/USB). |
In applications such as electric vehicle motor controllers, unmanned aerial vehicles (drones), or medical ventilators, halting the CPU for even 10 milliseconds causes disastrous hardware failure (e.g. motor MOSFET bridge shoot-through without active PWM commutation, aircraft loss of altitude, or patient hypoxia). Debug Monitor Mode allows developers to inspect application threads while safety-critical emergency ISRs continue active closed-loop control!
4 23ECE313_ES_Ch14_Txt 1a.pdf (Slides 12β14)TXEV output pin to signal partner cores, and sets the local Event Register to 1.1, the instruction clears the Event Register to 0 and continues execution immediately without sleeping.0, the CPU enters low-power sleep mode and halts until an event signal arrives on the RXEV pin or an interrupt occurs!Eliminates bus thrashing and wasteful spinning locks (while(flag == 0);). Slave processors sleep at microamp currents until work is dispatched by the master via SEV!
lab_plan.txt (Lab Session 3b)0 (Write to DAC register).0 (Input buffer disabled).1 (Output gain = 1x).1 (Active mode / output enabled).0x3000. Packet = 0x3000 | (val & 0x0FFF).#include "stm32f4xx.h"
void SPI1_Init(void) {
// 1. Enable Clocks: GPIOA on AHB1, SPI1 on APB2
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
// 2. Configure PA5 (SCK) and PA7 (MOSI) as Alternate Function AF5
GPIOA->MODER &= ~((3U << 10) | (3U << 14));
GPIOA->MODER |= ((2U << 10) | (2U << 14));
GPIOA->AFR[0] |= (5U << 20) | (5U << 28); // AF5 = SPI1
// 3. Configure PA4 as General Purpose Output for CS
GPIOA->MODER &= ~(3U << 8);
GPIOA->MODER |= (1U << 8);
GPIOA->ODR |= (1U << 4); // Deselect DAC (CS High)
// 4. Configure SPI1: Master mode, Baud = fPCLK/8, 16-bit data, Mode 0 (CPOL=0, CPHA=0)
SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_1 | SPI_CR1_DFF | SPI_CR1_SSM | SPI_CR1_SSI;
SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI1
}
void DAC_Write(uint16_t val) {
uint16_t packet = 0x3000 | (val & 0x0FFF); // 12-bit data with 0x3 control nibble
GPIOA->ODR &= ~(1U << 4); // Select DAC (CS Low)
while (!(SPI1->SR & SPI_SR_TXE)); // Wait until Transmit Buffer empty
SPI1->DR = packet; // Transmit 16-bit word
while (SPI1->SR & SPI_SR_BSY); // Wait until transfer completes
GPIOA->ODR |= (1U << 4); // Deselect DAC (CS High)
}
int main(void) {
SPI1_Init();
uint16_t ramp = 0;
while (1) {
DAC_Write(ramp); // Generate Sawtooth wave
ramp = (ramp + 64) % 4096;
for (volatile int i = 0; i < 100; i++); // Adjust frequency
}
}
lab_plan.txt (Term Project System Level Design)__WFI()).EXTI15_10_IRQHandler."ALARM! HIGH VOLTAGE EXCEEDED
".EXTI->PR = (1U << 13);.#include "stm32f4xx.h"
void System_Init(void) {
// 1. Enable Peripheral Clocks
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN; // GPIOA & GPIOC
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADC1EN; // SYSCFG & ADC1
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_USART2EN; // TIM2 & USART2
// 2. Configure PC13 Pushbutton (Input with Pull-Up)
GPIOC->MODER &= ~(3U << (13 * 2));
GPIOC->PUPDR |= (1U << (13 * 2)); // Pull-Up
// 3. Configure EXTI13
SYSCFG->EXTICR[3] &= ~(0xFU << 4);
SYSCFG->EXTICR[3] |= (2U << 4); // Port C
EXTI->FTSR |= (1U << 13); // Falling edge trigger
EXTI->IMR |= (1U << 13); // Unmask EXTI13
NVIC_EnableIRQ(EXTI15_10_IRQn);
// 4. Configure PA0 as Analog (ADC1_IN0)
GPIOA->MODER |= (3U << 0);
ADC1->SMPR2 |= (4U << 0); // 84 cycles sample time
ADC1->SQR3 = 0; // Channel 0
ADC1->CR2 |= ADC_CR2_ADON;
// 5. Configure PA5 as AF1 (TIM2_CH1 PWM Alarm)
GPIOA->MODER &= ~(3U << 10);
GPIOA->MODER |= (2U << 10);
GPIOA->AFR[0] |= (1U << 20); // AF1 = TIM2
TIM2->PSC = 15; // 16 MHz / 16 = 1 MHz timer clock
TIM2->ARR = 499; // 1 MHz / 500 = 2 kHz audible frequency
TIM2->CCR1 = 0; // Start with 0% duty (Off)
TIM2->CCMR1 |= (6U << 4); // PWM Mode 1
TIM2->CCER |= TIM_CCER_CC1E;
TIM2->CR1 |= TIM_CR1_CEN;
// 6. Configure USART2 (115200 Baud)
GPIOA->MODER &= ~(3U << 4);
GPIOA->MODER |= (2U << 4);
GPIOA->AFR[0] |= (7U << 8); // AF7 = USART2
USART2->BRR = 0x008B;
USART2->CR1 |= USART_CR1_TE | USART_CR1_UE;
}
void UART2_Print(char *str) {
while (*str) {
while (!(USART2->SR & USART_SR_TXE));
USART2->DR = (*str++ & 0xFF);
}
}
// EXTI Pushbutton ISR
void EXTI15_10_IRQHandler(void) {
if (EXTI->PR & (1U << 13)) {
// Read Analog Sensor
ADC1->CR2 |= ADC_CR2_SWSTART;
while (!(ADC1->SR & ADC_SR_EOC));
uint16_t adc = ADC1->DR;
// Threshold check (> 2.0V)
if (adc > 2482) {
TIM2->CCR1 = 250; // 50% duty cycle: Sound 2 kHz alarm!
UART2_Print("[SECURITY ALERT] Critical Sensor Limit Exceeded!
");
} else {
TIM2->CCR1 = 0; // Turn off alarm
UART2_Print("[NORMAL] Sensor in safe operational range.
");
}
// Clear Pending Bit (Write 1 to clear)
EXTI->PR = (1U << 13);
}
}
int main(void) {
System_Init();
while (1) {
__WFI(); // Low-power sleep until button interrupt
}
}
Professor Giriraja C. V. prioritizes neat, properly labeled diagrams. Master every essential visual representation directly extracted from the course slides:
Key Exam Notes: Shows transitions between Thread Mode (Privileged/Unprivileged) and Handler Mode (Always Privileged). Reset enters Thread Privileged. Exceptions force transition to Handler Mode. Exit via EXC_RETURN.
Key Exam Notes: Bit 0 = nPRIV (0: Privileged, 1: Unprivileged). Bit 1 = SPSEL (0: MSP, 1: PSP in Thread mode). Handler mode always forces MSP.
Key Exam Notes: SRAM: 0x20000000 (1MB) mapped to Alias 0x22000000 (32MB). Peripheral: 0x40000000 (1MB) mapped to Alias 0x42000000 (32MB). Formula: Alias = Base + (ByteOffset * 32) + (Bit * 4).
Key Exam Notes: I-Code (Instruction fetch from Flash), D-Code (Literal pool data from Flash), System Bus (SRAM & Peripherals). Harvard architecture allows concurrent code and data transfers.
Key Exam Notes: Step 1: Read address 0x00000000 into MSP. Step 2: Read address 0x00000004 into PC (Reset Handler address). Step 3: Branch to Reset Handler. Bit 0 of PC must be 1 (Thumb state).
Key Exam Notes: Visual memory layout of initial MSP (e.g. 0x20008000 top of SRAM) and Reset Handler vector with LSB=1 (e.g. 0x08000101 for code at 0x08000100).
Key Exam Notes: Shows how PRIGROUP bits [10:8] split 8-bit priority into Preempt Priority (determines nesting) and Sub-Priority (determines pending resolution when preemption is equal).
Key Exam Notes: Timing diagram showing interrupt pulse latching into pending state, transition from Pending to Active upon ISR entry, and clearing of pending bit.
Key Exam Notes: Without PendSV, SysTick preempting an external IRQ causes delayed IRQ completion. PendSV (lowest priority) defers context switching until all active IRQs complete!
Key Exam Notes: 8 words pushed by hardware: R0, R1, R2, R3, R12, LR, Return PC, xPSR. SVC immediate extracted from ((uint8_t*)stacked_pc)[-2].
Key Exam Notes: Core clock gated, PLL disabled. WIC stays powered, detects external edge, signals PMU to restore high-speed regulator and clocks, and transfers control to NVIC.
Key Exam Notes: DWT (Data watchpoints & cycle counting), ITM (printf via SWO pin), ETM (Instruction trace), TPIU (Trace packet serializer). Differentiates intrusive vs non-intrusive debug.
Reviewed and audited by Senior Embedded Systems Faculty. These are the subtle technical corner cases where 90% of students lose marks:
Question: "Calculate the vector table size and alignment for 75 external interrupts."
Total Vectors = 16 (Core) + 75 (External) = 91 words.
Table Size = 91 Γ 4 = 364 bytes.
Alignment Rule: ARMv7-M VTOR requires alignment to the next power of 2 β₯ size.
Question: "Why is BFAR not always valid during a BusFault?"
BFSR->BFARVALID = 0 (Fault address in BFAR is completely invalid).SCB->ACTLR |= SCB_ACTLR_DISDEFWBUF_Msk to disable write buffer. This forces precise faults where BFARVALID = 1!Question: "Explain how Cortex-M returns from an exception handler."
LR on exception entry. You MUST memorize these three:
0xFFFFFFF9: Return to Thread Mode using MSP (Main Stack Pointer).0xFFFFFFFD: Return to Thread Mode using PSP (Process Stack Pointer).0xFFFFFFE9: Return to Handler Mode using MSP (Nested exception return).Question: "Why does SP decrement by 0x24 (36 bytes) instead of 0x20 (32 bytes) during an interrupt?"
32 + 4 = 36 bytes (0x24)! Controlled by CCR->STKALIGN. Bit 9 of stacked xPSR records if padding was added.Question: "What happens if a function pointer points to address 0x08002000?"
UsageFault.0x08002001).Coding Trap: In EXTI15_10_IRQHandler, how do you clear the interrupt?
EXTI->PR &= ~(1U << 13); (Fails to clear! Writing 0 does nothing!).EXTI->PR = (1U << 13); (Must write a 1 to clear pending status!).Circuit Design Trap: Whenever the question asks to interface a Relay or DC Motor:
Coding Trap: Always enable clocks in RCC->AHB1ENR, RCC->APB1ENR, or RCC->APB2ENR before touching any peripheral register. Touching a peripheral register without its clock enabled triggers an immediate BusFault!