4. LED驱动基础实现:单LED点亮与闪烁、使用SysTick定时器实现非阻塞延时、多LED流水灯实现

4.1 单LED点亮:GPIO输出模式配置

在XMC系列MCU中,控制LED的第一步是将对应的GPIO引脚配置为推挽输出(Push-Pull Output)模式。以XMC1300系列为例,LED通常连接在P1.0引脚(高电平点亮)。

配置步骤 寄存器/函数 说明
1. 使能端口时钟 SCU_CLK->CLKCR 设置对应端口位时钟使能
2. 配置引脚模式 P1_IOCR0 (P1.0) 写入 0x80 为推挽输出
3. 设置输出电平 P1_OUT 写1点亮,写0熄灭
// 单LED点亮示例:XMC1300 P1.0
#include "xmc_gpio.h"

#define LED_PIN P1_0

void LED_Init(void) {
    // 配置P1.0为推挽输出,初始低电平
    XMC_GPIO_SetMode(LED_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
    XMC_GPIO_SetOutputLow(LED_PIN);
}

void LED_On(void) {
    XMC_GPIO_SetOutputHigh(LED_PIN);
}

void LED_Off(void) {
    XMC_GPIO_SetOutputLow(LED_PIN);
}

int main(void) {
    LED_Init();
    LED_On();  // 点亮LED
    while(1);
}
硬件确认: 实际开发中务必核对原理图,确认LED是高电平点亮还是低电平点亮。若为低电平点亮,则需配置为XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2或使用SetOutputLow/SetOutputHigh反向操作。

4.2 使用SysTick定时器实现非阻塞延时

传统的for循环阻塞延时浪费CPU资源,且无法处理其他任务。XMC系列MCU内置SysTick定时器(24位递减计数器),非常适合生成精确的非阻塞延时。

4.2.1 SysTick工作原理

SysTick非阻塞延时工作流程 SysTick_Config(COUNT) 每1ms中断一次 全局变量 tick++ uint32_t GetTick(void) while(GetTick() < target) 非阻塞轮询 CPU在while循环中可执行其他任务,不浪费等待时间 实现“延时同时做其他事”的低功耗/多任务基础

4.2.2 非阻塞延时实现代码

// SysTick非阻塞延时驱动
#include "xmc_scu.h"

static volatile uint32_t sysTickCounter = 0;

// SysTick中断服务函数(1ms周期)
void SysTick_Handler(void) {
    sysTickCounter++;
}

// 初始化SysTick,产生1ms中断
void SysTick_Init(void) {
    // 系统时钟频率 32MHz,重装载值 32000-1 = 1ms
    SysTick_Config(SystemCoreClock / 1000);
}

// 获取当前tick值
uint32_t GetTick(void) {
    return sysTickCounter;
}

// 非阻塞延时函数
void Delay_NonBlocking(uint32_t ms) {
    uint32_t start = GetTick();
    while ((GetTick() - start) < ms) {
        // 此处可插入其他任务,如按键扫描、通信处理等
        // 实现真正的非阻塞
    }
}

// 使用示例:LED闪烁(非阻塞)
void LED_Blink_NonBlocking(void) {
    static uint32_t lastTick = 0;
    uint32_t now = GetTick();

    if ((now - lastTick) >= 500) {  // 500ms切换一次
        lastTick = now;
        XMC_GPIO_ToggleOutput(LED_PIN);
    }
}
关键优势: 非阻塞延时函数Delay_NonBlocking在等待期间CPU可以执行其他任务,而LED_Blink_NonBlocking函数则完全避免了while等待,适合在超级循环(super loop)中周期调用。

4.3 多LED流水灯实现

流水灯是嵌入式入门的经典实验,通过依次点亮多个LED形成“流水”效果。在XMC系列上,我们可以利用位带操作(Bit-Banding)或直接寄存器赋值来高效控制多个引脚。

4.3.1 硬件连接与引脚定义

LED编号 MCU引脚 端口寄存器位
LED1 P1.0 P1_OUT bit 0
LED2 P1.1 P1_OUT bit 1
LED3 P1.2 P1_OUT bit 2
LED4 P1.3 P1_OUT bit 3

4.3.2 流水灯状态机设计

流水灯状态转移图(4个LED) S0: 0b0001 S1: 0b0010 S2: 0b0100 S3: 0b1000 每个状态持续200ms(非阻塞延时),然后转移到下一个状态 状态值直接写入P1_OUT寄存器,实现并行更新

4.3.3 流水灯完整代码

// 4个LED流水灯实现(非阻塞)
#include "xmc_gpio.h"

#define LED_PORT       P1
#define LED1_PIN       P1_0
#define LED2_PIN       P1_1
#define LED3_PIN       P1_2
#define LED4_PIN       P1_3

// 流水灯状态表:依次点亮LED1~LED4
const uint8_t flowPattern[] = {
    0x01,  // 0b0001 -> LED1亮
    0x02,  // 0b0010 -> LED2亮
    0x04,  // 0b0100 -> LED3亮
    0x08   // 0b1000 -> LED4亮
};

void LED_Flow_Init(void) {
    // 配置4个引脚为推挽输出
    XMC_GPIO_SetMode(LED1_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
    XMC_GPIO_SetMode(LED2_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
    XMC_GPIO_SetMode(LED3_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);
    XMC_GPIO_SetMode(LED4_PIN, XMC_GPIO_MODE_OUTPUT_PUSH_PULL);

    // 初始全部熄灭
    LED_PORT->OUT = 0x00;
}

void LED_Flow_Run(void) {
    static uint8_t index = 0;
    static uint32_t lastTick = 0;
    uint32_t now = GetTick();

    // 每200ms切换一次流水灯状态
    if ((now - lastTick) >= 200) {
        lastTick = now;

        // 直接写入端口输出寄存器,实现并行更新
        LED_PORT->OUT = flowPattern[index];

        // 索引循环
        index++;
        if (index >= 4) {
            index = 0;
        }
    }
}

int main(void) {
    SysTick_Init();      // 初始化SysTick
    LED_Flow_Init();     // 初始化LED

    while(1) {
        LED_Flow_Run();  // 非阻塞流水灯
        // 此处可添加其他任务,如按键扫描
    }
}
注意: 直接操作LED_PORT->OUT寄存器时,务必确保只修改低4位(bit0~bit3),避免影响同一端口其他引脚的状态。若需保留其他位,应使用读-改-写操作:LED_PORT->OUT = (LED_PORT->OUT & 0xF0) | flowPattern[index];

4.4 三种实现方式对比

实现方式 CPU占用 实时性 可扩展性 推荐场景
阻塞延时(for循环) 100%占用 差 低 仅用于极简测试
SysTick非阻塞延时 仅中断占用 良好 高 多任务、低功耗系统
硬件定时器+PWM 几乎为0 优秀 中 固定频率闪烁/呼吸灯